aicodeman 0.2.9 → 0.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (374) hide show
  1. package/README.md +118 -4
  2. package/dist/ai-idle-checker.d.ts.map +1 -1
  3. package/dist/ai-idle-checker.js +3 -2
  4. package/dist/ai-idle-checker.js.map +1 -1
  5. package/dist/ai-plan-checker.d.ts.map +1 -1
  6. package/dist/ai-plan-checker.js +3 -2
  7. package/dist/ai-plan-checker.js.map +1 -1
  8. package/dist/bash-tool-parser.d.ts +2 -3
  9. package/dist/bash-tool-parser.d.ts.map +1 -1
  10. package/dist/bash-tool-parser.js +14 -31
  11. package/dist/bash-tool-parser.js.map +1 -1
  12. package/dist/config/ai-defaults.d.ts +16 -0
  13. package/dist/config/ai-defaults.d.ts.map +1 -0
  14. package/dist/config/ai-defaults.js +16 -0
  15. package/dist/config/ai-defaults.js.map +1 -0
  16. package/dist/config/auth-config.d.ts +19 -0
  17. package/dist/config/auth-config.d.ts.map +1 -0
  18. package/dist/config/auth-config.js +28 -0
  19. package/dist/config/auth-config.js.map +1 -0
  20. package/dist/config/exec-timeout.d.ts +10 -0
  21. package/dist/config/exec-timeout.d.ts.map +1 -0
  22. package/dist/config/exec-timeout.js +10 -0
  23. package/dist/config/exec-timeout.js.map +1 -0
  24. package/dist/config/map-limits.d.ts +4 -0
  25. package/dist/config/map-limits.d.ts.map +1 -1
  26. package/dist/config/map-limits.js +7 -0
  27. package/dist/config/map-limits.js.map +1 -1
  28. package/dist/config/server-timing.d.ts +42 -0
  29. package/dist/config/server-timing.d.ts.map +1 -0
  30. package/dist/config/server-timing.js +57 -0
  31. package/dist/config/server-timing.js.map +1 -0
  32. package/dist/config/team-config.d.ts +16 -0
  33. package/dist/config/team-config.d.ts.map +1 -0
  34. package/dist/config/team-config.js +16 -0
  35. package/dist/config/team-config.js.map +1 -0
  36. package/dist/config/terminal-limits.d.ts +18 -0
  37. package/dist/config/terminal-limits.d.ts.map +1 -0
  38. package/dist/config/terminal-limits.js +18 -0
  39. package/dist/config/terminal-limits.js.map +1 -0
  40. package/dist/config/tunnel-config.d.ts +27 -0
  41. package/dist/config/tunnel-config.d.ts.map +1 -0
  42. package/dist/config/tunnel-config.js +36 -0
  43. package/dist/config/tunnel-config.js.map +1 -0
  44. package/dist/hooks-config.d.ts +21 -6
  45. package/dist/hooks-config.d.ts.map +1 -1
  46. package/dist/hooks-config.js +28 -12
  47. package/dist/hooks-config.js.map +1 -1
  48. package/dist/image-watcher.d.ts +4 -4
  49. package/dist/image-watcher.d.ts.map +1 -1
  50. package/dist/image-watcher.js +17 -30
  51. package/dist/image-watcher.js.map +1 -1
  52. package/dist/index.js +1 -2
  53. package/dist/index.js.map +1 -1
  54. package/dist/plan-orchestrator.d.ts +2 -24
  55. package/dist/plan-orchestrator.d.ts.map +1 -1
  56. package/dist/plan-orchestrator.js.map +1 -1
  57. package/dist/prompts/planner.d.ts +7 -8
  58. package/dist/prompts/planner.d.ts.map +1 -1
  59. package/dist/prompts/planner.js +7 -8
  60. package/dist/prompts/planner.js.map +1 -1
  61. package/dist/prompts/research-agent.d.ts +6 -4
  62. package/dist/prompts/research-agent.d.ts.map +1 -1
  63. package/dist/prompts/research-agent.js +6 -4
  64. package/dist/prompts/research-agent.js.map +1 -1
  65. package/dist/push-store.d.ts +1 -1
  66. package/dist/push-store.d.ts.map +1 -1
  67. package/dist/push-store.js +4 -12
  68. package/dist/push-store.js.map +1 -1
  69. package/dist/ralph-fix-plan-watcher.d.ts +91 -0
  70. package/dist/ralph-fix-plan-watcher.d.ts.map +1 -0
  71. package/dist/ralph-fix-plan-watcher.js +326 -0
  72. package/dist/ralph-fix-plan-watcher.js.map +1 -0
  73. package/dist/ralph-loop.d.ts +14 -4
  74. package/dist/ralph-loop.d.ts.map +1 -1
  75. package/dist/ralph-loop.js +14 -4
  76. package/dist/ralph-loop.js.map +1 -1
  77. package/dist/ralph-plan-tracker.d.ts +201 -0
  78. package/dist/ralph-plan-tracker.d.ts.map +1 -0
  79. package/dist/ralph-plan-tracker.js +325 -0
  80. package/dist/ralph-plan-tracker.js.map +1 -0
  81. package/dist/ralph-stall-detector.d.ts +84 -0
  82. package/dist/ralph-stall-detector.d.ts.map +1 -0
  83. package/dist/ralph-stall-detector.js +139 -0
  84. package/dist/ralph-stall-detector.js.map +1 -0
  85. package/dist/ralph-status-parser.d.ts +141 -0
  86. package/dist/ralph-status-parser.d.ts.map +1 -0
  87. package/dist/ralph-status-parser.js +478 -0
  88. package/dist/ralph-status-parser.js.map +1 -0
  89. package/dist/ralph-tracker.d.ts +218 -692
  90. package/dist/ralph-tracker.d.ts.map +1 -1
  91. package/dist/ralph-tracker.js +389 -1723
  92. package/dist/ralph-tracker.js.map +1 -1
  93. package/dist/respawn-adaptive-timing.d.ts +61 -0
  94. package/dist/respawn-adaptive-timing.d.ts.map +1 -0
  95. package/dist/respawn-adaptive-timing.js +105 -0
  96. package/dist/respawn-adaptive-timing.js.map +1 -0
  97. package/dist/respawn-controller.d.ts +35 -115
  98. package/dist/respawn-controller.d.ts.map +1 -1
  99. package/dist/respawn-controller.js +167 -607
  100. package/dist/respawn-controller.js.map +1 -1
  101. package/dist/respawn-health.d.ts +54 -0
  102. package/dist/respawn-health.d.ts.map +1 -0
  103. package/dist/respawn-health.js +183 -0
  104. package/dist/respawn-health.js.map +1 -0
  105. package/dist/respawn-metrics.d.ts +81 -0
  106. package/dist/respawn-metrics.d.ts.map +1 -0
  107. package/dist/respawn-metrics.js +198 -0
  108. package/dist/respawn-metrics.js.map +1 -0
  109. package/dist/respawn-patterns.d.ts +45 -0
  110. package/dist/respawn-patterns.d.ts.map +1 -0
  111. package/dist/respawn-patterns.js +125 -0
  112. package/dist/respawn-patterns.js.map +1 -0
  113. package/dist/session-auto-ops.d.ts +89 -0
  114. package/dist/session-auto-ops.d.ts.map +1 -0
  115. package/dist/session-auto-ops.js +224 -0
  116. package/dist/session-auto-ops.js.map +1 -0
  117. package/dist/session-cli-builder.d.ts +62 -0
  118. package/dist/session-cli-builder.d.ts.map +1 -0
  119. package/dist/session-cli-builder.js +121 -0
  120. package/dist/session-cli-builder.js.map +1 -0
  121. package/dist/session-manager.d.ts +17 -5
  122. package/dist/session-manager.d.ts.map +1 -1
  123. package/dist/session-manager.js +17 -5
  124. package/dist/session-manager.js.map +1 -1
  125. package/dist/session-task-cache.d.ts +52 -0
  126. package/dist/session-task-cache.d.ts.map +1 -0
  127. package/dist/session-task-cache.js +90 -0
  128. package/dist/session-task-cache.js.map +1 -0
  129. package/dist/session.d.ts +23 -41
  130. package/dist/session.d.ts.map +1 -1
  131. package/dist/session.js +79 -317
  132. package/dist/session.js.map +1 -1
  133. package/dist/state-store.d.ts +19 -9
  134. package/dist/state-store.d.ts.map +1 -1
  135. package/dist/state-store.js +29 -30
  136. package/dist/state-store.js.map +1 -1
  137. package/dist/subagent-watcher.d.ts +26 -7
  138. package/dist/subagent-watcher.d.ts.map +1 -1
  139. package/dist/subagent-watcher.js +47 -64
  140. package/dist/subagent-watcher.js.map +1 -1
  141. package/dist/team-watcher.d.ts.map +1 -1
  142. package/dist/team-watcher.js +2 -5
  143. package/dist/team-watcher.js.map +1 -1
  144. package/dist/tmux-manager.d.ts.map +1 -1
  145. package/dist/tmux-manager.js +1 -2
  146. package/dist/tmux-manager.js.map +1 -1
  147. package/dist/tunnel-manager.d.ts +26 -0
  148. package/dist/tunnel-manager.d.ts.map +1 -1
  149. package/dist/tunnel-manager.js +126 -7
  150. package/dist/tunnel-manager.js.map +1 -1
  151. package/dist/types/api.d.ts +108 -0
  152. package/dist/types/api.d.ts.map +1 -0
  153. package/dist/types/api.js +98 -0
  154. package/dist/types/api.js.map +1 -0
  155. package/dist/types/app-state.d.ts +117 -0
  156. package/dist/types/app-state.d.ts.map +1 -0
  157. package/dist/types/app-state.js +76 -0
  158. package/dist/types/app-state.js.map +1 -0
  159. package/dist/types/common.d.ts +79 -0
  160. package/dist/types/common.d.ts.map +1 -0
  161. package/dist/types/common.js +17 -0
  162. package/dist/types/common.js.map +1 -0
  163. package/dist/types/index.d.ts +66 -0
  164. package/dist/types/index.d.ts.map +1 -0
  165. package/dist/types/index.js +66 -0
  166. package/dist/types/index.js.map +1 -0
  167. package/dist/types/lifecycle.d.ts +28 -0
  168. package/dist/types/lifecycle.d.ts.map +1 -0
  169. package/dist/types/lifecycle.js +16 -0
  170. package/dist/types/lifecycle.js.map +1 -0
  171. package/dist/types/plan.d.ts +45 -0
  172. package/dist/types/plan.d.ts.map +1 -0
  173. package/dist/types/plan.js +18 -0
  174. package/dist/types/plan.js.map +1 -0
  175. package/dist/types/push.d.ts +36 -0
  176. package/dist/types/push.d.ts.map +1 -0
  177. package/dist/types/push.js +18 -0
  178. package/dist/types/push.js.map +1 -0
  179. package/dist/types/ralph.d.ts +262 -0
  180. package/dist/types/ralph.d.ts.map +1 -0
  181. package/dist/types/ralph.js +70 -0
  182. package/dist/types/ralph.js.map +1 -0
  183. package/dist/types/respawn.d.ts +271 -0
  184. package/dist/types/respawn.d.ts.map +1 -0
  185. package/dist/types/respawn.js +26 -0
  186. package/dist/types/respawn.js.map +1 -0
  187. package/dist/types/run-summary.d.ts +96 -0
  188. package/dist/types/run-summary.d.ts.map +1 -0
  189. package/dist/types/run-summary.js +37 -0
  190. package/dist/types/run-summary.js.map +1 -0
  191. package/dist/types/session.d.ts +152 -0
  192. package/dist/types/session.d.ts.map +1 -0
  193. package/dist/types/session.js +27 -0
  194. package/dist/types/session.js.map +1 -0
  195. package/dist/types/task.d.ts +72 -0
  196. package/dist/types/task.d.ts.map +1 -0
  197. package/dist/types/task.js +19 -0
  198. package/dist/types/task.js.map +1 -0
  199. package/dist/types/teams.d.ts +73 -0
  200. package/dist/types/teams.d.ts.map +1 -0
  201. package/dist/types/teams.js +23 -0
  202. package/dist/types/teams.js.map +1 -0
  203. package/dist/types/tools.d.ts +61 -0
  204. package/dist/types/tools.d.ts.map +1 -0
  205. package/dist/types/tools.js +20 -0
  206. package/dist/types/tools.js.map +1 -0
  207. package/dist/types.d.ts +8 -1134
  208. package/dist/types.d.ts.map +1 -1
  209. package/dist/types.js +8 -210
  210. package/dist/types.js.map +1 -1
  211. package/dist/utils/claude-cli-resolver.d.ts.map +1 -1
  212. package/dist/utils/claude-cli-resolver.js +1 -2
  213. package/dist/utils/claude-cli-resolver.js.map +1 -1
  214. package/dist/utils/debouncer.d.ts +111 -0
  215. package/dist/utils/debouncer.d.ts.map +1 -0
  216. package/dist/utils/debouncer.js +162 -0
  217. package/dist/utils/debouncer.js.map +1 -0
  218. package/dist/utils/index.d.ts +3 -2
  219. package/dist/utils/index.d.ts.map +1 -1
  220. package/dist/utils/index.js +3 -2
  221. package/dist/utils/index.js.map +1 -1
  222. package/dist/utils/opencode-cli-resolver.d.ts.map +1 -1
  223. package/dist/utils/opencode-cli-resolver.js +1 -2
  224. package/dist/utils/opencode-cli-resolver.js.map +1 -1
  225. package/dist/utils/string-similarity.d.ts +0 -57
  226. package/dist/utils/string-similarity.d.ts.map +1 -1
  227. package/dist/utils/string-similarity.js +3 -18
  228. package/dist/utils/string-similarity.js.map +1 -1
  229. package/dist/web/middleware/auth.d.ts +31 -0
  230. package/dist/web/middleware/auth.d.ts.map +1 -0
  231. package/dist/web/middleware/auth.js +154 -0
  232. package/dist/web/middleware/auth.js.map +1 -0
  233. package/dist/web/ports/auth-port.d.ts +18 -0
  234. package/dist/web/ports/auth-port.d.ts.map +1 -0
  235. package/dist/web/ports/auth-port.js +6 -0
  236. package/dist/web/ports/auth-port.js.map +1 -0
  237. package/dist/web/ports/config-port.d.ts +28 -0
  238. package/dist/web/ports/config-port.d.ts.map +1 -0
  239. package/dist/web/ports/config-port.js +6 -0
  240. package/dist/web/ports/config-port.js.map +1 -0
  241. package/dist/web/ports/event-port.d.ts +13 -0
  242. package/dist/web/ports/event-port.d.ts.map +1 -0
  243. package/dist/web/ports/event-port.js +6 -0
  244. package/dist/web/ports/event-port.js.map +1 -0
  245. package/dist/web/ports/index.d.ts +14 -0
  246. package/dist/web/ports/index.d.ts.map +1 -0
  247. package/dist/web/ports/index.js +9 -0
  248. package/dist/web/ports/index.js.map +1 -0
  249. package/dist/web/ports/infra-port.d.ts +36 -0
  250. package/dist/web/ports/infra-port.d.ts.map +1 -0
  251. package/dist/web/ports/infra-port.js +6 -0
  252. package/dist/web/ports/infra-port.js.map +1 -0
  253. package/dist/web/ports/respawn-port.d.ts +20 -0
  254. package/dist/web/ports/respawn-port.d.ts.map +1 -0
  255. package/dist/web/ports/respawn-port.js +6 -0
  256. package/dist/web/ports/respawn-port.js.map +1 -0
  257. package/dist/web/ports/session-port.d.ts +15 -0
  258. package/dist/web/ports/session-port.d.ts.map +1 -0
  259. package/dist/web/ports/session-port.js +6 -0
  260. package/dist/web/ports/session-port.js.map +1 -0
  261. package/dist/web/public/api-client.js +82 -0
  262. package/dist/web/public/api-client.js.br +0 -0
  263. package/dist/web/public/api-client.js.gz +0 -0
  264. package/dist/web/public/app.js +117 -201
  265. package/dist/web/public/app.js.br +0 -0
  266. package/dist/web/public/app.js.gz +0 -0
  267. package/dist/web/public/constants.js +365 -0
  268. package/dist/web/public/constants.js.br +0 -0
  269. package/dist/web/public/constants.js.gz +0 -0
  270. package/dist/web/public/index.html +15 -3
  271. package/dist/web/public/index.html.br +0 -0
  272. package/dist/web/public/index.html.gz +0 -0
  273. package/dist/web/public/keyboard-accessory.js +302 -0
  274. package/dist/web/public/keyboard-accessory.js.br +0 -0
  275. package/dist/web/public/keyboard-accessory.js.gz +0 -0
  276. package/dist/web/public/mobile-handlers.js +491 -0
  277. package/dist/web/public/mobile-handlers.js.br +0 -0
  278. package/dist/web/public/mobile-handlers.js.gz +0 -0
  279. package/dist/web/public/mobile.css.gz +0 -0
  280. package/dist/web/public/notification-manager.js +472 -0
  281. package/dist/web/public/notification-manager.js.br +0 -0
  282. package/dist/web/public/notification-manager.js.gz +0 -0
  283. package/dist/web/public/ralph-wizard.js +33 -9
  284. package/dist/web/public/ralph-wizard.js.br +0 -0
  285. package/dist/web/public/ralph-wizard.js.gz +0 -0
  286. package/dist/web/public/styles.css.gz +0 -0
  287. package/dist/web/public/subagent-windows.js +1149 -0
  288. package/dist/web/public/subagent-windows.js.br +0 -0
  289. package/dist/web/public/subagent-windows.js.gz +0 -0
  290. package/dist/web/public/sw.js +15 -0
  291. package/dist/web/public/sw.js.br +0 -0
  292. package/dist/web/public/sw.js.gz +0 -0
  293. package/dist/web/public/upload.html.gz +0 -0
  294. package/dist/web/public/vendor/xterm-addon-fit.min.js.gz +0 -0
  295. package/dist/web/public/vendor/xterm-addon-unicode11.min.js.gz +0 -0
  296. package/dist/web/public/vendor/xterm-addon-webgl.min.js.gz +0 -0
  297. package/dist/web/public/vendor/xterm-zerolag-input.js +4 -0
  298. package/dist/web/public/vendor/xterm-zerolag-input.js.br +0 -0
  299. package/dist/web/public/vendor/xterm-zerolag-input.js.gz +0 -0
  300. package/dist/web/public/vendor/xterm.css.gz +0 -0
  301. package/dist/web/public/vendor/xterm.min.js.gz +0 -0
  302. package/dist/web/public/voice-input.js +882 -0
  303. package/dist/web/public/voice-input.js.br +0 -0
  304. package/dist/web/public/voice-input.js.gz +0 -0
  305. package/dist/web/route-helpers.d.ts +38 -0
  306. package/dist/web/route-helpers.d.ts.map +1 -0
  307. package/dist/web/route-helpers.js +144 -0
  308. package/dist/web/route-helpers.js.map +1 -0
  309. package/dist/web/routes/case-routes.d.ts +9 -0
  310. package/dist/web/routes/case-routes.d.ts.map +1 -0
  311. package/dist/web/routes/case-routes.js +426 -0
  312. package/dist/web/routes/case-routes.js.map +1 -0
  313. package/dist/web/routes/file-routes.d.ts +8 -0
  314. package/dist/web/routes/file-routes.d.ts.map +1 -0
  315. package/dist/web/routes/file-routes.js +337 -0
  316. package/dist/web/routes/file-routes.js.map +1 -0
  317. package/dist/web/routes/hook-event-routes.d.ts +9 -0
  318. package/dist/web/routes/hook-event-routes.d.ts.map +1 -0
  319. package/dist/web/routes/hook-event-routes.js +57 -0
  320. package/dist/web/routes/hook-event-routes.js.map +1 -0
  321. package/dist/web/routes/index.d.ts +16 -0
  322. package/dist/web/routes/index.d.ts.map +1 -0
  323. package/dist/web/routes/index.js +16 -0
  324. package/dist/web/routes/index.js.map +1 -0
  325. package/dist/web/routes/mux-routes.d.ts +8 -0
  326. package/dist/web/routes/mux-routes.d.ts.map +1 -0
  327. package/dist/web/routes/mux-routes.js +32 -0
  328. package/dist/web/routes/mux-routes.js.map +1 -0
  329. package/dist/web/routes/plan-routes.d.ts +9 -0
  330. package/dist/web/routes/plan-routes.d.ts.map +1 -0
  331. package/dist/web/routes/plan-routes.js +385 -0
  332. package/dist/web/routes/plan-routes.js.map +1 -0
  333. package/dist/web/routes/push-routes.d.ts +8 -0
  334. package/dist/web/routes/push-routes.d.ts.map +1 -0
  335. package/dist/web/routes/push-routes.js +49 -0
  336. package/dist/web/routes/push-routes.js.map +1 -0
  337. package/dist/web/routes/ralph-routes.d.ts +9 -0
  338. package/dist/web/routes/ralph-routes.d.ts.map +1 -0
  339. package/dist/web/routes/ralph-routes.js +485 -0
  340. package/dist/web/routes/ralph-routes.js.map +1 -0
  341. package/dist/web/routes/respawn-routes.d.ts +8 -0
  342. package/dist/web/routes/respawn-routes.d.ts.map +1 -0
  343. package/dist/web/routes/respawn-routes.js +270 -0
  344. package/dist/web/routes/respawn-routes.js.map +1 -0
  345. package/dist/web/routes/scheduled-routes.d.ts +8 -0
  346. package/dist/web/routes/scheduled-routes.d.ts.map +1 -0
  347. package/dist/web/routes/scheduled-routes.js +51 -0
  348. package/dist/web/routes/scheduled-routes.js.map +1 -0
  349. package/dist/web/routes/session-routes.d.ts +9 -0
  350. package/dist/web/routes/session-routes.d.ts.map +1 -0
  351. package/dist/web/routes/session-routes.js +751 -0
  352. package/dist/web/routes/session-routes.js.map +1 -0
  353. package/dist/web/routes/system-routes.d.ts +9 -0
  354. package/dist/web/routes/system-routes.d.ts.map +1 -0
  355. package/dist/web/routes/system-routes.js +699 -0
  356. package/dist/web/routes/system-routes.js.map +1 -0
  357. package/dist/web/routes/team-routes.d.ts +8 -0
  358. package/dist/web/routes/team-routes.d.ts.map +1 -0
  359. package/dist/web/routes/team-routes.js +14 -0
  360. package/dist/web/routes/team-routes.js.map +1 -0
  361. package/dist/web/schemas.d.ts +43 -3
  362. package/dist/web/schemas.d.ts.map +1 -1
  363. package/dist/web/schemas.js +6 -2
  364. package/dist/web/schemas.js.map +1 -1
  365. package/dist/web/server.d.ts +35 -15
  366. package/dist/web/server.d.ts.map +1 -1
  367. package/dist/web/server.js +563 -3971
  368. package/dist/web/server.js.map +1 -1
  369. package/dist/web/sse-events.d.ts +361 -0
  370. package/dist/web/sse-events.d.ts.map +1 -0
  371. package/dist/web/sse-events.js +396 -0
  372. package/dist/web/sse-events.js.map +1 -0
  373. package/package.json +2 -1
  374. package/scripts/postinstall.js +58 -0
