aicodeman 1.34.0 → 1.40.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 (299) hide show
  1. package/README.md +11 -0
  2. package/README.zh-CN.md +8 -0
  3. package/dist/attachment-registry.d.ts +6 -0
  4. package/dist/attachment-registry.d.ts.map +1 -1
  5. package/dist/attachment-registry.js +9 -3
  6. package/dist/attachment-registry.js.map +1 -1
  7. package/dist/cli.d.ts.map +1 -1
  8. package/dist/cli.js +2 -2
  9. package/dist/cli.js.map +1 -1
  10. package/dist/config/cli-registry/patterns.d.ts +6 -0
  11. package/dist/config/cli-registry/patterns.d.ts.map +1 -1
  12. package/dist/config/cli-registry/patterns.js +13 -0
  13. package/dist/config/cli-registry/patterns.js.map +1 -1
  14. package/dist/config/cli-registry/schema.d.ts +13 -0
  15. package/dist/config/cli-registry/schema.d.ts.map +1 -1
  16. package/dist/config/cli-registry/schema.js +60 -2
  17. package/dist/config/cli-registry/schema.js.map +1 -1
  18. package/dist/config/cli-registry/stock.d.ts.map +1 -1
  19. package/dist/config/cli-registry/stock.js +175 -7
  20. package/dist/config/cli-registry/stock.js.map +1 -1
  21. package/dist/config/cli-registry/types.d.ts +93 -6
  22. package/dist/config/cli-registry/types.d.ts.map +1 -1
  23. package/dist/config/dependency-registry.d.ts +7 -0
  24. package/dist/config/dependency-registry.d.ts.map +1 -1
  25. package/dist/config/dependency-registry.js +15 -0
  26. package/dist/config/dependency-registry.js.map +1 -1
  27. package/dist/config/path-probe.d.ts +37 -0
  28. package/dist/config/path-probe.d.ts.map +1 -0
  29. package/dist/config/path-probe.js +43 -0
  30. package/dist/config/path-probe.js.map +1 -0
  31. package/dist/deepseek-route-config.d.ts +110 -0
  32. package/dist/deepseek-route-config.d.ts.map +1 -0
  33. package/dist/deepseek-route-config.js +477 -0
  34. package/dist/deepseek-route-config.js.map +1 -0
  35. package/dist/git-workspace-status.d.ts +246 -0
  36. package/dist/git-workspace-status.d.ts.map +1 -0
  37. package/dist/git-workspace-status.js +626 -0
  38. package/dist/git-workspace-status.js.map +1 -0
  39. package/dist/hooks-config.d.ts.map +1 -1
  40. package/dist/hooks-config.js +83 -13
  41. package/dist/hooks-config.js.map +1 -1
  42. package/dist/model-config-resolvers.d.ts +29 -0
  43. package/dist/model-config-resolvers.d.ts.map +1 -0
  44. package/dist/model-config-resolvers.js +34 -0
  45. package/dist/model-config-resolvers.js.map +1 -0
  46. package/dist/session-display-model.d.ts +110 -0
  47. package/dist/session-display-model.d.ts.map +1 -0
  48. package/dist/session-display-model.js +154 -0
  49. package/dist/session-display-model.js.map +1 -0
  50. package/dist/session.d.ts +145 -8
  51. package/dist/session.d.ts.map +1 -1
  52. package/dist/session.js +280 -35
  53. package/dist/session.js.map +1 -1
  54. package/dist/types/api.d.ts +7 -0
  55. package/dist/types/api.d.ts.map +1 -1
  56. package/dist/types/api.js.map +1 -1
  57. package/dist/types/session.d.ts +28 -1
  58. package/dist/types/session.d.ts.map +1 -1
  59. package/dist/types/session.js.map +1 -1
  60. package/dist/types/tools.d.ts +1 -1
  61. package/dist/types/tools.d.ts.map +1 -1
  62. package/dist/utils/bounded-path-probe.d.ts +99 -0
  63. package/dist/utils/bounded-path-probe.d.ts.map +1 -0
  64. package/dist/utils/bounded-path-probe.js +228 -0
  65. package/dist/utils/bounded-path-probe.js.map +1 -0
  66. package/dist/utils/cli-executable-resolver.d.ts +2 -0
  67. package/dist/utils/cli-executable-resolver.d.ts.map +1 -1
  68. package/dist/utils/cli-executable-resolver.js +2 -1
  69. package/dist/utils/cli-executable-resolver.js.map +1 -1
  70. package/dist/utils/deepseek-cli-resolver.d.ts +8 -0
  71. package/dist/utils/deepseek-cli-resolver.d.ts.map +1 -1
  72. package/dist/utils/deepseek-cli-resolver.js +18 -2
  73. package/dist/utils/deepseek-cli-resolver.js.map +1 -1
  74. package/dist/utils/dependency-checker.d.ts +2 -0
  75. package/dist/utils/dependency-checker.d.ts.map +1 -1
  76. package/dist/utils/dependency-checker.js +37 -9
  77. package/dist/utils/dependency-checker.js.map +1 -1
  78. package/dist/utils/index.d.ts +2 -0
  79. package/dist/utils/index.d.ts.map +1 -1
  80. package/dist/utils/index.js +1 -0
  81. package/dist/utils/index.js.map +1 -1
  82. package/dist/web/case-path.d.ts +58 -0
  83. package/dist/web/case-path.d.ts.map +1 -0
  84. package/dist/web/case-path.js +182 -0
  85. package/dist/web/case-path.js.map +1 -0
  86. package/dist/web/launch-defaults.d.ts +22 -0
  87. package/dist/web/launch-defaults.d.ts.map +1 -0
  88. package/dist/web/launch-defaults.js +46 -0
  89. package/dist/web/launch-defaults.js.map +1 -0
  90. package/dist/web/public/admin-ui.js.gz +0 -0
  91. package/dist/web/public/api-client.c9b1cddc.js.gz +0 -0
  92. package/dist/web/public/app.abadc970.js +43 -0
  93. package/dist/web/public/app.abadc970.js.br +0 -0
  94. package/dist/web/public/app.abadc970.js.gz +0 -0
  95. package/dist/web/public/approvals-ui.js.gz +0 -0
  96. package/dist/web/public/{constants.d95fade9.js → constants.c0f8f9de.js} +1009 -127
  97. package/dist/web/public/constants.c0f8f9de.js.br +0 -0
  98. package/dist/web/public/constants.c0f8f9de.js.gz +0 -0
  99. package/dist/web/public/cron-ui.js.gz +0 -0
  100. package/dist/web/public/entrance-animations.js +1 -0
  101. package/dist/web/public/entrance-animations.js.br +0 -0
  102. package/dist/web/public/entrance-animations.js.gz +0 -0
  103. package/dist/web/public/git-status-ui.js +719 -0
  104. package/dist/web/public/git-status-ui.js.br +0 -0
  105. package/dist/web/public/git-status-ui.js.gz +0 -0
  106. package/dist/web/public/home-sessions.js +12 -9
  107. package/dist/web/public/home-sessions.js.br +0 -0
  108. package/dist/web/public/home-sessions.js.gz +0 -0
  109. package/dist/web/public/host-wake-ui.js.gz +0 -0
  110. package/dist/web/public/i18n.a31747a9.js +2 -0
  111. package/dist/web/public/i18n.a31747a9.js.br +0 -0
  112. package/dist/web/public/i18n.a31747a9.js.gz +0 -0
  113. package/dist/web/public/{image-input.a216cf9e.js → image-input.898a538b.js} +18 -8
  114. package/dist/web/public/image-input.898a538b.js.br +0 -0
  115. package/dist/web/public/image-input.898a538b.js.gz +0 -0
  116. package/dist/web/public/index.html +205 -42
  117. package/dist/web/public/index.html.br +0 -0
  118. package/dist/web/public/index.html.gz +0 -0
  119. package/dist/web/public/input-cjk.8bc46081.js.gz +0 -0
  120. package/dist/web/public/keyboard-accessory.c1e7c7b7.js.gz +0 -0
  121. package/dist/web/public/{mobile-handlers.23a85db1.js → mobile-handlers.0ef9c311.js} +20 -0
  122. package/dist/web/public/mobile-handlers.0ef9c311.js.br +0 -0
  123. package/dist/web/public/mobile-handlers.0ef9c311.js.gz +0 -0
  124. package/dist/web/public/mobile-ime-preview.2dbaf262.js.gz +0 -0
  125. package/dist/web/public/mobile-overview.js.gz +0 -0
  126. package/dist/web/public/mobile.111b187d.css +1 -0
  127. package/dist/web/public/mobile.111b187d.css.br +0 -0
  128. package/dist/web/public/mobile.111b187d.css.gz +0 -0
  129. package/dist/web/public/notification-manager.50bb87d8.js.gz +0 -0
  130. package/dist/web/public/orchestrator-panel.js.gz +0 -0
  131. package/dist/web/public/{panels-ui.6a5898d9.js → panels-ui.4735dfc2.js} +4 -4
  132. package/dist/web/public/panels-ui.4735dfc2.js.br +0 -0
  133. package/dist/web/public/{panels-ui.6a5898d9.js.gz → panels-ui.4735dfc2.js.gz} +0 -0
  134. package/dist/web/public/ralph-panel.b350e2ef.js.gz +0 -0
  135. package/dist/web/public/ralph-wizard.b6c0dd19.js.gz +0 -0
  136. package/dist/web/public/readmymind-ui.js.gz +0 -0
  137. package/dist/web/public/reboot-restore-ui.js.gz +0 -0
  138. package/dist/web/public/respawn-ui.ff0dae4c.js.gz +0 -0
  139. package/dist/web/public/sanitize-html.e7147e76.js.gz +0 -0
  140. package/dist/web/public/session-lineage.js +229 -86
  141. package/dist/web/public/session-lineage.js.br +0 -0
  142. package/dist/web/public/session-lineage.js.gz +0 -0
  143. package/dist/web/public/session-ui.f731602c.js +100 -0
  144. package/dist/web/public/session-ui.f731602c.js.br +0 -0
  145. package/dist/web/public/session-ui.f731602c.js.gz +0 -0
  146. package/dist/web/public/settings-ui.a97ab16f.js +97 -0
  147. package/dist/web/public/settings-ui.a97ab16f.js.br +0 -0
  148. package/dist/web/public/settings-ui.a97ab16f.js.gz +0 -0
  149. package/dist/web/public/spreadsheet-preview-worker.js +290 -0
  150. package/dist/web/public/spreadsheet-preview-worker.js.br +0 -0
  151. package/dist/web/public/spreadsheet-preview-worker.js.gz +0 -0
  152. package/dist/web/public/spreadsheet-preview.af82fa2f.js +555 -0
  153. package/dist/web/public/spreadsheet-preview.af82fa2f.js.br +0 -0
  154. package/dist/web/public/spreadsheet-preview.af82fa2f.js.gz +0 -0
  155. package/dist/web/public/spreadsheet-xlsx-core.js +1006 -0
  156. package/dist/web/public/spreadsheet-xlsx-core.js.br +0 -0
  157. package/dist/web/public/spreadsheet-xlsx-core.js.gz +0 -0
  158. package/dist/web/public/styles.8ee12666.css +1 -0
  159. package/dist/web/public/styles.8ee12666.css.br +0 -0
  160. package/dist/web/public/styles.8ee12666.css.gz +0 -0
  161. package/dist/web/public/subagent-windows.e6ca799f.js.gz +0 -0
  162. package/dist/web/public/sw.js +2 -2
  163. package/dist/web/public/sw.js.br +0 -0
  164. package/dist/web/public/sw.js.gz +0 -0
  165. package/dist/web/public/tab-layout-browser.065fcba4.js +1 -0
  166. package/dist/web/public/tab-layout-browser.065fcba4.js.br +0 -0
  167. package/dist/web/public/tab-layout-browser.065fcba4.js.gz +0 -0
  168. package/dist/web/public/{tab-rail-resize.dcdc5772.js → tab-rail-resize.a5867793.js} +1 -1
  169. package/dist/web/public/tab-rail-resize.a5867793.js.br +0 -0
  170. package/dist/web/public/tab-rail-resize.a5867793.js.gz +0 -0
  171. package/dist/web/public/terminal-keycode229-recovery.af09f99a.js +1 -0
  172. package/dist/web/public/terminal-keycode229-recovery.af09f99a.js.br +0 -0
  173. package/dist/web/public/terminal-keycode229-recovery.af09f99a.js.gz +0 -0
  174. package/dist/web/public/terminal-split.js +146 -649
  175. package/dist/web/public/terminal-split.js.br +0 -0
  176. package/dist/web/public/terminal-split.js.gz +0 -0
  177. package/dist/web/public/terminal-tile.js +1611 -0
  178. package/dist/web/public/terminal-tile.js.br +0 -0
  179. package/dist/web/public/terminal-tile.js.gz +0 -0
  180. package/dist/web/public/terminal-ui.5059b30e.js +2 -0
  181. package/dist/web/public/terminal-ui.5059b30e.js.br +0 -0
  182. package/dist/web/public/terminal-ui.5059b30e.js.gz +0 -0
  183. package/dist/web/public/tile-grid.js +2684 -0
  184. package/dist/web/public/tile-grid.js.br +0 -0
  185. package/dist/web/public/tile-grid.js.gz +0 -0
  186. package/dist/web/public/ultracode-panel.js.gz +0 -0
  187. package/dist/web/public/ultracode-windows.js.gz +0 -0
  188. package/dist/web/public/vendor/dompurify.min.js.gz +0 -0
  189. package/dist/web/public/vendor/exceljs.min.js +44 -0
  190. package/dist/web/public/vendor/exceljs.min.js.br +0 -0
  191. package/dist/web/public/vendor/exceljs.min.js.gz +0 -0
  192. package/dist/web/public/vendor/fflate.min.js +1 -0
  193. package/dist/web/public/vendor/fflate.min.js.br +0 -0
  194. package/dist/web/public/vendor/fflate.min.js.gz +0 -0
  195. package/dist/web/public/vendor/marked.min.js.gz +0 -0
  196. package/dist/web/public/vendor/xterm-addon-fit.min.js.gz +0 -0
  197. package/dist/web/public/vendor/xterm-addon-serialize.min.js.gz +0 -0
  198. package/dist/web/public/vendor/xterm-addon-unicode11.min.js.gz +0 -0
  199. package/dist/web/public/vendor/xterm-addon-webgl.min.js.gz +0 -0
  200. package/dist/web/public/vendor/xterm-predictive-echo.ae5e3d5d.js.gz +0 -0
  201. package/dist/web/public/vendor/xterm-zerolag-input.bbb5f84c.js.gz +0 -0
  202. package/dist/web/public/vendor/xterm.css.gz +0 -0
  203. package/dist/web/public/vendor/xterm.min.js.gz +0 -0
  204. package/dist/web/public/{voice-input.c4b51eb6.js → voice-input.2342c37b.js} +64 -10
  205. package/dist/web/public/voice-input.2342c37b.js.br +0 -0
  206. package/dist/web/public/voice-input.2342c37b.js.gz +0 -0
  207. package/dist/web/public/voice-pcm-worklet.js.gz +0 -0
  208. package/dist/web/public/webview-tabs.js +9 -1
  209. package/dist/web/public/webview-tabs.js.br +0 -0
  210. package/dist/web/public/webview-tabs.js.gz +0 -0
  211. package/dist/web/routes/case-routes.d.ts.map +1 -1
  212. package/dist/web/routes/case-routes.js +136 -18
  213. package/dist/web/routes/case-routes.js.map +1 -1
  214. package/dist/web/routes/cli-registry-routes.d.ts.map +1 -1
  215. package/dist/web/routes/cli-registry-routes.js +3 -1
  216. package/dist/web/routes/cli-registry-routes.js.map +1 -1
  217. package/dist/web/routes/doctor-routes.d.ts +23 -0
  218. package/dist/web/routes/doctor-routes.d.ts.map +1 -0
  219. package/dist/web/routes/doctor-routes.js +83 -0
  220. package/dist/web/routes/doctor-routes.js.map +1 -0
  221. package/dist/web/routes/file-routes.d.ts +7 -0
  222. package/dist/web/routes/file-routes.d.ts.map +1 -1
  223. package/dist/web/routes/file-routes.js +34 -4
  224. package/dist/web/routes/file-routes.js.map +1 -1
  225. package/dist/web/routes/git-status-routes.d.ts +16 -0
  226. package/dist/web/routes/git-status-routes.d.ts.map +1 -0
  227. package/dist/web/routes/git-status-routes.js +98 -0
  228. package/dist/web/routes/git-status-routes.js.map +1 -0
  229. package/dist/web/routes/index.d.ts +2 -0
  230. package/dist/web/routes/index.d.ts.map +1 -1
  231. package/dist/web/routes/index.js +2 -0
  232. package/dist/web/routes/index.js.map +1 -1
  233. package/dist/web/routes/session-routes.d.ts +18 -0
  234. package/dist/web/routes/session-routes.d.ts.map +1 -1
  235. package/dist/web/routes/session-routes.js +106 -42
  236. package/dist/web/routes/session-routes.js.map +1 -1
  237. package/dist/web/routes/status-telemetry-routes.d.ts +3 -0
  238. package/dist/web/routes/status-telemetry-routes.d.ts.map +1 -1
  239. package/dist/web/routes/status-telemetry-routes.js +12 -0
  240. package/dist/web/routes/status-telemetry-routes.js.map +1 -1
  241. package/dist/web/routes/system-routes.d.ts.map +1 -1
  242. package/dist/web/routes/system-routes.js +13 -0
  243. package/dist/web/routes/system-routes.js.map +1 -1
  244. package/dist/web/schemas.d.ts +28 -0
  245. package/dist/web/schemas.d.ts.map +1 -1
  246. package/dist/web/schemas.js +41 -0
  247. package/dist/web/schemas.js.map +1 -1
  248. package/dist/web/server.d.ts +5 -0
  249. package/dist/web/server.d.ts.map +1 -1
  250. package/dist/web/server.js +35 -12
  251. package/dist/web/server.js.map +1 -1
  252. package/dist/web/session-listener-wiring.d.ts +1 -0
  253. package/dist/web/session-listener-wiring.d.ts.map +1 -1
  254. package/dist/web/session-listener-wiring.js +11 -0
  255. package/dist/web/session-listener-wiring.js.map +1 -1
  256. package/package.json +3 -1
  257. package/scripts/postinstall.js +16 -0
  258. package/skills/codeman/reference/verbs.md +3 -0
  259. package/dist/web/public/app.cf0db8ce.js +0 -43
  260. package/dist/web/public/app.cf0db8ce.js.br +0 -0
  261. package/dist/web/public/app.cf0db8ce.js.gz +0 -0
  262. package/dist/web/public/constants.d95fade9.js.br +0 -0
  263. package/dist/web/public/constants.d95fade9.js.gz +0 -0
  264. package/dist/web/public/i18n.398b8331.js +0 -1
  265. package/dist/web/public/i18n.398b8331.js.br +0 -0
  266. package/dist/web/public/i18n.398b8331.js.gz +0 -0
  267. package/dist/web/public/image-input.a216cf9e.js.br +0 -0
  268. package/dist/web/public/image-input.a216cf9e.js.gz +0 -0
  269. package/dist/web/public/mobile-handlers.23a85db1.js.br +0 -0
  270. package/dist/web/public/mobile-handlers.23a85db1.js.gz +0 -0
  271. package/dist/web/public/mobile.1aa4952d.css +0 -1
  272. package/dist/web/public/mobile.1aa4952d.css.br +0 -0
  273. package/dist/web/public/mobile.1aa4952d.css.gz +0 -0
  274. package/dist/web/public/panels-ui.6a5898d9.js.br +0 -0
  275. package/dist/web/public/session-ui.bf66c13b.js +0 -89
  276. package/dist/web/public/session-ui.bf66c13b.js.br +0 -0
  277. package/dist/web/public/session-ui.bf66c13b.js.gz +0 -0
  278. package/dist/web/public/settings-ui.331d3b1d.js +0 -97
  279. package/dist/web/public/settings-ui.331d3b1d.js.br +0 -0
  280. package/dist/web/public/settings-ui.331d3b1d.js.gz +0 -0
  281. package/dist/web/public/styles.8c485d26.css +0 -1
  282. package/dist/web/public/styles.8c485d26.css.br +0 -0
  283. package/dist/web/public/styles.8c485d26.css.gz +0 -0
  284. package/dist/web/public/tab-layout-browser.4e0ed50c.js +0 -1
  285. package/dist/web/public/tab-layout-browser.4e0ed50c.js.br +0 -0
  286. package/dist/web/public/tab-layout-browser.4e0ed50c.js.gz +0 -0
  287. package/dist/web/public/tab-rail-resize.dcdc5772.js.br +0 -0
  288. package/dist/web/public/tab-rail-resize.dcdc5772.js.gz +0 -0
  289. package/dist/web/public/terminal-keycode229-recovery.fb91b25b.js +0 -1
  290. package/dist/web/public/terminal-keycode229-recovery.fb91b25b.js.br +0 -0
  291. package/dist/web/public/terminal-keycode229-recovery.fb91b25b.js.gz +0 -0
  292. package/dist/web/public/terminal-ui.a4535311.js +0 -2
  293. package/dist/web/public/terminal-ui.a4535311.js.br +0 -0
  294. package/dist/web/public/terminal-ui.a4535311.js.gz +0 -0
  295. package/dist/web/public/upload.html +0 -155
  296. package/dist/web/public/upload.html.br +0 -0
  297. package/dist/web/public/upload.html.gz +0 -0
  298. package/dist/web/public/voice-input.c4b51eb6.js.br +0 -0
  299. package/dist/web/public/voice-input.c4b51eb6.js.gz +0 -0
