@astralyn/pi 1.4.5 → 1.4.7

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 (324) hide show
  1. package/CHANGELOG.md +42 -0
  2. package/dist/bundle/chunks/chunk-FODCTZXW.js +230 -0
  3. package/dist/bundle/chunks/{chunk-I5S7H7GA.js → chunk-FYKNSJG6.js} +1 -1
  4. package/dist/bundle/chunks/{chunk-AXFWZIHH.js → chunk-MG5XDTXV.js} +1 -1
  5. package/dist/bundle/chunks/{chunk-ALHAFUHP.js → chunk-PU2IWWXV.js} +1 -1
  6. package/dist/bundle/chunks/{chunk-5M3YJ54Z.js → chunk-RO2VH6UZ.js} +1 -1
  7. package/dist/bundle/chunks/chunk-YJTSOLP2.js +1499 -0
  8. package/dist/bundle/chunks/{cli-FH77R7JR.js → cli-MEL7SGJD.js} +1 -1
  9. package/dist/bundle/chunks/{execute-GMFGC27P.js → execute-4CWRNHPO.js} +1 -1
  10. package/dist/bundle/chunks/{runtime-R5CMHJ5P.js → runtime-R3X67XMR.js} +1 -1
  11. package/dist/bundle/chunks/{virtual-modules-6FUV7ZDV.js → virtual-modules-PKEOKGYI.js} +1 -1
  12. package/dist/bundle/cli.js +1 -1
  13. package/dist/bundle/index.js +1 -1
  14. package/dist/bundle/rpc-entry.js +1 -1
  15. package/dist/core/agent-session.d.ts +0 -3
  16. package/dist/core/agent-session.d.ts.map +1 -1
  17. package/dist/core/agent-session.js +0 -4
  18. package/dist/core/agent-session.js.map +1 -1
  19. package/dist/core/extensions/index.d.ts +1 -1
  20. package/dist/core/extensions/index.d.ts.map +1 -1
  21. package/dist/core/extensions/index.js.map +1 -1
  22. package/dist/core/isolated-session.d.ts +23 -0
  23. package/dist/core/isolated-session.d.ts.map +1 -0
  24. package/dist/core/isolated-session.js +55 -0
  25. package/dist/core/isolated-session.js.map +1 -0
  26. package/dist/core/package-manager.d.ts +7 -1
  27. package/dist/core/package-manager.d.ts.map +1 -1
  28. package/dist/core/package-manager.js +72 -1
  29. package/dist/core/package-manager.js.map +1 -1
  30. package/dist/core/resource-loader.d.ts +1 -0
  31. package/dist/core/resource-loader.d.ts.map +1 -1
  32. package/dist/core/resource-loader.js +23 -7
  33. package/dist/core/resource-loader.js.map +1 -1
  34. package/dist/core/settings-manager.d.ts +4 -2
  35. package/dist/core/settings-manager.d.ts.map +1 -1
  36. package/dist/core/settings-manager.js +11 -0
  37. package/dist/core/settings-manager.js.map +1 -1
  38. package/dist/core/settings-schema.d.ts +7 -0
  39. package/dist/core/settings-schema.d.ts.map +1 -1
  40. package/dist/core/settings-schema.js +6 -0
  41. package/dist/core/settings-schema.js.map +1 -1
  42. package/dist/core/skills.d.ts +10 -0
  43. package/dist/core/skills.d.ts.map +1 -1
  44. package/dist/core/skills.js +23 -0
  45. package/dist/core/skills.js.map +1 -1
  46. package/dist/core/tasks/codec.d.ts +12 -0
  47. package/dist/core/tasks/codec.d.ts.map +1 -0
  48. package/dist/core/tasks/codec.js +139 -0
  49. package/dist/core/tasks/codec.js.map +1 -0
  50. package/dist/core/tasks/delivery.d.ts +11 -4
  51. package/dist/core/tasks/delivery.d.ts.map +1 -1
  52. package/dist/core/tasks/delivery.js +9 -9
  53. package/dist/core/tasks/delivery.js.map +1 -1
  54. package/dist/core/tasks/execution.d.ts +71 -0
  55. package/dist/core/tasks/execution.d.ts.map +1 -0
  56. package/dist/core/tasks/execution.js +292 -0
  57. package/dist/core/tasks/execution.js.map +1 -0
  58. package/dist/core/tasks/format.d.ts +0 -2
  59. package/dist/core/tasks/format.d.ts.map +1 -1
  60. package/dist/core/tasks/format.js +1 -16
  61. package/dist/core/tasks/format.js.map +1 -1
  62. package/dist/core/tasks/limits.d.ts +12 -1
  63. package/dist/core/tasks/limits.d.ts.map +1 -1
  64. package/dist/core/tasks/limits.js +20 -1
  65. package/dist/core/tasks/limits.js.map +1 -1
  66. package/dist/core/tasks/output.d.ts +17 -17
  67. package/dist/core/tasks/output.d.ts.map +1 -1
  68. package/dist/core/tasks/output.js +72 -69
  69. package/dist/core/tasks/output.js.map +1 -1
  70. package/dist/core/tasks/presentation.d.ts +14 -5
  71. package/dist/core/tasks/presentation.d.ts.map +1 -1
  72. package/dist/core/tasks/presentation.js +56 -185
  73. package/dist/core/tasks/presentation.js.map +1 -1
  74. package/dist/core/tasks/record.d.ts +45 -0
  75. package/dist/core/tasks/record.d.ts.map +1 -0
  76. package/dist/core/tasks/record.js +80 -0
  77. package/dist/core/tasks/record.js.map +1 -0
  78. package/dist/core/tasks/runtime.d.ts +22 -22
  79. package/dist/core/tasks/runtime.d.ts.map +1 -1
  80. package/dist/core/tasks/runtime.js +170 -343
  81. package/dist/core/tasks/runtime.js.map +1 -1
  82. package/dist/core/tasks/scheduler.d.ts +16 -0
  83. package/dist/core/tasks/scheduler.d.ts.map +1 -0
  84. package/dist/core/tasks/scheduler.js +42 -0
  85. package/dist/core/tasks/scheduler.js.map +1 -0
  86. package/dist/core/tasks/session.d.ts +1 -1
  87. package/dist/core/tasks/session.d.ts.map +1 -1
  88. package/dist/core/tasks/session.js +22 -35
  89. package/dist/core/tasks/session.js.map +1 -1
  90. package/dist/core/tasks/store.d.ts +18 -25
  91. package/dist/core/tasks/store.d.ts.map +1 -1
  92. package/dist/core/tasks/store.js +45 -43
  93. package/dist/core/tasks/store.js.map +1 -1
  94. package/dist/core/tasks/types.d.ts +159 -70
  95. package/dist/core/tasks/types.d.ts.map +1 -1
  96. package/dist/core/tasks/types.js +3 -9
  97. package/dist/core/tasks/types.js.map +1 -1
  98. package/dist/core/tools/shell-execution.d.ts +8 -7
  99. package/dist/core/tools/shell-execution.d.ts.map +1 -1
  100. package/dist/core/tools/shell-execution.js +19 -27
  101. package/dist/core/tools/shell-execution.js.map +1 -1
  102. package/dist/core/tools/shell-tool.d.ts.map +1 -1
  103. package/dist/core/tools/shell-tool.js +42 -47
  104. package/dist/core/tools/shell-tool.js.map +1 -1
  105. package/dist/core/usage-totals.d.ts +4 -3
  106. package/dist/core/usage-totals.d.ts.map +1 -1
  107. package/dist/core/usage-totals.js +32 -8
  108. package/dist/core/usage-totals.js.map +1 -1
  109. package/dist/extensions/agent/activity.d.ts +4 -4
  110. package/dist/extensions/agent/activity.d.ts.map +1 -1
  111. package/dist/extensions/agent/activity.js +15 -24
  112. package/dist/extensions/agent/activity.js.map +1 -1
  113. package/dist/extensions/agent/index.d.ts +2 -11
  114. package/dist/extensions/agent/index.d.ts.map +1 -1
  115. package/dist/extensions/agent/index.js +3 -88
  116. package/dist/extensions/agent/index.js.map +1 -1
  117. package/dist/extensions/agent/progress.d.ts +31 -0
  118. package/dist/extensions/agent/progress.d.ts.map +1 -0
  119. package/dist/extensions/agent/progress.js +135 -0
  120. package/dist/extensions/agent/progress.js.map +1 -0
  121. package/dist/extensions/agent/render.d.ts.map +1 -1
  122. package/dist/extensions/agent/render.js +8 -17
  123. package/dist/extensions/agent/render.js.map +1 -1
  124. package/dist/extensions/agent/report.d.ts +9 -0
  125. package/dist/extensions/agent/report.d.ts.map +1 -0
  126. package/dist/extensions/agent/report.js +40 -0
  127. package/dist/extensions/agent/report.js.map +1 -0
  128. package/dist/extensions/agent/settings-panel.d.ts +43 -0
  129. package/dist/extensions/agent/settings-panel.d.ts.map +1 -0
  130. package/dist/extensions/agent/settings-panel.js +125 -0
  131. package/dist/extensions/agent/settings-panel.js.map +1 -0
  132. package/dist/extensions/agent/settings.d.ts +1 -1
  133. package/dist/extensions/agent/settings.d.ts.map +1 -1
  134. package/dist/extensions/agent/settings.js +101 -83
  135. package/dist/extensions/agent/settings.js.map +1 -1
  136. package/dist/extensions/agent/tool.d.ts +13 -0
  137. package/dist/extensions/agent/tool.d.ts.map +1 -0
  138. package/dist/extensions/agent/tool.js +88 -0
  139. package/dist/extensions/agent/tool.js.map +1 -0
  140. package/dist/extensions/agent/types.d.ts +19 -21
  141. package/dist/extensions/agent/types.d.ts.map +1 -1
  142. package/dist/extensions/agent/types.js +2 -10
  143. package/dist/extensions/agent/types.js.map +1 -1
  144. package/dist/extensions/agent/worker.d.ts +16 -0
  145. package/dist/extensions/agent/worker.d.ts.map +1 -0
  146. package/dist/extensions/agent/worker.js +66 -0
  147. package/dist/extensions/agent/worker.js.map +1 -0
  148. package/dist/extensions/btw/agent.d.ts +1 -1
  149. package/dist/extensions/btw/agent.d.ts.map +1 -1
  150. package/dist/extensions/btw/agent.js +3 -20
  151. package/dist/extensions/btw/agent.js.map +1 -1
  152. package/dist/extensions/cached-compaction/index.d.ts.map +1 -1
  153. package/dist/extensions/cached-compaction/index.js +3 -22
  154. package/dist/extensions/cached-compaction/index.js.map +1 -1
  155. package/dist/extensions/cached-compaction/render.d.ts +1 -0
  156. package/dist/extensions/cached-compaction/render.d.ts.map +1 -1
  157. package/dist/extensions/cached-compaction/render.js +42 -14
  158. package/dist/extensions/cached-compaction/render.js.map +1 -1
  159. package/dist/extensions/deepwiki/client.d.ts.map +1 -1
  160. package/dist/extensions/deepwiki/client.js +6 -78
  161. package/dist/extensions/deepwiki/client.js.map +1 -1
  162. package/dist/extensions/deepwiki/transport.d.ts +3 -0
  163. package/dist/extensions/deepwiki/transport.d.ts.map +1 -0
  164. package/dist/extensions/deepwiki/transport.js +96 -0
  165. package/dist/extensions/deepwiki/transport.js.map +1 -0
  166. package/dist/extensions/provider/ui/builtin-data.d.ts +3 -3
  167. package/dist/extensions/provider/ui/builtin-data.d.ts.map +1 -1
  168. package/dist/extensions/provider/ui/builtin-data.js.map +1 -1
  169. package/dist/extensions/provider/ui/compat.d.ts +3 -3
  170. package/dist/extensions/provider/ui/compat.d.ts.map +1 -1
  171. package/dist/extensions/provider/ui/compat.js.map +1 -1
  172. package/dist/extensions/provider/ui/controls.d.ts +2 -2
  173. package/dist/extensions/provider/ui/controls.d.ts.map +1 -1
  174. package/dist/extensions/provider/ui/controls.js.map +1 -1
  175. package/dist/extensions/provider/ui/dialogs.d.ts +3 -3
  176. package/dist/extensions/provider/ui/dialogs.d.ts.map +1 -1
  177. package/dist/extensions/provider/ui/dialogs.js.map +1 -1
  178. package/dist/extensions/provider/ui/dictionary.d.ts +2 -2
  179. package/dist/extensions/provider/ui/dictionary.d.ts.map +1 -1
  180. package/dist/extensions/provider/ui/dictionary.js.map +1 -1
  181. package/dist/extensions/provider/ui/editor.d.ts.map +1 -1
  182. package/dist/extensions/provider/ui/editor.js +1 -3
  183. package/dist/extensions/provider/ui/editor.js.map +1 -1
  184. package/dist/extensions/provider/ui/fetch-models.d.ts +2 -2
  185. package/dist/extensions/provider/ui/fetch-models.d.ts.map +1 -1
  186. package/dist/extensions/provider/ui/fetch-models.js.map +1 -1
  187. package/dist/extensions/provider/ui/model-fields.d.ts +2 -2
  188. package/dist/extensions/provider/ui/model-fields.d.ts.map +1 -1
  189. package/dist/extensions/provider/ui/model-fields.js.map +1 -1
  190. package/dist/extensions/provider/ui/model-options.d.ts +6 -6
  191. package/dist/extensions/provider/ui/model-options.d.ts.map +1 -1
  192. package/dist/extensions/provider/ui/model-options.js.map +1 -1
  193. package/dist/extensions/provider/ui/pane.d.ts +21 -12
  194. package/dist/extensions/provider/ui/pane.d.ts.map +1 -1
  195. package/dist/extensions/provider/ui/pane.js.map +1 -1
  196. package/dist/extensions/provider/ui/provider-fields.d.ts +3 -3
  197. package/dist/extensions/provider/ui/provider-fields.d.ts.map +1 -1
  198. package/dist/extensions/provider/ui/provider-fields.js.map +1 -1
  199. package/dist/extensions/provider/ui/thinking-map.d.ts +2 -2
  200. package/dist/extensions/provider/ui/thinking-map.d.ts.map +1 -1
  201. package/dist/extensions/provider/ui/thinking-map.js.map +1 -1
  202. package/dist/extensions/run-summary/format.js +1 -1
  203. package/dist/extensions/run-summary/format.js.map +1 -1
  204. package/dist/extensions/statusline/usage.d.ts.map +1 -1
  205. package/dist/extensions/statusline/usage.js +2 -6
  206. package/dist/extensions/statusline/usage.js.map +1 -1
  207. package/dist/extensions/tasks/tool/actions.d.ts +1 -1
  208. package/dist/extensions/tasks/tool/actions.d.ts.map +1 -1
  209. package/dist/extensions/tasks/tool/actions.js +50 -115
  210. package/dist/extensions/tasks/tool/actions.js.map +1 -1
  211. package/dist/extensions/tasks/tool/constants.d.ts +1 -6
  212. package/dist/extensions/tasks/tool/constants.d.ts.map +1 -1
  213. package/dist/extensions/tasks/tool/constants.js +1 -6
  214. package/dist/extensions/tasks/tool/constants.js.map +1 -1
  215. package/dist/extensions/tasks/tool/schema.d.ts +0 -3
  216. package/dist/extensions/tasks/tool/schema.d.ts.map +1 -1
  217. package/dist/extensions/tasks/tool/schema.js +5 -14
  218. package/dist/extensions/tasks/tool/schema.js.map +1 -1
  219. package/dist/extensions/tasks/tool/types.d.ts +2 -8
  220. package/dist/extensions/tasks/tool/types.d.ts.map +1 -1
  221. package/dist/extensions/tasks/tool/types.js +0 -2
  222. package/dist/extensions/tasks/tool/types.js.map +1 -1
  223. package/dist/extensions/tasks/tool.d.ts.map +1 -1
  224. package/dist/extensions/tasks/tool.js +2 -2
  225. package/dist/extensions/tasks/tool.js.map +1 -1
  226. package/dist/extensions/tasks/ui/completion-render.d.ts.map +1 -1
  227. package/dist/extensions/tasks/ui/completion-render.js +46 -47
  228. package/dist/extensions/tasks/ui/completion-render.js.map +1 -1
  229. package/dist/extensions/tasks/ui/inspector.d.ts +5 -3
  230. package/dist/extensions/tasks/ui/inspector.d.ts.map +1 -1
  231. package/dist/extensions/tasks/ui/inspector.js +18 -6
  232. package/dist/extensions/tasks/ui/inspector.js.map +1 -1
  233. package/dist/extensions/tasks/ui/layout.d.ts +7 -7
  234. package/dist/extensions/tasks/ui/layout.d.ts.map +1 -1
  235. package/dist/extensions/tasks/ui/layout.js +118 -84
  236. package/dist/extensions/tasks/ui/layout.js.map +1 -1
  237. package/dist/extensions/tasks/ui/list.d.ts.map +1 -1
  238. package/dist/extensions/tasks/ui/list.js +45 -31
  239. package/dist/extensions/tasks/ui/list.js.map +1 -1
  240. package/dist/extensions/tasks/ui/manager.d.ts +16 -7
  241. package/dist/extensions/tasks/ui/manager.d.ts.map +1 -1
  242. package/dist/extensions/tasks/ui/manager.js +167 -129
  243. package/dist/extensions/tasks/ui/manager.js.map +1 -1
  244. package/dist/extensions/tasks/ui/model.d.ts +29 -0
  245. package/dist/extensions/tasks/ui/model.d.ts.map +1 -1
  246. package/dist/extensions/tasks/ui/model.js +16 -1
  247. package/dist/extensions/tasks/ui/model.js.map +1 -1
  248. package/dist/extensions/tasks/ui/render.d.ts +1 -1
  249. package/dist/extensions/tasks/ui/render.d.ts.map +1 -1
  250. package/dist/extensions/tasks/ui/render.js +12 -9
  251. package/dist/extensions/tasks/ui/render.js.map +1 -1
  252. package/dist/extensions/tasks/ui/status.d.ts +15 -0
  253. package/dist/extensions/tasks/ui/status.d.ts.map +1 -0
  254. package/dist/extensions/tasks/ui/status.js +16 -0
  255. package/dist/extensions/tasks/ui/status.js.map +1 -0
  256. package/dist/extensions/tasks/ui/tree.js +1 -1
  257. package/dist/extensions/tasks/ui/tree.js.map +1 -1
  258. package/dist/extensions/tasks/ui/views.d.ts +10 -5
  259. package/dist/extensions/tasks/ui/views.d.ts.map +1 -1
  260. package/dist/extensions/tasks/ui/views.js +61 -36
  261. package/dist/extensions/tasks/ui/views.js.map +1 -1
  262. package/dist/index.d.ts +2 -1
  263. package/dist/index.d.ts.map +1 -1
  264. package/dist/index.js.map +1 -1
  265. package/dist/modes/interactive/components/config-selector.d.ts +14 -1
  266. package/dist/modes/interactive/components/config-selector.d.ts.map +1 -1
  267. package/dist/modes/interactive/components/config-selector.js +258 -7
  268. package/dist/modes/interactive/components/config-selector.js.map +1 -1
  269. package/dist/modes/interactive/components/keybinding-hints.d.ts.map +1 -1
  270. package/dist/modes/interactive/components/keybinding-hints.js +2 -1
  271. package/dist/modes/interactive/components/keybinding-hints.js.map +1 -1
  272. package/dist/modes/interactive/components/model-selector.d.ts.map +1 -1
  273. package/dist/modes/interactive/components/model-selector.js +7 -4
  274. package/dist/modes/interactive/components/model-selector.js.map +1 -1
  275. package/dist/modes/interactive/components/status-marker.d.ts +2 -1
  276. package/dist/modes/interactive/components/status-marker.d.ts.map +1 -1
  277. package/dist/modes/interactive/components/status-marker.js +0 -1
  278. package/dist/modes/interactive/components/status-marker.js.map +1 -1
  279. package/dist/modes/interactive/components/thinking-selector.d.ts.map +1 -1
  280. package/dist/modes/interactive/components/thinking-selector.js +10 -3
  281. package/dist/modes/interactive/components/thinking-selector.js.map +1 -1
  282. package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  283. package/dist/modes/interactive/interactive-mode.js +7 -1
  284. package/dist/modes/interactive/interactive-mode.js.map +1 -1
  285. package/dist/utils/duration.d.ts +3 -0
  286. package/dist/utils/duration.d.ts.map +1 -0
  287. package/dist/utils/duration.js +17 -0
  288. package/dist/utils/duration.js.map +1 -0
  289. package/dist/utils/number.d.ts +3 -0
  290. package/dist/utils/number.d.ts.map +1 -0
  291. package/dist/utils/number.js +5 -0
  292. package/dist/utils/number.js.map +1 -0
  293. package/docs/bundled/extensions/agent.md +14 -12
  294. package/docs/bundled/extensions/cached-compaction.md +13 -4
  295. package/docs/bundled/extensions/deepwiki.md +3 -1
  296. package/docs/bundled/extensions/statusline.md +1 -1
  297. package/docs/bundled/tasks.md +15 -14
  298. package/docs/extensions.md +26 -10
  299. package/docs/packages.md +5 -3
  300. package/docs/sdk.md +3 -3
  301. package/docs/session-format.md +28 -24
  302. package/docs/settings.md +11 -0
  303. package/docs/skills.md +23 -0
  304. package/npm-shrinkwrap.json +2 -2
  305. package/package.json +1 -1
  306. package/schemas/settings.schema.json +36 -0
  307. package/dist/bundle/chunks/chunk-FSJ45CRP.js +0 -229
  308. package/dist/bundle/chunks/chunk-W3FIUMGM.js +0 -1501
  309. package/dist/core/tasks/history.d.ts +0 -4
  310. package/dist/core/tasks/history.d.ts.map +0 -1
  311. package/dist/core/tasks/history.js +0 -131
  312. package/dist/core/tasks/history.js.map +0 -1
  313. package/dist/extensions/agent/runner.d.ts +0 -24
  314. package/dist/extensions/agent/runner.d.ts.map +0 -1
  315. package/dist/extensions/agent/runner.js +0 -274
  316. package/dist/extensions/agent/runner.js.map +0 -1
  317. package/dist/extensions/deepwiki/http.d.ts +0 -13
  318. package/dist/extensions/deepwiki/http.d.ts.map +0 -1
  319. package/dist/extensions/deepwiki/http.js +0 -88
  320. package/dist/extensions/deepwiki/http.js.map +0 -1
  321. package/dist/extensions/tasks/ui/task-view.d.ts +0 -13
  322. package/dist/extensions/tasks/ui/task-view.d.ts.map +0 -1
  323. package/dist/extensions/tasks/ui/task-view.js +0 -20
  324. package/dist/extensions/tasks/ui/task-view.js.map +0 -1
