@agimon-ai/doompi-workflow 0.0.1-alpha.76 → 0.0.1-alpha.78

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 (332) hide show
  1. package/dist/_virtual/_rolldown/runtime.cjs +23 -0
  2. package/dist/api-contracts.cjs +7 -0
  3. package/dist/api-contracts.d.cts +2 -0
  4. package/dist/api-contracts.d.mts +2 -0
  5. package/dist/api-contracts.mjs +2 -0
  6. package/dist/extensions/pi.cjs +63 -0
  7. package/dist/extensions/pi.cjs.map +1 -0
  8. package/dist/extensions/pi.d.cts +6 -0
  9. package/dist/extensions/pi.d.cts.map +1 -0
  10. package/dist/extensions/pi.d.mts +6 -0
  11. package/dist/extensions/pi.d.mts.map +1 -0
  12. package/dist/extensions/pi.mjs +58 -0
  13. package/dist/extensions/pi.mjs.map +1 -0
  14. package/dist/extensions/server.cjs +96 -0
  15. package/dist/extensions/server.cjs.map +1 -0
  16. package/dist/extensions/server.d.cts +5 -0
  17. package/dist/extensions/server.d.cts.map +1 -0
  18. package/dist/extensions/server.d.mts +5 -0
  19. package/dist/extensions/server.d.mts.map +1 -0
  20. package/dist/extensions/server.mjs +91 -0
  21. package/dist/extensions/server.mjs.map +1 -0
  22. package/dist/extensions/web.mjs +3219 -0
  23. package/dist/extensions/web.mjs.map +1 -0
  24. package/dist/index.cjs +4 -0
  25. package/dist/index.d.cts +2 -0
  26. package/dist/index.d.mts +2 -0
  27. package/dist/index.mjs +2 -0
  28. package/dist/src/constants/workflow.cjs +32 -0
  29. package/dist/src/constants/workflow.cjs.map +1 -0
  30. package/dist/src/constants/workflow.mjs +21 -0
  31. package/dist/src/constants/workflow.mjs.map +1 -0
  32. package/dist/src/extensions/(backend)/_lib/index.server.cjs +12 -0
  33. package/dist/src/extensions/(backend)/_lib/index.server.cjs.map +1 -0
  34. package/dist/src/extensions/(backend)/_lib/index.server.mjs +12 -0
  35. package/dist/src/extensions/(backend)/_lib/index.server.mjs.map +1 -0
  36. package/dist/src/extensions/(backend)/api/workflow/route.server.cjs +7 -0
  37. package/dist/src/extensions/(backend)/api/workflow/route.server.cjs.map +1 -0
  38. package/dist/src/extensions/(backend)/api/workflow/route.server.mjs +8 -0
  39. package/dist/src/extensions/(backend)/api/workflow/route.server.mjs.map +1 -0
  40. package/dist/src/extensions/(backend)/channel/workflow-catalog.server.cjs +7 -0
  41. package/dist/src/extensions/(backend)/channel/workflow-catalog.server.cjs.map +1 -0
  42. package/dist/src/extensions/(backend)/channel/workflow-catalog.server.mjs +8 -0
  43. package/dist/src/extensions/(backend)/channel/workflow-catalog.server.mjs.map +1 -0
  44. package/dist/src/extensions/(backend)/channel/workflows.server.cjs +7 -0
  45. package/dist/src/extensions/(backend)/channel/workflows.server.cjs.map +1 -0
  46. package/dist/src/extensions/(backend)/channel/workflows.server.mjs +8 -0
  47. package/dist/src/extensions/(backend)/channel/workflows.server.mjs.map +1 -0
  48. package/dist/src/extensions/workspaces/sessions/(backend)/_lib/index.cli.cjs +100 -0
  49. package/dist/src/extensions/workspaces/sessions/(backend)/_lib/index.cli.cjs.map +1 -0
  50. package/dist/src/extensions/workspaces/sessions/(backend)/_lib/index.cli.mjs +100 -0
  51. package/dist/src/extensions/workspaces/sessions/(backend)/_lib/index.cli.mjs.map +1 -0
  52. package/dist/src/extensions/workspaces/sessions/(backend)/_lib/index.server.cjs +11 -0
  53. package/dist/src/extensions/workspaces/sessions/(backend)/_lib/index.server.cjs.map +1 -0
  54. package/dist/src/extensions/workspaces/sessions/(backend)/_lib/index.server.mjs +11 -0
  55. package/dist/src/extensions/workspaces/sessions/(backend)/_lib/index.server.mjs.map +1 -0
  56. package/dist/src/extensions/workspaces/sessions/(backend)/_lib/workflowSkillsResource.cjs +12 -0
  57. package/dist/src/extensions/workspaces/sessions/(backend)/_lib/workflowSkillsResource.cjs.map +1 -0
  58. package/dist/src/extensions/workspaces/sessions/(backend)/_lib/workflowSkillsResource.mjs +12 -0
  59. package/dist/src/extensions/workspaces/sessions/(backend)/_lib/workflowSkillsResource.mjs.map +1 -0
  60. package/dist/src/extensions/workspaces/sessions/(backend)/api/workflow/route.server.cjs +6 -0
  61. package/dist/src/extensions/workspaces/sessions/(backend)/api/workflow/route.server.cjs.map +1 -0
  62. package/dist/src/extensions/workspaces/sessions/(backend)/api/workflow/route.server.mjs +7 -0
  63. package/dist/src/extensions/workspaces/sessions/(backend)/api/workflow/route.server.mjs.map +1 -0
  64. package/dist/src/extensions/workspaces/sessions/(backend)/command/workflow-launch.cli.cjs +6 -0
  65. package/dist/src/extensions/workspaces/sessions/(backend)/command/workflow-launch.cli.cjs.map +1 -0
  66. package/dist/src/extensions/workspaces/sessions/(backend)/command/workflow-launch.cli.mjs +7 -0
  67. package/dist/src/extensions/workspaces/sessions/(backend)/command/workflow-launch.cli.mjs.map +1 -0
  68. package/dist/src/extensions/workspaces/sessions/(backend)/command/workflow-launch.server.cjs +6 -0
  69. package/dist/src/extensions/workspaces/sessions/(backend)/command/workflow-launch.server.cjs.map +1 -0
  70. package/dist/src/extensions/workspaces/sessions/(backend)/command/workflow-launch.server.mjs +7 -0
  71. package/dist/src/extensions/workspaces/sessions/(backend)/command/workflow-launch.server.mjs.map +1 -0
  72. package/dist/src/extensions/workspaces/sessions/(backend)/hook/resources-discover.cli.cjs +6 -0
  73. package/dist/src/extensions/workspaces/sessions/(backend)/hook/resources-discover.cli.cjs.map +1 -0
  74. package/dist/src/extensions/workspaces/sessions/(backend)/hook/resources-discover.cli.mjs +7 -0
  75. package/dist/src/extensions/workspaces/sessions/(backend)/hook/resources-discover.cli.mjs.map +1 -0
  76. package/dist/src/extensions/workspaces/sessions/(backend)/hook/session-shutdown.server.cjs +6 -0
  77. package/dist/src/extensions/workspaces/sessions/(backend)/hook/session-shutdown.server.cjs.map +1 -0
  78. package/dist/src/extensions/workspaces/sessions/(backend)/hook/session-shutdown.server.mjs +7 -0
  79. package/dist/src/extensions/workspaces/sessions/(backend)/hook/session-shutdown.server.mjs.map +1 -0
  80. package/dist/src/extensions/workspaces/sessions/(backend)/hook/session-start.cli.cjs +6 -0
  81. package/dist/src/extensions/workspaces/sessions/(backend)/hook/session-start.cli.cjs.map +1 -0
  82. package/dist/src/extensions/workspaces/sessions/(backend)/hook/session-start.cli.mjs +7 -0
  83. package/dist/src/extensions/workspaces/sessions/(backend)/hook/session-start.cli.mjs.map +1 -0
  84. package/dist/src/extensions/workspaces/sessions/(backend)/hook/tool-execution-end.server.cjs +6 -0
  85. package/dist/src/extensions/workspaces/sessions/(backend)/hook/tool-execution-end.server.cjs.map +1 -0
  86. package/dist/src/extensions/workspaces/sessions/(backend)/hook/tool-execution-end.server.mjs +7 -0
  87. package/dist/src/extensions/workspaces/sessions/(backend)/hook/tool-execution-end.server.mjs.map +1 -0
  88. package/dist/src/extensions/workspaces/sessions/(backend)/resource/doompi-author-workflow.server.cjs +6 -0
  89. package/dist/src/extensions/workspaces/sessions/(backend)/resource/doompi-author-workflow.server.cjs.map +1 -0
  90. package/dist/src/extensions/workspaces/sessions/(backend)/resource/doompi-author-workflow.server.mjs +7 -0
  91. package/dist/src/extensions/workspaces/sessions/(backend)/resource/doompi-author-workflow.server.mjs.map +1 -0
  92. package/dist/src/extensions/workspaces/sessions/(backend)/resource/doompi-use-workflow.server.cjs +6 -0
  93. package/dist/src/extensions/workspaces/sessions/(backend)/resource/doompi-use-workflow.server.cjs.map +1 -0
  94. package/dist/src/extensions/workspaces/sessions/(backend)/resource/doompi-use-workflow.server.mjs +7 -0
  95. package/dist/src/extensions/workspaces/sessions/(backend)/resource/doompi-use-workflow.server.mjs.map +1 -0
  96. package/dist/src/extensions/workspaces/sessions/(backend)/resource/workflow-recovery.server.cjs +6 -0
  97. package/dist/src/extensions/workspaces/sessions/(backend)/resource/workflow-recovery.server.cjs.map +1 -0
  98. package/dist/src/extensions/workspaces/sessions/(backend)/resource/workflow-recovery.server.mjs +7 -0
  99. package/dist/src/extensions/workspaces/sessions/(backend)/resource/workflow-recovery.server.mjs.map +1 -0
  100. package/dist/src/extensions/workspaces/sessions/(backend)/resource/workflow-skills.cli.cjs +7 -0
  101. package/dist/src/extensions/workspaces/sessions/(backend)/resource/workflow-skills.cli.cjs.map +1 -0
  102. package/dist/src/extensions/workspaces/sessions/(backend)/resource/workflow-skills.cli.mjs +8 -0
  103. package/dist/src/extensions/workspaces/sessions/(backend)/resource/workflow-skills.cli.mjs.map +1 -0
  104. package/dist/src/extensions/workspaces/sessions/(backend)/root.cli.cjs +15 -0
  105. package/dist/src/extensions/workspaces/sessions/(backend)/root.cli.cjs.map +1 -0
  106. package/dist/src/extensions/workspaces/sessions/(backend)/root.cli.mjs +16 -0
  107. package/dist/src/extensions/workspaces/sessions/(backend)/root.cli.mjs.map +1 -0
  108. package/dist/src/extensions/workspaces/sessions/(backend)/root.server.cjs +17 -0
  109. package/dist/src/extensions/workspaces/sessions/(backend)/root.server.cjs.map +1 -0
  110. package/dist/src/extensions/workspaces/sessions/(backend)/root.server.mjs +18 -0
  111. package/dist/src/extensions/workspaces/sessions/(backend)/root.server.mjs.map +1 -0
  112. package/dist/src/extensions/workspaces/sessions/(backend)/shortcut/workflow-panel.cli.cjs +6 -0
  113. package/dist/src/extensions/workspaces/sessions/(backend)/shortcut/workflow-panel.cli.cjs.map +1 -0
  114. package/dist/src/extensions/workspaces/sessions/(backend)/shortcut/workflow-panel.cli.mjs +7 -0
  115. package/dist/src/extensions/workspaces/sessions/(backend)/shortcut/workflow-panel.cli.mjs.map +1 -0
  116. package/dist/src/extensions/workspaces/sessions/(backend)/shortcut/workflow-view-close.cli.cjs +6 -0
  117. package/dist/src/extensions/workspaces/sessions/(backend)/shortcut/workflow-view-close.cli.cjs.map +1 -0
  118. package/dist/src/extensions/workspaces/sessions/(backend)/shortcut/workflow-view-close.cli.mjs +7 -0
  119. package/dist/src/extensions/workspaces/sessions/(backend)/shortcut/workflow-view-close.cli.mjs.map +1 -0
  120. package/dist/src/extensions/workspaces/sessions/(backend)/tool/launch-workflow.server.cjs +6 -0
  121. package/dist/src/extensions/workspaces/sessions/(backend)/tool/launch-workflow.server.cjs.map +1 -0
  122. package/dist/src/extensions/workspaces/sessions/(backend)/tool/launch-workflow.server.mjs +7 -0
  123. package/dist/src/extensions/workspaces/sessions/(backend)/tool/launch-workflow.server.mjs.map +1 -0
  124. package/dist/src/extensions/workspaces/sessions/(backend)/tool/list-workflows.server.cjs +6 -0
  125. package/dist/src/extensions/workspaces/sessions/(backend)/tool/list-workflows.server.cjs.map +1 -0
  126. package/dist/src/extensions/workspaces/sessions/(backend)/tool/list-workflows.server.mjs +7 -0
  127. package/dist/src/extensions/workspaces/sessions/(backend)/tool/list-workflows.server.mjs.map +1 -0
  128. package/dist/src/extensions/workspaces/sessions/(backend)/tool/workflow-run.server.cjs +6 -0
  129. package/dist/src/extensions/workspaces/sessions/(backend)/tool/workflow-run.server.cjs.map +1 -0
  130. package/dist/src/extensions/workspaces/sessions/(backend)/tool/workflow-run.server.mjs +7 -0
  131. package/dist/src/extensions/workspaces/sessions/(backend)/tool/workflow-run.server.mjs.map +1 -0
  132. package/dist/src/extensions/workspaces/sessions/(backend)/tool/workflow.cli.cjs +6 -0
  133. package/dist/src/extensions/workspaces/sessions/(backend)/tool/workflow.cli.cjs.map +1 -0
  134. package/dist/src/extensions/workspaces/sessions/(backend)/tool/workflow.cli.mjs +7 -0
  135. package/dist/src/extensions/workspaces/sessions/(backend)/tool/workflow.cli.mjs.map +1 -0
  136. package/dist/src/extensions/workspaces/sessions/(backend)/tool-restriction/workflow.cli.cjs +6 -0
  137. package/dist/src/extensions/workspaces/sessions/(backend)/tool-restriction/workflow.cli.cjs.map +1 -0
  138. package/dist/src/extensions/workspaces/sessions/(backend)/tool-restriction/workflow.cli.mjs +7 -0
  139. package/dist/src/extensions/workspaces/sessions/(backend)/tool-restriction/workflow.cli.mjs.map +1 -0
  140. package/dist/src/extensions/workspaces/sessions/(backend)/tool-restriction/workflow.server.cjs +6 -0
  141. package/dist/src/extensions/workspaces/sessions/(backend)/tool-restriction/workflow.server.cjs.map +1 -0
  142. package/dist/src/extensions/workspaces/sessions/(backend)/tool-restriction/workflow.server.mjs +7 -0
  143. package/dist/src/extensions/workspaces/sessions/(backend)/tool-restriction/workflow.server.mjs.map +1 -0
  144. package/dist/src/extensions/workspaces/sessions/(frontend)/message/_lib/workflowFinishedRenderer.cjs +50 -0
  145. package/dist/src/extensions/workspaces/sessions/(frontend)/message/_lib/workflowFinishedRenderer.cjs.map +1 -0
  146. package/dist/src/extensions/workspaces/sessions/(frontend)/message/_lib/workflowFinishedRenderer.mjs +47 -0
  147. package/dist/src/extensions/workspaces/sessions/(frontend)/message/_lib/workflowFinishedRenderer.mjs.map +1 -0
  148. package/dist/src/extensions/workspaces/sessions/(frontend)/message/_lib/workflowStepRenderer.cjs +36 -0
  149. package/dist/src/extensions/workspaces/sessions/(frontend)/message/_lib/workflowStepRenderer.cjs.map +1 -0
  150. package/dist/src/extensions/workspaces/sessions/(frontend)/message/_lib/workflowStepRenderer.mjs +35 -0
  151. package/dist/src/extensions/workspaces/sessions/(frontend)/message/_lib/workflowStepRenderer.mjs.map +1 -0
  152. package/dist/src/extensions/workspaces/sessions/(frontend)/message/workflow-finished.cli.cjs +7 -0
  153. package/dist/src/extensions/workspaces/sessions/(frontend)/message/workflow-finished.cli.cjs.map +1 -0
  154. package/dist/src/extensions/workspaces/sessions/(frontend)/message/workflow-finished.cli.mjs +8 -0
  155. package/dist/src/extensions/workspaces/sessions/(frontend)/message/workflow-finished.cli.mjs.map +1 -0
  156. package/dist/src/extensions/workspaces/sessions/(frontend)/message/workflow-step.cli.cjs +7 -0
  157. package/dist/src/extensions/workspaces/sessions/(frontend)/message/workflow-step.cli.cjs.map +1 -0
  158. package/dist/src/extensions/workspaces/sessions/(frontend)/message/workflow-step.cli.mjs +8 -0
  159. package/dist/src/extensions/workspaces/sessions/(frontend)/message/workflow-step.cli.mjs.map +1 -0
  160. package/dist/src/schemas/apiContracts.cjs +330 -0
  161. package/dist/src/schemas/apiContracts.cjs.map +1 -0
  162. package/dist/src/schemas/apiContracts.d.cts +53 -0
  163. package/dist/src/schemas/apiContracts.d.cts.map +1 -0
  164. package/dist/src/schemas/apiContracts.d.mts +53 -0
  165. package/dist/src/schemas/apiContracts.d.mts.map +1 -0
  166. package/dist/src/schemas/apiContracts.mjs +327 -0
  167. package/dist/src/schemas/apiContracts.mjs.map +1 -0
  168. package/dist/src/schemas/workflowPi.cjs +77 -0
  169. package/dist/src/schemas/workflowPi.cjs.map +1 -0
  170. package/dist/src/schemas/workflowPi.d.cts +1 -0
  171. package/dist/src/schemas/workflowPi.d.mts +1 -0
  172. package/dist/src/schemas/workflowPi.mjs +74 -0
  173. package/dist/src/schemas/workflowPi.mjs.map +1 -0
  174. package/dist/src/services/backgroundWork/index.cjs +61 -0
  175. package/dist/src/services/backgroundWork/index.cjs.map +1 -0
  176. package/dist/src/services/backgroundWork/index.mjs +61 -0
  177. package/dist/src/services/backgroundWork/index.mjs.map +1 -0
  178. package/dist/src/services/piToolBridge/index.cjs +205 -0
  179. package/dist/src/services/piToolBridge/index.cjs.map +1 -0
  180. package/dist/src/services/piToolBridge/index.mjs +195 -0
  181. package/dist/src/services/piToolBridge/index.mjs.map +1 -0
  182. package/dist/src/services/serverRuntime/index.cjs +387 -0
  183. package/dist/src/services/serverRuntime/index.cjs.map +1 -0
  184. package/dist/src/services/serverRuntime/index.mjs +387 -0
  185. package/dist/src/services/serverRuntime/index.mjs.map +1 -0
  186. package/dist/src/services/webWorkflowCatalog/index.cjs +53 -0
  187. package/dist/src/services/webWorkflowCatalog/index.cjs.map +1 -0
  188. package/dist/src/services/webWorkflowCatalog/index.mjs +52 -0
  189. package/dist/src/services/webWorkflowCatalog/index.mjs.map +1 -0
  190. package/dist/src/services/workflowCatalogChannel/index.cjs +74 -0
  191. package/dist/src/services/workflowCatalogChannel/index.cjs.map +1 -0
  192. package/dist/src/services/workflowCatalogChannel/index.mjs +74 -0
  193. package/dist/src/services/workflowCatalogChannel/index.mjs.map +1 -0
  194. package/dist/src/services/workflowCatalogDeps/index.cjs +25 -0
  195. package/dist/src/services/workflowCatalogDeps/index.cjs.map +1 -0
  196. package/dist/src/services/workflowCatalogDeps/index.mjs +25 -0
  197. package/dist/src/services/workflowCatalogDeps/index.mjs.map +1 -0
  198. package/dist/src/services/workflowExecution/index.cjs +205 -0
  199. package/dist/src/services/workflowExecution/index.cjs.map +1 -0
  200. package/dist/src/services/workflowExecution/index.d.cts +6 -0
  201. package/dist/src/services/workflowExecution/index.d.mts +6 -0
  202. package/dist/src/services/workflowExecution/index.mjs +200 -0
  203. package/dist/src/services/workflowExecution/index.mjs.map +1 -0
  204. package/dist/src/services/workflowFence/index.cjs +170 -0
  205. package/dist/src/services/workflowFence/index.cjs.map +1 -0
  206. package/dist/src/services/workflowFence/index.mjs +166 -0
  207. package/dist/src/services/workflowFence/index.mjs.map +1 -0
  208. package/dist/src/services/workflowHubApi/index.cjs +415 -0
  209. package/dist/src/services/workflowHubApi/index.cjs.map +1 -0
  210. package/dist/src/services/workflowHubApi/index.mjs +411 -0
  211. package/dist/src/services/workflowHubApi/index.mjs.map +1 -0
  212. package/dist/src/services/workflowLaunchCommand/index.cjs +121 -0
  213. package/dist/src/services/workflowLaunchCommand/index.cjs.map +1 -0
  214. package/dist/src/services/workflowLaunchCommand/index.d.cts +52 -0
  215. package/dist/src/services/workflowLaunchCommand/index.d.cts.map +1 -0
  216. package/dist/src/services/workflowLaunchCommand/index.d.mts +52 -0
  217. package/dist/src/services/workflowLaunchCommand/index.d.mts.map +1 -0
  218. package/dist/src/services/workflowLaunchCommand/index.mjs +118 -0
  219. package/dist/src/services/workflowLaunchCommand/index.mjs.map +1 -0
  220. package/dist/src/services/workflowLauncher/index.cjs +181 -0
  221. package/dist/src/services/workflowLauncher/index.cjs.map +1 -0
  222. package/dist/src/services/workflowLauncher/index.mjs +177 -0
  223. package/dist/src/services/workflowLauncher/index.mjs.map +1 -0
  224. package/dist/src/services/workflowNarration/index.cjs +19 -0
  225. package/dist/src/services/workflowNarration/index.cjs.map +1 -0
  226. package/dist/src/services/workflowNarration/index.mjs +19 -0
  227. package/dist/src/services/workflowNarration/index.mjs.map +1 -0
  228. package/dist/src/services/workflowResource/index.cjs +27 -0
  229. package/dist/src/services/workflowResource/index.cjs.map +1 -0
  230. package/dist/src/services/workflowResource/index.mjs +24 -0
  231. package/dist/src/services/workflowResource/index.mjs.map +1 -0
  232. package/dist/src/services/workflowRuns/index.cjs +333 -0
  233. package/dist/src/services/workflowRuns/index.cjs.map +1 -0
  234. package/dist/src/services/workflowRuns/index.mjs +319 -0
  235. package/dist/src/services/workflowRuns/index.mjs.map +1 -0
  236. package/dist/src/services/workflowTerminal/index.cjs +69 -0
  237. package/dist/src/services/workflowTerminal/index.cjs.map +1 -0
  238. package/dist/src/services/workflowTerminal/index.mjs +69 -0
  239. package/dist/src/services/workflowTerminal/index.mjs.map +1 -0
  240. package/dist/src/services/workflowTools/index.cjs +221 -0
  241. package/dist/src/services/workflowTools/index.cjs.map +1 -0
  242. package/dist/src/services/workflowTools/index.mjs +221 -0
  243. package/dist/src/services/workflowTools/index.mjs.map +1 -0
  244. package/dist/src/services/workflowWatcher/index.cjs +78 -0
  245. package/dist/src/services/workflowWatcher/index.cjs.map +1 -0
  246. package/dist/src/services/workflowWatcher/index.mjs +74 -0
  247. package/dist/src/services/workflowWatcher/index.mjs.map +1 -0
  248. package/dist/src/services/workflowsHubChannel/index.cjs +58 -0
  249. package/dist/src/services/workflowsHubChannel/index.cjs.map +1 -0
  250. package/dist/src/services/workflowsHubChannel/index.mjs +58 -0
  251. package/dist/src/services/workflowsHubChannel/index.mjs.map +1 -0
  252. package/dist/src/tui/leader.cjs +84 -0
  253. package/dist/src/tui/leader.cjs.map +1 -0
  254. package/dist/src/tui/leader.mjs +83 -0
  255. package/dist/src/tui/leader.mjs.map +1 -0
  256. package/dist/src/tui/workflow/doomOverlay.cjs +159 -0
  257. package/dist/src/tui/workflow/doomOverlay.cjs.map +1 -0
  258. package/dist/src/tui/workflow/doomOverlay.mjs +157 -0
  259. package/dist/src/tui/workflow/doomOverlay.mjs.map +1 -0
  260. package/dist/src/tui/workflow/overlayText.cjs +83 -0
  261. package/dist/src/tui/workflow/overlayText.cjs.map +1 -0
  262. package/dist/src/tui/workflow/overlayText.mjs +78 -0
  263. package/dist/src/tui/workflow/overlayText.mjs.map +1 -0
  264. package/dist/src/tui/workflow/rendering.cjs +22 -0
  265. package/dist/src/tui/workflow/rendering.cjs.map +1 -0
  266. package/dist/src/tui/workflow/rendering.mjs +20 -0
  267. package/dist/src/tui/workflow/rendering.mjs.map +1 -0
  268. package/dist/src/tui/workflow/workflowCatalog.cjs +336 -0
  269. package/dist/src/tui/workflow/workflowCatalog.cjs.map +1 -0
  270. package/dist/src/tui/workflow/workflowCatalog.mjs +333 -0
  271. package/dist/src/tui/workflow/workflowCatalog.mjs.map +1 -0
  272. package/dist/src/tui/workflow/workflowFinishedMessage.cjs +50 -0
  273. package/dist/src/tui/workflow/workflowFinishedMessage.cjs.map +1 -0
  274. package/dist/src/tui/workflow/workflowFinishedMessage.mjs +47 -0
  275. package/dist/src/tui/workflow/workflowFinishedMessage.mjs.map +1 -0
  276. package/dist/src/tui/workflow/workflowInspector.cjs +276 -0
  277. package/dist/src/tui/workflow/workflowInspector.cjs.map +1 -0
  278. package/dist/src/tui/workflow/workflowInspector.mjs +276 -0
  279. package/dist/src/tui/workflow/workflowInspector.mjs.map +1 -0
  280. package/dist/src/tui/workflow/workflowOverlay.cjs +126 -0
  281. package/dist/src/tui/workflow/workflowOverlay.cjs.map +1 -0
  282. package/dist/src/tui/workflow/workflowOverlay.mjs +125 -0
  283. package/dist/src/tui/workflow/workflowOverlay.mjs.map +1 -0
  284. package/dist/src/tui/workflow/workflowPicker.cjs +138 -0
  285. package/dist/src/tui/workflow/workflowPicker.cjs.map +1 -0
  286. package/dist/src/tui/workflow/workflowPicker.mjs +136 -0
  287. package/dist/src/tui/workflow/workflowPicker.mjs.map +1 -0
  288. package/dist/src/tui/workflow/workflowProgressOverlay.cjs +197 -0
  289. package/dist/src/tui/workflow/workflowProgressOverlay.cjs.map +1 -0
  290. package/dist/src/tui/workflow/workflowProgressOverlay.mjs +192 -0
  291. package/dist/src/tui/workflow/workflowProgressOverlay.mjs.map +1 -0
  292. package/dist/src/tui/workflow/workflowPrompt.cjs +142 -0
  293. package/dist/src/tui/workflow/workflowPrompt.cjs.map +1 -0
  294. package/dist/src/tui/workflow/workflowPrompt.mjs +139 -0
  295. package/dist/src/tui/workflow/workflowPrompt.mjs.map +1 -0
  296. package/dist/src/tui/workflow/workflowRunPanel.cjs +95 -0
  297. package/dist/src/tui/workflow/workflowRunPanel.cjs.map +1 -0
  298. package/dist/src/tui/workflow/workflowRunPanel.mjs +94 -0
  299. package/dist/src/tui/workflow/workflowRunPanel.mjs.map +1 -0
  300. package/dist/src/tui/workflow/workflowStatusRow.cjs +20 -0
  301. package/dist/src/tui/workflow/workflowStatusRow.cjs.map +1 -0
  302. package/dist/src/tui/workflow/workflowStatusRow.mjs +20 -0
  303. package/dist/src/tui/workflow/workflowStatusRow.mjs.map +1 -0
  304. package/dist/src/tui/workflow/workflowStepMessage.cjs +32 -0
  305. package/dist/src/tui/workflow/workflowStepMessage.cjs.map +1 -0
  306. package/dist/src/tui/workflow/workflowStepMessage.mjs +31 -0
  307. package/dist/src/tui/workflow/workflowStepMessage.mjs.map +1 -0
  308. package/dist/src/tui/workflow/workflowToolRender.cjs +491 -0
  309. package/dist/src/tui/workflow/workflowToolRender.cjs.map +1 -0
  310. package/dist/src/tui/workflow/workflowToolRender.mjs +490 -0
  311. package/dist/src/tui/workflow/workflowToolRender.mjs.map +1 -0
  312. package/dist/src/tui/workflowRuntime.cjs +1934 -0
  313. package/dist/src/tui/workflowRuntime.cjs.map +1 -0
  314. package/dist/src/tui/workflowRuntime.d.cts +33 -0
  315. package/dist/src/tui/workflowRuntime.d.cts.map +1 -0
  316. package/dist/src/tui/workflowRuntime.d.mts +33 -0
  317. package/dist/src/tui/workflowRuntime.d.mts.map +1 -0
  318. package/dist/src/tui/workflowRuntime.mjs +1930 -0
  319. package/dist/src/tui/workflowRuntime.mjs.map +1 -0
  320. package/dist/src/types/apiRoutes.cjs +82 -0
  321. package/dist/src/types/apiRoutes.cjs.map +1 -0
  322. package/dist/src/types/apiRoutes.mjs +80 -0
  323. package/dist/src/types/apiRoutes.mjs.map +1 -0
  324. package/dist/src/types/webWorkflowTerminal.cjs +23 -0
  325. package/dist/src/types/webWorkflowTerminal.cjs.map +1 -0
  326. package/dist/src/types/webWorkflowTerminal.mjs +22 -0
  327. package/dist/src/types/webWorkflowTerminal.mjs.map +1 -0
  328. package/dist/src/types/webWorkflows.cjs +8 -0
  329. package/dist/src/types/webWorkflows.cjs.map +1 -0
  330. package/dist/src/types/webWorkflows.mjs +7 -0
  331. package/dist/src/types/webWorkflows.mjs.map +1 -0
  332. package/package.json +8 -8