@@ -16,8 +16,8 @@
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.8c485d26.css">
20
- <link rel="stylesheet" href="mobile.1aa4952d.css" media="(max-width: 1023px)">
19
+ <link rel="stylesheet" href="styles.8ee12666.css">
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'">
23
23
  <noscript><link rel="stylesheet" href="vendor/xterm.css"></noscript>
@@ -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.d95fade9.js" as="script">
31
- <link rel="preload" href="app.cf0db8ce.js" as="script">
30
+ <link rel="preload" href="constants.c0f8f9de.js" as="script">
31
+ <link rel="preload" href="app.abadc970.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>
@@ -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 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';}</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';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>
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)}
@@ -147,13 +147,14 @@
147
147
  <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z"/></svg>
148
148
  <span>Admin Panel</span>
149
149
  </button>
150
- <button class="btn-icon-header btn-solo-redock" id="soloRedockBtn" style="display: none;" onclick="window.close()" title="Re-dock to dashboard (close window)" aria-label="Re-dock session to dashboard">&#x229E;</button>
150
+ <button class="btn-icon-header btn-solo-redock" id="soloRedockBtn" style="display: none;" onclick="app._closeSoloWindow()" title="Re-dock to dashboard (close window)" aria-label="Re-dock session to dashboard">&#x229E;</button>
151
151
  <button class="tunnel-indicator" id="tunnelIndicator" style="display: none;" onclick="app.toggleTunnelPanel()" title="Cloudflare Tunnel" aria-label="Tunnel status">
