claude-code-plus-plus 0.1.1 → 0.2.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 (493) hide show
  1. package/dist/app.d.ts +52 -0
  2. package/dist/app.d.ts.map +1 -0
  3. package/dist/app.js +318 -0
  4. package/dist/app.js.map +1 -0
  5. package/dist/cli/commands/index.d.ts +5 -0
  6. package/dist/cli/commands/index.d.ts.map +1 -0
  7. package/dist/cli/commands/index.js +9 -0
  8. package/dist/cli/commands/index.js.map +1 -0
  9. package/dist/cli/commands/start.d.ts +16 -0
  10. package/dist/cli/commands/start.d.ts.map +1 -0
  11. package/dist/cli/commands/start.js +23 -0
  12. package/dist/cli/commands/start.js.map +1 -0
  13. package/dist/cli/index.d.ts +7 -0
  14. package/dist/cli/index.d.ts.map +1 -0
  15. package/dist/cli/index.js +22 -0
  16. package/dist/cli/index.js.map +1 -0
  17. package/dist/cli/parser.d.ts +31 -0
  18. package/dist/cli/parser.d.ts.map +1 -0
  19. package/dist/cli/parser.js +117 -0
  20. package/dist/cli/parser.js.map +1 -0
  21. package/dist/cli/validators.d.ts +46 -0
  22. package/dist/cli/validators.d.ts.map +1 -0
  23. package/dist/cli/validators.js +159 -0
  24. package/dist/cli/validators.js.map +1 -0
  25. package/dist/config/defaults.d.ts +14 -0
  26. package/dist/config/defaults.d.ts.map +1 -0
  27. package/dist/config/defaults.js +32 -0
  28. package/dist/config/defaults.js.map +1 -0
  29. package/dist/config/index.d.ts +10 -0
  30. package/dist/config/index.d.ts.map +1 -0
  31. package/dist/config/index.js +32 -0
  32. package/dist/config/index.js.map +1 -0
  33. package/dist/config/loader.d.ts +38 -0
  34. package/dist/config/loader.d.ts.map +1 -0
  35. package/dist/config/loader.js +116 -0
  36. package/dist/config/loader.js.map +1 -0
  37. package/dist/config/paths.d.ts +47 -0
  38. package/dist/config/paths.d.ts.map +1 -0
  39. package/dist/config/paths.js +95 -0
  40. package/dist/config/paths.js.map +1 -0
  41. package/dist/config/schema.d.ts +15 -0
  42. package/dist/config/schema.d.ts.map +1 -0
  43. package/dist/config/schema.js +36 -0
  44. package/dist/config/schema.js.map +1 -0
  45. package/dist/core/index.d.ts +13 -1
  46. package/dist/core/index.d.ts.map +1 -1
  47. package/dist/core/index.js +13 -1
  48. package/dist/core/index.js.map +1 -1
  49. package/dist/core/session/index.d.ts +7 -0
  50. package/dist/core/session/index.d.ts.map +1 -0
  51. package/dist/core/session/index.js +6 -0
  52. package/dist/core/session/index.js.map +1 -0
  53. package/dist/core/session/session-manager.d.ts +46 -0
  54. package/dist/core/session/session-manager.d.ts.map +1 -0
  55. package/dist/core/session/session-manager.js +245 -0
  56. package/dist/core/session/session-manager.js.map +1 -0
  57. package/dist/core/session/session.d.ts +44 -0
  58. package/dist/core/session/session.d.ts.map +1 -0
  59. package/dist/core/session/session.js +78 -0
  60. package/dist/core/session/session.js.map +1 -0
  61. package/dist/core/terminal/index.d.ts +7 -0
  62. package/dist/core/terminal/index.d.ts.map +1 -0
  63. package/dist/core/terminal/index.js +6 -0
  64. package/dist/core/terminal/index.js.map +1 -0
  65. package/dist/core/terminal/terminal-manager.d.ts +36 -0
  66. package/dist/core/terminal/terminal-manager.d.ts.map +1 -0
  67. package/dist/core/terminal/terminal-manager.js +182 -0
  68. package/dist/core/terminal/terminal-manager.js.map +1 -0
  69. package/dist/core/terminal/terminal.d.ts +27 -0
  70. package/dist/core/terminal/terminal.d.ts.map +1 -0
  71. package/dist/core/terminal/terminal.js +38 -0
  72. package/dist/core/terminal/terminal.js.map +1 -0
  73. package/dist/core/workspace/index.d.ts +7 -0
  74. package/dist/core/workspace/index.d.ts.map +1 -0
  75. package/dist/core/workspace/index.js +6 -0
  76. package/dist/core/workspace/index.js.map +1 -0
  77. package/dist/core/workspace/workspace-manager.d.ts +59 -0
  78. package/dist/core/workspace/workspace-manager.d.ts.map +1 -0
  79. package/dist/core/workspace/workspace-manager.js +224 -0
  80. package/dist/core/workspace/workspace-manager.js.map +1 -0
  81. package/dist/core/workspace/workspace.d.ts +56 -0
  82. package/dist/core/workspace/workspace.d.ts.map +1 -0
  83. package/dist/core/workspace/workspace.js +96 -0
  84. package/dist/core/workspace/workspace.js.map +1 -0
  85. package/dist/core/worktree/index.d.ts +7 -0
  86. package/dist/core/worktree/index.d.ts.map +1 -0
  87. package/dist/core/worktree/index.js +6 -0
  88. package/dist/core/worktree/index.js.map +1 -0
  89. package/dist/core/worktree/worktree-manager.d.ts +47 -0
  90. package/dist/core/worktree/worktree-manager.d.ts.map +1 -0
  91. package/dist/core/worktree/worktree-manager.js +170 -0
  92. package/dist/core/worktree/worktree-manager.js.map +1 -0
  93. package/dist/core/worktree/worktree.d.ts +35 -0
  94. package/dist/core/worktree/worktree.d.ts.map +1 -0
  95. package/dist/core/worktree/worktree.js +50 -0
  96. package/dist/core/worktree/worktree.js.map +1 -0
  97. package/dist/events/bus.d.ts +76 -0
  98. package/dist/events/bus.d.ts.map +1 -0
  99. package/dist/events/bus.js +223 -0
  100. package/dist/events/bus.js.map +1 -0
  101. package/dist/events/handlers/index.d.ts +28 -0
  102. package/dist/events/handlers/index.d.ts.map +1 -0
  103. package/dist/events/handlers/index.js +44 -0
  104. package/dist/events/handlers/index.js.map +1 -0
  105. package/dist/events/handlers/session-events.d.ts +12 -0
  106. package/dist/events/handlers/session-events.d.ts.map +1 -0
  107. package/dist/events/handlers/session-events.js +38 -0
  108. package/dist/events/handlers/session-events.js.map +1 -0
  109. package/dist/events/handlers/terminal-events.d.ts +12 -0
  110. package/dist/events/handlers/terminal-events.d.ts.map +1 -0
  111. package/dist/events/handlers/terminal-events.js +37 -0
  112. package/dist/events/handlers/terminal-events.js.map +1 -0
  113. package/dist/events/handlers/ui-events.d.ts +11 -0
  114. package/dist/events/handlers/ui-events.d.ts.map +1 -0
  115. package/dist/events/handlers/ui-events.js +202 -0
  116. package/dist/events/handlers/ui-events.js.map +1 -0
  117. package/dist/events/index.d.ts +9 -0
  118. package/dist/events/index.d.ts.map +1 -0
  119. package/dist/events/index.js +10 -0
  120. package/dist/events/index.js.map +1 -0
  121. package/dist/git/index.d.ts +5 -0
  122. package/dist/git/index.d.ts.map +1 -0
  123. package/dist/git/index.js +10 -0
  124. package/dist/git/index.js.map +1 -0
  125. package/dist/git/worktree.d.ts +44 -0
  126. package/dist/git/worktree.d.ts.map +1 -0
  127. package/dist/git/worktree.js +163 -0
  128. package/dist/git/worktree.js.map +1 -0
  129. package/dist/index.d.ts +0 -1
  130. package/dist/index.d.ts.map +1 -1
  131. package/dist/index.js +73 -145
  132. package/dist/index.js.map +1 -1
  133. package/dist/launcher/index.d.ts +24 -0
  134. package/dist/launcher/index.d.ts.map +1 -0
  135. package/dist/launcher/index.js +260 -0
  136. package/dist/launcher/index.js.map +1 -0
  137. package/dist/multiplexer/base.d.ts +73 -0
  138. package/dist/multiplexer/base.d.ts.map +1 -0
  139. package/dist/multiplexer/base.js +68 -0
  140. package/dist/multiplexer/base.js.map +1 -0
  141. package/dist/multiplexer/factory.d.ts +28 -0
  142. package/dist/multiplexer/factory.d.ts.map +1 -0
  143. package/dist/multiplexer/factory.js +72 -0
  144. package/dist/multiplexer/factory.js.map +1 -0
  145. package/dist/multiplexer/index.d.ts +14 -0
  146. package/dist/multiplexer/index.d.ts.map +1 -0
  147. package/dist/multiplexer/index.js +15 -0
  148. package/dist/multiplexer/index.js.map +1 -0
  149. package/dist/multiplexer/tmux/index.d.ts +9 -0
  150. package/dist/multiplexer/tmux/index.d.ts.map +1 -0
  151. package/dist/multiplexer/tmux/index.js +7 -0
  152. package/dist/multiplexer/tmux/index.js.map +1 -0
  153. package/dist/multiplexer/tmux/tmux-commands.d.ts +96 -0
  154. package/dist/multiplexer/tmux/tmux-commands.d.ts.map +1 -0
  155. package/dist/multiplexer/tmux/tmux-commands.js +282 -0
  156. package/dist/multiplexer/tmux/tmux-commands.js.map +1 -0
  157. package/dist/multiplexer/tmux/tmux-config.d.ts +57 -0
  158. package/dist/multiplexer/tmux/tmux-config.d.ts.map +1 -0
  159. package/dist/multiplexer/tmux/tmux-config.js +103 -0
  160. package/dist/multiplexer/tmux/tmux-config.js.map +1 -0
  161. package/dist/multiplexer/tmux/tmux-multiplexer.d.ts +94 -0
  162. package/dist/multiplexer/tmux/tmux-multiplexer.d.ts.map +1 -0
  163. package/dist/multiplexer/tmux/tmux-multiplexer.js +302 -0
  164. package/dist/multiplexer/tmux/tmux-multiplexer.js.map +1 -0
  165. package/dist/multiplexer/windows/index.d.ts +5 -0
  166. package/dist/multiplexer/windows/index.d.ts.map +1 -0
  167. package/dist/multiplexer/windows/index.js +5 -0
  168. package/dist/multiplexer/windows/index.js.map +1 -0
  169. package/dist/multiplexer/windows/windows-multiplexer.d.ts +61 -0
  170. package/dist/multiplexer/windows/windows-multiplexer.d.ts.map +1 -0
  171. package/dist/multiplexer/windows/windows-multiplexer.js +151 -0
  172. package/dist/multiplexer/windows/windows-multiplexer.js.map +1 -0
  173. package/dist/platform/clipboard.d.ts +26 -0
  174. package/dist/platform/clipboard.d.ts.map +1 -0
  175. package/dist/platform/clipboard.js +161 -0
  176. package/dist/platform/clipboard.js.map +1 -0
  177. package/dist/platform/detector.d.ts +78 -0
  178. package/dist/platform/detector.d.ts.map +1 -0
  179. package/dist/platform/detector.js +184 -0
  180. package/dist/platform/detector.js.map +1 -0
  181. package/dist/platform/index.d.ts +14 -0
  182. package/dist/platform/index.d.ts.map +1 -0
  183. package/dist/platform/index.js +53 -0
  184. package/dist/platform/index.js.map +1 -0
  185. package/dist/platform/paths.d.ts +63 -0
  186. package/dist/platform/paths.d.ts.map +1 -0
  187. package/dist/platform/paths.js +162 -0
  188. package/dist/platform/paths.js.map +1 -0
  189. package/dist/platform/shell.d.ts +43 -0
  190. package/dist/platform/shell.d.ts.map +1 -0
  191. package/dist/platform/shell.js +195 -0
  192. package/dist/platform/shell.js.map +1 -0
  193. package/dist/services/claude/claude-detector.d.ts +37 -0
  194. package/dist/services/claude/claude-detector.d.ts.map +1 -0
  195. package/dist/services/claude/claude-detector.js +83 -0
  196. package/dist/services/claude/claude-detector.js.map +1 -0
  197. package/dist/services/claude/claude-service.d.ts +57 -0
  198. package/dist/services/claude/claude-service.d.ts.map +1 -0
  199. package/dist/services/claude/claude-service.js +108 -0
  200. package/dist/services/claude/claude-service.js.map +1 -0
  201. package/dist/services/claude/index.d.ts +7 -0
  202. package/dist/services/claude/index.d.ts.map +1 -0
  203. package/dist/services/claude/index.js +6 -0
  204. package/dist/services/claude/index.js.map +1 -0
  205. package/dist/services/git/branch-service.d.ts +64 -0
  206. package/dist/services/git/branch-service.d.ts.map +1 -0
  207. package/dist/services/git/branch-service.js +217 -0
  208. package/dist/services/git/branch-service.js.map +1 -0
  209. package/dist/services/git/git-service.d.ts +44 -0
  210. package/dist/services/git/git-service.d.ts.map +1 -0
  211. package/dist/services/git/git-service.js +145 -0
  212. package/dist/services/git/git-service.js.map +1 -0
  213. package/dist/services/git/index.d.ts +8 -0
  214. package/dist/services/git/index.d.ts.map +1 -0
  215. package/dist/services/git/index.js +7 -0
  216. package/dist/services/git/index.js.map +1 -0
  217. package/dist/services/git/worktree-service.d.ts +61 -0
  218. package/dist/services/git/worktree-service.d.ts.map +1 -0
  219. package/dist/services/git/worktree-service.js +243 -0
  220. package/dist/services/git/worktree-service.js.map +1 -0
  221. package/dist/services/index.d.ts +12 -0
  222. package/dist/services/index.d.ts.map +1 -0
  223. package/dist/services/index.js +12 -0
  224. package/dist/services/index.js.map +1 -0
  225. package/dist/services/process/index.d.ts +8 -0
  226. package/dist/services/process/index.d.ts.map +1 -0
  227. package/dist/services/process/index.js +6 -0
  228. package/dist/services/process/index.js.map +1 -0
  229. package/dist/services/process/process-monitor.d.ts +98 -0
  230. package/dist/services/process/process-monitor.d.ts.map +1 -0
  231. package/dist/services/process/process-monitor.js +173 -0
  232. package/dist/services/process/process-monitor.js.map +1 -0
  233. package/dist/services/process/process-spawner.d.ts +55 -0
  234. package/dist/services/process/process-spawner.d.ts.map +1 -0
  235. package/dist/services/process/process-spawner.js +93 -0
  236. package/dist/services/process/process-spawner.js.map +1 -0
  237. package/dist/sidebar/app.d.ts +70 -0
  238. package/dist/sidebar/app.d.ts.map +1 -0
  239. package/dist/sidebar/app.js +1237 -0
  240. package/dist/sidebar/app.js.map +1 -0
  241. package/dist/sidebar/index.d.ts +8 -0
  242. package/dist/sidebar/index.d.ts.map +1 -0
  243. package/dist/sidebar/index.js +28 -0
  244. package/dist/sidebar/index.js.map +1 -0
  245. package/dist/sidebar/input.d.ts +33 -0
  246. package/dist/sidebar/input.d.ts.map +1 -0
  247. package/dist/sidebar/input.js +160 -0
  248. package/dist/sidebar/input.js.map +1 -0
  249. package/dist/sidebar/render.d.ts +73 -0
  250. package/dist/sidebar/render.d.ts.map +1 -0
  251. package/dist/sidebar/render.js +415 -0
  252. package/dist/sidebar/render.js.map +1 -0
  253. package/dist/state/actions.d.ts +49 -0
  254. package/dist/state/actions.d.ts.map +1 -0
  255. package/dist/state/actions.js +512 -0
  256. package/dist/state/actions.js.map +1 -0
  257. package/dist/state/index.d.ts +16 -0
  258. package/dist/state/index.d.ts.map +1 -0
  259. package/dist/state/index.js +28 -0
  260. package/dist/state/index.js.map +1 -0
  261. package/dist/state/middleware/index.d.ts +7 -0
  262. package/dist/state/middleware/index.d.ts.map +1 -0
  263. package/dist/state/middleware/index.js +6 -0
  264. package/dist/state/middleware/index.js.map +1 -0
  265. package/dist/state/middleware/logger.d.ts +22 -0
  266. package/dist/state/middleware/logger.d.ts.map +1 -0
  267. package/dist/state/middleware/logger.js +45 -0
  268. package/dist/state/middleware/logger.js.map +1 -0
  269. package/dist/state/middleware/persistence.d.ts +29 -0
  270. package/dist/state/middleware/persistence.d.ts.map +1 -0
  271. package/dist/state/middleware/persistence.js +71 -0
  272. package/dist/state/middleware/persistence.js.map +1 -0
  273. package/dist/state/persistence/file-adapter.d.ts +48 -0
  274. package/dist/state/persistence/file-adapter.d.ts.map +1 -0
  275. package/dist/state/persistence/file-adapter.js +96 -0
  276. package/dist/state/persistence/file-adapter.js.map +1 -0
  277. package/dist/state/persistence/index.d.ts +8 -0
  278. package/dist/state/persistence/index.d.ts.map +1 -0
  279. package/dist/state/persistence/index.js +6 -0
  280. package/dist/state/persistence/index.js.map +1 -0
  281. package/dist/state/persistence/serializer.d.ts +41 -0
  282. package/dist/state/persistence/serializer.d.ts.map +1 -0
  283. package/dist/state/persistence/serializer.js +114 -0
  284. package/dist/state/persistence/serializer.js.map +1 -0
  285. package/dist/state/selectors.d.ts +67 -0
  286. package/dist/state/selectors.d.ts.map +1 -0
  287. package/dist/state/selectors.js +177 -0
  288. package/dist/state/selectors.js.map +1 -0
  289. package/dist/state/store.d.ts +86 -0
  290. package/dist/state/store.d.ts.map +1 -0
  291. package/dist/state/store.js +161 -0
  292. package/dist/state/store.js.map +1 -0
  293. package/dist/state/types.d.ts +269 -0
  294. package/dist/state/types.d.ts.map +1 -0
  295. package/dist/state/types.js +7 -0
  296. package/dist/state/types.js.map +1 -0
  297. package/dist/terminal/bar-handler.d.ts +15 -0
  298. package/dist/terminal/bar-handler.d.ts.map +1 -0
  299. package/dist/terminal/bar-handler.js +244 -0
  300. package/dist/terminal/bar-handler.js.map +1 -0
  301. package/dist/terminal/bar-render.d.ts +33 -0
  302. package/dist/terminal/bar-render.d.ts.map +1 -0
  303. package/dist/terminal/bar-render.js +126 -0
  304. package/dist/terminal/bar-render.js.map +1 -0
  305. package/dist/terminal/index.d.ts +8 -0
  306. package/dist/terminal/index.d.ts.map +1 -0
  307. package/dist/terminal/index.js +14 -0
  308. package/dist/terminal/index.js.map +1 -0
  309. package/dist/tmux/commands.d.ts +26 -0
  310. package/dist/tmux/commands.d.ts.map +1 -0
  311. package/dist/tmux/commands.js +74 -0
  312. package/dist/tmux/commands.js.map +1 -0
  313. package/dist/tmux/index.d.ts +6 -0
  314. package/dist/tmux/index.d.ts.map +1 -0
  315. package/dist/tmux/index.js +42 -0
  316. package/dist/tmux/index.js.map +1 -0
  317. package/dist/tmux/pane.d.ts +111 -0
  318. package/dist/tmux/pane.d.ts.map +1 -0
  319. package/dist/tmux/pane.js +231 -0
  320. package/dist/tmux/pane.js.map +1 -0
  321. package/dist/types/config.d.ts +103 -0
  322. package/dist/types/config.d.ts.map +1 -0
  323. package/dist/types/config.js +7 -0
  324. package/dist/types/config.js.map +1 -0
  325. package/dist/types/entities.d.ts +88 -0
  326. package/dist/types/entities.d.ts.map +1 -0
  327. package/dist/types/entities.js +8 -0
  328. package/dist/types/entities.js.map +1 -0
  329. package/dist/types/events.d.ts +156 -0
  330. package/dist/types/events.d.ts.map +1 -0
  331. package/dist/types/events.js +8 -0
  332. package/dist/types/events.js.map +1 -0
  333. package/dist/types/index.d.ts +11 -0
  334. package/dist/types/index.d.ts.map +1 -0
  335. package/dist/types/index.js +8 -0
  336. package/dist/types/index.js.map +1 -0
  337. package/dist/types/multiplexer.d.ts +238 -0
  338. package/dist/types/multiplexer.d.ts.map +1 -0
  339. package/dist/types/multiplexer.js +8 -0
  340. package/dist/types/multiplexer.js.map +1 -0
  341. package/dist/types.d.ts +67 -3
  342. package/dist/types.d.ts.map +1 -1
  343. package/dist/types.js +10 -3
  344. package/dist/types.js.map +1 -1
  345. package/dist/ui/components/base.d.ts +155 -0
  346. package/dist/ui/components/base.d.ts.map +1 -0
  347. package/dist/ui/components/base.js +269 -0
  348. package/dist/ui/components/base.js.map +1 -0
  349. package/dist/ui/components/index.d.ts +10 -0
  350. package/dist/ui/components/index.d.ts.map +1 -0
  351. package/dist/ui/components/index.js +10 -0
  352. package/dist/ui/components/index.js.map +1 -0
  353. package/dist/ui/components/input-field.d.ts +98 -0
  354. package/dist/ui/components/input-field.d.ts.map +1 -0
  355. package/dist/ui/components/input-field.js +274 -0
  356. package/dist/ui/components/input-field.js.map +1 -0
  357. package/dist/ui/components/list.d.ts +94 -0
  358. package/dist/ui/components/list.d.ts.map +1 -0
  359. package/dist/ui/components/list.js +294 -0
  360. package/dist/ui/components/list.js.map +1 -0
  361. package/dist/ui/components/modal.d.ts +83 -0
  362. package/dist/ui/components/modal.d.ts.map +1 -0
  363. package/dist/ui/components/modal.js +249 -0
  364. package/dist/ui/components/modal.js.map +1 -0
  365. package/dist/ui/components/tabs.d.ts +97 -0
  366. package/dist/ui/components/tabs.d.ts.map +1 -0
  367. package/dist/ui/components/tabs.js +252 -0
  368. package/dist/ui/components/tabs.js.map +1 -0
  369. package/dist/ui/components/text.d.ts +60 -0
  370. package/dist/ui/components/text.d.ts.map +1 -0
  371. package/dist/ui/components/text.js +224 -0
  372. package/dist/ui/components/text.js.map +1 -0
  373. package/dist/ui/index.d.ts +12 -0
  374. package/dist/ui/index.d.ts.map +1 -0
  375. package/dist/ui/index.js +18 -0
  376. package/dist/ui/index.js.map +1 -0
  377. package/dist/ui/input/index.d.ts +8 -0
  378. package/dist/ui/input/index.d.ts.map +1 -0
  379. package/dist/ui/input/index.js +8 -0
  380. package/dist/ui/input/index.js.map +1 -0
  381. package/dist/ui/input/input-manager.d.ts +83 -0
  382. package/dist/ui/input/input-manager.d.ts.map +1 -0
  383. package/dist/ui/input/input-manager.js +200 -0
  384. package/dist/ui/input/input-manager.js.map +1 -0
  385. package/dist/ui/input/keybindings.d.ts +47 -0
  386. package/dist/ui/input/keybindings.d.ts.map +1 -0
  387. package/dist/ui/input/keybindings.js +168 -0
  388. package/dist/ui/input/keybindings.js.map +1 -0
  389. package/dist/ui/input/keyboard.d.ts +24 -0
  390. package/dist/ui/input/keyboard.d.ts.map +1 -0
  391. package/dist/ui/input/keyboard.js +230 -0
  392. package/dist/ui/input/keyboard.js.map +1 -0
  393. package/dist/ui/input/mouse.d.ts +44 -0
  394. package/dist/ui/input/mouse.d.ts.map +1 -0
  395. package/dist/ui/input/mouse.js +161 -0
  396. package/dist/ui/input/mouse.js.map +1 -0
  397. package/dist/ui/layout/box.d.ts +63 -0
  398. package/dist/ui/layout/box.d.ts.map +1 -0
  399. package/dist/ui/layout/box.js +160 -0
  400. package/dist/ui/layout/box.js.map +1 -0
  401. package/dist/ui/layout/constraints.d.ts +77 -0
  402. package/dist/ui/layout/constraints.d.ts.map +1 -0
  403. package/dist/ui/layout/constraints.js +154 -0
  404. package/dist/ui/layout/constraints.js.map +1 -0
  405. package/dist/ui/layout/flex.d.ts +59 -0
  406. package/dist/ui/layout/flex.d.ts.map +1 -0
  407. package/dist/ui/layout/flex.js +163 -0
  408. package/dist/ui/layout/flex.js.map +1 -0
  409. package/dist/ui/layout/index.d.ts +7 -0
  410. package/dist/ui/layout/index.d.ts.map +1 -0
  411. package/dist/ui/layout/index.js +7 -0
  412. package/dist/ui/layout/index.js.map +1 -0
  413. package/dist/ui/modals/delete-modal.d.ts +27 -0
  414. package/dist/ui/modals/delete-modal.d.ts.map +1 -0
  415. package/dist/ui/modals/delete-modal.js +59 -0
  416. package/dist/ui/modals/delete-modal.js.map +1 -0
  417. package/dist/ui/modals/index.d.ts +7 -0
  418. package/dist/ui/modals/index.d.ts.map +1 -0
  419. package/dist/ui/modals/index.js +7 -0
  420. package/dist/ui/modals/index.js.map +1 -0
  421. package/dist/ui/modals/input-modal.d.ts +66 -0
  422. package/dist/ui/modals/input-modal.d.ts.map +1 -0
  423. package/dist/ui/modals/input-modal.js +185 -0
  424. package/dist/ui/modals/input-modal.js.map +1 -0
  425. package/dist/ui/modals/quit-modal.d.ts +30 -0
  426. package/dist/ui/modals/quit-modal.d.ts.map +1 -0
  427. package/dist/ui/modals/quit-modal.js +65 -0
  428. package/dist/ui/modals/quit-modal.js.map +1 -0
  429. package/dist/ui/renderer/ansi.d.ts +206 -0
  430. package/dist/ui/renderer/ansi.d.ts.map +1 -0
  431. package/dist/ui/renderer/ansi.js +318 -0
  432. package/dist/ui/renderer/ansi.js.map +1 -0
  433. package/dist/ui/renderer/canvas.d.ts +126 -0
  434. package/dist/ui/renderer/canvas.d.ts.map +1 -0
  435. package/dist/ui/renderer/canvas.js +346 -0
  436. package/dist/ui/renderer/canvas.js.map +1 -0
  437. package/dist/ui/renderer/colors.d.ts +65 -0
  438. package/dist/ui/renderer/colors.d.ts.map +1 -0
  439. package/dist/ui/renderer/colors.js +260 -0
  440. package/dist/ui/renderer/colors.js.map +1 -0
  441. package/dist/ui/renderer/index.d.ts +8 -0
  442. package/dist/ui/renderer/index.d.ts.map +1 -0
  443. package/dist/ui/renderer/index.js +8 -0
  444. package/dist/ui/renderer/index.js.map +1 -0
  445. package/dist/ui/renderer/symbols.d.ts +176 -0
  446. package/dist/ui/renderer/symbols.d.ts.map +1 -0
  447. package/dist/ui/renderer/symbols.js +246 -0
  448. package/dist/ui/renderer/symbols.js.map +1 -0
  449. package/dist/ui/views/index.d.ts +7 -0
  450. package/dist/ui/views/index.d.ts.map +1 -0
  451. package/dist/ui/views/index.js +7 -0
  452. package/dist/ui/views/index.js.map +1 -0
  453. package/dist/ui/views/sidebar-view.d.ts +89 -0
  454. package/dist/ui/views/sidebar-view.d.ts.map +1 -0
  455. package/dist/ui/views/sidebar-view.js +259 -0
  456. package/dist/ui/views/sidebar-view.js.map +1 -0
  457. package/dist/ui/views/terminal-bar-view.d.ts +67 -0
  458. package/dist/ui/views/terminal-bar-view.d.ts.map +1 -0
  459. package/dist/ui/views/terminal-bar-view.js +172 -0
  460. package/dist/ui/views/terminal-bar-view.js.map +1 -0
  461. package/dist/ui/views/welcome-view.d.ts +34 -0
  462. package/dist/ui/views/welcome-view.d.ts.map +1 -0
  463. package/dist/ui/views/welcome-view.js +116 -0
  464. package/dist/ui/views/welcome-view.js.map +1 -0
  465. package/dist/utils/async.d.ts +68 -0
  466. package/dist/utils/async.d.ts.map +1 -0
  467. package/dist/utils/async.js +147 -0
  468. package/dist/utils/async.js.map +1 -0
  469. package/dist/utils/errors.d.ts +136 -0
  470. package/dist/utils/errors.d.ts.map +1 -0
  471. package/dist/utils/errors.js +241 -0
  472. package/dist/utils/errors.js.map +1 -0
  473. package/dist/utils/id.d.ts +51 -0
  474. package/dist/utils/id.d.ts.map +1 -0
  475. package/dist/utils/id.js +88 -0
  476. package/dist/utils/id.js.map +1 -0
  477. package/dist/utils/index.d.ts +15 -0
  478. package/dist/utils/index.d.ts.map +1 -0
  479. package/dist/utils/index.js +78 -0
  480. package/dist/utils/index.js.map +1 -0
  481. package/dist/utils/logger.d.ts +75 -0
  482. package/dist/utils/logger.d.ts.map +1 -0
  483. package/dist/utils/logger.js +219 -0
  484. package/dist/utils/logger.js.map +1 -0
  485. package/dist/utils/string.d.ts +71 -0
  486. package/dist/utils/string.d.ts.map +1 -0
  487. package/dist/utils/string.js +175 -0
  488. package/dist/utils/string.js.map +1 -0
  489. package/dist/utils/validation.d.ts +72 -0
  490. package/dist/utils/validation.d.ts.map +1 -0
  491. package/dist/utils/validation.js +211 -0
  492. package/dist/utils/validation.js.map +1 -0
  493. package/package.json +4 -4