@@ -0,0 +1,126 @@
1
+ let _earendil_works_pi_tui = require("@earendil-works/pi-tui");
2
+ //#region src/tui/workflow/workflowOverlay.ts
3
+ /**
4
+ * Input plumbing for the run panel: keystroke batching and the escape hatch.
5
+ *
6
+ * The panel's rendering lives in `workflowRunPanel.ts`, which is a doom overlay;
7
+ * what stays here is the behaviour that has nothing to do with drawing.
8
+ *
9
+ * DESIGN PATTERNS:
10
+ * - Input batching. Every keystroke sent individually costs one subprocess
11
+ * round trip, measured at ~18ms, which caps sustained typing near 56 c/s and
12
+ * makes held keys queue visibly. Coalescing a frame's bytes into one send
13
+ * keeps latency at roughly one frame no matter how fast the user types.
14
+ * - Stateless render. The component recomputes from the latest snapshot on
15
+ * every frame, so a theme change needs no cache invalidation.
16
+ * - Terminal-independent escape hatch. The focused panel forwards every key to
17
+ * the run, so the way out cannot itself depend on a modifier the terminal
18
+ * may not transmit.
19
+ *
20
+ * CODING STANDARDS:
21
+ * - Named exports only
22
+ * - Explicit return types on exported members
23
+ *
24
+ * AVOID:
25
+ * - Sending per keystroke: it is the difference between usable and unusable
26
+ * - Holding the flush timer open after dispose, which would keep writing into
27
+ * a run the user has stopped watching
28
+ */
29
+ /** One frame at 60Hz. Long enough to coalesce a burst, short enough to feel direct. */
30
+ const FLUSH_INTERVAL_MS = 16;
31
+ /**
32
+ * The panel's guaranteed way out: press Escape twice, quickly.
33
+ *
34
+ * A focused panel hands every key to the run so its agent stays interruptible,
35
+ * which leaves the two `ctrl+alt` chords as the only exits. Those are exactly
36
+ * the keys a terminal may not deliver: macOS without Option-as-Meta sends plain
37
+ * `ctrl+w` instead, and the byte is forwarded into the run rather than matching.
38
+ * The user is then inside a view they cannot leave. Escape is the one key every
39
+ * terminal transmits identically, which is what makes it the failsafe.
40
+ *
41
+ * The first Escape is still forwarded, so interrupting the step stays as fast as
42
+ * it ever was. Only the second one is consumed, and only when it lands inside
43
+ * the window. The cost is one keystroke: sending the run two literal Escapes in
44
+ * quick succession is no longer possible, which is worth an escapable panel.
45
+ */
46
+ var DoubleEscapeDetector = class {
47
+ windowMs;
48
+ lastEscapeAt;
49
+ constructor(windowMs = 350) {
50
+ this.windowMs = windowMs;
51
+ }
52
+ /**
53
+ * Classify one chunk of terminal input.
54
+ *
55
+ * `now` is injected so the decision is a pure function of its inputs, which
56
+ * is what makes the window testable without timers.
57
+ */
58
+ observe(data, now = Date.now()) {
59
+ if (data === "\x1B\x1B") {
60
+ this.lastEscapeAt = void 0;
61
+ return "close";
62
+ }
63
+ if ((0, _earendil_works_pi_tui.isKeyRelease)(data)) return "forward";
64
+ if (!(0, _earendil_works_pi_tui.matchesKey)(data, "escape")) {
65
+ this.lastEscapeAt = void 0;
66
+ return "forward";
67
+ }
68
+ if (this.lastEscapeAt !== void 0 && now - this.lastEscapeAt <= this.windowMs) {
69
+ this.lastEscapeAt = void 0;
70
+ return "close";
71
+ }
72
+ this.lastEscapeAt = now;
73
+ return "forward";
74
+ }
75
+ };
76
+ /**
77
+ * Collects keystrokes and forwards them in frame-sized batches.
78
+ *
79
+ * Failures are swallowed on purpose: a dropped keystroke is a nuisance, but an
80
+ * exception raised from a terminal input handler would tear down the overlay.
81
+ */
82
+ var TerminalInputBatcher = class {
83
+ send;
84
+ flushIntervalMs;
85
+ pending = "";
86
+ timer;
87
+ disposed = false;
88
+ constructor(send, flushIntervalMs = FLUSH_INTERVAL_MS) {
89
+ this.send = send;
90
+ this.flushIntervalMs = flushIntervalMs;
91
+ }
92
+ write(data) {
93
+ if (this.disposed || data.length === 0) return;
94
+ this.pending += data;
95
+ if (this.timer) return;
96
+ this.timer = setTimeout(() => void this.flush(), this.flushIntervalMs);
97
+ this.timer.unref?.();
98
+ }
99
+ /** Send whatever has accumulated. Safe to call with nothing pending. */
100
+ async flush() {
101
+ if (this.timer) clearTimeout(this.timer);
102
+ this.timer = void 0;
103
+ const text = this.pending;
104
+ this.pending = "";
105
+ if (!text || this.disposed) return;
106
+ try {
107
+ await this.send(text);
108
+ } catch {}
109
+ }
110
+ /** Stop accepting and forwarding input. Pending bytes are discarded. */
111
+ dispose() {
112
+ this.disposed = true;
113
+ if (this.timer) clearTimeout(this.timer);
114
+ this.timer = void 0;
115
+ this.pending = "";
116
+ }
117
+ /** Exposed for tests: whether a flush is scheduled. */
118
+ hasPending() {
119
+ return this.pending.length > 0;
120
+ }
121
+ };
122
+ //#endregion
123
+ exports.DoubleEscapeDetector = DoubleEscapeDetector;
124
+ exports.TerminalInputBatcher = TerminalInputBatcher;
125
+
126
+ //# sourceMappingURL=workflowOverlay.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"workflowOverlay.cjs","names":["isKeyRelease","matchesKey"],"sources":["../../../../src/tui/workflow/workflowOverlay.ts"],"sourcesContent":["/**\n * Input plumbing for the run panel: keystroke batching and the escape hatch.\n *\n * The panel's rendering lives in `workflowRunPanel.ts`, which is a doom overlay;\n * what stays here is the behaviour that has nothing to do with drawing.\n *\n * DESIGN PATTERNS:\n * - Input batching. Every keystroke sent individually costs one subprocess\n * round trip, measured at ~18ms, which caps sustained typing near 56 c/s and\n * makes held keys queue visibly. Coalescing a frame's bytes into one send\n * keeps latency at roughly one frame no matter how fast the user types.\n * - Stateless render. The component recomputes from the latest snapshot on\n * every frame, so a theme change needs no cache invalidation.\n * - Terminal-independent escape hatch. The focused panel forwards every key to\n * the run, so the way out cannot itself depend on a modifier the terminal\n * may not transmit.\n *\n * CODING STANDARDS:\n * - Named exports only\n * - Explicit return types on exported members\n *\n * AVOID:\n * - Sending per keystroke: it is the difference between usable and unusable\n * - Holding the flush timer open after dispose, which would keep writing into\n * a run the user has stopped watching\n */\n\nimport { isKeyRelease, matchesKey } from '@earendil-works/pi-tui';\n\n/** One frame at 60Hz. Long enough to coalesce a burst, short enough to feel direct. */\nconst FLUSH_INTERVAL_MS = 16;\n\n/**\n * How close two Escapes must be to read as \"get me out\".\n *\n * Long enough to be reachable without hurrying, short enough that two\n * deliberate, separate interrupts sent to the run are not mistaken for it.\n */\nexport const DOUBLE_ESCAPE_WINDOW_MS = 350;\n\n/** What the overlay should do with a keystroke. */\nexport type EscapeAction = 'forward' | 'close';\n\n/**\n * The panel's guaranteed way out: press Escape twice, quickly.\n *\n * A focused panel hands every key to the run so its agent stays interruptible,\n * which leaves the two `ctrl+alt` chords as the only exits. Those are exactly\n * the keys a terminal may not deliver: macOS without Option-as-Meta sends plain\n * `ctrl+w` instead, and the byte is forwarded into the run rather than matching.\n * The user is then inside a view they cannot leave. Escape is the one key every\n * terminal transmits identically, which is what makes it the failsafe.\n *\n * The first Escape is still forwarded, so interrupting the step stays as fast as\n * it ever was. Only the second one is consumed, and only when it lands inside\n * the window. The cost is one keystroke: sending the run two literal Escapes in\n * quick succession is no longer possible, which is worth an escapable panel.\n */\nexport class DoubleEscapeDetector {\n private lastEscapeAt: number | undefined;\n\n constructor(private readonly windowMs: number = DOUBLE_ESCAPE_WINDOW_MS) {}\n\n /**\n * Classify one chunk of terminal input.\n *\n * `now` is injected so the decision is a pure function of its inputs, which\n * is what makes the window testable without timers.\n */\n observe(data: string, now: number = Date.now()): EscapeAction {\n // Two Escapes coalesced into one read. A terminal in legacy mode delivers\n // this when they arrive inside the same poll, so it is the same gesture and\n // has to reach the same answer. `matchesKey` never sees it as an escape:\n // it parses as ctrl+alt+[, which has no meaning to a run either way.\n if (data === '\\x1b\\x1b') {\n this.lastEscapeAt = undefined;\n return 'close';\n }\n\n // A Kitty release event repeats the press's own bytes. Counting it would\n // turn one deliberate press into a pair and close the panel under the user.\n if (isKeyRelease(data)) return 'forward';\n\n if (!matchesKey(data, 'escape')) {\n // Anything typed in between makes these two separate interrupts rather\n // than one gesture, so the pair has to start over.\n this.lastEscapeAt = undefined;\n return 'forward';\n }\n\n if (this.lastEscapeAt !== undefined && now - this.lastEscapeAt <= this.windowMs) {\n this.lastEscapeAt = undefined;\n return 'close';\n }\n\n this.lastEscapeAt = now;\n return 'forward';\n }\n}\n\n/**\n * Collects keystrokes and forwards them in frame-sized batches.\n *\n * Failures are swallowed on purpose: a dropped keystroke is a nuisance, but an\n * exception raised from a terminal input handler would tear down the overlay.\n */\nexport class TerminalInputBatcher {\n private pending = '';\n private timer: NodeJS.Timeout | undefined;\n private disposed = false;\n\n constructor(\n private readonly send: (text: string) => Promise<void>,\n private readonly flushIntervalMs: number = FLUSH_INTERVAL_MS,\n ) {}\n\n write(data: string): void {\n if (this.disposed || data.length === 0) return;\n this.pending += data;\n if (this.timer) return;\n this.timer = setTimeout(() => void this.flush(), this.flushIntervalMs);\n this.timer.unref?.();\n }\n\n /** Send whatever has accumulated. Safe to call with nothing pending. */\n async flush(): Promise<void> {\n if (this.timer) clearTimeout(this.timer);\n this.timer = undefined;\n const text = this.pending;\n this.pending = '';\n if (!text || this.disposed) return;\n try {\n await this.send(text);\n } catch {\n // A dropped keystroke must not take the overlay down with it.\n }\n }\n\n /** Stop accepting and forwarding input. Pending bytes are discarded. */\n dispose(): void {\n this.disposed = true;\n if (this.timer) clearTimeout(this.timer);\n this.timer = undefined;\n this.pending = '';\n }\n\n /** Exposed for tests: whether a flush is scheduled. */\n hasPending(): boolean {\n return this.pending.length > 0;\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA8BA,MAAM,oBAAoB;;;;;;;;;;;;;;;;AA4B1B,IAAa,uBAAb,MAAkC;CAGH;CAF7B;CAEA,YAAY,WAAiB,KAA4C;EAA5C,KAAA,WAAA;CAA6C;;;;;;;CAQ1E,QAAQ,MAAc,MAAc,KAAK,IAAI,GAAiB;EAK5D,IAAI,SAAS,YAAY;GACvB,KAAK,eAAe,KAAA;GACpB,OAAO;EACT;EAIA,KAAA,GAAIA,uBAAAA,aAAAA,CAAa,IAAI,GAAG,OAAO;EAE/B,IAAI,EAAA,GAACC,uBAAAA,WAAAA,CAAW,MAAM,QAAQ,GAAG;GAG/B,KAAK,eAAe,KAAA;GACpB,OAAO;EACT;EAEA,IAAI,KAAK,iBAAiB,KAAA,KAAa,MAAM,KAAK,gBAAgB,KAAK,UAAU;GAC/E,KAAK,eAAe,KAAA;GACpB,OAAO;EACT;EAEA,KAAK,eAAe;EACpB,OAAO;CACT;AACF;;;;;;;AAQA,IAAa,uBAAb,MAAkC;CAMb;CACA;CANnB,UAAkB;CAClB;CACA,WAAmB;CAEnB,YACE,MACA,kBAA2C,mBAC3C;EAFiB,KAAA,OAAA;EACA,KAAA,kBAAA;CAChB;CAEH,MAAM,MAAoB;EACxB,IAAI,KAAK,YAAY,KAAK,WAAW,GAAG;EACxC,KAAK,WAAW;EAChB,IAAI,KAAK,OAAO;EAChB,KAAK,QAAQ,iBAAiB,KAAK,KAAK,MAAM,GAAG,KAAK,eAAe;EACrE,KAAK,MAAM,QAAQ;CACrB;;CAGA,MAAM,QAAuB;EAC3B,IAAI,KAAK,OAAO,aAAa,KAAK,KAAK;EACvC,KAAK,QAAQ,KAAA;EACb,MAAM,OAAO,KAAK;EAClB,KAAK,UAAU;EACf,IAAI,CAAC,QAAQ,KAAK,UAAU;EAC5B,IAAI;GACF,MAAM,KAAK,KAAK,IAAI;EACtB,QAAQ,CAER;CACF;;CAGA,UAAgB;EACd,KAAK,WAAW;EAChB,IAAI,KAAK,OAAO,aAAa,KAAK,KAAK;EACvC,KAAK,QAAQ,KAAA;EACb,KAAK,UAAU;CACjB;;CAGA,aAAsB;EACpB,OAAO,KAAK,QAAQ,SAAS;CAC/B;AACF"}
@@ -0,0 +1,125 @@
1
+ import { isKeyRelease, matchesKey } from "@earendil-works/pi-tui";
2
+ //#region src/tui/workflow/workflowOverlay.ts
3
+ /**
4
+ * Input plumbing for the run panel: keystroke batching and the escape hatch.
5
+ *
6
+ * The panel's rendering lives in `workflowRunPanel.ts`, which is a doom overlay;
7
+ * what stays here is the behaviour that has nothing to do with drawing.
8
+ *
9
+ * DESIGN PATTERNS:
10
+ * - Input batching. Every keystroke sent individually costs one subprocess
11
+ * round trip, measured at ~18ms, which caps sustained typing near 56 c/s and
12
+ * makes held keys queue visibly. Coalescing a frame's bytes into one send
13
+ * keeps latency at roughly one frame no matter how fast the user types.
14
+ * - Stateless render. The component recomputes from the latest snapshot on
15
+ * every frame, so a theme change needs no cache invalidation.
16
+ * - Terminal-independent escape hatch. The focused panel forwards every key to
17
+ * the run, so the way out cannot itself depend on a modifier the terminal
18
+ * may not transmit.
19
+ *
20
+ * CODING STANDARDS:
21
+ * - Named exports only
22
+ * - Explicit return types on exported members
23
+ *
24
+ * AVOID:
25
+ * - Sending per keystroke: it is the difference between usable and unusable
26
+ * - Holding the flush timer open after dispose, which would keep writing into
27
+ * a run the user has stopped watching
28
+ */
29
+ /** One frame at 60Hz. Long enough to coalesce a burst, short enough to feel direct. */
30
+ const FLUSH_INTERVAL_MS = 16;
31
+ /**
32
+ * The panel's guaranteed way out: press Escape twice, quickly.
33
+ *
34
+ * A focused panel hands every key to the run so its agent stays interruptible,
35
+ * which leaves the two `ctrl+alt` chords as the only exits. Those are exactly
36
+ * the keys a terminal may not deliver: macOS without Option-as-Meta sends plain
37
+ * `ctrl+w` instead, and the byte is forwarded into the run rather than matching.
38
+ * The user is then inside a view they cannot leave. Escape is the one key every
39
+ * terminal transmits identically, which is what makes it the failsafe.
40
+ *
41
+ * The first Escape is still forwarded, so interrupting the step stays as fast as
42
+ * it ever was. Only the second one is consumed, and only when it lands inside
43
+ * the window. The cost is one keystroke: sending the run two literal Escapes in
44
+ * quick succession is no longer possible, which is worth an escapable panel.
45
+ */
46
+ var DoubleEscapeDetector = class {
47
+ windowMs;
48
+ lastEscapeAt;
49
+ constructor(windowMs = 350) {
50
+ this.windowMs = windowMs;
51
+ }
52
+ /**
53
+ * Classify one chunk of terminal input.
54
+ *
55
+ * `now` is injected so the decision is a pure function of its inputs, which
56
+ * is what makes the window testable without timers.
57
+ */
58
+ observe(data, now = Date.now()) {
59
+ if (data === "\x1B\x1B") {
60
+ this.lastEscapeAt = void 0;
61
+ return "close";
62
+ }
63
+ if (isKeyRelease(data)) return "forward";
64
+ if (!matchesKey(data, "escape")) {
65
+ this.lastEscapeAt = void 0;
66
+ return "forward";
67
+ }
68
+ if (this.lastEscapeAt !== void 0 && now - this.lastEscapeAt <= this.windowMs) {
69
+ this.lastEscapeAt = void 0;
70
+ return "close";
71
+ }
72
+ this.lastEscapeAt = now;
73
+ return "forward";
74
+ }
75
+ };
76
+ /**
77
+ * Collects keystrokes and forwards them in frame-sized batches.
78
+ *
79
+ * Failures are swallowed on purpose: a dropped keystroke is a nuisance, but an
80
+ * exception raised from a terminal input handler would tear down the overlay.
81
+ */
82
+ var TerminalInputBatcher = class {
83
+ send;
84
+ flushIntervalMs;
85
+ pending = "";
86
+ timer;
87
+ disposed = false;
88
+ constructor(send, flushIntervalMs = FLUSH_INTERVAL_MS) {
89
+ this.send = send;
90
+ this.flushIntervalMs = flushIntervalMs;
91
+ }
92
+ write(data) {
93
+ if (this.disposed || data.length === 0) return;
94
+ this.pending += data;
95
+ if (this.timer) return;
96
+ this.timer = setTimeout(() => void this.flush(), this.flushIntervalMs);
97
+ this.timer.unref?.();
98
+ }
99
+ /** Send whatever has accumulated. Safe to call with nothing pending. */
100
+ async flush() {
101
+ if (this.timer) clearTimeout(this.timer);
102
+ this.timer = void 0;
103
+ const text = this.pending;
104
+ this.pending = "";
105
+ if (!text || this.disposed) return;
106
+ try {
107
+ await this.send(text);
108
+ } catch {}
109
+ }
110
+ /** Stop accepting and forwarding input. Pending bytes are discarded. */
111
+ dispose() {
112
+ this.disposed = true;
113
+ if (this.timer) clearTimeout(this.timer);
114
+ this.timer = void 0;
115
+ this.pending = "";
116
+ }
117
+ /** Exposed for tests: whether a flush is scheduled. */
118
+ hasPending() {
119
+ return this.pending.length > 0;
120
+ }
121
+ };
122
+ //#endregion
123
+ export { DoubleEscapeDetector, TerminalInputBatcher };
124
+
125
+ //# sourceMappingURL=workflowOverlay.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"workflowOverlay.mjs","names":[],"sources":["../../../../src/tui/workflow/workflowOverlay.ts"],"sourcesContent":["/**\n * Input plumbing for the run panel: keystroke batching and the escape hatch.\n *\n * The panel's rendering lives in `workflowRunPanel.ts`, which is a doom overlay;\n * what stays here is the behaviour that has nothing to do with drawing.\n *\n * DESIGN PATTERNS:\n * - Input batching. Every keystroke sent individually costs one subprocess\n * round trip, measured at ~18ms, which caps sustained typing near 56 c/s and\n * makes held keys queue visibly. Coalescing a frame's bytes into one send\n * keeps latency at roughly one frame no matter how fast the user types.\n * - Stateless render. The component recomputes from the latest snapshot on\n * every frame, so a theme change needs no cache invalidation.\n * - Terminal-independent escape hatch. The focused panel forwards every key to\n * the run, so the way out cannot itself depend on a modifier the terminal\n * may not transmit.\n *\n * CODING STANDARDS:\n * - Named exports only\n * - Explicit return types on exported members\n *\n * AVOID:\n * - Sending per keystroke: it is the difference between usable and unusable\n * - Holding the flush timer open after dispose, which would keep writing into\n * a run the user has stopped watching\n */\n\nimport { isKeyRelease, matchesKey } from '@earendil-works/pi-tui';\n\n/** One frame at 60Hz. Long enough to coalesce a burst, short enough to feel direct. */\nconst FLUSH_INTERVAL_MS = 16;\n\n/**\n * How close two Escapes must be to read as \"get me out\".\n *\n * Long enough to be reachable without hurrying, short enough that two\n * deliberate, separate interrupts sent to the run are not mistaken for it.\n */\nexport const DOUBLE_ESCAPE_WINDOW_MS = 350;\n\n/** What the overlay should do with a keystroke. */\nexport type EscapeAction = 'forward' | 'close';\n\n/**\n * The panel's guaranteed way out: press Escape twice, quickly.\n *\n * A focused panel hands every key to the run so its agent stays interruptible,\n * which leaves the two `ctrl+alt` chords as the only exits. Those are exactly\n * the keys a terminal may not deliver: macOS without Option-as-Meta sends plain\n * `ctrl+w` instead, and the byte is forwarded into the run rather than matching.\n * The user is then inside a view they cannot leave. Escape is the one key every\n * terminal transmits identically, which is what makes it the failsafe.\n *\n * The first Escape is still forwarded, so interrupting the step stays as fast as\n * it ever was. Only the second one is consumed, and only when it lands inside\n * the window. The cost is one keystroke: sending the run two literal Escapes in\n * quick succession is no longer possible, which is worth an escapable panel.\n */\nexport class DoubleEscapeDetector {\n private lastEscapeAt: number | undefined;\n\n constructor(private readonly windowMs: number = DOUBLE_ESCAPE_WINDOW_MS) {}\n\n /**\n * Classify one chunk of terminal input.\n *\n * `now` is injected so the decision is a pure function of its inputs, which\n * is what makes the window testable without timers.\n */\n observe(data: string, now: number = Date.now()): EscapeAction {\n // Two Escapes coalesced into one read. A terminal in legacy mode delivers\n // this when they arrive inside the same poll, so it is the same gesture and\n // has to reach the same answer. `matchesKey` never sees it as an escape:\n // it parses as ctrl+alt+[, which has no meaning to a run either way.\n if (data === '\\x1b\\x1b') {\n this.lastEscapeAt = undefined;\n return 'close';\n }\n\n // A Kitty release event repeats the press's own bytes. Counting it would\n // turn one deliberate press into a pair and close the panel under the user.\n if (isKeyRelease(data)) return 'forward';\n\n if (!matchesKey(data, 'escape')) {\n // Anything typed in between makes these two separate interrupts rather\n // than one gesture, so the pair has to start over.\n this.lastEscapeAt = undefined;\n return 'forward';\n }\n\n if (this.lastEscapeAt !== undefined && now - this.lastEscapeAt <= this.windowMs) {\n this.lastEscapeAt = undefined;\n return 'close';\n }\n\n this.lastEscapeAt = now;\n return 'forward';\n }\n}\n\n/**\n * Collects keystrokes and forwards them in frame-sized batches.\n *\n * Failures are swallowed on purpose: a dropped keystroke is a nuisance, but an\n * exception raised from a terminal input handler would tear down the overlay.\n */\nexport class TerminalInputBatcher {\n private pending = '';\n private timer: NodeJS.Timeout | undefined;\n private disposed = false;\n\n constructor(\n private readonly send: (text: string) => Promise<void>,\n private readonly flushIntervalMs: number = FLUSH_INTERVAL_MS,\n ) {}\n\n write(data: string): void {\n if (this.disposed || data.length === 0) return;\n this.pending += data;\n if (this.timer) return;\n this.timer = setTimeout(() => void this.flush(), this.flushIntervalMs);\n this.timer.unref?.();\n }\n\n /** Send whatever has accumulated. Safe to call with nothing pending. */\n async flush(): Promise<void> {\n if (this.timer) clearTimeout(this.timer);\n this.timer = undefined;\n const text = this.pending;\n this.pending = '';\n if (!text || this.disposed) return;\n try {\n await this.send(text);\n } catch {\n // A dropped keystroke must not take the overlay down with it.\n }\n }\n\n /** Stop accepting and forwarding input. Pending bytes are discarded. */\n dispose(): void {\n this.disposed = true;\n if (this.timer) clearTimeout(this.timer);\n this.timer = undefined;\n this.pending = '';\n }\n\n /** Exposed for tests: whether a flush is scheduled. */\n hasPending(): boolean {\n return this.pending.length > 0;\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA8BA,MAAM,oBAAoB;;;;;;;;;;;;;;;;AA4B1B,IAAa,uBAAb,MAAkC;CAGH;CAF7B;CAEA,YAAY,WAAiB,KAA4C;EAA5C,KAAA,WAAA;CAA6C;;;;;;;CAQ1E,QAAQ,MAAc,MAAc,KAAK,IAAI,GAAiB;EAK5D,IAAI,SAAS,YAAY;GACvB,KAAK,eAAe,KAAA;GACpB,OAAO;EACT;EAIA,IAAI,aAAa,IAAI,GAAG,OAAO;EAE/B,IAAI,CAAC,WAAW,MAAM,QAAQ,GAAG;GAG/B,KAAK,eAAe,KAAA;GACpB,OAAO;EACT;EAEA,IAAI,KAAK,iBAAiB,KAAA,KAAa,MAAM,KAAK,gBAAgB,KAAK,UAAU;GAC/E,KAAK,eAAe,KAAA;GACpB,OAAO;EACT;EAEA,KAAK,eAAe;EACpB,OAAO;CACT;AACF;;;;;;;AAQA,IAAa,uBAAb,MAAkC;CAMb;CACA;CANnB,UAAkB;CAClB;CACA,WAAmB;CAEnB,YACE,MACA,kBAA2C,mBAC3C;EAFiB,KAAA,OAAA;EACA,KAAA,kBAAA;CAChB;CAEH,MAAM,MAAoB;EACxB,IAAI,KAAK,YAAY,KAAK,WAAW,GAAG;EACxC,KAAK,WAAW;EAChB,IAAI,KAAK,OAAO;EAChB,KAAK,QAAQ,iBAAiB,KAAK,KAAK,MAAM,GAAG,KAAK,eAAe;EACrE,KAAK,MAAM,QAAQ;CACrB;;CAGA,MAAM,QAAuB;EAC3B,IAAI,KAAK,OAAO,aAAa,KAAK,KAAK;EACvC,KAAK,QAAQ,KAAA;EACb,MAAM,OAAO,KAAK;EAClB,KAAK,UAAU;EACf,IAAI,CAAC,QAAQ,KAAK,UAAU;EAC5B,IAAI;GACF,MAAM,KAAK,KAAK,IAAI;EACtB,QAAQ,CAER;CACF;;CAGA,UAAgB;EACd,KAAK,WAAW;EAChB,IAAI,KAAK,OAAO,aAAa,KAAK,KAAK;EACvC,KAAK,QAAQ,KAAA;EACb,KAAK,UAAU;CACjB;;CAGA,aAAsB;EACpB,OAAO,KAAK,QAAQ,SAAS;CAC/B;AACF"}
@@ -0,0 +1,138 @@
1
+ const require_doomOverlay = require("./doomOverlay.cjs");
2
+ const require_overlayText = require("./overlayText.cjs");
3
+ let _earendil_works_pi_tui = require("@earendil-works/pi-tui");
4
+ require_doomOverlay.DOOM_FULLSCREEN_UI_OPTIONS.overlayOptions;
5
+ const FILTER_LABEL = "FILTER";
6
+ const EMPTY_NAME = "(unnamed)";
7
+ const KEY_CLEAR = "";
8
+ /** Rows the filter field and its trailing spacer take from the body. */
9
+ const FILTER_ROWS = 3;
10
+ /** Columns the marker and its trailing space take from a row. */
11
+ const MARKER_COLUMNS = 2;
12
+ /** Blank columns between the name column and the detail column. */
13
+ const COLUMN_GAP = 2;
14
+ const MIN_NAME_WIDTH = 16;
15
+ const NAME_RATIO = .45;
16
+ const DIM = "dim";
17
+ const ACCENT = "accent";
18
+ const SELECTED_BG = "selectedBg";
19
+ const ROW_BG = "userMessageBg";
20
+ function filterPickerRows(rows, query) {
21
+ if (!query.trim()) return [...rows];
22
+ return rows.filter((row) => require_overlayText.matchesQuery([
23
+ row.name,
24
+ row.detail,
25
+ row.search ?? ""
26
+ ].join(" "), query));
27
+ }
28
+ var WorkflowPickerComponent = class extends require_doomOverlay.DoomOverlay {
29
+ config;
30
+ rows;
31
+ done;
32
+ query = "";
33
+ selected = 0;
34
+ bodyHeight = 1;
35
+ constructor(tui, theme, config, rows, done) {
36
+ super(tui, theme);
37
+ this.config = config;
38
+ this.rows = rows;
39
+ this.done = done;
40
+ }
41
+ matches() {
42
+ return filterPickerRows(this.rows, this.query);
43
+ }
44
+ /** Rows the list can draw, once the filter field has taken its share. */
45
+ capacity() {
46
+ return Math.max(1, this.bodyHeight - FILTER_ROWS);
47
+ }
48
+ handleInput(data) {
49
+ if ((0, _earendil_works_pi_tui.matchesKey)(data, "escape") || (0, _earendil_works_pi_tui.matchesKey)(data, "ctrl+c")) {
50
+ this.done(void 0);
51
+ return;
52
+ }
53
+ const matches = this.matches();
54
+ if ((0, _earendil_works_pi_tui.matchesKey)(data, "enter")) {
55
+ const row = matches[Math.min(this.selected, matches.length - 1)];
56
+ if (row) this.done(row.value);
57
+ return;
58
+ }
59
+ if ((0, _earendil_works_pi_tui.matchesKey)(data, "up") || (0, _earendil_works_pi_tui.matchesKey)(data, "ctrl+p")) this.move(-1, matches.length);
60
+ else if ((0, _earendil_works_pi_tui.matchesKey)(data, "down") || (0, _earendil_works_pi_tui.matchesKey)(data, "ctrl+n")) this.move(1, matches.length);
61
+ else if ((0, _earendil_works_pi_tui.matchesKey)(data, "pageUp")) this.move(-this.capacity(), matches.length);
62
+ else if ((0, _earendil_works_pi_tui.matchesKey)(data, "pageDown")) this.move(this.capacity(), matches.length);
63
+ else if ((0, _earendil_works_pi_tui.matchesKey)(data, "backspace")) this.setQuery(this.query.slice(0, -1));
64
+ else if (data === KEY_CLEAR) this.setQuery("");
65
+ else if (!require_overlayText.isControlInput(data)) this.setQuery(this.query + data);
66
+ else return;
67
+ this.tui.requestRender();
68
+ }
69
+ /** Editing the filter resets the cursor: the previous index named another row. */
70
+ setQuery(query) {
71
+ if (query === this.query) return;
72
+ this.query = query;
73
+ this.selected = 0;
74
+ }
75
+ move(delta, count) {
76
+ if (count === 0) return;
77
+ this.selected = Math.max(0, Math.min(count - 1, this.selected + delta));
78
+ }
79
+ getChrome() {
80
+ const matches = this.matches();
81
+ const scope = this.query ? `${matches.length} of ${this.rows.length}` : `${this.rows.length}`;
82
+ return {
83
+ title: this.config.title,
84
+ accent: require_doomOverlay.DOOM_OVERLAY_ACCENT,
85
+ breadcrumb: this.config.breadcrumb,
86
+ headerRight: `${scope} ${this.config.unit}`,
87
+ footer: `↑↓ select · enter ${this.config.action} · type to filter · ctrl+u clear · esc cancel`,
88
+ footerHints: [
89
+ ["↑↓", "select"],
90
+ ["enter", this.config.action],
91
+ ["type", "filter"],
92
+ ["ctrl+u", "clear"],
93
+ ["esc", "cancel"]
94
+ ],
95
+ footerRight: matches.length > 0 ? `${Math.min(this.selected, matches.length - 1) + 1}/${matches.length}` : "no match"
96
+ };
97
+ }
98
+ renderBody(width, height) {
99
+ this.bodyHeight = height;
100
+ return [...this.filterField(width), ...this.listLines(width)].slice(0, height);
101
+ }
102
+ filterField(width) {
103
+ const value = this.query ? `${this.query}█` : `█${this.theme.fg(DIM, this.config.filterPlaceholder)}`;
104
+ return [
105
+ this.theme.fg(DIM, FILTER_LABEL),
106
+ this.theme.bg(SELECTED_BG, require_overlayText.fit(` ${value}`, width)),
107
+ ""
108
+ ];
109
+ }
110
+ listLines(width) {
111
+ if (this.rows.length === 0) return [this.theme.fg(DIM, this.config.emptyMessage)];
112
+ const matches = this.matches();
113
+ if (matches.length === 0) return [this.theme.fg(DIM, `No ${this.config.unit} match "${this.query}".`)];
114
+ const capacity = this.capacity();
115
+ const active = Math.min(this.selected, matches.length - 1);
116
+ const start = Math.max(0, Math.min(active - capacity + 1, Math.max(0, matches.length - capacity)));
117
+ const nameWidth = Math.max(MIN_NAME_WIDTH, Math.floor((width - MARKER_COLUMNS - COLUMN_GAP) * NAME_RATIO));
118
+ const detailWidth = Math.max(0, width - MARKER_COLUMNS - COLUMN_GAP - nameWidth);
119
+ return matches.slice(start, start + capacity).map((row, offset) => {
120
+ const current = start + offset === active;
121
+ const marker = current ? this.theme.fg(ACCENT, "›") : " ";
122
+ const name = row.name || EMPTY_NAME;
123
+ const label = current ? this.theme.bold(require_overlayText.fit(name, nameWidth)) : require_overlayText.fit(name, nameWidth);
124
+ const detail = this.theme.fg(DIM, require_overlayText.fit(require_overlayText.pathTail(row.detail, detailWidth), detailWidth));
125
+ const background = current ? SELECTED_BG : ROW_BG;
126
+ return this.theme.bg(background, require_overlayText.fit(`${marker} ${label}${" ".repeat(COLUMN_GAP)}${detail}`, width));
127
+ });
128
+ }
129
+ };
130
+ async function openWorkflowPickerOverlay(ctx, config, rows) {
131
+ return ctx.ui.custom((tui, theme, _keybindings, done) => new WorkflowPickerComponent(tui, theme, config, rows, done), require_doomOverlay.DOOM_FULLSCREEN_UI_OPTIONS);
132
+ }
133
+ //#endregion
134
+ exports.WorkflowPickerComponent = WorkflowPickerComponent;
135
+ exports.filterPickerRows = filterPickerRows;
136
+ exports.openWorkflowPickerOverlay = openWorkflowPickerOverlay;
137
+
138
+ //# sourceMappingURL=workflowPicker.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"workflowPicker.cjs","names":["DOOM_FULLSCREEN_UI_OPTIONS","matchesQuery","DoomOverlay","matchesKey","isControlInput","DOOM_OVERLAY_ACCENT","fit","pathTail"],"sources":["../../../../src/tui/workflow/workflowPicker.ts"],"sourcesContent":["/**\n * Scrollable, filterable list picker for every long list this extension shows.\n *\n * Replaces a bare `ui.select`, which wrapped each `name · detail` pair across\n * two or three terminal rows and left no way to reach a row other than scrolling\n * past all of them. This is a doom overlay instead: one row per item, a windowed\n * list that scrolls with the selection, and a filter field that narrows as you\n * type.\n *\n * Generic over the value it hands back. `SPC w c` picks a failed run to recover\n * through it; browsing and launching workflows moved to `workflowCatalog.ts`,\n * which needs the detail on screen beside the list rather than behind a pick.\n *\n * Rendering is pure, so the surface can be asserted as text without a terminal.\n */\n\nimport type { ExtensionContext, Theme } from '@earendil-works/pi-coding-agent';\nimport { matchesKey } from '@earendil-works/pi-tui';\n\nimport {\n DOOM_FULLSCREEN_UI_OPTIONS,\n DOOM_OVERLAY_ACCENT,\n DoomOverlay,\n type DoomOverlayChrome,\n type DoomOverlayTui,\n} from './doomOverlay';\nimport { CURSOR_BLOCK, fit, isControlInput, matchesQuery, pathTail, SELECTION_MARKER } from './overlayText';\n\nexport const WORKFLOW_PICKER_OVERLAY_OPTIONS = DOOM_FULLSCREEN_UI_OPTIONS.overlayOptions;\n\nconst FILTER_LABEL = 'FILTER';\nconst EMPTY_NAME = '(unnamed)';\nconst KEY_CLEAR = '\\x15';\n/** Rows the filter field and its trailing spacer take from the body. */\nconst FILTER_ROWS = 3;\n/** Columns the marker and its trailing space take from a row. */\nconst MARKER_COLUMNS = 2;\n/** Blank columns between the name column and the detail column. */\nconst COLUMN_GAP = 2;\nconst MIN_NAME_WIDTH = 16;\nconst NAME_RATIO = 0.45;\n\n/** Neither palette name is exported, so take both from the theme's methods. */\ntype ThemeBg = Parameters<Theme['bg']>[0];\ntype ThemeColor = Parameters<Theme['fg']>[0];\n\nconst DIM: ThemeColor = 'dim';\nconst ACCENT: ThemeColor = 'accent';\nconst SELECTED_BG: ThemeBg = 'selectedBg';\nconst ROW_BG: ThemeBg = 'userMessageBg';\n\nexport interface PickerRow<T> {\n /** Stable identity, used only to keep rows distinct. */\n key: string;\n /** Left column. */\n name: string;\n /** Right column, trimmed from the front so its tail survives. */\n detail: string;\n /** Extra text the filter should match but the row does not show. */\n search?: string;\n value: T;\n}\n\nexport interface WorkflowPickerConfig {\n title: string;\n breadcrumb: string;\n /** Plural noun for the header count, e.g. `workflows`. */\n unit: string;\n /** Verb on the enter key cap, e.g. `launch`. */\n action: string;\n filterPlaceholder: string;\n /** Shown when the source list itself is empty, as opposed to over-filtered. */\n emptyMessage: string;\n}\n\nexport function filterPickerRows<T>(rows: readonly PickerRow<T>[], query: string): PickerRow<T>[] {\n if (!query.trim()) return [...rows];\n return rows.filter((row) => matchesQuery([row.name, row.detail, row.search ?? ''].join(' '), query));\n}\n\nexport class WorkflowPickerComponent<T> extends DoomOverlay {\n private query = '';\n private selected = 0;\n private bodyHeight = 1;\n\n constructor(\n tui: DoomOverlayTui,\n theme: Theme,\n private readonly config: WorkflowPickerConfig,\n private readonly rows: readonly PickerRow<T>[],\n private readonly done: (value: T | undefined) => void,\n ) {\n super(tui, theme);\n }\n\n private matches(): PickerRow<T>[] {\n return filterPickerRows(this.rows, this.query);\n }\n\n /** Rows the list can draw, once the filter field has taken its share. */\n private capacity(): number {\n return Math.max(1, this.bodyHeight - FILTER_ROWS);\n }\n\n handleInput(data: string): void {\n if (matchesKey(data, 'escape') || matchesKey(data, 'ctrl+c')) {\n this.done(undefined);\n return;\n }\n const matches = this.matches();\n if (matchesKey(data, 'enter')) {\n const row = matches[Math.min(this.selected, matches.length - 1)];\n if (row) this.done(row.value);\n return;\n }\n\n if (matchesKey(data, 'up') || matchesKey(data, 'ctrl+p')) this.move(-1, matches.length);\n else if (matchesKey(data, 'down') || matchesKey(data, 'ctrl+n')) this.move(1, matches.length);\n else if (matchesKey(data, 'pageUp')) this.move(-this.capacity(), matches.length);\n else if (matchesKey(data, 'pageDown')) this.move(this.capacity(), matches.length);\n else if (matchesKey(data, 'backspace')) this.setQuery(this.query.slice(0, -1));\n else if (data === KEY_CLEAR) this.setQuery('');\n else if (!isControlInput(data)) this.setQuery(this.query + data);\n else return;\n\n this.tui.requestRender();\n }\n\n /** Editing the filter resets the cursor: the previous index named another row. */\n private setQuery(query: string): void {\n if (query === this.query) return;\n this.query = query;\n this.selected = 0;\n }\n\n private move(delta: number, count: number): void {\n if (count === 0) return;\n this.selected = Math.max(0, Math.min(count - 1, this.selected + delta));\n }\n\n protected getChrome(): DoomOverlayChrome {\n const matches = this.matches();\n const scope = this.query ? `${matches.length} of ${this.rows.length}` : `${this.rows.length}`;\n return {\n title: this.config.title,\n accent: DOOM_OVERLAY_ACCENT,\n breadcrumb: this.config.breadcrumb,\n headerRight: `${scope} ${this.config.unit}`,\n footer: `↑↓ select · enter ${this.config.action} · type to filter · ctrl+u clear · esc cancel`,\n footerHints: [\n ['↑↓', 'select'],\n ['enter', this.config.action],\n ['type', 'filter'],\n ['ctrl+u', 'clear'],\n ['esc', 'cancel'],\n ],\n footerRight:\n matches.length > 0 ? `${Math.min(this.selected, matches.length - 1) + 1}/${matches.length}` : 'no match',\n };\n }\n\n protected renderBody(width: number, height: number): string[] {\n this.bodyHeight = height;\n return [...this.filterField(width), ...this.listLines(width)].slice(0, height);\n }\n\n private filterField(width: number): string[] {\n const value = this.query\n ? `${this.query}${CURSOR_BLOCK}`\n : `${CURSOR_BLOCK}${this.theme.fg(DIM, this.config.filterPlaceholder)}`;\n return [this.theme.fg(DIM, FILTER_LABEL), this.theme.bg(SELECTED_BG, fit(` ${value}`, width)), ''];\n }\n\n private listLines(width: number): string[] {\n if (this.rows.length === 0) return [this.theme.fg(DIM, this.config.emptyMessage)];\n const matches = this.matches();\n if (matches.length === 0) return [this.theme.fg(DIM, `No ${this.config.unit} match \"${this.query}\".`)];\n\n const capacity = this.capacity();\n const active = Math.min(this.selected, matches.length - 1);\n // The window follows the cursor rather than the other way round, so a filter\n // that shrinks the list can never strand the selection off-screen.\n const start = Math.max(0, Math.min(active - capacity + 1, Math.max(0, matches.length - capacity)));\n\n const nameWidth = Math.max(MIN_NAME_WIDTH, Math.floor((width - MARKER_COLUMNS - COLUMN_GAP) * NAME_RATIO));\n const detailWidth = Math.max(0, width - MARKER_COLUMNS - COLUMN_GAP - nameWidth);\n return matches.slice(start, start + capacity).map((row, offset) => {\n const current = start + offset === active;\n const marker = current ? this.theme.fg(ACCENT, SELECTION_MARKER) : ' ';\n const name = row.name || EMPTY_NAME;\n const label = current ? this.theme.bold(fit(name, nameWidth)) : fit(name, nameWidth);\n const detail = this.theme.fg(DIM, fit(pathTail(row.detail, detailWidth), detailWidth));\n const background: ThemeBg = current ? SELECTED_BG : ROW_BG;\n return this.theme.bg(background, fit(`${marker} ${label}${' '.repeat(COLUMN_GAP)}${detail}`, width));\n });\n }\n}\n\nexport async function openWorkflowPickerOverlay<T>(\n ctx: ExtensionContext,\n config: WorkflowPickerConfig,\n rows: readonly PickerRow<T>[],\n): Promise<T | undefined> {\n return ctx.ui.custom<T | undefined>(\n (tui, theme, _keybindings, done) => new WorkflowPickerComponent(tui, theme, config, rows, done),\n DOOM_FULLSCREEN_UI_OPTIONS,\n );\n}\n"],"mappings":"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"}
@@ -0,0 +1,136 @@
1
+ import { DOOM_FULLSCREEN_UI_OPTIONS, DOOM_OVERLAY_ACCENT, DoomOverlay } from "./doomOverlay.mjs";
2
+ import { fit, isControlInput, matchesQuery, pathTail } from "./overlayText.mjs";
3
+ import { matchesKey } from "@earendil-works/pi-tui";
4
+ DOOM_FULLSCREEN_UI_OPTIONS.overlayOptions;
5
+ const FILTER_LABEL = "FILTER";
6
+ const EMPTY_NAME = "(unnamed)";
7
+ const KEY_CLEAR = "";
8
+ /** Rows the filter field and its trailing spacer take from the body. */
9
+ const FILTER_ROWS = 3;
10
+ /** Columns the marker and its trailing space take from a row. */
11
+ const MARKER_COLUMNS = 2;
12
+ /** Blank columns between the name column and the detail column. */
13
+ const COLUMN_GAP = 2;
14
+ const MIN_NAME_WIDTH = 16;
15
+ const NAME_RATIO = .45;
16
+ const DIM = "dim";
17
+ const ACCENT = "accent";
18
+ const SELECTED_BG = "selectedBg";
19
+ const ROW_BG = "userMessageBg";
20
+ function filterPickerRows(rows, query) {
21
+ if (!query.trim()) return [...rows];
22
+ return rows.filter((row) => matchesQuery([
23
+ row.name,
24
+ row.detail,
25
+ row.search ?? ""
26
+ ].join(" "), query));
27
+ }
28
+ var WorkflowPickerComponent = class extends DoomOverlay {
29
+ config;
30
+ rows;
31
+ done;
32
+ query = "";
33
+ selected = 0;
34
+ bodyHeight = 1;
35
+ constructor(tui, theme, config, rows, done) {
36
+ super(tui, theme);
37
+ this.config = config;
38
+ this.rows = rows;
39
+ this.done = done;
40
+ }
41
+ matches() {
42
+ return filterPickerRows(this.rows, this.query);
43
+ }
44
+ /** Rows the list can draw, once the filter field has taken its share. */
45
+ capacity() {
46
+ return Math.max(1, this.bodyHeight - FILTER_ROWS);
47
+ }
48
+ handleInput(data) {
49
+ if (matchesKey(data, "escape") || matchesKey(data, "ctrl+c")) {
50
+ this.done(void 0);
51
+ return;
52
+ }
53
+ const matches = this.matches();
54
+ if (matchesKey(data, "enter")) {
55
+ const row = matches[Math.min(this.selected, matches.length - 1)];
56
+ if (row) this.done(row.value);
57
+ return;
58
+ }
59
+ if (matchesKey(data, "up") || matchesKey(data, "ctrl+p")) this.move(-1, matches.length);
60
+ else if (matchesKey(data, "down") || matchesKey(data, "ctrl+n")) this.move(1, matches.length);
61
+ else if (matchesKey(data, "pageUp")) this.move(-this.capacity(), matches.length);
62
+ else if (matchesKey(data, "pageDown")) this.move(this.capacity(), matches.length);
63
+ else if (matchesKey(data, "backspace")) this.setQuery(this.query.slice(0, -1));
64
+ else if (data === KEY_CLEAR) this.setQuery("");
65
+ else if (!isControlInput(data)) this.setQuery(this.query + data);
66
+ else return;
67
+ this.tui.requestRender();
68
+ }
69
+ /** Editing the filter resets the cursor: the previous index named another row. */
70
+ setQuery(query) {
71
+ if (query === this.query) return;
72
+ this.query = query;
73
+ this.selected = 0;
74
+ }
75
+ move(delta, count) {
76
+ if (count === 0) return;
77
+ this.selected = Math.max(0, Math.min(count - 1, this.selected + delta));
78
+ }
79
+ getChrome() {
80
+ const matches = this.matches();
81
+ const scope = this.query ? `${matches.length} of ${this.rows.length}` : `${this.rows.length}`;
82
+ return {
83
+ title: this.config.title,
84
+ accent: DOOM_OVERLAY_ACCENT,
85
+ breadcrumb: this.config.breadcrumb,
86
+ headerRight: `${scope} ${this.config.unit}`,
87
+ footer: `↑↓ select · enter ${this.config.action} · type to filter · ctrl+u clear · esc cancel`,
88
+ footerHints: [
89
+ ["↑↓", "select"],
90
+ ["enter", this.config.action],
91
+ ["type", "filter"],
92
+ ["ctrl+u", "clear"],
93
+ ["esc", "cancel"]
94
+ ],
95
+ footerRight: matches.length > 0 ? `${Math.min(this.selected, matches.length - 1) + 1}/${matches.length}` : "no match"
96
+ };
97
+ }
98
+ renderBody(width, height) {
99
+ this.bodyHeight = height;
100
+ return [...this.filterField(width), ...this.listLines(width)].slice(0, height);
101
+ }
102
+ filterField(width) {
103
+ const value = this.query ? `${this.query}█` : `█${this.theme.fg(DIM, this.config.filterPlaceholder)}`;
104
+ return [
105
+ this.theme.fg(DIM, FILTER_LABEL),
106
+ this.theme.bg(SELECTED_BG, fit(` ${value}`, width)),
107
+ ""
108
+ ];
109
+ }
110
+ listLines(width) {
111
+ if (this.rows.length === 0) return [this.theme.fg(DIM, this.config.emptyMessage)];
112
+ const matches = this.matches();
113
+ if (matches.length === 0) return [this.theme.fg(DIM, `No ${this.config.unit} match "${this.query}".`)];
114
+ const capacity = this.capacity();
115
+ const active = Math.min(this.selected, matches.length - 1);
116
+ const start = Math.max(0, Math.min(active - capacity + 1, Math.max(0, matches.length - capacity)));
117
+ const nameWidth = Math.max(MIN_NAME_WIDTH, Math.floor((width - MARKER_COLUMNS - COLUMN_GAP) * NAME_RATIO));
118
+ const detailWidth = Math.max(0, width - MARKER_COLUMNS - COLUMN_GAP - nameWidth);
119
+ return matches.slice(start, start + capacity).map((row, offset) => {
120
+ const current = start + offset === active;
121
+ const marker = current ? this.theme.fg(ACCENT, "›") : " ";
122
+ const name = row.name || EMPTY_NAME;
123
+ const label = current ? this.theme.bold(fit(name, nameWidth)) : fit(name, nameWidth);
124
+ const detail = this.theme.fg(DIM, fit(pathTail(row.detail, detailWidth), detailWidth));
125
+ const background = current ? SELECTED_BG : ROW_BG;
126
+ return this.theme.bg(background, fit(`${marker} ${label}${" ".repeat(COLUMN_GAP)}${detail}`, width));
127
+ });
128
+ }
129
+ };
130
+ async function openWorkflowPickerOverlay(ctx, config, rows) {
131
+ return ctx.ui.custom((tui, theme, _keybindings, done) => new WorkflowPickerComponent(tui, theme, config, rows, done), DOOM_FULLSCREEN_UI_OPTIONS);
132
+ }
133
+ //#endregion
134
+ export { WorkflowPickerComponent, filterPickerRows, openWorkflowPickerOverlay };
135
+
136
+ //# sourceMappingURL=workflowPicker.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"workflowPicker.mjs","names":[],"sources":["../../../../src/tui/workflow/workflowPicker.ts"],"sourcesContent":["/**\n * Scrollable, filterable list picker for every long list this extension shows.\n *\n * Replaces a bare `ui.select`, which wrapped each `name · detail` pair across\n * two or three terminal rows and left no way to reach a row other than scrolling\n * past all of them. This is a doom overlay instead: one row per item, a windowed\n * list that scrolls with the selection, and a filter field that narrows as you\n * type.\n *\n * Generic over the value it hands back. `SPC w c` picks a failed run to recover\n * through it; browsing and launching workflows moved to `workflowCatalog.ts`,\n * which needs the detail on screen beside the list rather than behind a pick.\n *\n * Rendering is pure, so the surface can be asserted as text without a terminal.\n */\n\nimport type { ExtensionContext, Theme } from '@earendil-works/pi-coding-agent';\nimport { matchesKey } from '@earendil-works/pi-tui';\n\nimport {\n DOOM_FULLSCREEN_UI_OPTIONS,\n DOOM_OVERLAY_ACCENT,\n DoomOverlay,\n type DoomOverlayChrome,\n type DoomOverlayTui,\n} from './doomOverlay';\nimport { CURSOR_BLOCK, fit, isControlInput, matchesQuery, pathTail, SELECTION_MARKER } from './overlayText';\n\nexport const WORKFLOW_PICKER_OVERLAY_OPTIONS = DOOM_FULLSCREEN_UI_OPTIONS.overlayOptions;\n\nconst FILTER_LABEL = 'FILTER';\nconst EMPTY_NAME = '(unnamed)';\nconst KEY_CLEAR = '\\x15';\n/** Rows the filter field and its trailing spacer take from the body. */\nconst FILTER_ROWS = 3;\n/** Columns the marker and its trailing space take from a row. */\nconst MARKER_COLUMNS = 2;\n/** Blank columns between the name column and the detail column. */\nconst COLUMN_GAP = 2;\nconst MIN_NAME_WIDTH = 16;\nconst NAME_RATIO = 0.45;\n\n/** Neither palette name is exported, so take both from the theme's methods. */\ntype ThemeBg = Parameters<Theme['bg']>[0];\ntype ThemeColor = Parameters<Theme['fg']>[0];\n\nconst DIM: ThemeColor = 'dim';\nconst ACCENT: ThemeColor = 'accent';\nconst SELECTED_BG: ThemeBg = 'selectedBg';\nconst ROW_BG: ThemeBg = 'userMessageBg';\n\nexport interface PickerRow<T> {\n /** Stable identity, used only to keep rows distinct. */\n key: string;\n /** Left column. */\n name: string;\n /** Right column, trimmed from the front so its tail survives. */\n detail: string;\n /** Extra text the filter should match but the row does not show. */\n search?: string;\n value: T;\n}\n\nexport interface WorkflowPickerConfig {\n title: string;\n breadcrumb: string;\n /** Plural noun for the header count, e.g. `workflows`. */\n unit: string;\n /** Verb on the enter key cap, e.g. `launch`. */\n action: string;\n filterPlaceholder: string;\n /** Shown when the source list itself is empty, as opposed to over-filtered. */\n emptyMessage: string;\n}\n\nexport function filterPickerRows<T>(rows: readonly PickerRow<T>[], query: string): PickerRow<T>[] {\n if (!query.trim()) return [...rows];\n return rows.filter((row) => matchesQuery([row.name, row.detail, row.search ?? ''].join(' '), query));\n}\n\nexport class WorkflowPickerComponent<T> extends DoomOverlay {\n private query = '';\n private selected = 0;\n private bodyHeight = 1;\n\n constructor(\n tui: DoomOverlayTui,\n theme: Theme,\n private readonly config: WorkflowPickerConfig,\n private readonly rows: readonly PickerRow<T>[],\n private readonly done: (value: T | undefined) => void,\n ) {\n super(tui, theme);\n }\n\n private matches(): PickerRow<T>[] {\n return filterPickerRows(this.rows, this.query);\n }\n\n /** Rows the list can draw, once the filter field has taken its share. */\n private capacity(): number {\n return Math.max(1, this.bodyHeight - FILTER_ROWS);\n }\n\n handleInput(data: string): void {\n if (matchesKey(data, 'escape') || matchesKey(data, 'ctrl+c')) {\n this.done(undefined);\n return;\n }\n const matches = this.matches();\n if (matchesKey(data, 'enter')) {\n const row = matches[Math.min(this.selected, matches.length - 1)];\n if (row) this.done(row.value);\n return;\n }\n\n if (matchesKey(data, 'up') || matchesKey(data, 'ctrl+p')) this.move(-1, matches.length);\n else if (matchesKey(data, 'down') || matchesKey(data, 'ctrl+n')) this.move(1, matches.length);\n else if (matchesKey(data, 'pageUp')) this.move(-this.capacity(), matches.length);\n else if (matchesKey(data, 'pageDown')) this.move(this.capacity(), matches.length);\n else if (matchesKey(data, 'backspace')) this.setQuery(this.query.slice(0, -1));\n else if (data === KEY_CLEAR) this.setQuery('');\n else if (!isControlInput(data)) this.setQuery(this.query + data);\n else return;\n\n this.tui.requestRender();\n }\n\n /** Editing the filter resets the cursor: the previous index named another row. */\n private setQuery(query: string): void {\n if (query === this.query) return;\n this.query = query;\n this.selected = 0;\n }\n\n private move(delta: number, count: number): void {\n if (count === 0) return;\n this.selected = Math.max(0, Math.min(count - 1, this.selected + delta));\n }\n\n protected getChrome(): DoomOverlayChrome {\n const matches = this.matches();\n const scope = this.query ? `${matches.length} of ${this.rows.length}` : `${this.rows.length}`;\n return {\n title: this.config.title,\n accent: DOOM_OVERLAY_ACCENT,\n breadcrumb: this.config.breadcrumb,\n headerRight: `${scope} ${this.config.unit}`,\n footer: `↑↓ select · enter ${this.config.action} · type to filter · ctrl+u clear · esc cancel`,\n footerHints: [\n ['↑↓', 'select'],\n ['enter', this.config.action],\n ['type', 'filter'],\n ['ctrl+u', 'clear'],\n ['esc', 'cancel'],\n ],\n footerRight:\n matches.length > 0 ? `${Math.min(this.selected, matches.length - 1) + 1}/${matches.length}` : 'no match',\n };\n }\n\n protected renderBody(width: number, height: number): string[] {\n this.bodyHeight = height;\n return [...this.filterField(width), ...this.listLines(width)].slice(0, height);\n }\n\n private filterField(width: number): string[] {\n const value = this.query\n ? `${this.query}${CURSOR_BLOCK}`\n : `${CURSOR_BLOCK}${this.theme.fg(DIM, this.config.filterPlaceholder)}`;\n return [this.theme.fg(DIM, FILTER_LABEL), this.theme.bg(SELECTED_BG, fit(` ${value}`, width)), ''];\n }\n\n private listLines(width: number): string[] {\n if (this.rows.length === 0) return [this.theme.fg(DIM, this.config.emptyMessage)];\n const matches = this.matches();\n if (matches.length === 0) return [this.theme.fg(DIM, `No ${this.config.unit} match \"${this.query}\".`)];\n\n const capacity = this.capacity();\n const active = Math.min(this.selected, matches.length - 1);\n // The window follows the cursor rather than the other way round, so a filter\n // that shrinks the list can never strand the selection off-screen.\n const start = Math.max(0, Math.min(active - capacity + 1, Math.max(0, matches.length - capacity)));\n\n const nameWidth = Math.max(MIN_NAME_WIDTH, Math.floor((width - MARKER_COLUMNS - COLUMN_GAP) * NAME_RATIO));\n const detailWidth = Math.max(0, width - MARKER_COLUMNS - COLUMN_GAP - nameWidth);\n return matches.slice(start, start + capacity).map((row, offset) => {\n const current = start + offset === active;\n const marker = current ? this.theme.fg(ACCENT, SELECTION_MARKER) : ' ';\n const name = row.name || EMPTY_NAME;\n const label = current ? this.theme.bold(fit(name, nameWidth)) : fit(name, nameWidth);\n const detail = this.theme.fg(DIM, fit(pathTail(row.detail, detailWidth), detailWidth));\n const background: ThemeBg = current ? SELECTED_BG : ROW_BG;\n return this.theme.bg(background, fit(`${marker} ${label}${' '.repeat(COLUMN_GAP)}${detail}`, width));\n });\n }\n}\n\nexport async function openWorkflowPickerOverlay<T>(\n ctx: ExtensionContext,\n config: WorkflowPickerConfig,\n rows: readonly PickerRow<T>[],\n): Promise<T | undefined> {\n return ctx.ui.custom<T | undefined>(\n (tui, theme, _keybindings, done) => new WorkflowPickerComponent(tui, theme, config, rows, done),\n DOOM_FULLSCREEN_UI_OPTIONS,\n );\n}\n"],"mappings":"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"}