152
152
  <span class="tunnel-dot"></span>
153
153
  </button>
154
154
  <div class="connection-indicator" id="connectionIndicator" style="display: none;">
155
155
  <span class="connection-dot" id="connectionDot"></span>
156
156
  <span class="connection-text" id="connectionText"></span>
157
+ <span class="connection-tile"><span class="connection-tile-label" id="connectionTileLabel"></span><span class="connection-tile-value" id="connectionTileValue" data-i18n-skip></span></span>
157
158
  </div>
158
159
  <div class="header-font-controls">
159
160
  <button class="btn-icon-header btn-sm" onclick="app.decreaseFontSize()" title="Decrease font (Ctrl+-)" aria-label="Decrease font size">A-</button>
@@ -162,6 +163,7 @@
162
163
  </div>
163
164
  <div class="header-system-stats" id="headerSystemStats" title="System resource usage">
164
165
  <div class="stat-item">
166
+ <span class="stat-ring" id="statCpuRing" aria-hidden="true"></span>
165
167
  <span class="stat-label">CPU</span>
166
168
  <div class="stat-bar">
167
169
  <div class="stat-bar-fill stat-bar-cpu" id="statCpuBar"></div>
@@ -169,6 +171,7 @@
169
171
  <span class="stat-value" id="statCpu">--%</span>