@@ -0,0 +1,1237 @@
1
+ "use strict";
2
+ /**
3
+ * Sidebar Application
4
+ *
5
+ * Main sidebar class that handles state management, input handling, and rendering.
6
+ */
7
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
8
+ if (k2 === undefined) k2 = k;
9
+ var desc = Object.getOwnPropertyDescriptor(m, k);
10
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
11
+ desc = { enumerable: true, get: function() { return m[k]; } };
12
+ }
13
+ Object.defineProperty(o, k2, desc);
14
+ }) : (function(o, m, k, k2) {
15
+ if (k2 === undefined) k2 = k;
16
+ o[k2] = m[k];
17
+ }));
18
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
19
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
20
+ }) : function(o, v) {
21
+ o["default"] = v;
22
+ });
23
+ var __importStar = (this && this.__importStar) || (function () {
24
+ var ownKeys = function(o) {
25
+ ownKeys = Object.getOwnPropertyNames || function (o) {
26
+ var ar = [];
27
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
28
+ return ar;
29
+ };
30
+ return ownKeys(o);
31
+ };
32
+ return function (mod) {
33
+ if (mod && mod.__esModule) return mod;
34
+ var result = {};
35
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
36
+ __setModuleDefault(result, mod);
37
+ return result;
38
+ };
39
+ })();
40
+ Object.defineProperty(exports, "__esModule", { value: true });
41
+ exports.SidebarApp = void 0;
42
+ const fs_1 = require("fs");
43
+ const child_process_1 = require("child_process");
44
+ const path_1 = require("path");
45
+ const tmux = __importStar(require("../tmux"));
46
+ const git_1 = require("../git");
47
+ const input_1 = require("./input");
48
+ const render_1 = require("./render");
49
+ // Debug logging
50
+ function debugLog(...args) {
51
+ const msg = `[${new Date().toISOString()}] ${args.map(a => typeof a === 'object' ? JSON.stringify(a) : String(a)).join(' ')}\n`;
52
+ (0, fs_1.appendFileSync)('/tmp/claude-pp-sidebar.log', msg);
53
+ }
54
+ // ============================================================================
55
+ // Constants
56
+ // ============================================================================
57
+ const SIDEBAR_WIDTH = 25;
58
+ const DEFAULT_CLAUDE_CMD = 'claude --dangerously-skip-permissions';
59
+ const TERMINAL_BAR_HEIGHT = 1;
60
+ const CLAUDE_PANE_PERCENT = 70; // Claude pane gets 70%, terminal area gets 30%
61
+ /**
62
+ * Set up terminal bar resize enforcement (hook + mouse binding).
63
+ * Uses a file-based script to avoid complex quoting issues.
64
+ * Sets up both after-resize-pane hook AND MouseDragEnd1Border binding.
65
+ */
66
+ function setupTerminalBarResize(sessionName, claudePaneId, barPaneId, terminalBodyPaneId) {
67
+ const hookCmd = getTerminalBarResizeHook(claudePaneId, barPaneId, terminalBodyPaneId);
68
+ // Set up the after-resize-pane hook
69
+ tmux.setHook(sessionName, 'after-resize-pane', hookCmd);
70
+ // Also bind MouseDragEnd1Border to run the script when mouse drag ends
71
+ // This ensures the fix runs when user releases mouse after dragging border
72
+ const safeName = `${claudePaneId}-${barPaneId}`.replace(/%/g, '');
73
+ const scriptPath = `/tmp/cpp-resize-hook-${safeName}.sh`;
74
+ try {
75
+ (0, child_process_1.execSync)(`tmux bind-key -T root MouseDragEnd1Border run-shell "sh ${scriptPath}"`, { stdio: 'ignore' });
76
+ }
77
+ catch {
78
+ // Ignore errors
79
+ }
80
+ }
81
+ /**
82
+ * Create a shell script for terminal bar resize hook and return the hook command.
83
+ * Uses a file-based script to avoid complex quoting issues.
84
+ */
85
+ function getTerminalBarResizeHook(claudePaneId, barPaneId, terminalBodyPaneId) {
86
+ // Use pane IDs in filename to make it unique per terminal configuration
87
+ const safeName = `${claudePaneId}-${barPaneId}`.replace(/%/g, '');
88
+ const scriptPath = `/tmp/cpp-resize-hook-${safeName}.sh`;
89
+ // Write the script file
90
+ const scriptContent = `#!/bin/sh
91
+ # Terminal bar resize hook - keeps bar at ${TERMINAL_BAR_HEIGHT} row(s)
92
+ # Lock check - prevent recursion
93
+ LOCK=$(tmux show-option -gqv @cpp-resizing 2>/dev/null)
94
+ [ -n "$LOCK" ] && exit 0
95
+
96
+ # Get current heights
97
+ BAR_H=$(tmux display-message -p -t "${barPaneId}" '#{pane_height}' 2>/dev/null)
98
+ [ -z "$BAR_H" ] && exit 0
99
+ [ "$BAR_H" -eq ${TERMINAL_BAR_HEIGHT} ] && exit 0
100
+
101
+ CLAUDE_H=$(tmux display-message -p -t "${claudePaneId}" '#{pane_height}' 2>/dev/null)
102
+ BODY_H=$(tmux display-message -p -t "${terminalBodyPaneId}" '#{pane_height}' 2>/dev/null)
103
+
104
+ # Get previous heights (to detect which pane shrank)
105
+ PREV_CLAUDE=$(tmux show-option -gqv @cpp-prev-claude 2>/dev/null)
106
+ PREV_BODY=$(tmux show-option -gqv @cpp-prev-body 2>/dev/null)
107
+
108
+ # Acquire lock and set trap
109
+ tmux set-option -g @cpp-resizing 1
110
+ trap 'tmux set-option -gu @cpp-resizing 2>/dev/null' EXIT
111
+
112
+ # Calculate how much bar is over target
113
+ D=$((BAR_H - ${TERMINAL_BAR_HEIGHT}))
114
+
115
+ # Determine which pane to grow based on which one shrank
116
+ if [ -n "$PREV_CLAUDE" ] && [ "$CLAUDE_H" -lt "$PREV_CLAUDE" ]; then
117
+ # Claude shrank (user dragged tabs ceiling UP) -> grow Terminal body
118
+ tmux resize-pane -t "${terminalBodyPaneId}" -U "$D" 2>/dev/null
119
+ elif [ -n "$PREV_BODY" ] && [ "$BODY_H" -lt "$PREV_BODY" ]; then
120
+ # Terminal body shrank (user dragged body ceiling DOWN) -> grow Claude
121
+ tmux resize-pane -t "${claudePaneId}" -D "$D" 2>/dev/null
122
+ else
123
+ # Fallback: just set bar height directly, let tmux decide
124
+ :
125
+ fi
126
+
127
+ # Set bar to exact height
128
+ tmux resize-pane -t "${barPaneId}" -y ${TERMINAL_BAR_HEIGHT} 2>/dev/null
129
+
130
+ # Store FINAL heights (after adjustment) for next comparison
131
+ FINAL_CLAUDE=$(tmux display-message -p -t "${claudePaneId}" '#{pane_height}' 2>/dev/null)
132
+ FINAL_BODY=$(tmux display-message -p -t "${terminalBodyPaneId}" '#{pane_height}' 2>/dev/null)
133
+ tmux set-option -g @cpp-prev-claude "$FINAL_CLAUDE"
134
+ tmux set-option -g @cpp-prev-body "$FINAL_BODY"
135
+ `;
136
+ // Write script to file (synchronously)
137
+ (0, fs_1.writeFileSync)(scriptPath, scriptContent, { mode: 0o755 });
138
+ return `"run-shell 'sh ${scriptPath}'"`;
139
+ }
140
+ // ============================================================================
141
+ // Sidebar App
142
+ // ============================================================================
143
+ class SidebarApp {
144
+ state;
145
+ worktreeManager;
146
+ running = false;
147
+ constructor(repoPath, sessionName, mainPaneId, sidebarPaneId) {
148
+ this.worktreeManager = new git_1.WorktreeManager(repoPath);
149
+ this.state = {
150
+ repoPath,
151
+ sessionName,
152
+ mainPaneId,
153
+ sidebarPaneId,
154
+ worktrees: [],
155
+ sessions: [],
156
+ selectedIndex: 0,
157
+ activeSessionId: null,
158
+ expandedWorktrees: new Set(),
159
+ modal: 'none',
160
+ modalSelection: 0,
161
+ inputBuffer: '',
162
+ deleteTarget: null,
163
+ fullscreenModal: false,
164
+ hiddenPaneId: null,
165
+ collapsed: false,
166
+ terminalCommandMode: false,
167
+ terminalCommandBuffer: '',
168
+ };
169
+ }
170
+ // ==========================================================================
171
+ // Lifecycle
172
+ // ==========================================================================
173
+ async init() {
174
+ this.state.worktrees = await this.worktreeManager.list();
175
+ // Fallback if not a git repo
176
+ if (this.state.worktrees.length === 0) {
177
+ const { basename } = await Promise.resolve().then(() => __importStar(require('path')));
178
+ this.state.worktrees = [{
179
+ id: 'current',
180
+ path: this.state.repoPath,
181
+ branch: basename(this.state.repoPath),
182
+ isMain: true,
183
+ }];
184
+ }
185
+ }
186
+ start() {
187
+ this.running = true;
188
+ // Set up terminal
189
+ process.stdout.write(render_1.ansi.hideCursor);
190
+ process.stdout.write(render_1.ansi.enableMouse);
191
+ (0, input_1.setupRawMode)();
192
+ // Input handling
193
+ process.stdin.on('data', (data) => this.handleInput(data));
194
+ // Resize handling
195
+ process.stdout.on('resize', () => {
196
+ this.syncCollapsedState();
197
+ this.render();
198
+ });
199
+ // Initial render
200
+ this.render();
201
+ }
202
+ stop() {
203
+ this.running = false;
204
+ process.stdout.write(render_1.ansi.disableMouse);
205
+ process.stdout.write(render_1.ansi.showCursor);
206
+ process.stdout.write(render_1.ansi.reset);
207
+ (0, input_1.restoreMode)();
208
+ }
209
+ // ==========================================================================
210
+ // Rendering
211
+ // ==========================================================================
212
+ render() {
213
+ if (!this.running)
214
+ return;
215
+ let output;
216
+ // Get dimensions - use tmux dimensions for fullscreen modals
217
+ let dims;
218
+ if (this.state.fullscreenModal) {
219
+ const paneDims = tmux.getPaneDimensions(this.state.sidebarPaneId);
220
+ dims = { cols: paneDims.width, rows: paneDims.height };
221
+ debugLog('render: fullscreen dims', dims);
222
+ }
223
+ if (this.state.collapsed) {
224
+ output = (0, render_1.renderCollapsed)(this.state.sessions.length);
225
+ }
226
+ else if (this.state.modal === 'quit') {
227
+ output = (0, render_1.renderQuitModal)(this.state, dims);
228
+ }
229
+ else if (this.state.modal === 'delete') {
230
+ const target = this.state.deleteTarget;
231
+ const name = target?.name || '';
232
+ output = (0, render_1.renderDeleteModal)(this.state, name, dims);
233
+ }
234
+ else if (this.state.modal === 'new-worktree') {
235
+ output = (0, render_1.renderInputModal)(this.state, 'New Worktree', 'Branch name:', dims);
236
+ }
237
+ else if (this.state.modal === 'rename') {
238
+ const target = this.getSelectedItem();
239
+ const title = target?.type === 'session' ? 'Rename Session' : 'Rename Branch';
240
+ output = (0, render_1.renderInputModal)(this.state, title, 'New name:', dims);
241
+ }
242
+ else if (this.state.modal === 'new-session') {
243
+ output = (0, render_1.renderInputModal)(this.state, 'New Session', 'Session name:', dims);
244
+ }
245
+ else {
246
+ output = (0, render_1.renderMain)(this.state);
247
+ }
248
+ process.stdout.write(output);
249
+ }
250
+ syncCollapsedState() {
251
+ const width = process.stdout.columns || SIDEBAR_WIDTH;
252
+ this.state.collapsed = width < SIDEBAR_WIDTH / 2;
253
+ }
254
+ // ==========================================================================
255
+ // Input Handling
256
+ // ==========================================================================
257
+ handleInput(data) {
258
+ const str = data.toString();
259
+ debugLog('handleInput:', 'hex=' + data.toString('hex'), 'modal=' + this.state.modal);
260
+ // Handle terminal command mode (commands from terminal bar handler)
261
+ if (this.state.terminalCommandMode) {
262
+ if (str === '\r' || str === '\n') {
263
+ // Enter - execute command
264
+ this.executeTerminalCommand(this.state.terminalCommandBuffer);
265
+ this.state.terminalCommandMode = false;
266
+ this.state.terminalCommandBuffer = '';
267
+ return;
268
+ }
269
+ else if (str === '\x1b') {
270
+ // Escape - cancel
271
+ this.state.terminalCommandMode = false;
272
+ this.state.terminalCommandBuffer = '';
273
+ return;
274
+ }
275
+ else {
276
+ // Accumulate command
277
+ this.state.terminalCommandBuffer += str;
278
+ return;
279
+ }
280
+ }
281
+ // Handle mouse events
282
+ if ((0, input_1.isMouseEvent)(str)) {
283
+ const event = (0, input_1.parseMouseEvent)(str);
284
+ if (event && event.button === 0 && event.release) {
285
+ this.handleClick(event.y, event.x);
286
+ }
287
+ return;
288
+ }
289
+ const key = (0, input_1.parseKey)(data);
290
+ debugLog('parsedKey:', key.key, 'ctrl=' + key.ctrl);
291
+ // Ctrl+U - enter terminal command mode (for commands from bar handler)
292
+ if (key.ctrl && key.key === 'u') {
293
+ this.state.terminalCommandMode = true;
294
+ this.state.terminalCommandBuffer = '';
295
+ return;
296
+ }
297
+ // Route input based on modal state
298
+ switch (this.state.modal) {
299
+ case 'quit':
300
+ this.handleQuitModalInput(key);
301
+ break;
302
+ case 'delete':
303
+ this.handleDeleteModalInput(key);
304
+ break;
305
+ case 'new-worktree':
306
+ case 'rename':
307
+ case 'new-session':
308
+ this.handleTextInput(key, data);
309
+ break;
310
+ default:
311
+ this.handleMainInput(key);
312
+ }
313
+ }
314
+ handleMainInput(key) {
315
+ // Ctrl+C - show quit modal (when sidebar is focused)
316
+ if (key.ctrl && key.key === 'c') {
317
+ this.enterFullscreenModal();
318
+ this.state.modal = 'quit';
319
+ this.state.modalSelection = 0;
320
+ this.render();
321
+ return;
322
+ }
323
+ // If collapsed, only expand on any key
324
+ if (this.state.collapsed) {
325
+ this.state.collapsed = false;
326
+ this.enforceSidebarWidth();
327
+ this.render();
328
+ return;
329
+ }
330
+ // Ctrl+Q - show quit modal (works from any pane via tmux binding)
331
+ if (key.ctrl && key.key === 'q') {
332
+ this.enterFullscreenModal();
333
+ this.state.modal = 'quit';
334
+ this.state.modalSelection = 0;
335
+ this.render();
336
+ return;
337
+ }
338
+ // Ctrl+G - toggle collapsed
339
+ if (key.ctrl && key.key === 'g') {
340
+ this.toggleCollapsed();
341
+ return;
342
+ }
343
+ // Ctrl+T - create new terminal for active session
344
+ if (key.ctrl && key.key === 't') {
345
+ this.createTerminal();
346
+ return;
347
+ }
348
+ // Navigation
349
+ if (key.key === 'up' || key.key === 'k') {
350
+ this.state.selectedIndex = Math.max(0, this.state.selectedIndex - 1);
351
+ this.render();
352
+ return;
353
+ }
354
+ if (key.key === 'down' || key.key === 'j') {
355
+ const maxIndex = this.getMaxIndex();
356
+ this.state.selectedIndex = Math.min(maxIndex, this.state.selectedIndex + 1);
357
+ this.render();
358
+ return;
359
+ }
360
+ // Enter - activate selected
361
+ if (key.key === 'enter') {
362
+ debugLog('handleMainInput: enter pressed, calling activateSelected');
363
+ this.activateSelected();
364
+ return;
365
+ }
366
+ // n - new worktree
367
+ if (key.key === 'n') {
368
+ this.enterFullscreenModal();
369
+ this.state.modal = 'new-worktree';
370
+ this.state.inputBuffer = '';
371
+ this.render();
372
+ return;
373
+ }
374
+ // d - delete
375
+ if (key.key === 'd') {
376
+ const item = this.getSelectedItem();
377
+ if (item && !(item.type === 'worktree' && item.worktree?.isMain)) {
378
+ // Store delete target info for context in modal
379
+ this.state.deleteTarget = {
380
+ type: item.type,
381
+ id: item.id,
382
+ name: item.type === 'session' ? (item.session?.title || '') : (item.worktree?.branch || ''),
383
+ worktree: item.worktree,
384
+ session: item.session,
385
+ };
386
+ this.enterFullscreenModal();
387
+ this.state.modal = 'delete';
388
+ this.state.modalSelection = 0; // Default to No
389
+ this.render();
390
+ }
391
+ return;
392
+ }
393
+ // r - rename
394
+ if (key.key === 'r') {
395
+ const item = this.getSelectedItem();
396
+ if (item && !(item.type === 'worktree' && item.worktree?.isMain)) {
397
+ this.enterFullscreenModal();
398
+ this.state.modal = 'rename';
399
+ this.state.inputBuffer = item.type === 'session'
400
+ ? item.session?.title || ''
401
+ : item.worktree?.branch || '';
402
+ this.render();
403
+ }
404
+ return;
405
+ }
406
+ }
407
+ handleQuitModalInput(key) {
408
+ // Escape - close modal
409
+ if (key.key === 'escape') {
410
+ this.state.modal = 'none';
411
+ this.exitFullscreenModal();
412
+ this.render();
413
+ return;
414
+ }
415
+ // Arrow keys - toggle selection
416
+ if (key.key === 'up' || key.key === 'down' || key.key === 'j' || key.key === 'k') {
417
+ this.state.modalSelection = this.state.modalSelection === 0 ? 1 : 0;
418
+ this.render();
419
+ return;
420
+ }
421
+ // Enter - confirm
422
+ if (key.key === 'enter') {
423
+ if (this.state.modalSelection === 0) {
424
+ // Detach
425
+ this.state.modal = 'none';
426
+ this.exitFullscreenModal();
427
+ this.render();
428
+ tmux.detachClient();
429
+ }
430
+ else {
431
+ // Kill - no need to restore pane since we're exiting
432
+ this.stop();
433
+ tmux.killSession(this.state.sessionName);
434
+ process.exit(0);
435
+ }
436
+ return;
437
+ }
438
+ }
439
+ handleDeleteModalInput(key) {
440
+ // Escape - cancel
441
+ if (key.key === 'escape') {
442
+ this.state.modal = 'none';
443
+ this.state.deleteTarget = null;
444
+ this.exitFullscreenModal();
445
+ this.render();
446
+ return;
447
+ }
448
+ // Arrow keys - toggle selection
449
+ if (key.key === 'up' || key.key === 'down' || key.key === 'j' || key.key === 'k') {
450
+ this.state.modalSelection = this.state.modalSelection === 0 ? 1 : 0;
451
+ this.render();
452
+ return;
453
+ }
454
+ // Enter - confirm
455
+ if (key.key === 'enter') {
456
+ if (this.state.modalSelection === 1) {
457
+ this.deleteSelected();
458
+ }
459
+ this.state.modal = 'none';
460
+ this.state.deleteTarget = null;
461
+ this.exitFullscreenModal();
462
+ this.render();
463
+ return;
464
+ }
465
+ // Quick keys
466
+ if (key.key === 'y' || key.key === 'Y') {
467
+ this.deleteSelected();
468
+ this.state.modal = 'none';
469
+ this.state.deleteTarget = null;
470
+ this.exitFullscreenModal();
471
+ this.render();
472
+ return;
473
+ }
474
+ if (key.key === 'n' || key.key === 'N') {
475
+ this.state.modal = 'none';
476
+ this.state.deleteTarget = null;
477
+ this.exitFullscreenModal();
478
+ this.render();
479
+ return;
480
+ }
481
+ }
482
+ handleTextInput(key, data) {
483
+ debugLog('handleTextInput: key=' + key.key, 'modal=' + this.state.modal, 'buffer=' + this.state.inputBuffer);
484
+ // Escape - cancel
485
+ if (key.key === 'escape') {
486
+ debugLog('handleTextInput: escape pressed, canceling');
487
+ this.state.modal = 'none';
488
+ this.state.inputBuffer = '';
489
+ this.exitFullscreenModal();
490
+ this.render();
491
+ return;
492
+ }
493
+ // Enter - confirm
494
+ if (key.key === 'enter') {
495
+ const value = this.state.inputBuffer.trim();
496
+ debugLog('handleTextInput: enter pressed, value=' + value);
497
+ const wasNewSession = this.state.modal === 'new-session';
498
+ if (value) {
499
+ debugLog('handleTextInput: calling confirmTextInput');
500
+ this.confirmTextInput(value);
501
+ }
502
+ this.state.modal = 'none';
503
+ this.state.inputBuffer = '';
504
+ this.exitFullscreenModal();
505
+ // After creating a new session, focus the Claude pane (exitFullscreenModal selects sidebar)
506
+ if (wasNewSession && value) {
507
+ const activeSession = this.state.sessions.find(s => s.id === this.state.activeSessionId);
508
+ if (activeSession) {
509
+ tmux.selectPane(activeSession.paneId);
510
+ }
511
+ }
512
+ this.render();
513
+ return;
514
+ }
515
+ // Backspace
516
+ if (key.key === 'backspace') {
517
+ this.state.inputBuffer = this.state.inputBuffer.slice(0, -1);
518
+ this.render();
519
+ return;
520
+ }
521
+ // Regular characters
522
+ if (data.length === 1 && data[0] >= 32 && data[0] < 127) {
523
+ const char = String.fromCharCode(data[0]);
524
+ // For branch names, only allow valid characters
525
+ if (this.state.modal === 'new-worktree') {
526
+ if (/[a-zA-Z0-9\-_\/.]/.test(char)) {
527
+ this.state.inputBuffer += char;
528
+ this.render();
529
+ }
530
+ }
531
+ else {
532
+ this.state.inputBuffer += char;
533
+ this.render();
534
+ }
535
+ }
536
+ }
537
+ confirmTextInput(value) {
538
+ switch (this.state.modal) {
539
+ case 'new-worktree':
540
+ this.createWorktree(value);
541
+ break;
542
+ case 'rename':
543
+ this.renameSelected(value);
544
+ break;
545
+ case 'new-session':
546
+ this.createSession(value);
547
+ break;
548
+ }
549
+ }
550
+ handleClick(row, col) {
551
+ const cols = process.stdout.columns || SIDEBAR_WIDTH;
552
+ if (this.state.collapsed) {
553
+ this.state.collapsed = false;
554
+ this.enforceSidebarWidth();
555
+ this.render();
556
+ return;
557
+ }
558
+ // Ignore clicks when modals are open
559
+ if (this.state.modal !== 'none')
560
+ return;
561
+ // Check for collapse button click (row 1, right side)
562
+ if (row === 1 && col >= cols - 3) {
563
+ this.toggleCollapsed();
564
+ return;
565
+ }
566
+ // Build items and find clicked item
567
+ const items = (0, render_1.buildListItems)(this.state);
568
+ const itemRow = row - 3; // Header takes 2 rows
569
+ // Check for "New Worktree" button click (after items + 1 empty row)
570
+ const newWorktreeRow = items.length + 1; // +1 for empty row after items
571
+ if (itemRow === newWorktreeRow) {
572
+ this.enterFullscreenModal();
573
+ this.state.modal = 'new-worktree';
574
+ this.state.inputBuffer = '';
575
+ this.render();
576
+ return;
577
+ }
578
+ if (itemRow >= 0 && itemRow < items.length) {
579
+ this.state.selectedIndex = itemRow;
580
+ this.activateSelected();
581
+ }
582
+ }
583
+ // ==========================================================================
584
+ // Actions
585
+ // ==========================================================================
586
+ activateSelected() {
587
+ debugLog('activateSelected: selectedIndex=' + this.state.selectedIndex);
588
+ const item = this.getSelectedItem();
589
+ debugLog('activateSelected: item=', item);
590
+ if (!item) {
591
+ debugLog('activateSelected: no item found');
592
+ return;
593
+ }
594
+ if (item.type === 'worktree') {
595
+ // Show new session modal
596
+ debugLog('activateSelected: showing new-session modal for worktree', item.worktree?.branch);
597
+ this.enterFullscreenModal();
598
+ this.state.modal = 'new-session';
599
+ const sessions = this.state.sessions.filter(s => s.worktreeId === item.id);
600
+ this.state.inputBuffer = `${sessions.length + 1}: ${item.worktree?.branch || 'session'}`;
601
+ this.render();
602
+ }
603
+ else {
604
+ // Switch to session
605
+ debugLog('activateSelected: switching to session', item.session?.title);
606
+ this.switchToSession(item.session);
607
+ }
608
+ }
609
+ async createWorktree(branchName) {
610
+ try {
611
+ const worktree = await this.worktreeManager.create(branchName, true);
612
+ this.state.worktrees.push(worktree);
613
+ this.state.selectedIndex = this.getMaxIndex();
614
+ this.render();
615
+ }
616
+ catch (err) {
617
+ // Failed to create worktree
618
+ }
619
+ }
620
+ createSession(title) {
621
+ debugLog('createSession: title=' + title);
622
+ const item = this.getSelectedItem();
623
+ debugLog('createSession: selectedItem=', item);
624
+ if (!item || item.type !== 'worktree') {
625
+ debugLog('createSession: FAILED - item is not a worktree or is null');
626
+ return;
627
+ }
628
+ const worktree = item.worktree;
629
+ debugLog('createSession: worktree=' + worktree.branch, 'path=' + worktree.path);
630
+ const sessionId = `session-${Date.now()}`;
631
+ let paneId;
632
+ const claudeCmd = DEFAULT_CLAUDE_CMD;
633
+ if (this.state.sessions.length === 0) {
634
+ // First session - use existing main pane
635
+ // If in fullscreen mode, the pane is broken but we can still send keys to it
636
+ // exitFullscreenModal will join it back
637
+ paneId = this.state.mainPaneId;
638
+ tmux.sendControlKey(paneId, 'C-c');
639
+ tmux.sendKeys(paneId, `cd "${worktree.path}" && clear && ${claudeCmd}`, true);
640
+ }
641
+ else {
642
+ // Additional session - need to handle fullscreen mode specially
643
+ const currentSession = this.state.sessions.find(s => s.id === this.state.activeSessionId);
644
+ if (this.state.fullscreenModal) {
645
+ // In fullscreen mode, the current session pane is already broken
646
+ // We'll create a new pane and DON'T want to rejoin the old one
647
+ // Clear hiddenPaneId so exitFullscreenModal won't join it back
648
+ // The old session stays hidden and can be switched to later
649
+ debugLog('createSession: in fullscreen mode, clearing hiddenPaneId to keep old session hidden');
650
+ this.state.hiddenPaneId = null;
651
+ }
652
+ else if (currentSession) {
653
+ // Normal mode - break the current session pane
654
+ tmux.breakPane(currentSession.paneId);
655
+ }
656
+ paneId = tmux.splitHorizontal(this.state.sessionName, 80, worktree.path);
657
+ tmux.sendKeys(paneId, claudeCmd, true);
658
+ }
659
+ const session = {
660
+ id: sessionId,
661
+ worktreeId: worktree.id,
662
+ paneId,
663
+ title,
664
+ createdAt: Date.now(),
665
+ // Terminal management
666
+ terminals: [],
667
+ activeTerminalIndex: 0,
668
+ terminalBarPaneId: null,
669
+ };
670
+ this.state.sessions.push(session);
671
+ this.state.activeSessionId = sessionId;
672
+ this.enforceSidebarWidth();
673
+ // Focus the Claude pane so user can start interacting immediately
674
+ tmux.selectPane(paneId);
675
+ this.render();
676
+ }
677
+ switchToSession(session) {
678
+ if (session.id === this.state.activeSessionId) {
679
+ // Already active - focus pane
680
+ tmux.selectPane(session.paneId);
681
+ return;
682
+ }
683
+ // Break current session's panes (Claude pane + terminals)
684
+ const currentSession = this.state.sessions.find(s => s.id === this.state.activeSessionId);
685
+ if (currentSession) {
686
+ // Break active terminal first (if any)
687
+ if (currentSession.terminals.length > 0) {
688
+ const activeTerminal = currentSession.terminals[currentSession.activeTerminalIndex];
689
+ if (activeTerminal) {
690
+ tmux.breakPane(activeTerminal.paneId);
691
+ }
692
+ // Break terminal bar
693
+ if (currentSession.terminalBarPaneId) {
694
+ tmux.breakPane(currentSession.terminalBarPaneId);
695
+ }
696
+ }
697
+ // Break Claude pane
698
+ tmux.breakPane(currentSession.paneId);
699
+ }
700
+ // Join new session's Claude pane
701
+ tmux.joinPane(session.paneId, this.state.sidebarPaneId, true);
702
+ // Join terminal bar and active terminal if session has terminals
703
+ if (session.terminals.length > 0 && session.terminalBarPaneId) {
704
+ // Join terminal bar below Claude pane
705
+ tmux.joinPane(session.terminalBarPaneId, session.paneId, false);
706
+ // Join active terminal below terminal bar
707
+ const activeTerminal = session.terminals[session.activeTerminalIndex];
708
+ if (activeTerminal) {
709
+ tmux.joinPane(activeTerminal.paneId, session.terminalBarPaneId, false);
710
+ }
711
+ // Ensure terminal bar is exactly 1 row
712
+ tmux.resizePane(session.terminalBarPaneId, undefined, TERMINAL_BAR_HEIGHT);
713
+ // Update resize enforcement for this session's terminal bar
714
+ if (activeTerminal) {
715
+ setupTerminalBarResize(this.state.sessionName, session.paneId, session.terminalBarPaneId, activeTerminal.paneId);
716
+ }
717
+ // Update terminal bar display
718
+ this.updateTerminalBar(session);
719
+ }
720
+ this.state.activeSessionId = session.id;
721
+ this.enforceSidebarWidth();
722
+ tmux.selectPane(this.state.sidebarPaneId);
723
+ this.render();
724
+ }
725
+ async deleteSelected() {
726
+ const item = this.getSelectedItem();
727
+ if (!item)
728
+ return;
729
+ if (item.type === 'session') {
730
+ this.deleteSession(item.session);
731
+ }
732
+ else if (item.type === 'worktree' && !item.worktree?.isMain) {
733
+ await this.deleteWorktree(item.worktree);
734
+ }
735
+ }
736
+ deleteSession(session) {
737
+ // Kill all terminal panes
738
+ for (const terminal of session.terminals) {
739
+ tmux.killPane(terminal.paneId);
740
+ }
741
+ // Kill terminal bar pane and remove hook
742
+ if (session.terminalBarPaneId) {
743
+ try {
744
+ tmux.removeHook(this.state.sessionName, 'after-resize-pane');
745
+ }
746
+ catch {
747
+ // Hook may not exist, ignore
748
+ }
749
+ tmux.killPane(session.terminalBarPaneId);
750
+ }
751
+ // Kill the Claude pane
752
+ tmux.killPane(session.paneId);
753
+ // Remove from sessions
754
+ this.state.sessions = this.state.sessions.filter(s => s.id !== session.id);
755
+ // If this was active, switch to another
756
+ if (this.state.activeSessionId === session.id) {
757
+ this.state.activeSessionId = null;
758
+ if (this.state.sessions.length > 0) {
759
+ const nextSession = this.state.sessions[0];
760
+ tmux.joinPane(nextSession.paneId, this.state.sidebarPaneId, true);
761
+ this.state.activeSessionId = nextSession.id;
762
+ // Join terminal panes if next session has terminals
763
+ if (nextSession.terminals.length > 0 && nextSession.terminalBarPaneId) {
764
+ tmux.joinPane(nextSession.terminalBarPaneId, nextSession.paneId, false);
765
+ const activeTerminal = nextSession.terminals[nextSession.activeTerminalIndex];
766
+ if (activeTerminal) {
767
+ tmux.joinPane(activeTerminal.paneId, nextSession.terminalBarPaneId, false);
768
+ }
769
+ tmux.resizePane(nextSession.terminalBarPaneId, undefined, TERMINAL_BAR_HEIGHT);
770
+ // Update resize enforcement for the new session's terminal bar
771
+ if (activeTerminal) {
772
+ setupTerminalBarResize(this.state.sessionName, nextSession.paneId, nextSession.terminalBarPaneId, activeTerminal.paneId);
773
+ }
774
+ this.updateTerminalBar(nextSession);
775
+ }
776
+ this.enforceSidebarWidth();
777
+ }
778
+ else {
779
+ // No more sessions - create empty pane
780
+ const newPaneId = tmux.splitHorizontal(this.state.sessionName, 80, this.state.repoPath);
781
+ this.state.mainPaneId = newPaneId;
782
+ tmux.sendKeys(newPaneId, 'echo "Press Enter in sidebar to start a session"', true);
783
+ this.enforceSidebarWidth();
784
+ }
785
+ tmux.selectPane(this.state.sidebarPaneId);
786
+ }
787
+ // Adjust selection
788
+ this.state.selectedIndex = Math.max(0, this.state.selectedIndex - 1);
789
+ this.render();
790
+ }
791
+ async deleteWorktree(worktree) {
792
+ // Delete all sessions for this worktree (including their terminals)
793
+ const sessionsToDelete = this.state.sessions.filter(s => s.worktreeId === worktree.id);
794
+ for (const session of sessionsToDelete) {
795
+ // Kill all terminal panes
796
+ for (const terminal of session.terminals) {
797
+ tmux.killPane(terminal.paneId);
798
+ }
799
+ // Kill terminal bar pane and remove hook
800
+ if (session.terminalBarPaneId) {
801
+ try {
802
+ tmux.removeHook(this.state.sessionName, 'after-resize-pane');
803
+ }
804
+ catch {
805
+ // Hook may not exist, ignore
806
+ }
807
+ tmux.killPane(session.terminalBarPaneId);
808
+ }
809
+ // Kill Claude pane
810
+ tmux.killPane(session.paneId);
811
+ }
812
+ this.state.sessions = this.state.sessions.filter(s => s.worktreeId !== worktree.id);
813
+ // If active session was deleted, switch to another
814
+ if (sessionsToDelete.some(s => s.id === this.state.activeSessionId)) {
815
+ this.state.activeSessionId = null;
816
+ if (this.state.sessions.length > 0) {
817
+ const nextSession = this.state.sessions[0];
818
+ tmux.joinPane(nextSession.paneId, this.state.sidebarPaneId, true);
819
+ this.state.activeSessionId = nextSession.id;
820
+ // Join terminal panes if next session has terminals
821
+ if (nextSession.terminals.length > 0 && nextSession.terminalBarPaneId) {
822
+ tmux.joinPane(nextSession.terminalBarPaneId, nextSession.paneId, false);
823
+ const activeTerminal = nextSession.terminals[nextSession.activeTerminalIndex];
824
+ if (activeTerminal) {
825
+ tmux.joinPane(activeTerminal.paneId, nextSession.terminalBarPaneId, false);
826
+ }
827
+ tmux.resizePane(nextSession.terminalBarPaneId, undefined, TERMINAL_BAR_HEIGHT);
828
+ // Update resize enforcement for the new session's terminal bar
829
+ if (activeTerminal) {
830
+ setupTerminalBarResize(this.state.sessionName, nextSession.paneId, nextSession.terminalBarPaneId, activeTerminal.paneId);
831
+ }
832
+ this.updateTerminalBar(nextSession);
833
+ }
834
+ this.enforceSidebarWidth();
835
+ }
836
+ tmux.selectPane(this.state.sidebarPaneId);
837
+ }
838
+ // Remove worktree via git
839
+ try {
840
+ await this.worktreeManager.remove(worktree.path, true);
841
+ }
842
+ catch {
843
+ // Ignore errors
844
+ }
845
+ // Remove from state
846
+ this.state.worktrees = this.state.worktrees.filter(w => w.id !== worktree.id);
847
+ // Adjust selection
848
+ const totalItems = this.getTotalItemCount();
849
+ if (this.state.selectedIndex >= totalItems) {
850
+ this.state.selectedIndex = Math.max(0, totalItems - 1);
851
+ }
852
+ this.render();
853
+ }
854
+ renameSelected(newName) {
855
+ const item = this.getSelectedItem();
856
+ if (!item)
857
+ return;
858
+ if (item.type === 'session' && item.session) {
859
+ item.session.title = newName;
860
+ }
861
+ // Worktree rename would require git branch rename - skip for now
862
+ }
863
+ toggleCollapsed() {
864
+ this.state.collapsed = !this.state.collapsed;
865
+ if (this.state.collapsed) {
866
+ tmux.resizePane(this.state.sidebarPaneId, 2);
867
+ }
868
+ else {
869
+ this.enforceSidebarWidth();
870
+ }
871
+ this.render();
872
+ }
873
+ enforceSidebarWidth() {
874
+ if (!this.state.collapsed) {
875
+ tmux.resizePane(this.state.sidebarPaneId, SIDEBAR_WIDTH);
876
+ }
877
+ }
878
+ // ==========================================================================
879
+ // Terminal Management
880
+ // ==========================================================================
881
+ /**
882
+ * Execute a terminal command received from the bar handler
883
+ * Format: "TERM:<action>:<data>"
884
+ */
885
+ executeTerminalCommand(command) {
886
+ debugLog('executeTerminalCommand:', command);
887
+ if (!command.startsWith('TERM:'))
888
+ return;
889
+ const parts = command.slice(5).split(':');
890
+ const action = parts[0];
891
+ const data = parts[1] || '';
892
+ const session = this.state.sessions.find(s => s.id === this.state.activeSessionId);
893
+ if (!session && action !== 'escape')
894
+ return;
895
+ switch (action) {
896
+ case 'switch':
897
+ const index = parseInt(data, 10);
898
+ if (!isNaN(index) && session) {
899
+ this.switchTerminal(session, index);
900
+ }
901
+ break;
902
+ case 'new':
903
+ this.createTerminal();
904
+ break;
905
+ case 'delete':
906
+ const delIndex = parseInt(data, 10);
907
+ if (!isNaN(delIndex) && session) {
908
+ this.deleteTerminal(session, delIndex);
909
+ }
910
+ break;
911
+ case 'focus':
912
+ if (session && session.terminals.length > 0) {
913
+ const activeTerminal = session.terminals[session.activeTerminalIndex];
914
+ if (activeTerminal) {
915
+ tmux.selectPane(activeTerminal.paneId);
916
+ }
917
+ }
918
+ break;
919
+ case 'escape':
920
+ tmux.selectPane(this.state.sidebarPaneId);
921
+ break;
922
+ }
923
+ }
924
+ /**
925
+ * Create a new terminal for the active session
926
+ */
927
+ createTerminal() {
928
+ if (!this.state.activeSessionId) {
929
+ debugLog('createTerminal: no active session');
930
+ return;
931
+ }
932
+ const session = this.state.sessions.find(s => s.id === this.state.activeSessionId);
933
+ if (!session)
934
+ return;
935
+ const worktree = this.state.worktrees.find(w => w.id === session.worktreeId);
936
+ if (!worktree)
937
+ return;
938
+ const terminalNum = session.terminals.length + 1;
939
+ const terminalTitle = `Terminal ${terminalNum}`;
940
+ const terminalId = `terminal-${Date.now()}`;
941
+ debugLog('createTerminal:', terminalTitle, 'for session', session.title);
942
+ if (session.terminals.length === 0) {
943
+ // First terminal - need to create terminal bar pane + terminal pane
944
+ // Split Claude pane vertically (Claude gets top 70%, terminal area gets bottom 30%)
945
+ tmux.selectPane(session.paneId);
946
+ const terminalAreaPaneId = tmux.splitVertical(this.state.sessionName, 100 - CLAUDE_PANE_PERCENT, worktree.path);
947
+ // Split terminal area: top 1 row for bar, rest for terminal
948
+ tmux.selectPane(terminalAreaPaneId);
949
+ const terminalPaneId = tmux.splitVertical(this.state.sessionName, 95, worktree.path);
950
+ // The terminalAreaPaneId is now the bar pane (top part after split)
951
+ const terminalBarPaneId = terminalAreaPaneId;
952
+ // Resize bar pane to exactly 1 row
953
+ tmux.resizePane(terminalBarPaneId, undefined, TERMINAL_BAR_HEIGHT);
954
+ // Set up resize enforcement (hook + mouse binding)
955
+ setupTerminalBarResize(this.state.sessionName, session.paneId, terminalBarPaneId, terminalPaneId);
956
+ // Update session
957
+ session.terminalBarPaneId = terminalBarPaneId;
958
+ session.terminals.push({
959
+ id: terminalId,
960
+ sessionId: session.id,
961
+ paneId: terminalPaneId,
962
+ title: terminalTitle,
963
+ createdAt: Date.now(),
964
+ });
965
+ session.activeTerminalIndex = 0;
966
+ // Start terminal bar handler in the bar pane
967
+ this.startTerminalBarHandler(session);
968
+ // Focus the terminal pane
969
+ tmux.selectPane(terminalPaneId);
970
+ }
971
+ else {
972
+ // Additional terminal - split current terminal, break old, show new
973
+ const currentTerminal = session.terminals[session.activeTerminalIndex];
974
+ // Split from current terminal to create new one
975
+ tmux.selectPane(currentTerminal.paneId);
976
+ const newTerminalPaneId = tmux.splitVertical(this.state.sessionName, 50, worktree.path);
977
+ // Break the current terminal to background
978
+ tmux.breakPane(currentTerminal.paneId);
979
+ // Add new terminal
980
+ session.terminals.push({
981
+ id: terminalId,
982
+ sessionId: session.id,
983
+ paneId: newTerminalPaneId,
984
+ title: terminalTitle,
985
+ createdAt: Date.now(),
986
+ });
987
+ session.activeTerminalIndex = session.terminals.length - 1;
988
+ // Ensure terminal bar stays at 1 row after the split
989
+ if (session.terminalBarPaneId) {
990
+ tmux.resizePane(session.terminalBarPaneId, undefined, TERMINAL_BAR_HEIGHT);
991
+ }
992
+ // Update terminal bar
993
+ this.updateTerminalBar(session);
994
+ // Focus the new terminal
995
+ tmux.selectPane(newTerminalPaneId);
996
+ }
997
+ // Ensure sidebar stays at fixed width
998
+ this.enforceSidebarWidth();
999
+ this.render();
1000
+ }
1001
+ /**
1002
+ * Switch to a different terminal tab within a session
1003
+ */
1004
+ switchTerminal(session, targetIndex) {
1005
+ if (targetIndex < 0 || targetIndex >= session.terminals.length)
1006
+ return;
1007
+ if (targetIndex === session.activeTerminalIndex)
1008
+ return;
1009
+ debugLog('switchTerminal:', targetIndex, 'in session', session.title);
1010
+ const currentTerminal = session.terminals[session.activeTerminalIndex];
1011
+ const newTerminal = session.terminals[targetIndex];
1012
+ // Break current terminal to background
1013
+ tmux.breakPane(currentTerminal.paneId);
1014
+ // Join new terminal below the bar pane
1015
+ if (session.terminalBarPaneId) {
1016
+ tmux.joinPane(newTerminal.paneId, session.terminalBarPaneId, false);
1017
+ // Ensure terminal bar stays at 1 row
1018
+ tmux.resizePane(session.terminalBarPaneId, undefined, TERMINAL_BAR_HEIGHT);
1019
+ }
1020
+ // Update active index
1021
+ session.activeTerminalIndex = targetIndex;
1022
+ // Update terminal bar
1023
+ this.updateTerminalBar(session);
1024
+ this.enforceSidebarWidth();
1025
+ }
1026
+ /**
1027
+ * Delete a terminal at the given index
1028
+ */
1029
+ deleteTerminal(session, index) {
1030
+ if (index < 0 || index >= session.terminals.length)
1031
+ return;
1032
+ debugLog('deleteTerminal:', index, 'in session', session.title);
1033
+ const terminal = session.terminals[index];
1034
+ const wasActive = index === session.activeTerminalIndex;
1035
+ // Kill the terminal pane
1036
+ tmux.killPane(terminal.paneId);
1037
+ // Remove from terminals array
1038
+ session.terminals.splice(index, 1);
1039
+ if (session.terminals.length === 0) {
1040
+ // No more terminals - kill terminal bar pane and remove resize hook
1041
+ if (session.terminalBarPaneId) {
1042
+ // Remove the after-resize-pane hook for this session
1043
+ try {
1044
+ tmux.removeHook(this.state.sessionName, 'after-resize-pane');
1045
+ }
1046
+ catch {
1047
+ // Hook may not exist, ignore
1048
+ }
1049
+ tmux.killPane(session.terminalBarPaneId);
1050
+ session.terminalBarPaneId = null;
1051
+ }
1052
+ session.activeTerminalIndex = 0;
1053
+ }
1054
+ else {
1055
+ // Adjust activeTerminalIndex
1056
+ if (index < session.activeTerminalIndex) {
1057
+ session.activeTerminalIndex--;
1058
+ }
1059
+ else if (session.activeTerminalIndex >= session.terminals.length) {
1060
+ session.activeTerminalIndex = session.terminals.length - 1;
1061
+ }
1062
+ // If deleted was the visible terminal, show the new active one
1063
+ if (wasActive) {
1064
+ const newActiveTerminal = session.terminals[session.activeTerminalIndex];
1065
+ if (newActiveTerminal && session.terminalBarPaneId) {
1066
+ tmux.joinPane(newActiveTerminal.paneId, session.terminalBarPaneId, false);
1067
+ tmux.resizePane(session.terminalBarPaneId, undefined, TERMINAL_BAR_HEIGHT);
1068
+ }
1069
+ }
1070
+ // Update terminal bar
1071
+ this.updateTerminalBar(session);
1072
+ }
1073
+ this.enforceSidebarWidth();
1074
+ this.render();
1075
+ }
1076
+ /**
1077
+ * Start the terminal bar handler in a session's bar pane
1078
+ */
1079
+ startTerminalBarHandler(session) {
1080
+ if (!session.terminalBarPaneId)
1081
+ return;
1082
+ // Build the command to run the bar handler
1083
+ // We pass the initial state as a JSON argument
1084
+ const initialState = JSON.stringify({
1085
+ terminals: session.terminals,
1086
+ activeIndex: session.activeTerminalIndex,
1087
+ });
1088
+ // Get path to bar-handler - check for .ts first (dev mode), then .js (compiled)
1089
+ // Check both src and dist locations
1090
+ const tsPath = (0, path_1.resolve)(__dirname, '../terminal/bar-handler.ts');
1091
+ const jsPath = (0, path_1.resolve)(__dirname, '../terminal/bar-handler.js');
1092
+ let cmd;
1093
+ const escapedState = initialState.replace(/'/g, "'\\''");
1094
+ const args = `"${this.state.sidebarPaneId}" "${session.id}" '${escapedState}'`;
1095
+ // Check which file exists
1096
+ if ((0, fs_1.existsSync)(tsPath)) {
1097
+ cmd = `npx tsx "${tsPath}" ${args}`;
1098
+ }
1099
+ else if ((0, fs_1.existsSync)(jsPath)) {
1100
+ cmd = `node "${jsPath}" ${args}`;
1101
+ }
1102
+ else {
1103
+ debugLog('startTerminalBarHandler: bar-handler not found at', tsPath, 'or', jsPath);
1104
+ return;
1105
+ }
1106
+ debugLog('startTerminalBarHandler:', cmd);
1107
+ tmux.sendKeys(session.terminalBarPaneId, cmd, true);
1108
+ }
1109
+ /**
1110
+ * Send updated state to the terminal bar handler
1111
+ */
1112
+ updateTerminalBar(session) {
1113
+ if (!session.terminalBarPaneId)
1114
+ return;
1115
+ const renderData = JSON.stringify({
1116
+ terminals: session.terminals,
1117
+ activeIndex: session.activeTerminalIndex,
1118
+ });
1119
+ // Send render command to bar handler
1120
+ tmux.sendKeys(session.terminalBarPaneId, `RENDER:${renderData}`, false);
1121
+ }
1122
+ // ==========================================================================
1123
+ // Fullscreen Modal Management
1124
+ // ==========================================================================
1125
+ /**
1126
+ * Enter fullscreen modal mode - hides all session panes so sidebar can expand
1127
+ */
1128
+ enterFullscreenModal() {
1129
+ if (this.state.fullscreenModal)
1130
+ return; // Already in fullscreen
1131
+ debugLog('enterFullscreenModal: hiding panes');
1132
+ if (this.state.activeSessionId) {
1133
+ // Hide the active session's panes (Claude pane + terminals)
1134
+ const activeSession = this.state.sessions.find(s => s.id === this.state.activeSessionId);
1135
+ if (activeSession) {
1136
+ try {
1137
+ // Break active terminal first (if any)
1138
+ if (activeSession.terminals.length > 0) {
1139
+ const activeTerminal = activeSession.terminals[activeSession.activeTerminalIndex];
1140
+ if (activeTerminal) {
1141
+ tmux.breakPane(activeTerminal.paneId);
1142
+ }
1143
+ // Break terminal bar
1144
+ if (activeSession.terminalBarPaneId) {
1145
+ tmux.breakPane(activeSession.terminalBarPaneId);
1146
+ }
1147
+ }
1148
+ // Break Claude pane
1149
+ tmux.breakPane(activeSession.paneId);
1150
+ this.state.hiddenPaneId = activeSession.paneId;
1151
+ debugLog('enterFullscreenModal: broke session panes');
1152
+ }
1153
+ catch (err) {
1154
+ debugLog('enterFullscreenModal: failed to break panes', err);
1155
+ }
1156
+ }
1157
+ }
1158
+ else {
1159
+ // Hide the main/welcome pane
1160
+ try {
1161
+ tmux.breakPane(this.state.mainPaneId);
1162
+ this.state.hiddenPaneId = this.state.mainPaneId;
1163
+ debugLog('enterFullscreenModal: broke main pane');
1164
+ }
1165
+ catch (err) {
1166
+ debugLog('enterFullscreenModal: failed to break main pane', err);
1167
+ }
1168
+ }
1169
+ this.state.fullscreenModal = true;
1170
+ }
1171
+ /**
1172
+ * Exit fullscreen modal mode - restores all session panes
1173
+ */
1174
+ exitFullscreenModal() {
1175
+ if (!this.state.fullscreenModal)
1176
+ return; // Not in fullscreen
1177
+ debugLog('exitFullscreenModal: restoring panes');
1178
+ if (this.state.hiddenPaneId) {
1179
+ try {
1180
+ // Join Claude pane
1181
+ tmux.joinPane(this.state.hiddenPaneId, this.state.sidebarPaneId, true);
1182
+ debugLog('exitFullscreenModal: joined Claude pane');
1183
+ // If active session has terminals, join those too
1184
+ const activeSession = this.state.sessions.find(s => s.id === this.state.activeSessionId);
1185
+ if (activeSession && activeSession.terminals.length > 0 && activeSession.terminalBarPaneId) {
1186
+ // Join terminal bar below Claude pane
1187
+ tmux.joinPane(activeSession.terminalBarPaneId, activeSession.paneId, false);
1188
+ // Join active terminal below terminal bar
1189
+ const activeTerminal = activeSession.terminals[activeSession.activeTerminalIndex];
1190
+ if (activeTerminal) {
1191
+ tmux.joinPane(activeTerminal.paneId, activeSession.terminalBarPaneId, false);
1192
+ }
1193
+ // Ensure terminal bar is exactly 1 row
1194
+ tmux.resizePane(activeSession.terminalBarPaneId, undefined, TERMINAL_BAR_HEIGHT);
1195
+ // Update terminal bar display
1196
+ this.updateTerminalBar(activeSession);
1197
+ debugLog('exitFullscreenModal: joined terminal panes');
1198
+ }
1199
+ this.enforceSidebarWidth();
1200
+ }
1201
+ catch (err) {
1202
+ debugLog('exitFullscreenModal: failed to join panes', err);
1203
+ }
1204
+ this.state.hiddenPaneId = null;
1205
+ }
1206
+ this.state.fullscreenModal = false;
1207
+ tmux.selectPane(this.state.sidebarPaneId);
1208
+ }
1209
+ // ==========================================================================
1210
+ // Helpers
1211
+ // ==========================================================================
1212
+ getMaxIndex() {
1213
+ return Math.max(0, this.getTotalItemCount() - 1);
1214
+ }
1215
+ getTotalItemCount() {
1216
+ let count = 0;
1217
+ for (const wt of this.state.worktrees) {
1218
+ count++; // worktree
1219
+ count += this.state.sessions.filter(s => s.worktreeId === wt.id).length;
1220
+ }
1221
+ return count;
1222
+ }
1223
+ getSelectedItem() {
1224
+ const items = (0, render_1.buildListItems)(this.state);
1225
+ const item = items[this.state.selectedIndex];
1226
+ if (!item)
1227
+ return null;
1228
+ return {
1229
+ type: item.type,
1230
+ id: item.id,
1231
+ worktree: item.worktree,
1232
+ session: item.session,
1233
+ };
1234
+ }
1235
+ }
1236
+ exports.SidebarApp = SidebarApp;
1237
+ //# sourceMappingURL=app.js.map