@@ -0,0 +1,302 @@
1
+ /**
2
+ * @fileoverview Mobile keyboard accessory bar and modal focus trap.
3
+ *
4
+ * Defines two exports:
5
+ *
6
+ * - KeyboardAccessoryBar (singleton object) — Quick action buttons shown above the virtual
7
+ * keyboard on mobile: arrow up/down, /init, /clear, /compact, paste, and dismiss.
8
+ * Destructive actions (/clear, /compact) require double-tap confirmation (2s amber state).
9
+ * Commands are sent as text + Enter separately for Ink compatibility.
10
+ * Only initializes on touch devices (MobileDetection.isTouchDevice guard).
11
+ *
12
+ * - FocusTrap (class) — Traps Tab/Shift+Tab keyboard focus within a modal element.
13
+ * Saves and restores previously focused element on deactivate. Used by Ralph wizard
14
+ * and other modal dialogs.
15
+ *
16
+ * @globals {object} KeyboardAccessoryBar
17
+ * @globals {class} FocusTrap
18
+ *
19
+ * @dependency mobile-handlers.js (MobileDetection.isTouchDevice)
20
+ * @dependency app.js (uses global `app` for sendInput, activeSessionId, terminal)
21
+ * @loadorder 5 of 9 — loaded after notification-manager.js, before app.js
22
+ */
23
+
24
+ // Codeman — Keyboard accessory bar and focus trap for modals
25
+ // Loaded after mobile-handlers.js, before app.js
26
+
27
+ // ═══════════════════════════════════════════════════════════════
28
+ // Mobile Keyboard Accessory Bar
29
+ // ═══════════════════════════════════════════════════════════════
30
+
31
+ /**
32
+ * KeyboardAccessoryBar - Quick action buttons shown above keyboard when typing.
33
+ */
34
+ const KeyboardAccessoryBar = {
35
+ element: null,
36
+
37
+ /** Create and inject the accessory bar */
38
+ init() {
39
+ // Only on mobile
40
+ if (!MobileDetection.isTouchDevice()) return;
41
+
42
+ // Create accessory bar element
43
+ this.element = document.createElement('div');
44
+ this.element.className = 'keyboard-accessory-bar';
45
+ this.element.innerHTML = `
46
+ <button class="accessory-btn accessory-btn-arrow" data-action="scroll-up" title="Arrow up">
47
+ <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5">
48
+ <path d="M5 15l7-7 7 7"/>
49
+ </svg>
50
+ </button>
51
+ <button class="accessory-btn accessory-btn-arrow" data-action="scroll-down" title="Arrow down">
52
+ <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5">
53
+ <path d="M19 9l-7 7-7-7"/>
54
+ </svg>
55
+ </button>
56
+ <button class="accessory-btn" data-action="init" title="/init">/init</button>
57
+ <button class="accessory-btn" data-action="clear" title="/clear">/clear</button>
58
+ <button class="accessory-btn" data-action="compact" title="/compact">/compact</button>
59
+ <button class="accessory-btn" data-action="paste" title="Paste from clipboard">
60
+ <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
61
+ <path d="M16 4h2a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2h2"/>
62
+ <rect x="8" y="2" width="8" height="4" rx="1" ry="1"/>
63
+ </svg>
64
+ </button>
65
+ <button class="accessory-btn accessory-btn-dismiss" data-action="dismiss" title="Dismiss keyboard">
66
+ <svg width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3">
67
+ <path d="M19 9l-7 7-7-7"/>
68
+ </svg>
69
+ </button>
70
+ `;
71
+
72
+ // Add click handlers — preventDefault stops event from reaching terminal
73
+ this.element.addEventListener('click', (e) => {
74
+ const btn = e.target.closest('.accessory-btn');
75
+ if (!btn) return;
76
+ e.preventDefault();
77
+ e.stopPropagation();
78
+
79
+ const action = btn.dataset.action;
80
+ this.handleAction(action, btn);
81
+
82
+ // Refocus terminal so keyboard stays open (tap blurs terminal → keyboard dismisses → toolbar shifts)
83
+ if ((action === 'scroll-up' || action === 'scroll-down') ||
84
+ ((action === 'clear' || action === 'compact') && this._confirmAction)) {
85
+ if (typeof app !== 'undefined' && app.terminal) {
86
+ app.terminal.focus();
87
+ }
88
+ }
89
+ });
90
+
91
+ // Insert before toolbar
92
+ const toolbar = document.querySelector('.toolbar');
93
+ if (toolbar && toolbar.parentNode) {
94
+ toolbar.parentNode.insertBefore(this.element, toolbar);
95
+ }
96
+ },
97
+
98
+ _confirmTimer: null,
99
+ _confirmAction: null,
100
+
101
+ /** Handle accessory button actions */
102
+ handleAction(action, btn) {
103
+ if (typeof app === 'undefined' || !app.activeSessionId) return;
104
+
105
+ switch (action) {
106
+ case 'scroll-up':
107
+ this.sendKey('\x1b[A');
108
+ break;
109
+ case 'scroll-down':
110
+ this.sendKey('\x1b[B');
111
+ break;
112
+ case 'init':
113
+ this.sendCommand('/init');
114
+ break;
115
+ case 'clear':
116
+ case 'compact': {
117
+ // Require double-tap: first tap turns amber, second tap within 2s sends
118
+ const cmd = action === 'clear' ? '/clear' : '/compact';
119
+ if (this._confirmAction === action && this._confirmTimer) {
120
+ this.clearConfirm();
121
+ this.sendCommand(cmd);
122
+ } else {
123
+ this.setConfirm(action, btn);
124
+ }
125
+ break;
126
+ }
127
+ case 'paste':
128
+ this.pasteFromClipboard();
129
+ break;
130
+ case 'dismiss':
131
+ // Blur active element to dismiss keyboard
132
+ document.activeElement?.blur();
133
+ break;
134
+ }
135
+ },
136
+
137
+ /** Enter confirm state: button turns amber for 2s waiting for second tap */
138
+ setConfirm(action, btn) {
139
+ this.clearConfirm();
140
+ this._confirmAction = action;
141
+ if (btn) {
142
+ btn.classList.add('confirming');
143
+ btn.dataset.origHtml = btn.innerHTML;
144
+ btn.textContent = 'Tap again';
145
+ }
146
+ this._confirmTimer = setTimeout(() => this.clearConfirm(), 2000);
147
+ },
148
+
149
+ /** Reset confirm state */
150
+ clearConfirm() {
151
+ if (this._confirmTimer) {
152
+ clearTimeout(this._confirmTimer);
153
+ this._confirmTimer = null;
154
+ }
155
+ if (this._confirmAction && this.element) {
156
+ const btn = this.element.querySelector(`[data-action="${this._confirmAction}"]`);
157
+ if (btn && btn.dataset.origHtml) {
158
+ btn.innerHTML = btn.dataset.origHtml;
159
+ delete btn.dataset.origHtml;
160
+ }
161
+ if (btn) btn.classList.remove('confirming');
162
+ }
163
+ this._confirmAction = null;
164
+ },
165
+
166
+ /** Send a slash command to the active session.
167
+ * Sends text and Enter separately so Ink processes them as distinct events. */
168
+ sendCommand(command) {
169
+ if (!app.activeSessionId) return;
170
+ // Send command text first (without Enter)
171
+ app.sendInput(command);
172
+ // Send Enter separately after a brief delay so Ink has time to process the text.
173
+ setTimeout(() => app.sendInput('\r'), 120);
174
+ },
175
+
176
+ /** Send a special key (arrow, escape, etc.) directly to the PTY.
177
+ * Bypasses tmux send-keys -l (literal mode) since escape sequences
178
+ * must be written raw to be interpreted as key presses by Ink. */
179
+ sendKey(escapeSequence) {
180
+ if (!app.activeSessionId) return;
181
+ fetch(`/api/sessions/${app.activeSessionId}/input`, {
182
+ method: 'POST',
183
+ headers: { 'Content-Type': 'application/json' },
184
+ body: JSON.stringify({ input: escapeSequence })
185
+ }).catch(() => {});
186
+ },
187
+
188
+ /** Read clipboard and send contents as input */
189
+ /** Show a paste overlay with a textarea for iOS compatibility */
190
+ pasteFromClipboard() {
191
+ if (typeof app === 'undefined' || !app.activeSessionId) return;
192
+
193
+ // Create overlay
194
+ const overlay = document.createElement('div');
195
+ overlay.className = 'paste-overlay';
196
+ overlay.innerHTML = `
197
+ <div class="paste-dialog">
198
+ <textarea class="paste-textarea" placeholder="Long-press here and tap Paste"></textarea>
199
+ <div class="paste-actions">
200
+ <button class="paste-cancel">Cancel</button>
201
+ <button class="paste-send">Send</button>
202
+ </div>
203
+ </div>
204
+ `;
205
+
206
+ const textarea = overlay.querySelector('.paste-textarea');
207
+ const send = () => {
208
+ const text = textarea.value;
209
+ overlay.remove();
210
+ if (text) app.sendInput(text);
211
+ };
212
+ overlay.querySelector('.paste-cancel').addEventListener('click', () => overlay.remove());
213
+ overlay.querySelector('.paste-send').addEventListener('click', send);
214
+ overlay.addEventListener('click', (e) => { if (e.target === overlay) overlay.remove(); });
215
+
216
+ document.body.appendChild(overlay);
217
+ textarea.focus();
218
+ },
219
+
220
+ /** Show the accessory bar */
221
+ show() {
222
+ if (this.element) {
223
+ this.element.classList.add('visible');
224
+ }
225
+ },
226
+
227
+ /** Hide the accessory bar */
228
+ hide() {
229
+ if (this.element) {
230
+ this.element.classList.remove('visible');
231
+ }
232
+ }
233
+ };
234
+
235
+ // ═══════════════════════════════════════════════════════════════
236
+ // Accessibility: Focus Trap for Modals
237
+ // ═══════════════════════════════════════════════════════════════
238
+
239
+ /**
240
+ * FocusTrap - Traps keyboard focus within an element (typically a modal).
241
+ * Saves the previously focused element and restores focus when deactivated.
242
+ */
243
+ class FocusTrap {
244
+ constructor(element) {
245
+ this.element = element;
246
+ this.previouslyFocused = null;
247
+ this.boundHandleKeydown = this.handleKeydown.bind(this);
248
+ }
249
+
250
+ activate() {
251
+ this.previouslyFocused = document.activeElement;
252
+ this.element.addEventListener('keydown', this.boundHandleKeydown);
253
+
254
+ // Focus first focusable element after a brief delay (for CSS transitions)
255
+ requestAnimationFrame(() => {
256
+ const focusable = this.getFocusableElements();
257
+ if (focusable.length) {
258
+ focusable[0].focus();
259
+ }
260
+ });
261
+ }
262
+
263
+ deactivate() {
264
+ this.element.removeEventListener('keydown', this.boundHandleKeydown);
265
+ if (this.previouslyFocused && typeof this.previouslyFocused.focus === 'function') {
266
+ this.previouslyFocused.focus();
267
+ }
268
+ }
269
+
270
+ getFocusableElements() {
271
+ const selector = [
272
+ 'button:not([disabled]):not([tabindex="-1"])',
273
+ 'input:not([disabled]):not([tabindex="-1"])',
274
+ 'select:not([disabled]):not([tabindex="-1"])',
275
+ 'textarea:not([disabled]):not([tabindex="-1"])',
276
+ 'a[href]:not([tabindex="-1"])',
277
+ '[tabindex]:not([tabindex="-1"]):not([disabled])'
278
+ ].join(', ');
279
+
280
+ return [...this.element.querySelectorAll(selector)].filter(
281
+ el => el.offsetParent !== null // Exclude hidden elements
282
+ );
283
+ }
284
+
285
+ handleKeydown(e) {
286
+ if (e.key !== 'Tab') return;
287
+
288
+ const focusable = this.getFocusableElements();
289
+ if (focusable.length === 0) return;
290
+
291
+ const first = focusable[0];
292
+ const last = focusable[focusable.length - 1];
293
+
294
+ if (e.shiftKey && document.activeElement === first) {
295
+ e.preventDefault();
296
+ last.focus();
297
+ } else if (!e.shiftKey && document.activeElement === last) {
298
+ e.preventDefault();
299
+ first.focus();
300
+ }
301
+ }
302
+ }