170
172
  </div>
171
173
  <div class="stat-item">
174
+ <span class="stat-ring" id="statMemRing" aria-hidden="true"></span>
172
175
  <span class="stat-label">MEM</span>
173
176
  <div class="stat-bar">
174
177
  <div class="stat-bar-fill stat-bar-mem" id="statMemBar"></div>
@@ -189,9 +192,10 @@
189
192
  <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="m21.44 11.05-9.19 9.19a6 6 0 0 1-8.49-8.49l9.19-9.19a4 4 0 0 1 5.66 5.66l-9.2 9.19a2 2 0 0 1-2.83-2.83l8.49-8.48"/></svg>
190
193
  <span class="attachment-history-badge" id="attachmentHistoryBadge" style="display:none;">0</span>
191
194
  </button>
192
- <button class="btn-icon-header btn-file-viewer" onclick="app.toggleFileBrowserButton()" title="File Viewer" aria-label="Open file viewer" aria-expanded="false"><svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M3 7a2 2 0 0 1 2-2h4l2 2h8a2 2 0 0 1 2 2v8a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z"/></svg></button>
195
+ <button class="btn-icon-header btn-file-viewer" onclick="app.toggleFileBrowserButton()" title="File Viewer" aria-label="Open file viewer" aria-expanded="false"><svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path class="icon-folder-closed" d="M3 7a2 2 0 0 1 2-2h4l2 2h8a2 2 0 0 1 2 2v8a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z"/><g class="icon-folder-open"><path d="M3 17V7a2 2 0 0 1 2-2h4l2 2h6a2 2 0 0 1 2 2v1.5"/><path d="M3 17l2.3-5.4A2 2 0 0 1 7.2 10.5H20a1.5 1.5 0 0 1 1.4 2l-1.9 5.2A2 2 0 0 1 17.6 19H5a2 2 0 0 1-2-2z"/></g></svg></button>
193
196
  <button class="btn-icon-header btn-multimonitor btn-multimonitor--hidden" onclick="app.launchMultiMonitor()" title="Open Codeman across all displays" aria-label="Open Codeman across all displays"><svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><rect x="2" y="4" width="13" height="9" rx="1.5"/><rect x="11" y="9" width="11" height="8" rx="1.5"/></svg></button>
194
197
  <button class="btn-icon-header btn-split btn-split--hidden" onclick="app.openSplitPicker(event)" title="Split: open a second session beside this one" aria-label="Split: open a second session beside this one" aria-pressed="false"><svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><rect x="2" y="3" width="20" height="18" rx="2"/><line x1="12" y1="3" x2="12" y2="21"/></svg></button>
198
+ <button class="btn-icon-header btn-tile-grid btn-tile-grid--hidden" onclick="app.toggleTileGrid()" oncontextmenu="app.openTileCountMenu(event)" aria-describedby="tileGridHint" aria-label="Tiles: show several sessions side by side (right-click for how many)" aria-pressed="false"><svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><rect x="3" y="3" width="8" height="8" rx="1"/><rect x="13" y="3" width="8" height="8" rx="1"/><rect x="3" y="13" width="8" height="8" rx="1"/><rect x="13" y="13" width="8" height="8" rx="1"/></svg></button>
195
199
  <button class="btn-icon-header btn-ultracode-agents btn-ultracode-agents--hidden" onclick="app.toggleUltracodeAgentsPanel()" title="Ultracode / Workflow agents" aria-label="Open ultracode workflow agents"><svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><circle cx="6" cy="6" r="2.5"/><circle cx="6" cy="18" r="2.5"/><circle cx="18" cy="12" r="2.5"/><path d="M8.2 7.2 15.6 11M8.2 16.8 15.6 13"/></svg></button>
196
200
  <div class="header-plan-usage header-plan-usage--hidden" id="planUsageChip" title="Claude and Codex plan usage limits">—</div>
197
201
  <button class="btn-icon-header btn-notifications" onclick="app.toggleNotifications()" title="Notifications" aria-label="Toggle notifications" style="display:none;">
@@ -435,6 +439,12 @@
435
439
  autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false"></textarea>
436
440
  </div>
437
441
 
442
+ <!-- Tile grid (tile-grid.js): 1 to 6 sessions side by side, each a
443
+ TerminalTile. A SIBLING of .terminal-wrap, never a parent: while
444
+ .main.tiles-active is set the main terminal is parked (hidden) and
445
+ this section takes its place. -->
446
+ <section class="tile-grid" id="tileGrid" aria-label="Tiled sessions"></section>
447
+
438
448
  <!-- Web tab layer: one iframe per open dashboard, shown in place of the
439
449
  terminal while a web tab is active. Frames stay mounted while hidden so
440
450
  switching tabs does not reload (and re-authenticate) a dashboard. -->
@@ -450,9 +460,14 @@
450
460
  <div class="welcome-content">
451
461
  <h1 class="welcome-title">Codeman</h1>
452
462
  <p class="welcome-desc">Manage AI Coding tools in persistent tmux sessions.</p>
463
+ <!-- Launchers (settings-ui.js renderWelcomeCliActions): one primary
464
+ button for the first agent in the registry catalog, then a chip
465
+ row for every other enabled CLI. The tunnel is not a launcher, so
466
+ it sits under them as a quiet link; settings-ui.js rewrites its
467
+ contents on every state change and owns its inline display. -->
453
468
  <div class="welcome-actions">
454
469
  <div class="welcome-cli-actions" id="welcomeCliActions"></div>
455
- <button class="welcome-btn welcome-btn-tunnel" id="welcomeTunnelBtn" style="display: none;" onclick="app.toggleTunnelFromWelcome()">
470
+ <button type="button" class="welcome-tunnel-link" id="welcomeTunnelBtn" style="display: none;" onclick="app.toggleTunnelFromWelcome()">
456
471
  <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M12 2L2 7l10 5 10-5-10-5z"/><path d="M2 17l10 5 10-5"/><path d="M2 12l10 5 10-5"/></svg>
457
472
  Cloudflare Tunnel
458
473
  </button>
@@ -567,6 +582,19 @@
567
582
  <div class="file-browser-status" id="fileBrowserStatus"></div>
568
583
  </div>
569
584
 
585
+ <!-- Git status panel (git-status-ui.js): what the active session's repo has not committed or pushed. -->
586
+ <div class="git-status-panel" id="gitStatusPanel" role="dialog" aria-label="Git status">
587
+ <div class="git-status-header">
588
+ <span class="git-status-title">Git <span class="git-status-branch" id="gitStatusBranch" data-i18n-skip></span></span>
589
+ <div class="git-status-actions">
590
+ <button class="btn-icon-sm" onclick="app.refreshGitStatusNow()" title="Refresh" aria-label="Refresh git status">&#x21BB;</button>
591
+ <button class="btn-icon-sm" onclick="app.closeGitStatusPanel()" title="Close" aria-label="Close git status">&times;</button>
592
+ </div>
593
+ </div>
594
+ <div class="git-status-body" id="gitStatusBody" data-i18n-skip></div>
595
+ <div class="git-status-footer" id="gitStatusFooter" data-i18n-skip></div>
596
+ </div>
597
+
570
598
  <!-- File Preview Overlay -->
571
599
  <div class="file-preview-overlay" id="filePreviewOverlay">
572
600
  <div class="file-preview-window">
@@ -677,11 +705,9 @@
677
705
  <button class="btn-toolbar btn-enter" onclick="app.sendEnterKey()" title="Send Enter">
678
706
  Enter
679
707
  </button>