@@ -3,12 +3,15 @@ import { runtimeLabel } from "../../../core/tasks/format.js";
3
3
  import { isTaskTerminal } from "../../../core/tasks/types.js";
4
4
  import { formatSize } from "../../../core/tools/truncate.js";
5
5
  import { keyLabel } from "../../../modes/interactive/components/keybinding-hints.js";
6
- import { statusName } from "./task-view.js";
6
+ import { isStoppable } from "./model.js";
7
+ import { modeName, statusName, statusView } from "./status.js";
7
8
  import { cleanTaskText, firstCommandLine } from "./text.js";
8
9
  const pad = (text, width) => truncateToWidth(text, Math.max(1, width), "…", true);
10
+ /** Visible width of "Output Details". */
11
+ const TABS_WIDTH = 15;
9
12
  /** Cell geometry and mouse targets are produced together, so resize cannot separate them. */
10
13
  export function renderTasksLayout(options) {
11
- const { width, height, theme, selection, inspector, list, focus, keybindings } = options;
14
+ const { width, height, theme, selection, inspector, list, focus, keybindings, mode } = options;
12
15
  const hint = (key) => keyLabel(key, { keybindings });
13
16
  const result = { hits: [], listWidth: 0, listPageSize: 1, lines: [] };
14
17
  if (width < 1 || height < 1)
@@ -20,7 +23,8 @@ export function renderTasksLayout(options) {
20
23
  const bodyHeight = height - 5;
21
24
  const hits = result.hits;
22
25
  result.listWidth = listWidth;
23
- result.listPageSize = Math.max(1, Math.floor((bodyHeight - 1) / 2));
26
+ // One row per task, less the group headings.
27
+ result.listPageSize = Math.max(1, bodyHeight - 2);
24
28
  const pane = (lines, size, active) => {
25
29
  const border = (text) => theme.fg(wide && active ? "borderAccent" : "borderMuted", text);
26
30
  return [
@@ -64,7 +68,7 @@ export function renderTasksLayout(options) {
64
68
  const visible = list.render(selection, theme, listWidth - 2, bodyHeight);
65
69
  let listHeader = theme.fg("text", "Tasks");
66
70
  const searchLabel = `${hint("app.tasks.search")} find`;
67
- if (options.searchLine)
71
+ if (mode.kind === "search")
68
72
  listHeader = options.searchLine(listWidth - 2);
69
73
  else {
70
74
  const x = Math.max(6, listWidth - 2 - visibleWidth(searchLabel));
@@ -85,45 +89,56 @@ export function renderTasksLayout(options) {
85
89
  const task = inspector.task;
86
90
  const tab = selection.state.tab;
87
91
  const tabLabel = (id, label) => theme.style(label, { fg: "text", underline: tab === id });
88
- const right = [`${tabLabel("output", "Output")} ${tabLabel("info", "Details")}`];
92
+ // One header row in every layout: the selected task's source and status, with the tabs on the right.
93
+ const tabsX = rightWidth - 2 - TABS_WIDTH;
94
+ const summary = task
95
+ ? [
96
+ theme.fg("muted", cleanTaskText(task.label)),
97
+ theme.fg(statusView(task.status).color, statusName(task.status)),
98
+ theme.fg("muted", runtimeLabel(task)),
99
+ ].join(theme.fg("dim", " · "))
100
+ : "";
101
+ const shownSummary = truncateToWidth(summary, Math.max(0, tabsX - 1), "…");
102
+ const right = [
103
+ `${shownSummary}${" ".repeat(tabsX - visibleWidth(shownSummary))}${tabLabel("output", "Output")} ${tabLabel("info", "Details")}`,
104
+ ];
89
105
  if (wide || focus === "inspector")
90
- hits.push({ x: rightX + 1, y: 1, width: 6, tab: "output" }, { x: rightX + 9, y: 1, width: 7, tab: "info" });
106
+ hits.push({ x: rightX + 1 + tabsX, y: 1, width: 6, tab: "output" }, { x: rightX + 9 + tabsX, y: 1, width: 7, tab: "info" });
91
107
  const content = [];
92
108
  let reading = "";
93
109
  if (task) {
110
+ // Without the list beside it, the inspector names the task it shows.
94
111
  if (!wide)
95
- content.push(theme.fg("text", cleanTaskText(firstCommandLine(task.command ?? task.title))), theme.fg("muted", `${statusName(task.status)} · ${runtimeLabel(task)}`));
96
- const lines = inspector.lines(tab, rightWidth - 2);
112
+ content.push(theme.fg("text", cleanTaskText(firstCommandLine(task.title))));
97
113
  const position = inspector.position;
98
114
  const read = position.read;
115
+ const terminal = isTaskTerminal(task.status);
99
116
  const diagnostics = [task.error, read?.readError].filter((text) => !!text).map(cleanTaskText);
100
117
  if (tab === "output" && diagnostics.length)
101
118
  content.push(theme.fg("warning", diagnostics.join(" · ").replace(/\n/g, " ")));
102
119
  const source = tab === "info"
103
- ? [
104
- `Task ID ${task.id}`,
105
- `Status ${statusName(task.status)} · ${statusName(task.mode)} · ${runtimeLabel(task)}`,
106
- ...diagnostics,
107
- ...lines,
108
- ]
109
- : lines;
120
+ ? inspector.details(`${statusName(task.status)} · ${modeName(task.mode)} · ${runtimeLabel(task)}`)
121
+ : inspector.output(rightWidth - 2);
110
122
  content.push(...position[tab].layout(source, rightWidth - 2, bodyHeight - content.length));
111
123
  if (tab === "output") {
112
- reading = position.output.follow
113
- ? isTaskTerminal(task.status)
114
- ? "Finished"
115
- : "Live"
116
- : isTaskTerminal(task.status)
117
- ? `Finished · ${hint("app.tasks.follow")} load final output`
118
- : `Browsing · ${hint("app.tasks.follow")} follow output`;
119
- if (!position.output.follow && (wide || focus === "inspector"))
124
+ // Only a log holds a browsed snapshot; other output is always the current result.
125
+ const settled = terminal && task.outputPath === undefined;
126
+ reading =
127
+ position.output.follow || settled
128
+ ? terminal
129
+ ? "Finished"
130
+ : "Live"
131
+ : terminal
132
+ ? `Finished · ${hint("app.tasks.follow")} load final output`
133
+ : `Browsing · ${hint("app.tasks.follow")} follow output`;
134
+ if (!position.output.follow && !settled && (wide || focus === "inspector"))
120
135
  hits.push({
121
136
  x: rightX + 1,
122
137
  y: height - 3,
123
138
  width: Math.min(rightWidth - 2, visibleWidth(reading)),
124
139
  key: "app.tasks.follow",
125
140
  });
126
- if (read?.truncated)
141
+ if (task.outputPath !== undefined && read?.truncated)
127
142
  reading += ` · Tail preview: ${formatSize(Buffer.byteLength(read.text))} of ${formatSize(read.totalBytes)}`;
128
143
  }
129
144
  else if (tab === "info")
@@ -146,60 +161,28 @@ export function renderTasksLayout(options) {
146
161
  if (target)
147
162
  hits.push({ x, y: height - 1, width: visibleWidth(text), ...target });
148
163
  };
149
- if (options.help || options.pendingKill) {
164
+ if (mode.kind === "help") {
150
165
  hits.length = 0;
151
- let body;
152
- if (options.help) {
153
- const controls = [
154
- ["app.tasks.nextFocus", "Switch task list / inspector"],
155
- ["app.tasks.previousFocus", "Switch inspector / task list"],
156
- ["app.tasks.previousTab", "Output (inspector only)"],
157
- ["app.tasks.nextTab", "Details (inspector only)"],
158
- ["tui.select.up", "Previous task / scroll up"],
159
- ["tui.select.down", "Next task / scroll down"],
160
- ["tui.select.pageUp", "Page up in task list"],
161
- ["tui.select.pageDown", "Page down in task list"],
162
- ["tui.editor.pageUp", "Page up in inspector"],
163
- ["tui.editor.pageDown", "Page down in inspector"],
164
- ["app.tasks.top", "Top of list / content"],
165
- ["app.tasks.bottom", "Bottom of list / content"],
166
- ["app.tasks.search", "Locate a retained task; Enter chooses, Escape restores selection"],
167
- ["app.tasks.follow", "Follow latest / final output (Output only)"],
168
- ["app.tasks.detach", "Move every eligible foreground execution to the background"],
169
- ["app.tasks.detachSelected", "Move selected foreground task to background"],
170
- ["app.tasks.kill", "Request stop of selected whole task"],
171
- ["app.tasks.confirmStop", "Confirm stop"],
172
- ["tui.select.cancel", "Close current search, help or confirmation; otherwise close panel"],
173
- ];
174
- body = [
175
- theme.fg("text", "Task controls"),
176
- ...options.help.layout(controls.map(([key, label]) => hint(key).padEnd(14) + label), width - 2, height - 5),
177
- "",
178
- ];
179
- addHint("tui.select.up", `/ ${hint("tui.select.down")} scroll`);
180
- addHint("tui.select.cancel", "back", { cancel: true });
181
- }
182
- else {
183
- const target = selection.all.find((task) => task.id === options.pendingKill);
184
- const command = wrapTextWithAnsi(cleanTaskText(target?.command ?? target?.title ?? options.pendingKill), width - 2);
185
- const preview = command.slice(0, height - 8);
186
- if (preview.length < command.length)
187
- preview[preview.length - 1] = "… Full command in Details.";
188
- body = [
189
- theme.fg("text", "Stop task?"),
190
- "Stops the whole task, including its active work.",
191
- `Task ID: ${options.pendingKill}`,
192
- "",
193
- ...preview,
194
- ];
195
- addHint("app.tasks.confirmStop", "confirm", { key: "app.tasks.confirmStop" });
196
- addHint("tui.select.cancel", "cancel", { cancel: true });
197
- }
198
- while (body.length < height - 3)
199
- body.push("");
200
- panels = pane(body, width, false);
166
+ panels = pane(helpLines(mode.viewport, hint, theme, width, height), width, false);
167
+ addHint("tui.select.up", `/ ${hint("tui.select.down")} scroll`);
168
+ addHint("tui.select.cancel", "back", { cancel: true });
201
169
  }
202
- else if (options.searchLine) {
170
+ else if (mode.kind === "confirmStop") {
171
+ // The question borrows only the footer row, so the task stays in view while confirming.
172
+ hits.length = 0;
173
+ const target = selection.all.find((task) => task.id === mode.taskId);
174
+ const source = cleanTaskText(target?.label ?? "task");
175
+ const confirm = `${hint("app.tasks.confirmStop")} stop`;
176
+ const cancel = `${hint("tui.select.cancel")} cancel`;
177
+ const actions = `${confirm} · ${cancel}`;
178
+ const room = width - visibleWidth(`Stop ${source} ""? ${actions}`);
179
+ const title = cleanTaskText(firstCommandLine(target?.title ?? mode.taskId));
180
+ const question = room >= 4 ? `Stop ${source} "${truncateToWidth(title, room, "…")}"? ` : `Stop ${source}? `;
181
+ footer = question + actions;
182
+ const x = visibleWidth(question);
183
+ hits.push({ x, y: height - 1, width: visibleWidth(confirm), key: "app.tasks.confirmStop" }, { x: x + visibleWidth(confirm) + 3, y: height - 1, width: visibleWidth(cancel), cancel: true });
184
+ }
185
+ else if (mode.kind === "search") {
203
186
  // Only search candidates and cancellation are clickable while locating a task.
204
187
  for (let i = hits.length - 1; i >= 0; i--)
205
188
  if (!hits[i].taskId)
@@ -210,9 +193,10 @@ export function renderTasksLayout(options) {
210
193
  }
211
194
  else {
212
195
  addHint("tui.select.up", `/ ${hint("tui.select.down")}${focus === "list" ? " tasks" : " scroll"}`);
213
- addHint("app.tasks.nextFocus", focus === "list" ? (tab === "output" ? "output" : "details") : "tasks", {
214
- key: "app.tasks.nextFocus",
215
- });
196
+ if (focus === "list")
197
+ addHint("tui.select.confirm", "open", { tab });
198
+ else
199
+ addHint("app.tasks.nextFocus", "tasks", { key: "app.tasks.nextFocus" });
216
200
  addHint("tui.select.cancel", "close", { cancel: true });
217
201
  if (focus === "list")
218
202
  addHint("app.tasks.search", "find", { key: "app.tasks.search" });
@@ -220,18 +204,68 @@ export function renderTasksLayout(options) {
220
204
  addHint("app.tasks.previousTab", "output", { tab: "output" });
221
205
  addHint("app.tasks.nextTab", "details", { tab: "info" });
222
206
  }
223
- if (task && !isTaskTerminal(task.status) && task.status !== "stopping") {
207
+ if (task && isStoppable(task))
224
208
  addHint("app.tasks.kill", "stop", { key: "app.tasks.kill" });
225
- if (task.mode === "foreground" && options.canDetach)
226
- addHint("app.tasks.detachSelected", "background", { key: "app.tasks.detachSelected" });
227
- }
209
+ if (task && options.canDetach(task.id))
210
+ addHint("app.tasks.detachSelected", "background", { key: "app.tasks.detachSelected" });
228
211
  addHint("app.tasks.help", "keys", { key: "app.tasks.help" });
229
212
  // Feedback borrows the hint row, so the hint hit targets stay live and the click that
230
213
  // dismisses it can act in the same gesture.
231
214
  if (options.feedback)
232
215
  footer = options.feedback;
233
216
  }
234
- result.lines = [...panels, theme.fg(options.pendingKill ? "warning" : "dim", pad(footer, width))];
217
+ result.lines = [...panels, theme.fg(mode.kind === "confirmStop" ? "warning" : "dim", pad(footer, width))];
235
218
  return result;
236
219
  }
220
+ /** Controls grouped by purpose; actions sharing a key, such as list and inspector paging, share a row. */
221
+ function helpLines(help, hint, theme, width, height) {
222
+ const keys = (...bindings) => [...new Set(bindings.map(hint).filter(Boolean))].join(" / ");
223
+ const listPage = keys("tui.select.pageUp", "tui.select.pageDown");
224
+ const inspectorPage = keys("tui.editor.pageUp", "tui.editor.pageDown");
225
+ const paging = listPage === inspectorPage
226
+ ? [[listPage, "Page the list or inspector"]]
227
+ : [
228
+ [listPage, "Page the list"],
229
+ [inspectorPage, "Page the inspector"],
230
+ ];
231
+ const sections = [
232
+ [
233
+ "Navigate",
234
+ [
235
+ [keys("tui.select.up", "tui.select.down"), "Select a task, or scroll the inspector"],
236
+ ...paging,
237
+ [keys("app.tasks.top", "app.tasks.bottom"), "Top or bottom"],
238
+ [hint("tui.select.confirm"), "Open the selected task"],
239
+ [keys("app.tasks.nextFocus", "app.tasks.previousFocus"), "Switch between list and inspector"],
240
+ ],
241
+ ],
242
+ [
243
+ "View",
244
+ [
245
+ [keys("app.tasks.previousTab", "app.tasks.nextTab"), "Output or Details, in the inspector"],
246
+ [hint("app.tasks.follow"), "Follow the latest output, or load the final output"],
247
+ [hint("app.tasks.search"), "Find a task; Enter locates it, Esc restores the selection"],
248
+ ],
249
+ ],
250
+ [
251
+ "Act",
252
+ [
253
+ [hint("app.tasks.kill"), `Stop the selected task; ${hint("app.tasks.confirmStop")} confirms`],
254
+ [hint("app.tasks.detachSelected"), "Move the selected foreground task to the background"],
255
+ [hint("app.tasks.detach"), "Move every foreground execution to the background"],
256
+ [hint("tui.select.cancel"), "Close search, help or confirmation; otherwise the panel"],
257
+ ],
258
+ ],
259
+ ];
260
+ const keyWidth = Math.max(...sections.flatMap(([, rows]) => rows.map(([key]) => visibleWidth(key)))) + 2;
261
+ const rows = sections.flatMap(([title, entries], index) => [
262
+ ...(index ? [""] : []),
263
+ theme.fg("accent", title),
264
+ ...entries.map(([key, label]) => ` ${key}${" ".repeat(keyWidth - visibleWidth(key))}${label}`),
265
+ ]);
266
+ const body = [theme.fg("text", "Task controls"), ...help.layout(rows, width - 2, height - 5), ""];
267
+ while (body.length < height - 3)
268
+ body.push("");
269
+ return body;
270
+ }
237
271
  //# sourceMappingURL=layout.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"layout.js","sourceRoot":"","sources":["../../../../src/extensions/tasks/ui/layout.ts"],"names":[],"mappings":"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","sourcesContent":["import { truncateToWidth, visibleWidth, wrapTextWithAnsi } from \"@earendil-works/pi-tui\";\nimport type { AppKeybinding, Keybinding, KeybindingsManager } from \"../../../core/keybindings.ts\";\nimport { runtimeLabel } from \"../../../core/tasks/format.ts\";\nimport { isTaskTerminal } from \"../../../core/tasks/types.ts\";\nimport { formatSize } from \"../../../core/tools/truncate.ts\";\nimport { keyLabel } from \"../../../modes/interactive/components/keybinding-hints.ts\";\nimport type { Theme } from \"../../../modes/interactive/theme/theme.ts\";\nimport type { TaskInspector } from \"./inspector.ts\";\nimport type { TaskList } from \"./list.ts\";\nimport type { TaskSelection, TasksTab } from \"./model.ts\";\nimport { statusName } from \"./task-view.ts\";\nimport { cleanTaskText, firstCommandLine } from \"./text.ts\";\nimport type { TaskViewport } from \"./viewport.ts\";\n\nexport interface TaskHit {\n\tx: number;\n\ty: number;\n\twidth: number;\n\tkey?: AppKeybinding;\n\ttaskId?: string;\n\ttab?: TasksTab;\n\tcancel?: boolean;\n}\nexport interface TasksLayout {\n\thits: TaskHit[];\n\tlistWidth: number;\n\tlistPageSize: number;\n\tlines: string[];\n\t/** The terminal is below the supported size; only a full-screen resize notice is shown. */\n\ttooSmall?: boolean;\n}\ninterface LayoutOptions {\n\twidth: number;\n\theight: number;\n\ttheme: Theme;\n\tkeybindings: Pick<KeybindingsManager, \"getKeys\">;\n\tselection: TaskSelection;\n\tinspector: TaskInspector;\n\tlist: TaskList;\n\tfocus: \"list\" | \"inspector\";\n\tsearchLine?: (width: number) => string;\n\tpendingKill?: string;\n\tfeedback?: string;\n\thelp?: TaskViewport;\n\tcanDetach: boolean;\n}\nconst pad = (text: string, width: number) => truncateToWidth(text, Math.max(1, width), \"…\", true);\n\n/** Cell geometry and mouse targets are produced together, so resize cannot separate them. */\nexport function renderTasksLayout(options: LayoutOptions): TasksLayout {\n\tconst { width, height, theme, selection, inspector, list, focus, keybindings } = options;\n\tconst hint = (key: Keybinding) => keyLabel(key, { keybindings });\n\tconst result: TasksLayout = { hits: [], listWidth: 0, listPageSize: 1, lines: [] };\n\tif (width < 1 || height < 1) return result;\n\tconst wide = width >= 100;\n\tconst listWidth = wide ? Math.min(42, Math.max(32, Math.floor(width * 0.32))) : width;\n\tconst rightWidth = wide ? width - listWidth - 1 : width;\n\tconst rightX = wide ? listWidth + 1 : 0;\n\tconst bodyHeight = height - 5;\n\tconst hits = result.hits;\n\tresult.listWidth = listWidth;\n\tresult.listPageSize = Math.max(1, Math.floor((bodyHeight - 1) / 2));\n\tconst pane = (lines: string[], size: number, active: boolean): string[] => {\n\t\tconst border = (text: string) => theme.fg(wide && active ? \"borderAccent\" : \"borderMuted\", text);\n\t\treturn [\n\t\t\tborder(`┌${\"─\".repeat(size - 2)}┐`),\n\t\t\t...lines.map((line) => border(\"│\") + pad(line, size - 2) + border(\"│\")),\n\t\t\tborder(`└${\"─\".repeat(size - 2)}┘`),\n\t\t];\n\t};\n\tif (width < 60 || height < 14) {\n\t\t// The panel is modal: a floating notice would leave the transcript visible yet\n\t\t// unscrollable, so the notice owns every row as a centered dialog. Only a truly\n\t\t// tiny terminal, where even the dialog does not fit, gets bare covering lines.\n\t\tresult.tooSmall = true;\n\t\tconst message = \"Terminal too small for /tasks. Enlarge the window to continue.\";\n\t\tconst close = `${hint(\"tui.select.cancel\")} close`;\n\t\tconst boxWidth = Math.min(width, 46);\n\t\tconst inner = boxWidth - 2;\n\t\tconst wrapped = wrapTextWithAnsi(message, inner).map((line) => theme.fg(\"warning\", line));\n\t\tconst spacious = [theme.fg(\"text\", \"Tasks\"), \"\", ...wrapped, \"\", theme.fg(\"dim\", close)];\n\t\tconst tight = [theme.fg(\"text\", \"Tasks\"), ...wrapped, theme.fg(\"dim\", close)];\n\t\tconst body = height >= spacious.length + 2 ? spacious : tight;\n\t\tif (inner >= 16 && height >= body.length + 2) {\n\t\t\tconst box = pane(body, boxWidth, false);\n\t\t\tconst left = Math.floor((width - boxWidth) / 2);\n\t\t\tconst top = Math.max(0, Math.floor((height - box.length) / 2));\n\t\t\tconst blank = \" \".repeat(width);\n\t\t\tresult.lines = [\n\t\t\t\t...Array.from({ length: top }, () => blank),\n\t\t\t\t...box.map((line) => \" \".repeat(left) + line + \" \".repeat(width - left - boxWidth)),\n\t\t\t\t...Array.from({ length: height - top - box.length }, () => blank),\n\t\t\t];\n\t\t\thits.push({ x: left + 1, y: top + body.length, width: visibleWidth(close), cancel: true });\n\t\t} else {\n\t\t\tresult.lines = [\"Tasks\", message, close].slice(0, height).map((line) => pad(line, width));\n\t\t\twhile (result.lines.length < height) result.lines.push(\" \".repeat(width));\n\t\t}\n\t\treturn result;\n\t}\n\tconst visible = list.render(selection, theme, listWidth - 2, bodyHeight);\n\tlet listHeader = theme.fg(\"text\", \"Tasks\");\n\tconst searchLabel = `${hint(\"app.tasks.search\")} find`;\n\tif (options.searchLine) listHeader = options.searchLine(listWidth - 2);\n\telse {\n\t\tconst x = Math.max(6, listWidth - 2 - visibleWidth(searchLabel));\n\t\tlistHeader += \" \".repeat(x - 5) + theme.fg(\"dim\", searchLabel);\n\t\tif (wide || focus === \"list\")\n\t\t\thits.push({ x: x + 1, y: 1, width: visibleWidth(searchLabel), key: \"app.tasks.search\" });\n\t}\n\tif (wide || focus === \"list\")\n\t\tvisible.forEach((row, index) => {\n\t\t\tif (row.taskId) hits.push({ x: 1, y: 2 + index, width: listWidth - 2, taskId: row.taskId });\n\t\t});\n\tconst left = [\n\t\tlistHeader,\n\t\t...Array.from({ length: bodyHeight }, (_, i) => visible[i]?.text ?? \"\"),\n\t\ttheme.fg(\"dim\", list.range),\n\t];\n\tconst task = inspector.task;\n\tconst tab = selection.state.tab;\n\tconst tabLabel = (id: TasksTab, label: string) => theme.style(label, { fg: \"text\", underline: tab === id });\n\tconst right = [`${tabLabel(\"output\", \"Output\")} ${tabLabel(\"info\", \"Details\")}`];\n\tif (wide || focus === \"inspector\")\n\t\thits.push({ x: rightX + 1, y: 1, width: 6, tab: \"output\" }, { x: rightX + 9, y: 1, width: 7, tab: \"info\" });\n\tconst content: string[] = [];\n\tlet reading = \"\";\n\tif (task) {\n\t\tif (!wide)\n\t\t\tcontent.push(\n\t\t\t\ttheme.fg(\"text\", cleanTaskText(firstCommandLine(task.command ?? task.title))),\n\t\t\t\ttheme.fg(\"muted\", `${statusName(task.status)} · ${runtimeLabel(task)}`),\n\t\t\t);\n\t\tconst lines = inspector.lines(tab, rightWidth - 2);\n\t\tconst position = inspector.position;\n\t\tconst read = position.read;\n\t\tconst diagnostics = [task.error, read?.readError].filter((text): text is string => !!text).map(cleanTaskText);\n\t\tif (tab === \"output\" && diagnostics.length)\n\t\t\tcontent.push(theme.fg(\"warning\", diagnostics.join(\" · \").replace(/\\n/g, \" \")));\n\t\tconst source =\n\t\t\ttab === \"info\"\n\t\t\t\t? [\n\t\t\t\t\t\t`Task ID ${task.id}`,\n\t\t\t\t\t\t`Status ${statusName(task.status)} · ${statusName(task.mode)} · ${runtimeLabel(task)}`,\n\t\t\t\t\t\t...diagnostics,\n\t\t\t\t\t\t...lines,\n\t\t\t\t\t]\n\t\t\t\t: lines;\n\t\tcontent.push(...position[tab].layout(source, rightWidth - 2, bodyHeight - content.length));\n\t\tif (tab === \"output\") {\n\t\t\treading = position.output.follow\n\t\t\t\t? isTaskTerminal(task.status)\n\t\t\t\t\t? \"Finished\"\n\t\t\t\t\t: \"Live\"\n\t\t\t\t: isTaskTerminal(task.status)\n\t\t\t\t\t? `Finished · ${hint(\"app.tasks.follow\")} load final output`\n\t\t\t\t\t: `Browsing · ${hint(\"app.tasks.follow\")} follow output`;\n\t\t\tif (!position.output.follow && (wide || focus === \"inspector\"))\n\t\t\t\thits.push({\n\t\t\t\t\tx: rightX + 1,\n\t\t\t\t\ty: height - 3,\n\t\t\t\t\twidth: Math.min(rightWidth - 2, visibleWidth(reading)),\n\t\t\t\t\tkey: \"app.tasks.follow\",\n\t\t\t\t});\n\t\t\tif (read?.truncated)\n\t\t\t\treading += ` · Tail preview: ${formatSize(Buffer.byteLength(read.text))} of ${formatSize(read.totalBytes)}`;\n\t\t} else if (tab === \"info\") reading = `Details ${position.info.range}`;\n\t} else content.push(theme.fg(\"muted\", \"Select a task to inspect its result.\"));\n\tright.push(...Array.from({ length: bodyHeight }, (_, i) => content[i] ?? \"\"), theme.fg(\"dim\", reading));\n\tconst rightPane = pane(right, rightWidth, focus === \"inspector\");\n\tlet panels = wide\n\t\t? pane(left, listWidth, focus === \"list\").map((line, i) => `${line} ${rightPane[i]}`)\n\t\t: pane(focus === \"list\" ? left : right, width, false);\n\n\tlet footer = \"\";\n\tconst addHint = (key: Keybinding, label: string, target?: Omit<TaskHit, \"x\" | \"y\" | \"width\">) => {\n\t\tconst text = `${hint(key)} ${label}`;\n\t\tconst x = visibleWidth(footer) + (footer ? 3 : 0);\n\t\tif (x + visibleWidth(text) > width) return;\n\t\tfooter += (footer ? \" · \" : \"\") + text;\n\t\tif (target) hits.push({ x, y: height - 1, width: visibleWidth(text), ...target });\n\t};\n\tif (options.help || options.pendingKill) {\n\t\thits.length = 0;\n\t\tlet body: string[];\n\t\tif (options.help) {\n\t\t\tconst controls: [Keybinding, string][] = [\n\t\t\t\t[\"app.tasks.nextFocus\", \"Switch task list / inspector\"],\n\t\t\t\t[\"app.tasks.previousFocus\", \"Switch inspector / task list\"],\n\t\t\t\t[\"app.tasks.previousTab\", \"Output (inspector only)\"],\n\t\t\t\t[\"app.tasks.nextTab\", \"Details (inspector only)\"],\n\t\t\t\t[\"tui.select.up\", \"Previous task / scroll up\"],\n\t\t\t\t[\"tui.select.down\", \"Next task / scroll down\"],\n\t\t\t\t[\"tui.select.pageUp\", \"Page up in task list\"],\n\t\t\t\t[\"tui.select.pageDown\", \"Page down in task list\"],\n\t\t\t\t[\"tui.editor.pageUp\", \"Page up in inspector\"],\n\t\t\t\t[\"tui.editor.pageDown\", \"Page down in inspector\"],\n\t\t\t\t[\"app.tasks.top\", \"Top of list / content\"],\n\t\t\t\t[\"app.tasks.bottom\", \"Bottom of list / content\"],\n\t\t\t\t[\"app.tasks.search\", \"Locate a retained task; Enter chooses, Escape restores selection\"],\n\t\t\t\t[\"app.tasks.follow\", \"Follow latest / final output (Output only)\"],\n\t\t\t\t[\"app.tasks.detach\", \"Move every eligible foreground execution to the background\"],\n\t\t\t\t[\"app.tasks.detachSelected\", \"Move selected foreground task to background\"],\n\t\t\t\t[\"app.tasks.kill\", \"Request stop of selected whole task\"],\n\t\t\t\t[\"app.tasks.confirmStop\", \"Confirm stop\"],\n\t\t\t\t[\"tui.select.cancel\", \"Close current search, help or confirmation; otherwise close panel\"],\n\t\t\t];\n\t\t\tbody = [\n\t\t\t\ttheme.fg(\"text\", \"Task controls\"),\n\t\t\t\t...options.help.layout(\n\t\t\t\t\tcontrols.map(([key, label]) => hint(key).padEnd(14) + label),\n\t\t\t\t\twidth - 2,\n\t\t\t\t\theight - 5,\n\t\t\t\t),\n\t\t\t\t\"\",\n\t\t\t];\n\t\t\taddHint(\"tui.select.up\", `/ ${hint(\"tui.select.down\")} scroll`);\n\t\t\taddHint(\"tui.select.cancel\", \"back\", { cancel: true });\n\t\t} else {\n\t\t\tconst target = selection.all.find((task) => task.id === options.pendingKill);\n\t\t\tconst command = wrapTextWithAnsi(\n\t\t\t\tcleanTaskText(target?.command ?? target?.title ?? options.pendingKill!),\n\t\t\t\twidth - 2,\n\t\t\t);\n\t\t\tconst preview = command.slice(0, height - 8);\n\t\t\tif (preview.length < command.length) preview[preview.length - 1] = \"… Full command in Details.\";\n\t\t\tbody = [\n\t\t\t\ttheme.fg(\"text\", \"Stop task?\"),\n\t\t\t\t\"Stops the whole task, including its active work.\",\n\t\t\t\t`Task ID: ${options.pendingKill}`,\n\t\t\t\t\"\",\n\t\t\t\t...preview,\n\t\t\t];\n\t\t\taddHint(\"app.tasks.confirmStop\", \"confirm\", { key: \"app.tasks.confirmStop\" });\n\t\t\taddHint(\"tui.select.cancel\", \"cancel\", { cancel: true });\n\t\t}\n\t\twhile (body.length < height - 3) body.push(\"\");\n\t\tpanels = pane(body, width, false);\n\t} else if (options.searchLine) {\n\t\t// Only search candidates and cancellation are clickable while locating a task.\n\t\tfor (let i = hits.length - 1; i >= 0; i--) if (!hits[i]!.taskId) hits.splice(i, 1);\n\t\taddHint(\"tui.select.up\", `/ ${hint(\"tui.select.down\")} choose`);\n\t\taddHint(\"tui.select.confirm\", \"locate\");\n\t\taddHint(\"tui.select.cancel\", \"cancel\", { cancel: true });\n\t} else {\n\t\taddHint(\"tui.select.up\", `/ ${hint(\"tui.select.down\")}${focus === \"list\" ? \" tasks\" : \" scroll\"}`);\n\t\taddHint(\"app.tasks.nextFocus\", focus === \"list\" ? (tab === \"output\" ? \"output\" : \"details\") : \"tasks\", {\n\t\t\tkey: \"app.tasks.nextFocus\",\n\t\t});\n\t\taddHint(\"tui.select.cancel\", \"close\", { cancel: true });\n\t\tif (focus === \"list\") addHint(\"app.tasks.search\", \"find\", { key: \"app.tasks.search\" });\n\t\telse {\n\t\t\taddHint(\"app.tasks.previousTab\", \"output\", { tab: \"output\" });\n\t\t\taddHint(\"app.tasks.nextTab\", \"details\", { tab: \"info\" });\n\t\t}\n\t\tif (task && !isTaskTerminal(task.status) && task.status !== \"stopping\") {\n\t\t\taddHint(\"app.tasks.kill\", \"stop\", { key: \"app.tasks.kill\" });\n\t\t\tif (task.mode === \"foreground\" && options.canDetach)\n\t\t\t\taddHint(\"app.tasks.detachSelected\", \"background\", { key: \"app.tasks.detachSelected\" });\n\t\t}\n\t\taddHint(\"app.tasks.help\", \"keys\", { key: \"app.tasks.help\" });\n\t\t// Feedback borrows the hint row, so the hint hit targets stay live and the click that\n\t\t// dismisses it can act in the same gesture.\n\t\tif (options.feedback) footer = options.feedback;\n\t}\n\tresult.lines = [...panels, theme.fg(options.pendingKill ? \"warning\" : \"dim\", pad(footer, width))];\n\treturn result;\n}\n"]}
1
+ {"version":3,"file":"layout.js","sourceRoot":"","sources":["../../../../src/extensions/tasks/ui/layout.ts"],"names":[],"mappings":"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","sourcesContent":["import { truncateToWidth, visibleWidth, wrapTextWithAnsi } from \"@earendil-works/pi-tui\";\nimport type { AppKeybinding, Keybinding, KeybindingsManager } from \"../../../core/keybindings.ts\";\nimport { runtimeLabel } from \"../../../core/tasks/format.ts\";\nimport { isTaskTerminal } from \"../../../core/tasks/types.ts\";\nimport { formatSize } from \"../../../core/tools/truncate.ts\";\nimport { keyLabel } from \"../../../modes/interactive/components/keybinding-hints.ts\";\nimport type { Theme } from \"../../../modes/interactive/theme/theme.ts\";\nimport type { TaskInspector } from \"./inspector.ts\";\nimport type { TaskList } from \"./list.ts\";\nimport { isStoppable, type PanelFocus, type PanelMode, type TaskSelection, type TasksTab } from \"./model.ts\";\nimport { modeName, statusName, statusView } from \"./status.ts\";\nimport { cleanTaskText, firstCommandLine } from \"./text.ts\";\nimport type { TaskViewport } from \"./viewport.ts\";\n\nexport interface TaskHit {\n\tx: number;\n\ty: number;\n\twidth: number;\n\tkey?: AppKeybinding;\n\ttaskId?: string;\n\ttab?: TasksTab;\n\tcancel?: boolean;\n}\nexport interface TasksLayout {\n\thits: TaskHit[];\n\tlistWidth: number;\n\tlistPageSize: number;\n\tlines: string[];\n\t/** The terminal is below the supported size; only a full-screen resize notice is shown. */\n\ttooSmall?: boolean;\n}\ninterface LayoutOptions {\n\twidth: number;\n\theight: number;\n\ttheme: Theme;\n\tkeybindings: Pick<KeybindingsManager, \"getKeys\">;\n\tselection: TaskSelection;\n\tinspector: TaskInspector;\n\tlist: TaskList;\n\tfocus: PanelFocus;\n\tmode: PanelMode;\n\t/** Renders the search input line; shown only in search mode. */\n\tsearchLine: (width: number) => string;\n\tfeedback?: string;\n\t/** Whether the host would move this task to the background now. */\n\tcanDetach: (id: string) => boolean;\n}\nconst pad = (text: string, width: number) => truncateToWidth(text, Math.max(1, width), \"…\", true);\n/** Visible width of \"Output Details\". */\nconst TABS_WIDTH = 15;\n\n/** Cell geometry and mouse targets are produced together, so resize cannot separate them. */\nexport function renderTasksLayout(options: LayoutOptions): TasksLayout {\n\tconst { width, height, theme, selection, inspector, list, focus, keybindings, mode } = options;\n\tconst hint = (key: Keybinding) => keyLabel(key, { keybindings });\n\tconst result: TasksLayout = { hits: [], listWidth: 0, listPageSize: 1, lines: [] };\n\tif (width < 1 || height < 1) return result;\n\tconst wide = width >= 100;\n\tconst listWidth = wide ? Math.min(42, Math.max(32, Math.floor(width * 0.32))) : width;\n\tconst rightWidth = wide ? width - listWidth - 1 : width;\n\tconst rightX = wide ? listWidth + 1 : 0;\n\tconst bodyHeight = height - 5;\n\tconst hits = result.hits;\n\tresult.listWidth = listWidth;\n\t// One row per task, less the group headings.\n\tresult.listPageSize = Math.max(1, bodyHeight - 2);\n\tconst pane = (lines: string[], size: number, active: boolean): string[] => {\n\t\tconst border = (text: string) => theme.fg(wide && active ? \"borderAccent\" : \"borderMuted\", text);\n\t\treturn [\n\t\t\tborder(`┌${\"─\".repeat(size - 2)}┐`),\n\t\t\t...lines.map((line) => border(\"│\") + pad(line, size - 2) + border(\"│\")),\n\t\t\tborder(`└${\"─\".repeat(size - 2)}┘`),\n\t\t];\n\t};\n\tif (width < 60 || height < 14) {\n\t\t// The panel is modal: a floating notice would leave the transcript visible yet\n\t\t// unscrollable, so the notice owns every row as a centered dialog. Only a truly\n\t\t// tiny terminal, where even the dialog does not fit, gets bare covering lines.\n\t\tresult.tooSmall = true;\n\t\tconst message = \"Terminal too small for /tasks. Enlarge the window to continue.\";\n\t\tconst close = `${hint(\"tui.select.cancel\")} close`;\n\t\tconst boxWidth = Math.min(width, 46);\n\t\tconst inner = boxWidth - 2;\n\t\tconst wrapped = wrapTextWithAnsi(message, inner).map((line) => theme.fg(\"warning\", line));\n\t\tconst spacious = [theme.fg(\"text\", \"Tasks\"), \"\", ...wrapped, \"\", theme.fg(\"dim\", close)];\n\t\tconst tight = [theme.fg(\"text\", \"Tasks\"), ...wrapped, theme.fg(\"dim\", close)];\n\t\tconst body = height >= spacious.length + 2 ? spacious : tight;\n\t\tif (inner >= 16 && height >= body.length + 2) {\n\t\t\tconst box = pane(body, boxWidth, false);\n\t\t\tconst left = Math.floor((width - boxWidth) / 2);\n\t\t\tconst top = Math.max(0, Math.floor((height - box.length) / 2));\n\t\t\tconst blank = \" \".repeat(width);\n\t\t\tresult.lines = [\n\t\t\t\t...Array.from({ length: top }, () => blank),\n\t\t\t\t...box.map((line) => \" \".repeat(left) + line + \" \".repeat(width - left - boxWidth)),\n\t\t\t\t...Array.from({ length: height - top - box.length }, () => blank),\n\t\t\t];\n\t\t\thits.push({ x: left + 1, y: top + body.length, width: visibleWidth(close), cancel: true });\n\t\t} else {\n\t\t\tresult.lines = [\"Tasks\", message, close].slice(0, height).map((line) => pad(line, width));\n\t\t\twhile (result.lines.length < height) result.lines.push(\" \".repeat(width));\n\t\t}\n\t\treturn result;\n\t}\n\tconst visible = list.render(selection, theme, listWidth - 2, bodyHeight);\n\tlet listHeader = theme.fg(\"text\", \"Tasks\");\n\tconst searchLabel = `${hint(\"app.tasks.search\")} find`;\n\tif (mode.kind === \"search\") listHeader = options.searchLine(listWidth - 2);\n\telse {\n\t\tconst x = Math.max(6, listWidth - 2 - visibleWidth(searchLabel));\n\t\tlistHeader += \" \".repeat(x - 5) + theme.fg(\"dim\", searchLabel);\n\t\tif (wide || focus === \"list\")\n\t\t\thits.push({ x: x + 1, y: 1, width: visibleWidth(searchLabel), key: \"app.tasks.search\" });\n\t}\n\tif (wide || focus === \"list\")\n\t\tvisible.forEach((row, index) => {\n\t\t\tif (row.taskId) hits.push({ x: 1, y: 2 + index, width: listWidth - 2, taskId: row.taskId });\n\t\t});\n\tconst left = [\n\t\tlistHeader,\n\t\t...Array.from({ length: bodyHeight }, (_, i) => visible[i]?.text ?? \"\"),\n\t\ttheme.fg(\"dim\", list.range),\n\t];\n\tconst task = inspector.task;\n\tconst tab = selection.state.tab;\n\tconst tabLabel = (id: TasksTab, label: string) => theme.style(label, { fg: \"text\", underline: tab === id });\n\t// One header row in every layout: the selected task's source and status, with the tabs on the right.\n\tconst tabsX = rightWidth - 2 - TABS_WIDTH;\n\tconst summary = task\n\t\t? [\n\t\t\t\ttheme.fg(\"muted\", cleanTaskText(task.label)),\n\t\t\t\ttheme.fg(statusView(task.status).color, statusName(task.status)),\n\t\t\t\ttheme.fg(\"muted\", runtimeLabel(task)),\n\t\t\t].join(theme.fg(\"dim\", \" · \"))\n\t\t: \"\";\n\tconst shownSummary = truncateToWidth(summary, Math.max(0, tabsX - 1), \"…\");\n\tconst right = [\n\t\t`${shownSummary}${\" \".repeat(tabsX - visibleWidth(shownSummary))}${tabLabel(\"output\", \"Output\")} ${tabLabel(\"info\", \"Details\")}`,\n\t];\n\tif (wide || focus === \"inspector\")\n\t\thits.push(\n\t\t\t{ x: rightX + 1 + tabsX, y: 1, width: 6, tab: \"output\" },\n\t\t\t{ x: rightX + 9 + tabsX, y: 1, width: 7, tab: \"info\" },\n\t\t);\n\tconst content: string[] = [];\n\tlet reading = \"\";\n\tif (task) {\n\t\t// Without the list beside it, the inspector names the task it shows.\n\t\tif (!wide) content.push(theme.fg(\"text\", cleanTaskText(firstCommandLine(task.title))));\n\t\tconst position = inspector.position;\n\t\tconst read = position.read;\n\t\tconst terminal = isTaskTerminal(task.status);\n\t\tconst diagnostics = [task.error, read?.readError].filter((text): text is string => !!text).map(cleanTaskText);\n\t\tif (tab === \"output\" && diagnostics.length)\n\t\t\tcontent.push(theme.fg(\"warning\", diagnostics.join(\" · \").replace(/\\n/g, \" \")));\n\t\tconst source =\n\t\t\ttab === \"info\"\n\t\t\t\t? inspector.details(`${statusName(task.status)} · ${modeName(task.mode)} · ${runtimeLabel(task)}`)\n\t\t\t\t: inspector.output(rightWidth - 2);\n\t\tcontent.push(...position[tab].layout(source, rightWidth - 2, bodyHeight - content.length));\n\t\tif (tab === \"output\") {\n\t\t\t// Only a log holds a browsed snapshot; other output is always the current result.\n\t\t\tconst settled = terminal && task.outputPath === undefined;\n\t\t\treading =\n\t\t\t\tposition.output.follow || settled\n\t\t\t\t\t? terminal\n\t\t\t\t\t\t? \"Finished\"\n\t\t\t\t\t\t: \"Live\"\n\t\t\t\t\t: terminal\n\t\t\t\t\t\t? `Finished · ${hint(\"app.tasks.follow\")} load final output`\n\t\t\t\t\t\t: `Browsing · ${hint(\"app.tasks.follow\")} follow output`;\n\t\t\tif (!position.output.follow && !settled && (wide || focus === \"inspector\"))\n\t\t\t\thits.push({\n\t\t\t\t\tx: rightX + 1,\n\t\t\t\t\ty: height - 3,\n\t\t\t\t\twidth: Math.min(rightWidth - 2, visibleWidth(reading)),\n\t\t\t\t\tkey: \"app.tasks.follow\",\n\t\t\t\t});\n\t\t\tif (task.outputPath !== undefined && read?.truncated)\n\t\t\t\treading += ` · Tail preview: ${formatSize(Buffer.byteLength(read.text))} of ${formatSize(read.totalBytes)}`;\n\t\t} else if (tab === \"info\") reading = `Details ${position.info.range}`;\n\t} else content.push(theme.fg(\"muted\", \"Select a task to inspect its result.\"));\n\tright.push(...Array.from({ length: bodyHeight }, (_, i) => content[i] ?? \"\"), theme.fg(\"dim\", reading));\n\tconst rightPane = pane(right, rightWidth, focus === \"inspector\");\n\tlet panels = wide\n\t\t? pane(left, listWidth, focus === \"list\").map((line, i) => `${line} ${rightPane[i]}`)\n\t\t: pane(focus === \"list\" ? left : right, width, false);\n\n\tlet footer = \"\";\n\tconst addHint = (key: Keybinding, label: string, target?: Omit<TaskHit, \"x\" | \"y\" | \"width\">) => {\n\t\tconst text = `${hint(key)} ${label}`;\n\t\tconst x = visibleWidth(footer) + (footer ? 3 : 0);\n\t\tif (x + visibleWidth(text) > width) return;\n\t\tfooter += (footer ? \" · \" : \"\") + text;\n\t\tif (target) hits.push({ x, y: height - 1, width: visibleWidth(text), ...target });\n\t};\n\tif (mode.kind === \"help\") {\n\t\thits.length = 0;\n\t\tpanels = pane(helpLines(mode.viewport, hint, theme, width, height), width, false);\n\t\taddHint(\"tui.select.up\", `/ ${hint(\"tui.select.down\")} scroll`);\n\t\taddHint(\"tui.select.cancel\", \"back\", { cancel: true });\n\t} else if (mode.kind === \"confirmStop\") {\n\t\t// The question borrows only the footer row, so the task stays in view while confirming.\n\t\thits.length = 0;\n\t\tconst target = selection.all.find((task) => task.id === mode.taskId);\n\t\tconst source = cleanTaskText(target?.label ?? \"task\");\n\t\tconst confirm = `${hint(\"app.tasks.confirmStop\")} stop`;\n\t\tconst cancel = `${hint(\"tui.select.cancel\")} cancel`;\n\t\tconst actions = `${confirm} · ${cancel}`;\n\t\tconst room = width - visibleWidth(`Stop ${source} \"\"? ${actions}`);\n\t\tconst title = cleanTaskText(firstCommandLine(target?.title ?? mode.taskId));\n\t\tconst question = room >= 4 ? `Stop ${source} \"${truncateToWidth(title, room, \"…\")}\"? ` : `Stop ${source}? `;\n\t\tfooter = question + actions;\n\t\tconst x = visibleWidth(question);\n\t\thits.push(\n\t\t\t{ x, y: height - 1, width: visibleWidth(confirm), key: \"app.tasks.confirmStop\" },\n\t\t\t{ x: x + visibleWidth(confirm) + 3, y: height - 1, width: visibleWidth(cancel), cancel: true },\n\t\t);\n\t} else if (mode.kind === \"search\") {\n\t\t// Only search candidates and cancellation are clickable while locating a task.\n\t\tfor (let i = hits.length - 1; i >= 0; i--) if (!hits[i]!.taskId) hits.splice(i, 1);\n\t\taddHint(\"tui.select.up\", `/ ${hint(\"tui.select.down\")} choose`);\n\t\taddHint(\"tui.select.confirm\", \"locate\");\n\t\taddHint(\"tui.select.cancel\", \"cancel\", { cancel: true });\n\t} else {\n\t\taddHint(\"tui.select.up\", `/ ${hint(\"tui.select.down\")}${focus === \"list\" ? \" tasks\" : \" scroll\"}`);\n\t\tif (focus === \"list\") addHint(\"tui.select.confirm\", \"open\", { tab });\n\t\telse addHint(\"app.tasks.nextFocus\", \"tasks\", { key: \"app.tasks.nextFocus\" });\n\t\taddHint(\"tui.select.cancel\", \"close\", { cancel: true });\n\t\tif (focus === \"list\") addHint(\"app.tasks.search\", \"find\", { key: \"app.tasks.search\" });\n\t\telse {\n\t\t\taddHint(\"app.tasks.previousTab\", \"output\", { tab: \"output\" });\n\t\t\taddHint(\"app.tasks.nextTab\", \"details\", { tab: \"info\" });\n\t\t}\n\t\tif (task && isStoppable(task)) addHint(\"app.tasks.kill\", \"stop\", { key: \"app.tasks.kill\" });\n\t\tif (task && options.canDetach(task.id))\n\t\t\taddHint(\"app.tasks.detachSelected\", \"background\", { key: \"app.tasks.detachSelected\" });\n\t\taddHint(\"app.tasks.help\", \"keys\", { key: \"app.tasks.help\" });\n\t\t// Feedback borrows the hint row, so the hint hit targets stay live and the click that\n\t\t// dismisses it can act in the same gesture.\n\t\tif (options.feedback) footer = options.feedback;\n\t}\n\tresult.lines = [...panels, theme.fg(mode.kind === \"confirmStop\" ? \"warning\" : \"dim\", pad(footer, width))];\n\treturn result;\n}\n\n/** Controls grouped by purpose; actions sharing a key, such as list and inspector paging, share a row. */\nfunction helpLines(\n\thelp: TaskViewport,\n\thint: (key: Keybinding) => string,\n\ttheme: Theme,\n\twidth: number,\n\theight: number,\n): string[] {\n\tconst keys = (...bindings: Keybinding[]) => [...new Set(bindings.map(hint).filter(Boolean))].join(\" / \");\n\tconst listPage = keys(\"tui.select.pageUp\", \"tui.select.pageDown\");\n\tconst inspectorPage = keys(\"tui.editor.pageUp\", \"tui.editor.pageDown\");\n\tconst paging: Array<[string, string]> =\n\t\tlistPage === inspectorPage\n\t\t\t? [[listPage, \"Page the list or inspector\"]]\n\t\t\t: [\n\t\t\t\t\t[listPage, \"Page the list\"],\n\t\t\t\t\t[inspectorPage, \"Page the inspector\"],\n\t\t\t\t];\n\tconst sections: Array<[string, Array<[string, string]>]> = [\n\t\t[\n\t\t\t\"Navigate\",\n\t\t\t[\n\t\t\t\t[keys(\"tui.select.up\", \"tui.select.down\"), \"Select a task, or scroll the inspector\"],\n\t\t\t\t...paging,\n\t\t\t\t[keys(\"app.tasks.top\", \"app.tasks.bottom\"), \"Top or bottom\"],\n\t\t\t\t[hint(\"tui.select.confirm\"), \"Open the selected task\"],\n\t\t\t\t[keys(\"app.tasks.nextFocus\", \"app.tasks.previousFocus\"), \"Switch between list and inspector\"],\n\t\t\t],\n\t\t],\n\t\t[\n\t\t\t\"View\",\n\t\t\t[\n\t\t\t\t[keys(\"app.tasks.previousTab\", \"app.tasks.nextTab\"), \"Output or Details, in the inspector\"],\n\t\t\t\t[hint(\"app.tasks.follow\"), \"Follow the latest output, or load the final output\"],\n\t\t\t\t[hint(\"app.tasks.search\"), \"Find a task; Enter locates it, Esc restores the selection\"],\n\t\t\t],\n\t\t],\n\t\t[\n\t\t\t\"Act\",\n\t\t\t[\n\t\t\t\t[hint(\"app.tasks.kill\"), `Stop the selected task; ${hint(\"app.tasks.confirmStop\")} confirms`],\n\t\t\t\t[hint(\"app.tasks.detachSelected\"), \"Move the selected foreground task to the background\"],\n\t\t\t\t[hint(\"app.tasks.detach\"), \"Move every foreground execution to the background\"],\n\t\t\t\t[hint(\"tui.select.cancel\"), \"Close search, help or confirmation; otherwise the panel\"],\n\t\t\t],\n\t\t],\n\t];\n\tconst keyWidth = Math.max(...sections.flatMap(([, rows]) => rows.map(([key]) => visibleWidth(key)))) + 2;\n\tconst rows = sections.flatMap(([title, entries], index) => [\n\t\t...(index ? [\"\"] : []),\n\t\ttheme.fg(\"accent\", title),\n\t\t...entries.map(([key, label]) => ` ${key}${\" \".repeat(keyWidth - visibleWidth(key))}${label}`),\n\t]);\n\tconst body = [theme.fg(\"text\", \"Task controls\"), ...help.layout(rows, width - 2, height - 5), \"\"];\n\twhile (body.length < height - 3) body.push(\"\");\n\treturn body;\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"list.d.ts","sourceRoot":"","sources":["../../../../src/extensions/tasks/ui/list.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,2CAA2C,CAAC;AACvE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAIhD,UAAU,OAAO;IAChB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,4FAA4F;AAC5F,qBAAa,QAAQ;IACpB,OAAO,CAAC,KAAK,CAAK;IAClB,OAAO,CAAC,MAAM,CAAK;IACnB,OAAO,CAAC,IAAI,CAAiB;IAC7B,OAAO,CAAC,UAAU,CAAC,CAAS;IAC5B,OAAO,CAAC,YAAY,CAAM;IAC1B,OAAO,CAAC,MAAM,CAAS;IACvB,aAAa,IAAI,IAAI;IAGrB,MAAM,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI;IAG3B,MAAM,CAAC,SAAS,EAAE,aAAa,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,EAAE;IA6DxF,IAAI,KAAK,IAAI,MAAM,CAOlB;CACD"}
1
+ {"version":3,"file":"list.d.ts","sourceRoot":"","sources":["../../../../src/extensions/tasks/ui/list.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,2CAA2C,CAAC;AACvE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAIhD,UAAU,OAAO;IAChB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,CAAC,EAAE,MAAM,CAAC;CAChB;AAgBD,4FAA4F;AAC5F,qBAAa,QAAQ;IACpB,OAAO,CAAC,KAAK,CAAK;IAClB,OAAO,CAAC,MAAM,CAAK;IACnB,OAAO,CAAC,IAAI,CAAiB;IAC7B,OAAO,CAAC,UAAU,CAAC,CAAS;IAC5B,OAAO,CAAC,YAAY,CAAM;IAC1B,OAAO,CAAC,MAAM,CAAS;IACvB,aAAa,IAAI,IAAI;IAGrB,MAAM,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI;IAG3B,MAAM,CAAC,SAAS,EAAE,aAAa,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,EAAE;IAgExF,IAAI,KAAK,IAAI,MAAM,CAOlB;CACD"}
@@ -1,9 +1,20 @@
1
- import { stripTerminalSequences, truncateToWidth } from "@earendil-works/pi-tui";
1
+ import { stripTerminalSequences, truncateToWidth, visibleWidth } from "@earendil-works/pi-tui";
2
2
  import { runtimeLabel } from "../../../core/tasks/format.js";
3
3
  import { isTaskTerminal } from "../../../core/tasks/types.js";
4
- import { statusMarker } from "../../../modes/interactive/components/status-marker.js";
5
- import { statusName } from "./task-view.js";
4
+ import { statusName, statusView } from "./status.js";
6
5
  import { cleanTaskText, firstCommandLine } from "./text.js";
6
+ /** Spinner, ✓ and the error × are unique; every other status also needs its name. */
7
+ const SELF_EVIDENT = new Set(["running", "completed", "failed"]);
8
+ /** Right-aligned row metadata: the status name when the glyph is ambiguous, a disambiguating ID, and runtime. */
9
+ function rowMeta(task, duplicate) {
10
+ return [
11
+ SELF_EVIDENT.has(task.status) ? "" : statusName(task.status),
12
+ duplicate ? task.id.slice(-8) : "",
13
+ runtimeLabel(task),
14
+ ]
15
+ .filter(Boolean)
16
+ .join(" · ");
17
+ }
7
18
  /** Keeps list scrolling independent of selection, with an identity anchor on reordering. */
8
19
  export class TaskList {
9
20
  constructor() {
@@ -21,8 +32,9 @@ export class TaskList {
21
32
  }
22
33
  render(selection, theme, width, height) {
23
34
  const rows = [];
24
- const labels = selection.rows.map((task) => cleanTaskText(firstCommandLine(task.command ?? task.title)));
25
- const fitted = labels.map((label) => truncateToWidth(label, Math.max(1, width - 3), "…"));
35
+ const labels = selection.rows.map((task) => cleanTaskText(task.label));
36
+ const labelWidth = Math.max(0, ...labels.map((label) => visibleWidth(label)));
37
+ const titles = selection.rows.map((task) => cleanTaskText(firstCommandLine(task.title)));
26
38
  let group = "", selectedLine = -1;
27
39
  for (const [index, task] of selection.rows.entries()) {
28
40
  const nextGroup = isTaskTerminal(task.status) ? "Finished" : "Active";
@@ -33,25 +45,26 @@ export class TaskList {
33
45
  const selected = task.id === selection.state.selectedId;
34
46
  if (selected)
35
47
  selectedLine = rows.length;
36
- const marker = statusMarker(task.status, { now: Date.now() });
37
- const duplicate = fitted.some((label, other) => other !== index && label === fitted[index]);
38
- const meta = `${runtimeLabel(task)}${duplicate ? ` · ${task.id.slice(-8)}` : ""}`;
39
- const texts = [
40
- `${theme.fg(marker.color, marker.glyph)} ${theme.fg("text", labels[index])}`,
41
- ` ${theme.fg(marker.color, statusName(task.status))}${theme.fg("muted", ` · ${meta}`)}`,
42
- ];
43
- for (const text of texts) {
44
- const padded = truncateToWidth(text, width, "…", true);
45
- // The system theme has no panel backgrounds until the terminal reports its colors.
46
- rows.push({
47
- text: !selected
48
- ? padded
49
- : theme.getBgAnsi("selectedBg") === "\x1b[49m"
50
- ? theme.style(stripTerminalSequences(padded), { inverse: true })
51
- : theme.bg("selectedBg", padded),
52
- taskId: task.id,
53
- });
54
- }
48
+ const marker = statusView(task.status, { now: Date.now() });
49
+ const label = labels[index];
50
+ const duplicate = titles.some((title, other) => other !== index && title === titles[index]);
51
+ const meta = rowMeta(task, duplicate);
52
+ // Labels form an aligned source column so titles start together.
53
+ const source = labelWidth ? `${label}${" ".repeat(labelWidth - visibleWidth(label))} ` : "";
54
+ const head = visibleWidth(marker.glyph) + 1 + visibleWidth(source);
55
+ const title = truncateToWidth(titles[index], Math.max(1, width - head - visibleWidth(meta) - 1), "…");
56
+ const gap = Math.max(1, width - head - visibleWidth(title) - visibleWidth(meta));
57
+ const text = `${theme.fg(marker.color, marker.glyph)} ${theme.fg("dim", source)}${theme.fg("text", title)}${" ".repeat(gap)}${theme.fg("muted", meta)}`;
58
+ const padded = truncateToWidth(text, width, "…", true);
59
+ // The system theme has no panel backgrounds until the terminal reports its colors.
60
+ rows.push({
61
+ text: !selected
62
+ ? padded
63
+ : theme.getBgAnsi("selectedBg") === "\x1b[49m"
64
+ ? theme.style(stripTerminalSequences(padded), { inverse: true })
65
+ : theme.bg("selectedBg", padded),
66
+ taskId: task.id,
67
+ });
55
68
  }
56
69
  if (!rows.length)
57
70
  rows.push({ text: theme.fg("muted", selection.query ? "No matching tasks." : "No retained tasks.") });
@@ -64,16 +77,17 @@ export class TaskList {
64
77
  this.start += selectedLine - this.selectedLine;
65
78
  else {
66
79
  const anchor = this.rows[this.start]?.taskId;
67
- const offset = anchor && this.rows[this.start - 1]?.taskId === anchor ? 1 : 0;
68
80
  const next = anchor ? rows.findIndex((row) => row.taskId === anchor) : -1;
69
81
  if (next >= 0)
70
- this.start = next + offset;
82
+ this.start = next;
71
83
  }
72
84
  if (selectedLine >= 0 && (!sameSelection || this.reveal || selectedWasVisible)) {
73
- if (selectedLine < this.start)
74
- this.start = selectedLine;
75
- else if (selectedLine + 1 >= this.start + height)
76
- this.start = selectedLine + 2 - height;
85
+ // Revealing the first task of a group keeps its heading in view.
86
+ const top = rows[selectedLine - 1]?.taskId === undefined ? selectedLine - 1 : selectedLine;
87
+ if (top < this.start)
88
+ this.start = Math.max(0, top);
89
+ else if (selectedLine >= this.start + height)
90
+ this.start = selectedLine + 1 - height;
77
91
  }
78
92
  this.start = Math.max(0, Math.min(Math.max(0, rows.length - height), this.start));
79
93
  this.rows = rows;
@@ -86,7 +100,7 @@ export class TaskList {
86
100
  get range() {
87
101
  if (this.rows.length <= this.height)
88
102
  return "";
89
- const ids = [...new Set(this.rows.flatMap((row) => (row.taskId ? [row.taskId] : [])))];
103
+ const ids = this.rows.flatMap((row) => (row.taskId ? [row.taskId] : []));
90
104
  const visible = this.rows
91
105
  .slice(this.start, this.start + this.height)
92
106
  .flatMap((row) => (row.taskId ? [row.taskId] : []));
@@ -1 +1 @@
1
- {"version":3,"file":"list.js","sourceRoot":"","sources":["../../../../src/extensions/tasks/ui/list.ts"],"names":[],"mappings":"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","sourcesContent":["import { stripTerminalSequences, truncateToWidth } from \"@earendil-works/pi-tui\";\nimport { runtimeLabel } from \"../../../core/tasks/format.ts\";\nimport { isTaskTerminal } from \"../../../core/tasks/types.ts\";\nimport { statusMarker } from \"../../../modes/interactive/components/status-marker.ts\";\nimport type { Theme } from \"../../../modes/interactive/theme/theme.ts\";\nimport type { TaskSelection } from \"./model.ts\";\nimport { statusName } from \"./task-view.ts\";\nimport { cleanTaskText, firstCommandLine } from \"./text.ts\";\n\ninterface ListRow {\n\ttext: string;\n\ttaskId?: string;\n}\n\n/** Keeps list scrolling independent of selection, with an identity anchor on reordering. */\nexport class TaskList {\n\tprivate start = 0;\n\tprivate height = 1;\n\tprivate rows: ListRow[] = [];\n\tprivate selectedId?: string;\n\tprivate selectedLine = -1;\n\tprivate reveal = false;\n\tshowSelection(): void {\n\t\tthis.reveal = true;\n\t}\n\tscroll(delta: number): void {\n\t\tthis.start = Math.max(0, Math.min(Math.max(0, this.rows.length - this.height), this.start + delta));\n\t}\n\trender(selection: TaskSelection, theme: Theme, width: number, height: number): ListRow[] {\n\t\tconst rows: ListRow[] = [];\n\t\tconst labels = selection.rows.map((task) => cleanTaskText(firstCommandLine(task.command ?? task.title)));\n\t\tconst fitted = labels.map((label) => truncateToWidth(label, Math.max(1, width - 3), \"…\"));\n\t\tlet group = \"\",\n\t\t\tselectedLine = -1;\n\t\tfor (const [index, task] of selection.rows.entries()) {\n\t\t\tconst nextGroup = isTaskTerminal(task.status) ? \"Finished\" : \"Active\";\n\t\t\tif (nextGroup !== group) {\n\t\t\t\trows.push({ text: theme.fg(\"dim\", nextGroup) });\n\t\t\t\tgroup = nextGroup;\n\t\t\t}\n\t\t\tconst selected = task.id === selection.state.selectedId;\n\t\t\tif (selected) selectedLine = rows.length;\n\t\t\tconst marker = statusMarker(task.status, { now: Date.now() });\n\t\t\tconst duplicate = fitted.some((label, other) => other !== index && label === fitted[index]);\n\t\t\tconst meta = `${runtimeLabel(task)}${duplicate ? ` · ${task.id.slice(-8)}` : \"\"}`;\n\t\t\tconst texts = [\n\t\t\t\t`${theme.fg(marker.color, marker.glyph)} ${theme.fg(\"text\", labels[index]!)}`,\n\t\t\t\t` ${theme.fg(marker.color, statusName(task.status))}${theme.fg(\"muted\", ` · ${meta}`)}`,\n\t\t\t];\n\t\t\tfor (const text of texts) {\n\t\t\t\tconst padded = truncateToWidth(text, width, \"…\", true);\n\t\t\t\t// The system theme has no panel backgrounds until the terminal reports its colors.\n\t\t\t\trows.push({\n\t\t\t\t\ttext: !selected\n\t\t\t\t\t\t? padded\n\t\t\t\t\t\t: theme.getBgAnsi(\"selectedBg\") === \"\\x1b[49m\"\n\t\t\t\t\t\t\t? theme.style(stripTerminalSequences(padded), { inverse: true })\n\t\t\t\t\t\t\t: theme.bg(\"selectedBg\", padded),\n\t\t\t\t\ttaskId: task.id,\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\t\tif (!rows.length)\n\t\t\trows.push({ text: theme.fg(\"muted\", selection.query ? \"No matching tasks.\" : \"No retained tasks.\") });\n\t\tconst sameSelection = this.selectedId === selection.state.selectedId;\n\t\tconst selectedWasVisible =\n\t\t\tsameSelection &&\n\t\t\tthis.selectedLine >= this.start &&\n\t\t\tthis.selectedLine < this.start + this.height &&\n\t\t\tselectedLine >= 0;\n\t\tif (selectedWasVisible) this.start += selectedLine - this.selectedLine;\n\t\telse {\n\t\t\tconst anchor = this.rows[this.start]?.taskId;\n\t\t\tconst offset = anchor && this.rows[this.start - 1]?.taskId === anchor ? 1 : 0;\n\t\t\tconst next = anchor ? rows.findIndex((row) => row.taskId === anchor) : -1;\n\t\t\tif (next >= 0) this.start = next + offset;\n\t\t}\n\t\tif (selectedLine >= 0 && (!sameSelection || this.reveal || selectedWasVisible)) {\n\t\t\tif (selectedLine < this.start) this.start = selectedLine;\n\t\t\telse if (selectedLine + 1 >= this.start + height) this.start = selectedLine + 2 - height;\n\t\t}\n\t\tthis.start = Math.max(0, Math.min(Math.max(0, rows.length - height), this.start));\n\t\tthis.rows = rows;\n\t\tthis.height = height;\n\t\tthis.selectedId = selection.state.selectedId;\n\t\tthis.selectedLine = selectedLine;\n\t\tthis.reveal = false;\n\t\treturn rows.slice(this.start, this.start + height);\n\t}\n\tget range(): string {\n\t\tif (this.rows.length <= this.height) return \"\";\n\t\tconst ids = [...new Set(this.rows.flatMap((row) => (row.taskId ? [row.taskId] : [])))];\n\t\tconst visible = this.rows\n\t\t\t.slice(this.start, this.start + this.height)\n\t\t\t.flatMap((row) => (row.taskId ? [row.taskId] : []));\n\t\treturn `Tasks ${ids.indexOf(visible[0]!) + 1}-${ids.indexOf(visible.at(-1)!) + 1} of ${ids.length}`;\n\t}\n}\n"]}
1
+ {"version":3,"file":"list.js","sourceRoot":"","sources":["../../../../src/extensions/tasks/ui/list.ts"],"names":[],"mappings":"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","sourcesContent":["import { stripTerminalSequences, truncateToWidth, visibleWidth } from \"@earendil-works/pi-tui\";\nimport { runtimeLabel } from \"../../../core/tasks/format.ts\";\nimport { isTaskTerminal, type TaskSnapshot, type TaskStatus } from \"../../../core/tasks/types.ts\";\nimport type { Theme } from \"../../../modes/interactive/theme/theme.ts\";\nimport type { TaskSelection } from \"./model.ts\";\nimport { statusName, statusView } from \"./status.ts\";\nimport { cleanTaskText, firstCommandLine } from \"./text.ts\";\n\ninterface ListRow {\n\ttext: string;\n\ttaskId?: string;\n}\n\n/** Spinner, ✓ and the error × are unique; every other status also needs its name. */\nconst SELF_EVIDENT = new Set<TaskStatus>([\"running\", \"completed\", \"failed\"]);\n\n/** Right-aligned row metadata: the status name when the glyph is ambiguous, a disambiguating ID, and runtime. */\nfunction rowMeta(task: TaskSnapshot, duplicate: boolean): string {\n\treturn [\n\t\tSELF_EVIDENT.has(task.status) ? \"\" : statusName(task.status),\n\t\tduplicate ? task.id.slice(-8) : \"\",\n\t\truntimeLabel(task),\n\t]\n\t\t.filter(Boolean)\n\t\t.join(\" · \");\n}\n\n/** Keeps list scrolling independent of selection, with an identity anchor on reordering. */\nexport class TaskList {\n\tprivate start = 0;\n\tprivate height = 1;\n\tprivate rows: ListRow[] = [];\n\tprivate selectedId?: string;\n\tprivate selectedLine = -1;\n\tprivate reveal = false;\n\tshowSelection(): void {\n\t\tthis.reveal = true;\n\t}\n\tscroll(delta: number): void {\n\t\tthis.start = Math.max(0, Math.min(Math.max(0, this.rows.length - this.height), this.start + delta));\n\t}\n\trender(selection: TaskSelection, theme: Theme, width: number, height: number): ListRow[] {\n\t\tconst rows: ListRow[] = [];\n\t\tconst labels = selection.rows.map((task) => cleanTaskText(task.label));\n\t\tconst labelWidth = Math.max(0, ...labels.map((label) => visibleWidth(label)));\n\t\tconst titles = selection.rows.map((task) => cleanTaskText(firstCommandLine(task.title)));\n\t\tlet group = \"\",\n\t\t\tselectedLine = -1;\n\t\tfor (const [index, task] of selection.rows.entries()) {\n\t\t\tconst nextGroup = isTaskTerminal(task.status) ? \"Finished\" : \"Active\";\n\t\t\tif (nextGroup !== group) {\n\t\t\t\trows.push({ text: theme.fg(\"dim\", nextGroup) });\n\t\t\t\tgroup = nextGroup;\n\t\t\t}\n\t\t\tconst selected = task.id === selection.state.selectedId;\n\t\t\tif (selected) selectedLine = rows.length;\n\t\t\tconst marker = statusView(task.status, { now: Date.now() });\n\t\t\tconst label = labels[index]!;\n\t\t\tconst duplicate = titles.some((title, other) => other !== index && title === titles[index]);\n\t\t\tconst meta = rowMeta(task, duplicate);\n\t\t\t// Labels form an aligned source column so titles start together.\n\t\t\tconst source = labelWidth ? `${label}${\" \".repeat(labelWidth - visibleWidth(label))} ` : \"\";\n\t\t\tconst head = visibleWidth(marker.glyph) + 1 + visibleWidth(source);\n\t\t\tconst title = truncateToWidth(titles[index]!, Math.max(1, width - head - visibleWidth(meta) - 1), \"…\");\n\t\t\tconst gap = Math.max(1, width - head - visibleWidth(title) - visibleWidth(meta));\n\t\t\tconst text = `${theme.fg(marker.color, marker.glyph)} ${theme.fg(\"dim\", source)}${theme.fg(\"text\", title)}${\" \".repeat(gap)}${theme.fg(\"muted\", meta)}`;\n\t\t\tconst padded = truncateToWidth(text, width, \"…\", true);\n\t\t\t// The system theme has no panel backgrounds until the terminal reports its colors.\n\t\t\trows.push({\n\t\t\t\ttext: !selected\n\t\t\t\t\t? padded\n\t\t\t\t\t: theme.getBgAnsi(\"selectedBg\") === \"\\x1b[49m\"\n\t\t\t\t\t\t? theme.style(stripTerminalSequences(padded), { inverse: true })\n\t\t\t\t\t\t: theme.bg(\"selectedBg\", padded),\n\t\t\t\ttaskId: task.id,\n\t\t\t});\n\t\t}\n\t\tif (!rows.length)\n\t\t\trows.push({ text: theme.fg(\"muted\", selection.query ? \"No matching tasks.\" : \"No retained tasks.\") });\n\t\tconst sameSelection = this.selectedId === selection.state.selectedId;\n\t\tconst selectedWasVisible =\n\t\t\tsameSelection &&\n\t\t\tthis.selectedLine >= this.start &&\n\t\t\tthis.selectedLine < this.start + this.height &&\n\t\t\tselectedLine >= 0;\n\t\tif (selectedWasVisible) this.start += selectedLine - this.selectedLine;\n\t\telse {\n\t\t\tconst anchor = this.rows[this.start]?.taskId;\n\t\t\tconst next = anchor ? rows.findIndex((row) => row.taskId === anchor) : -1;\n\t\t\tif (next >= 0) this.start = next;\n\t\t}\n\t\tif (selectedLine >= 0 && (!sameSelection || this.reveal || selectedWasVisible)) {\n\t\t\t// Revealing the first task of a group keeps its heading in view.\n\t\t\tconst top = rows[selectedLine - 1]?.taskId === undefined ? selectedLine - 1 : selectedLine;\n\t\t\tif (top < this.start) this.start = Math.max(0, top);\n\t\t\telse if (selectedLine >= this.start + height) this.start = selectedLine + 1 - height;\n\t\t}\n\t\tthis.start = Math.max(0, Math.min(Math.max(0, rows.length - height), this.start));\n\t\tthis.rows = rows;\n\t\tthis.height = height;\n\t\tthis.selectedId = selection.state.selectedId;\n\t\tthis.selectedLine = selectedLine;\n\t\tthis.reveal = false;\n\t\treturn rows.slice(this.start, this.start + height);\n\t}\n\tget range(): string {\n\t\tif (this.rows.length <= this.height) return \"\";\n\t\tconst ids = this.rows.flatMap((row) => (row.taskId ? [row.taskId] : []));\n\t\tconst visible = this.rows\n\t\t\t.slice(this.start, this.start + this.height)\n\t\t\t.flatMap((row) => (row.taskId ? [row.taskId] : []));\n\t\treturn `Tasks ${ids.indexOf(visible[0]!) + 1}-${ids.indexOf(visible.at(-1)!) + 1} of ${ids.length}`;\n\t}\n}\n"]}