680
- <div class="tab-count-group" title="Instance count">
681
- <button class="tab-count-btn" onclick="app.decrementShellCount()">−</button>
682
- <input type="number" id="shellCount" class="tab-count-input" value="1" min="1" max="20" readonly>
683
- <button class="tab-count-btn" onclick="app.incrementShellCount()">+</button>
684
- </div>
708
+ <!-- Run Shell had a second, identical instance-count stepper here. The
709
+ toolbar carried two of them side by side, so it is gone and Run
710
+ Shell reads the one above (#tabCount) like the Run button does. -->
685
711
  <div class="case-select-group">
686
712
  <div class="case-combobox" id="quickStartCasePicker">
687
713
  <input
@@ -701,8 +727,9 @@
701
727
  <select id="quickStartCase" class="toolbar-select case-native-select" title="Select case" aria-hidden="true" tabindex="-1">
702
728
  <option value="testcase">testcase</option>
703
729
  </select>
704
- <button class="btn-case-add" onclick="app.showCreateCaseModal()" title="Create new case">+</button>
705
- <button class="btn-case-settings" onclick="app.toggleCaseSettings()" title="Case settings">&#x2699;</button>
730
+ <!-- "New or link a case…" and "Case settings…" are the last two rows of
731
+ the case picker's own list (CASE_PICKER_ACTIONS, session-ui.js); the
732
+ "+" and gear buttons that sat here were removed (owner, 1.36.0). -->
706
733
  <div class="case-settings-popover hidden" id="caseSettingsPopover">
707
734
  <label class="checkbox-inline">
708
735
  <input type="checkbox" id="caseAgentTeams" onchange="app.onCaseSettingChanged()">
@@ -771,6 +798,13 @@
771
798
  <!-- Orchestrator button hidden until feature is ready -->
772
799
  <!-- <button class="btn-toolbar btn-sm" onclick="app.toggleOrchestratorPanel()" title="Orchestrator Loop">&#x2699; Orchestrator</button> -->
773
800
  <button class="btn-toolbar btn-sm btn-cron btn-cron--hidden" onclick="app.openCron()" title="Cron Jobs">&#x23F0; Cron</button>
801
+ <!-- Git status of the active session's repository (git-status-ui.js). Optional and per-device
802
+ (App Settings → Header & Panels → Bottom bar, default OFF), so it is hidden until that
803
+ setting is on AND the session is a local git repository. -->
804
+ <button type="button" class="btn-toolbar btn-sm btn-git-status" id="gitStatusBtn" hidden aria-expanded="false" aria-controls="gitStatusPanel" onclick="app.toggleGitStatusPanel()">
805
+ <svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><circle cx="6" cy="6" r="2.5"/><circle cx="6" cy="18" r="2.5"/><circle cx="18" cy="8" r="2.5"/><path d="M6 8.5v7"/><path d="M18 10.5c0 4-6 3-11 6"/></svg>
806
+ <span class="git-status-label" data-i18n-skip></span>
807
+ </button>
774
808
  <span class="version-display" id="versionDisplay" title="Codeman version">v0.0.0</span>
775
809
  </div>
776
810
  </footer>
@@ -787,7 +821,6 @@
787
821
  <section class="shortcut-section">
788
822
  <h4>Session</h4>
789
823
  <div class="shortcuts-grid">
790
- <div><kbd>Ctrl</kbd>+<kbd>W</kbd></div><div>Close Session</div>
791
824
  <div><kbd>Ctrl/Cmd/Option</kbd>+<kbd>K</kbd></div><div>Find Open Session</div>
792
825
  <div><kbd>Ctrl</kbd>+<kbd>Tab</kbd></div><div>Next Session</div>
793
826
  <div><kbd>Alt/Option</kbd>+<kbd>[</kbd> / <kbd>Alt/Option</kbd>+<kbd>]</kbd></div><div>Previous / Next Session</div>
@@ -802,11 +835,23 @@
802
835
  <div><kbd>Ctrl</kbd>+<kbd>}</kbd></div><div>Move Active Tab Right</div>
803
836
  <div><kbd>ArrowLeft</kbd> / <kbd>ArrowUp</kbd></div><div>Focus Previous Tab</div>
804
837
  <div><kbd>ArrowRight</kbd> / <kbd>ArrowDown</kbd></div><div>Focus Next Tab</div>
805
- <div><kbd>Home</kbd></div><div>Focus First Tab</div>
838
+ <div><kbd data-i18n-skip>Home</kbd></div><div>Focus First Tab</div>
806
839
  <div><kbd>End</kbd></div><div>Focus Last Tab</div>
807
840
  <div><kbd>Enter</kbd> / <kbd>Space</kbd></div><div>Activate Focused Tab</div>
808
841
  </div>
809
842
  </section>
843
+ <section class="shortcut-section">
844
+ <h4>Tiles</h4>
845
+ <div class="shortcuts-grid">
846
+ <div><kbd>Ctrl</kbd>+<kbd>Shift</kbd>+<kbd>G</kbd></div><div>Toggle Tile Grid</div>
847
+ <div><kbd>Alt/Option</kbd>+<kbd>Shift</kbd>+<kbd>Arrows</kbd></div><div>Focus Tile Left / Right / Up / Down</div>
848
+ <div><kbd>Ctrl</kbd>+<kbd>Shift</kbd>+<kbd>Arrows</kbd></div><div>Move Tile Left / Right / Up / Down</div>
849
+ <div><kbd>Drag</kbd> a tile's header</div><div>Move the Tile (onto Another: Swap)</div>
850
+ <div><kbd>Alt/Option</kbd>+<kbd>Shift</kbd>+<kbd>Enter</kbd></div><div>Zoom Focused Tile</div>
851
+ <div><kbd>Ctrl/Cmd</kbd>+<kbd>Click</kbd> a tab</div><div>Add the Session to the Tile Grid</div>
852
+ <div><kbd>Right-click</kbd> the Tiles button</div><div>Choose How Many Tiles (2, 4 or 6)</div>
853
+ </div>
854
+ </section>
810
855
  <section class="shortcut-section">
811
856
  <h4>Terminal</h4>
812
857
  <div class="shortcuts-grid">
@@ -1884,7 +1929,7 @@
1884
1929
  <div class="set-group-head"><h4>Header buttons</h4><span class="set-scope">device</span></div>
1885
1930
  <p class="set-group-hint">Tap to show a control in the header. Multi-monitor is the one entry here that syncs across devices.</p>
1886
1931
  <div class="set-group-body">
1887
- <div class="set-chips" data-search="header buttons plan usage font stats lifecycle response file viewer attachments monitor session away cron redraw">
1932
+ <div class="set-chips" data-search="header buttons plan usage font stats lifecycle response file viewer attachments monitor session away cron redraw split tiles">
1888
1933
  <label class="set-chip" data-preview="header" data-preview-order="1" data-preview-text="A+"><input type="checkbox" id="appSettingsShowFontControls"><svg class="set-chip-ico" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.9" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M4 20 10 5l6 15"/><path d="M6.5 15h7"/><path d="M18 12h4M20 10v4"/></svg><span>Font Size</span></label>
1889
1934
  <label class="set-chip" data-preview="header" data-preview-order="2" data-preview-text="CPU 12%"><input type="checkbox" id="appSettingsShowSystemStats" checked><svg class="set-chip-ico" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.9" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M3 12h4l2.5-7 4 14L16 12h5"/></svg><span>System Stats</span></label>
1890
1935
  <label class="set-chip" data-preview="header" data-preview-order="3"><input type="checkbox" id="appSettingsShowRedrawButton"><svg class="set-chip-ico" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.9" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><polyline points="1 4 1 10 7 10"/><polyline points="23 20 23 14 17 14"/><path d="M20.49 9A9 9 0 0 0 5.64 5.64L1 10m22 4l-4.64 4.36A9 9 0 0 1 3.51 15"/></svg><span>Redraw Terminal</span></label>
@@ -1894,10 +1939,22 @@
1894
1939
  <label class="set-chip" data-preview="header" data-preview-order="9"><input type="checkbox" id="appSettingsShowAttachmentsButton"><svg class="set-chip-ico" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.9" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="m21.44 11.05-9.19 9.19a6 6 0 0 1-8.49-8.49l9.19-9.19a4 4 0 0 1 5.66 5.66l-9.2 9.19a2 2 0 0 1-2.83-2.83l8.49-8.48"/></svg><span>Attachments</span></label>
1895
1940
  <label class="set-chip" data-preview="header" data-preview-order="10"><input type="checkbox" id="appSettingsShowFileViewerButton"><svg class="set-chip-ico" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.9" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M3 7a2 2 0 0 1 2-2h4l2 2h8a2 2 0 0 1 2 2v8a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z"/></svg><span>File Viewer</span></label>
1896
1941
  <label class="set-chip" data-preview="header" data-preview-order="11"><input type="checkbox" id="appSettingsShowMultiMonitorButton"><svg class="set-chip-ico" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.9" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><rect x="2" y="4" width="13" height="9" rx="1.5"/><rect x="11" y="9" width="11" height="8" rx="1.5"/></svg><span>Multi-monitor</span></label>
1897
- <label class="set-chip" data-preview="header" data-preview-order="11.5"><input type="checkbox" id="appSettingsShowSplitButton"><svg class="set-chip-ico" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.9" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><rect x="2" y="3" width="20" height="18" rx="2"/><line x1="12" y1="3" x2="12" y2="21"/></svg><span>Split</span></label>
1942
+ <label class="set-chip" data-search="split pane side by side two sessions" data-preview="header" data-preview-order="11.5"><input type="checkbox" id="appSettingsShowSplitButton"><svg class="set-chip-ico" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.9" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><rect x="2" y="3" width="20" height="18" rx="2"/><line x1="12" y1="3" x2="12" y2="21"/></svg><span>Split</span></label>
1943
+ <label class="set-chip" data-search="tiles tile grid side by side several sessions" data-preview="header" data-preview-order="11.6"><input type="checkbox" id="appSettingsShowTileGridButton"><svg class="set-chip-ico" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.9" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><rect x="3" y="3" width="8" height="8" rx="1"/><rect x="13" y="3" width="8" height="8" rx="1"/><rect x="3" y="13" width="8" height="8" rx="1"/><rect x="13" y="13" width="8" height="8" rx="1"/></svg><span>Tiles</span></label>
1898
1944
  <label class="set-chip" data-preview="header" data-preview-order="13" data-preview-text="42%"><input type="checkbox" id="appSettingsShowPlanUsageLimits"><svg class="set-chip-ico" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.9" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M4 18a8 8 0 1 1 16 0"/><path d="M12 18l4.5-5"/></svg><span>Plan Usage</span></label>
1899
1945
  <label class="set-chip" data-preview="header" data-preview-order="14"><input type="checkbox" id="appSettingsShowLifecycleLog"><svg class="set-chip-ico" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.9" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"/><polyline points="14 2 14 8 20 8"/><line x1="16" y1="13" x2="8" y2="13"/><line x1="16" y1="17" x2="8" y2="17"/></svg><span>Lifecycle Log</span></label>
1900
1946
  </div>
1947
+ <div class="set-row has-field" data-search="header stats style system cpu mem ws plan usage tiles compact rings">
1948
+ <div class="set-row-text">
1949
+ <span class="set-row-label">Header Stats Style <span class="set-tag">desktop</span></span>
1950
+ <span class="set-row-desc">How WS, CPU, MEM and the plan-usage windows are drawn. Compact puts a ring beside each value in two pills; Tiles put each label over its value with a bar underneath.</span>
1951
+ </div>
1952
+ <select id="appSettingsHeaderStatsStyle" class="set-select">
1953
+ <option value="classic">As before (bars)</option>
1954
+ <option value="compact">Compact (default)</option>
1955
+ <option value="tiles">Tiles (label over value)</option>
1956
+ </select>
1957
+ </div>
1901
1958
  </div>
1902
1959
  </div>
1903
1960
 
@@ -1935,6 +1992,40 @@
1935
1992
  </div>
1936
1993
  </div>
1937
1994
 
1995
+ <div class="set-group">
1996
+ <div class="set-group-head"><h4>Bottom bar</h4></div>
1997
+ <div class="set-group-body">
1998
+ <div class="set-row" data-search="git status uncommitted unpushed commit push indicator bottom bar toolbar">
1999
+ <div class="set-row-text">
2000
+ <span class="set-row-label">Git status <span class="set-scope">device</span></span>
2001
+ <span class="set-row-desc">Shows, at the right of the bottom bar, when the active session's repository (or each repository inside its folder, up to two levels down) has uncommitted files or commits that are not pushed. Click it for the list. Read-only: Codeman never fetches or changes the repository. Not shown for Docker or remote sessions. Off by default.</span>
2002
+ </div>
2003
+ <label class="switch switch-sm"><input type="checkbox" id="appSettingsShowGitStatus"><span class="slider"></span></label>
2004
+ </div>
2005
+ <div class="set-row" data-search="git status folders tree flat list collapsed expand files">
2006
+ <div class="set-row-text">
2007
+ <span class="set-row-label">Git status: group files by folder <span class="set-scope">device</span></span>
2008
+ <span class="set-row-desc">In the Git window, show changed files under their folders, collapsed until you click a folder. Off lists every file by its full path. On by default.</span>
2009
+ </div>
2010
+ <label class="switch switch-sm"><input type="checkbox" id="appSettingsGitStatusTree" checked><span class="slider"></span></label>
2011
+ </div>
2012
+ <div class="set-row" data-search="git status maximum repositories folder many repos limit">
2013
+ <div class="set-row-text">
2014
+ <span class="set-row-label">Git status: max repositories <span class="set-scope">device</span></span>
2015
+ <span class="set-row-desc">When the session's folder holds several projects instead of being one, the Git window lists up to this many (1 to 50, default 12). Each one costs a few git commands per refresh.</span>
2016
+ </div>
2017
+ <input type="number" id="appSettingsGitStatusMaxRepos" class="set-num" value="12" min="1" max="50" step="1">
2018
+ </div>
2019
+ <div class="set-row" data-search="git status timeout seconds slow share network">
2020
+ <div class="set-row-text">
2021
+ <span class="set-row-label">Git status: git timeout <span class="set-scope">device</span></span>
2022
+ <span class="set-row-desc">Seconds one git command may run before that repository is reported as unreadable (5 to 120, default 30). Raise it for repositories on a slow network share.</span>
2023
+ </div>
2024
+ <input type="number" id="appSettingsGitStatusTimeout" class="set-num" value="30" min="5" max="120" step="1">
2025
+ </div>
2026
+ </div>
2027
+ </div>
2028
+
1938
2029
  <div class="set-group">
1939
2030
  <div class="set-group-head"><h4>Subagent windows</h4></div>
1940
2031
  <div class="set-group-body">
@@ -2030,6 +2121,28 @@
2030
2121
  <div class="set-group">
2031
2122
  <div class="set-group-head"><h4>Tabs</h4><span class="set-scope">device</span></div>
2032
2123
  <div class="set-group-body">
2124
+ <div class="set-row has-field" data-search="tab layout grouping group by state triage needs you waiting working idle case clusters ledger columns classic old new">
2125
+ <div class="set-row-text">
2126
+ <span class="set-row-label">Tab Layout</span>
2127
+ <span class="set-row-desc">By state: a row each for needs you, waiting, working and idle. By case: one box per case. Ledger: an aligned column grid. Classic: the single list, as before. By state and By case group the side rail and sidebar too. Alt+1..9 keeps the tab order.</span>
2128
+ </div>
2129
+ <select id="appSettingsTabArrangement" class="set-select">
2130
+ <option value="state">By state (rows per state)</option>
2131
+ <option value="case">By case (clusters)</option>
2132
+ <option value="ledger">Ledger (aligned columns)</option>
2133
+ <option value="classic">Classic (default)</option>
2134
+ </select>
2135
+ </div>
2136
+ <div class="set-row has-field" data-search="state order reverse needs you bottom top rows sections">
2137
+ <div class="set-row-text">
2138
+ <span class="set-row-label">State Order</span>
2139
+ <span class="set-row-desc">For Tab Layout by state. At the bottom flips the rows, so needs you sits right above the terminal.</span>
2140
+ </div>
2141
+ <select id="appSettingsTabStateOrder" class="set-select">
2142
+ <option value="urgent-first">Needs you on top (default)</option>
2143
+ <option value="urgent-last">Needs you at the bottom</option>
2144
+ </select>
2145
+ </div>
2033
2146
  <div class="set-row has-field" data-search="tab orientation horizontal vertical side rail">
2034
2147
  <div class="set-row-text">
2035
2148
  <span class="set-row-label">Tab Orientation</span>
@@ -2053,7 +2166,7 @@
2053
2166
  <div class="set-row has-field" data-search="tab rail sort order activity manual drag reorder">
2054
2167
  <div class="set-row-text">
2055
2168
  <span class="set-row-label">Vertical Rail Order</span>
2056
- <span class="set-row-desc">By activity uses the home screen's order: blocked on you first, then whatever has been running longest, then the most recently quiet. Manual keeps your tab order and is the only mode you can drag rows in. Alt+1..9 always follows the tab order either way.</span>
2169
+ <span class="set-row-desc">By activity uses the home screen's order: blocked on you first, then whatever has been running longest, then the most recently quiet. Manual keeps your tab order and is the only mode you can drag rows in. With Tab Layout by state or by case it orders the rows inside each section. Alt+1..9 always follows the tab order either way.</span>
2057
2170
  </div>
2058
2171
  <select id="appSettingsTabRailSort" class="set-select">
2059
2172
  <option value="activity">By activity (home screen order)</option>
@@ -2113,7 +2226,7 @@
2113
2226
  <div class="set-row" id="appSettingsLineageLinesItem" data-search="lineage lines spawned worker parent connection">
2114
2227
  <div class="set-row-text">
2115
2228
  <span class="set-row-label">Spawn Lineage Lines <span class="set-tag">desktop</span></span>
2116
- <span class="set-row-desc">Draw a line under the tab strip from a session to the sessions it spawned.</span>
2229
+ <span class="set-row-desc">Draw lines from each session to the sessions it spawned. The selected tab's family is drawn thicker.</span>
2117
2230
  </div>
2118
2231
  <label class="switch switch-sm"><input type="checkbox" id="appSettingsLineageLines" checked><span class="slider"></span></label>
2119
2232
  </div>
@@ -2487,6 +2600,30 @@
2487
2600
  <div class="set-group" id="appSettingsCodexGroup">
2488
2601
  <div class="set-group-head"><h4>Codex</h4><span class="set-scope">synced</span></div>
2489
2602
  <div class="set-group-body">
2603
+ <div class="set-row has-field" data-search="codex default model">
2604
+ <div class="set-row-text">
2605
+ <span class="set-row-label">Default Codex model</span>
2606
+ <span class="set-row-desc">Model ID for new local Codex sessions, including WSL. Leave empty to use Codex configuration.</span>
2607
+ </div>
2608
+ <input id="appSettingsCodexModel" class="set-input" type="text" maxlength="100" aria-label="Default Codex model" placeholder="Use Codex configuration" autocomplete="off" spellcheck="false">
2609
+ </div>
2610
+ <div class="set-row has-field" data-search="codex default reasoning effort thinking">
2611
+ <div class="set-row-text">
2612
+ <span class="set-row-label">Default Codex reasoning effort</span>
2613
+ <span class="set-row-desc">Applies to new local sessions; supported levels depend on the model and Codex version. Custom endpoints, Docker and remote sessions keep their own settings.</span>
2614
+ </div>
2615
+ <select id="appSettingsCodexReasoningEffort" class="set-select" aria-label="Default Codex reasoning effort">
2616
+ <option value="">Use Codex configuration</option>
2617
+ <option value="none">none</option>
2618
+ <option value="minimal">minimal</option>
2619
+ <option value="low">low</option>
2620
+ <option value="medium">medium</option>
2621
+ <option value="high">high</option>
2622
+ <option value="xhigh">xhigh</option>
2623
+ <option value="max">max</option>
2624
+ <option value="ultra">ultra</option>
2625
+ </select>
2626
+ </div>
2490
2627
  <div class="set-row" data-search="codex bypass approvals sandbox">
2491
2628
  <div class="set-row-text">
2492
2629
  <span class="set-row-label">Bypass approvals and sandbox</span>
@@ -2829,6 +2966,20 @@
2829
2966
  </div>
2830
2967
  <p class="set-section-blurb">Paths, automation and remote access. Set once, rarely touched.</p>
2831
2968
 
2969
+ <div class="set-group" id="doctorGroup">
2970
+ <div class="set-group-head"><h4>Diagnostics</h4><span class="set-scope">server</span></div>
2971
+ <div class="set-group-body">
2972
+ <div class="set-row" data-search="diagnostics doctor dependencies tmux node claude codex check install">
2973
+ <div class="set-row-text">
2974
+ <span class="set-row-label">Check this machine</span>
2975
+ <span class="set-row-desc">Runs <code>codeman doctor</code> on the server: which agent CLIs, tmux, Node and the optional office tools are installed, their versions, and how to install what is missing.</span>
2976
+ </div>
2977
+ <button class="btn-toolbar btn-sm" id="doctorRunBtn" onclick="app.runDoctor()">Run checks</button>
2978
+ </div>
2979
+ <div id="doctorResult" class="set-note" style="display:none" data-i18n-skip></div>
2980
+ </div>
2981
+ </div>
2982
+
2832
2983
  <div class="set-group">
2833
2984
  <div class="set-group-head"><h4>Paths</h4><span class="set-scope">synced</span></div>
2834
2985
  <div class="set-group-body">
@@ -2879,10 +3030,6 @@
2879
3030
  <button class="btn-icon-sm" id="tunnelQrBtn" onclick="app.showTunnelQR()" title="Show QR code">&#x229E;</button>
2880
3031
  </div>
2881
3032
  </div>
2882
- <div class="set-row" id="tunnelUploadUrlRow" style="display:none">
2883
- <div class="set-row-text"><span class="set-row-label">Upload URL</span></div>
2884
- <span id="tunnelUploadUrlDisplay" class="set-copy" title="Click to copy"></span>
2885
- </div>
2886
3033
  </div>
2887
3034
  </div>
2888
3035
  </section>
@@ -2969,18 +3116,30 @@
2969
3116
  <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"><rect x="3" y="3" width="18" height="18" rx="2"/><path d="M12 8v8M8 12h8"/></svg>
2970
3117
  <h2>Create New</h2>
2971
3118
  </div>
2972
- <p class="set-section-blurb">A fresh workspace under ~/codeman-cases, scaffolded with its own CLAUDE.md.</p>
3119
+ <p class="set-section-blurb" id="newCaseBlurb">A fresh workspace under ~/codeman-cases, scaffolded with its own CLAUDE.md.</p>
2973
3120
  <div class="form-row">
2974
3121
  <label>Case Name</label>
2975
- <input type="text" id="newCaseName" placeholder="my-project" pattern="[a-zA-Z0-9_-]+" autocomplete="off" autocapitalize="off" spellcheck="false">
2976
- <span class="form-hint">Letters, numbers, hyphens, underscores only. Created in ~/codeman-cases/</span>
3122
+ <input type="text" id="newCaseName" placeholder="my-project" pattern="[a-zA-Z0-9_-]+" autocomplete="off" autocapitalize="off" spellcheck="false" oninput="app.updateNewCasePathPreview()">
3123
+ <span class="form-hint" id="newCaseNameHint">Letters, numbers, hyphens, underscores only. Created in ~/codeman-cases/</span>
2977
3124
  </div>
2978
3125
  <div class="form-row">
2979
3126
  <label>Description (optional)</label>
2980
3127
  <input type="text" id="newCaseDescription" placeholder="A brief description..." autocomplete="off">
2981
3128
  </div>
3129
+ <div class="form-row" id="newCaseCustomPathToggleRow">
3130
+ <label class="checkbox-row"><input type="checkbox" id="newCaseCustomPathToggle" onchange="app.toggleNewCaseCustomPath()"> 📁 Create in a custom folder</label>
3131
+ <span class="form-hint">By default a new case is created under ~/codeman-cases. Choose another folder and the case is created there instead; it is listed like any other case.</span>
3132
+ </div>
3133
+ <div class="form-row" id="newCaseCustomPathRow" style="display:none">
3134
+ <label>Parent Folder</label>
3135
+ <div class="path-input-group">
3136
+ <input type="text" id="newCasePath" placeholder="~/projects" autocomplete="off" autocapitalize="off" autocorrect="off" spellcheck="false" oninput="app.updateNewCasePathPreview()">
3137
+ <button type="button" class="btn path-input-browse" onclick="app.openNewCasePathPicker()">Browse&hellip;</button>
3138
+ </div>
3139
+ <span class="form-hint" id="newCasePathPreview">Pick the folder the new case folder should be created inside.</span>
3140
+ </div>
2982
3141
  <div class="form-row docker-quick-row">
2983
- <label class="checkbox-row"><input type="checkbox" id="newCaseDocker"> 🐳 Run in an isolated Docker container</label>
3142
+ <label class="checkbox-row"><input type="checkbox" id="newCaseDocker" onchange="app.toggleNewCaseCustomPath()"> 🐳 Run in an isolated Docker container</label>
2984
3143
  <span class="form-hint">Runs this case in a hardened, isolated container. The base image is built automatically on first use. Docker/Podman must be installed.</span>
2985
3144
  <span class="form-hint">Already have a container running? <button type="button" class="btn-inline-check" id="dockerAdoptJumpBtn">Attach to it instead</button> Codeman only runs docker exec into it and never touches its lifecycle.</span>
2986
3145
  </div>
@@ -3835,47 +3994,51 @@
3835
3994
  <div class="approvals-list"></div>
3836
3995
  </div>
3837
3996
 
3838
- <script defer src="constants.d95fade9.js"></script>
3839
- <script defer src="i18n.398b8331.js"></script>
3840
- <script defer src="mobile-handlers.23a85db1.js"></script>
3841
- <script defer src="voice-input.c4b51eb6.js"></script>
3997
+ <script defer src="constants.c0f8f9de.js"></script>
3998
+ <script defer src="i18n.a31747a9.js"></script>
3999
+ <script defer src="mobile-handlers.0ef9c311.js"></script>
4000
+ <script defer src="voice-input.2342c37b.js"></script>
3842
4001
  <script defer src="notification-manager.50bb87d8.js"></script>
3843
4002
  <script defer src="keyboard-accessory.c1e7c7b7.js"></script>
3844
4003
  <script defer src="input-cjk.8bc46081.js"></script>
3845
4004
  <!-- Shows iOS Safari IME composition text inside the terminal. Must precede terminal-ui.js. -->
3846
4005
  <script defer src="mobile-ime-preview.2dbaf262.js"></script>
3847
4006
  <!-- Forwards committed input events that xterm drops on Android/GBoard soft keyboards. Must precede terminal-ui.js. -->
3848
- <script defer src="terminal-keycode229-recovery.fb91b25b.js"></script>
4007
+ <script defer src="terminal-keycode229-recovery.af09f99a.js"></script>
3849
4008
  <!-- Hardened markdown HTML sanitizer (wires DOMPurify). Must precede app.js. -->
3850
4009
  <script defer src="sanitize-html.e7147e76.js"></script>
3851
4010
  <!-- Owner tab layout projection (grouped vertical rail); pure, read by app.js. -->
3852
- <script defer src="tab-layout-browser.4e0ed50c.js"></script>
3853
- <script defer src="app.cf0db8ce.js"></script>
3854
- <script defer src="tab-rail-resize.dcdc5772.js"></script>
3855
- <script defer src="terminal-ui.a4535311.js"></script>
4011
+ <script defer src="tab-layout-browser.065fcba4.js"></script>
4012
+ <script defer src="app.abadc970.js"></script>
4013
+ <script defer src="tab-rail-resize.a5867793.js"></script>
4014
+ <script defer src="terminal-ui.5059b30e.js"></script>
4015
+ <script defer src="terminal-tile.js"></script>
3856
4016
  <script defer src="terminal-split.js"></script>
4017
+ <script defer src="tile-grid.js"></script>
3857
4018
  <script defer src="respawn-ui.ff0dae4c.js"></script>
3858
4019
  <script defer src="ralph-panel.b350e2ef.js"></script>
3859
4020
  <script defer src="orchestrator-panel.js"></script>
3860
4021
  <script defer src="cron-ui.js"></script>
3861
- <script defer src="settings-ui.331d3b1d.js"></script>
3862
- <script defer src="panels-ui.6a5898d9.js"></script>
4022
+ <script defer src="settings-ui.a97ab16f.js"></script>
4023
+ <script defer src="panels-ui.4735dfc2.js"></script>
3863
4024
  <script defer src="readmymind-ui.js"></script>
3864
4025
  <script defer src="ultracode-panel.js"></script>
3865
4026
  <script defer src="approvals-ui.js"></script>
3866
4027
  <script defer src="reboot-restore-ui.js"></script>
3867
4028
  <script defer src="admin-ui.js"></script>
3868
- <script defer src="session-ui.bf66c13b.js"></script>
4029
+ <script defer src="session-ui.f731602c.js"></script>
3869
4030
  <script defer src="host-wake-ui.js"></script>
3870
4031
  <script defer src="webview-tabs.js"></script>
3871
4032
  <script defer src="mobile-overview.js"></script>
3872
4033
  <script defer src="home-sessions.js"></script>
4034
+ <script defer src="git-status-ui.js"></script>
3873
4035
  <script defer src="entrance-animations.js"></script>
3874
4036
  <script defer src="ralph-wizard.b6c0dd19.js"></script>
3875
4037
  <script defer src="api-client.c9b1cddc.js"></script>
3876
4038
  <script defer src="subagent-windows.e6ca799f.js"></script>
3877
4039
  <script defer src="ultracode-windows.js"></script>
3878
4040
  <script defer src="session-lineage.js"></script>
3879
- <script defer src="image-input.a216cf9e.js"></script>
4041
+ <script defer src="image-input.898a538b.js"></script>
4042
+ <script defer src="spreadsheet-preview.af82fa2f.js"></script>
3880
4043
  </body>
3881
4044
  </html>
Binary file
Binary file
@@ -410,6 +410,12 @@ const KeyboardHandler = {
410
410
  const keyboardHeight = this.initialViewportHeight - (window.visualViewport.height || window.innerHeight);
411
411
  const accessoryBar = document.querySelector('.keyboard-accessory-bar');
412
412
 
413
+ // The mobile case picker is a third position:fixed bottom-anchored
414
+ // surface, and since it gained a search field the keyboard can open over
415
+ // it. iOS does not shrink the layout viewport, so an unlifted sheet sits
416
+ // BEHIND the keyboard with its own search box out of sight.
417
+ const caseSheet = document.querySelector('.mobile-case-picker.active .mobile-case-picker-sheet');
418
+
413
419
  if (isSmallMedium) {
414
420
  // Phones/small tablets: toolbar and accessory bar are position:fixed
415
421
  // via CSS. Use translateY to lift them above the keyboard.
@@ -426,6 +432,9 @@ const KeyboardHandler = {
426
432
  if (accessoryBar) {
427
433
  accessoryBar.style.transform = keyboardOffset > 0 ? `translateY(${-keyboardOffset}px)` : '';
428
434
  }
435
+ if (caseSheet) {
436
+ caseSheet.style.transform = keyboardOffset > 0 ? `translateY(${-keyboardOffset}px)` : '';
437
+ }
429
438
  if (main && keyboardHeight > 0) {
430
439
  const cjkInputHeight = cjkInput?.classList.contains('cjk-input-visible') ? 44 : 0;
431
440
  main.style.paddingBottom = `${84 + cjkInputHeight}px`;
@@ -436,6 +445,9 @@ const KeyboardHandler = {
436
445
  if (accessoryBar) {
437
446
  accessoryBar.style.bottom = `${keyboardHeight}px`;
438
447
  }
448
+ if (caseSheet) {
449
+ caseSheet.style.bottom = `${keyboardHeight}px`;
450
+ }
439
451
  }
440
452
 
441
453
  // CJK textarea positioning (always position:fixed on touch devices).
@@ -464,6 +476,10 @@ const KeyboardHandler = {
464
476
  const accessoryBar = document.querySelector('.keyboard-accessory-bar');
465
477
  const cjkInput = document.getElementById('cjkInput');
466
478
  const main = document.querySelector('.main');
479
+ // Not scoped to `.active`, unlike the lift above: a sheet closed while the
480
+ // keyboard was still up must still have its inline offset cleared, or the
481
+ // next open slides in already displaced.
482
+ const caseSheet = document.querySelector('.mobile-case-picker-sheet');
467
483
 
468
484
  if (toolbar) {
469
485
  toolbar.style.transform = '';
@@ -476,6 +492,10 @@ const KeyboardHandler = {
476
492
  cjkInput.style.transform = '';
477
493
  cjkInput.style.bottom = '';
478
494
  }
495
+ if (caseSheet) {
496
+ caseSheet.style.transform = '';
497
+ caseSheet.style.bottom = '';
498
+ }
479
499
  if (main) {
480
500
  main.style.paddingBottom = '';
481
501
  }