@astralyn/pi 1.4.9 → 1.4.10

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 (204) hide show
  1. package/CHANGELOG.md +54 -0
  2. package/dist/bundle/chunks/{chunk-3PKXUKTB.js → chunk-6GTUUUCJ.js} +123 -123
  3. package/dist/bundle/chunks/{chunk-BAGHIP5J.js → chunk-MF3T3ETJ.js} +3 -3
  4. package/dist/bundle/chunks/{chunk-F66AY3WR.js → chunk-UCH27AVQ.js} +1 -1
  5. package/dist/bundle/chunks/{execute-P22VMM2H.js → execute-FQKRO4XJ.js} +1 -1
  6. package/dist/bundle/chunks/{virtual-modules-VKMBQTR2.js → virtual-modules-6IJ6V2CG.js} +1 -1
  7. package/dist/bundle/cli.js +1 -1
  8. package/dist/bundle/index.js +1 -1
  9. package/dist/bundle/rpc-entry.js +1 -1
  10. package/dist/core/agent-session.d.ts +34 -0
  11. package/dist/core/agent-session.d.ts.map +1 -1
  12. package/dist/core/agent-session.js +99 -2
  13. package/dist/core/agent-session.js.map +1 -1
  14. package/dist/core/isolated-session.d.ts +7 -2
  15. package/dist/core/isolated-session.d.ts.map +1 -1
  16. package/dist/core/isolated-session.js +7 -3
  17. package/dist/core/isolated-session.js.map +1 -1
  18. package/dist/core/session-manager.d.ts +17 -1
  19. package/dist/core/session-manager.d.ts.map +1 -1
  20. package/dist/core/session-manager.js +29 -0
  21. package/dist/core/session-manager.js.map +1 -1
  22. package/dist/core/tasks/delivery.d.ts +42 -7
  23. package/dist/core/tasks/delivery.d.ts.map +1 -1
  24. package/dist/core/tasks/delivery.js +129 -15
  25. package/dist/core/tasks/delivery.js.map +1 -1
  26. package/dist/core/tasks/execution.d.ts +27 -14
  27. package/dist/core/tasks/execution.d.ts.map +1 -1
  28. package/dist/core/tasks/execution.js +90 -86
  29. package/dist/core/tasks/execution.js.map +1 -1
  30. package/dist/core/tasks/limits.d.ts +13 -1
  31. package/dist/core/tasks/limits.d.ts.map +1 -1
  32. package/dist/core/tasks/limits.js +14 -1
  33. package/dist/core/tasks/limits.js.map +1 -1
  34. package/dist/core/tasks/output.d.ts +9 -1
  35. package/dist/core/tasks/output.d.ts.map +1 -1
  36. package/dist/core/tasks/output.js +23 -10
  37. package/dist/core/tasks/output.js.map +1 -1
  38. package/dist/core/tasks/presentation.d.ts +1 -1
  39. package/dist/core/tasks/presentation.d.ts.map +1 -1
  40. package/dist/core/tasks/presentation.js +6 -4
  41. package/dist/core/tasks/presentation.js.map +1 -1
  42. package/dist/core/tasks/record.d.ts +41 -29
  43. package/dist/core/tasks/record.d.ts.map +1 -1
  44. package/dist/core/tasks/record.js +82 -37
  45. package/dist/core/tasks/record.js.map +1 -1
  46. package/dist/core/tasks/reminder.js +2 -2
  47. package/dist/core/tasks/reminder.js.map +1 -1
  48. package/dist/core/tasks/runtime.d.ts +17 -14
  49. package/dist/core/tasks/runtime.d.ts.map +1 -1
  50. package/dist/core/tasks/runtime.js +68 -116
  51. package/dist/core/tasks/runtime.js.map +1 -1
  52. package/dist/core/tasks/scheduler.d.ts +3 -1
  53. package/dist/core/tasks/scheduler.d.ts.map +1 -1
  54. package/dist/core/tasks/scheduler.js +16 -12
  55. package/dist/core/tasks/scheduler.js.map +1 -1
  56. package/dist/core/tasks/session.d.ts +24 -3
  57. package/dist/core/tasks/session.d.ts.map +1 -1
  58. package/dist/core/tasks/session.js +73 -9
  59. package/dist/core/tasks/session.js.map +1 -1
  60. package/dist/core/tasks/store.d.ts +17 -4
  61. package/dist/core/tasks/store.d.ts.map +1 -1
  62. package/dist/core/tasks/store.js +45 -48
  63. package/dist/core/tasks/store.js.map +1 -1
  64. package/dist/core/tasks/types.d.ts +72 -29
  65. package/dist/core/tasks/types.d.ts.map +1 -1
  66. package/dist/core/tasks/types.js +7 -0
  67. package/dist/core/tasks/types.js.map +1 -1
  68. package/dist/core/tasks/wait.d.ts +19 -0
  69. package/dist/core/tasks/wait.d.ts.map +1 -0
  70. package/dist/core/tasks/wait.js +45 -0
  71. package/dist/core/tasks/wait.js.map +1 -0
  72. package/dist/core/tools/shell-execution.d.ts.map +1 -1
  73. package/dist/core/tools/shell-execution.js +2 -1
  74. package/dist/core/tools/shell-execution.js.map +1 -1
  75. package/dist/core/tools/shell-tool.d.ts.map +1 -1
  76. package/dist/core/tools/shell-tool.js +20 -13
  77. package/dist/core/tools/shell-tool.js.map +1 -1
  78. package/dist/extensions/agent/activity-lines.d.ts +10 -0
  79. package/dist/extensions/agent/activity-lines.d.ts.map +1 -0
  80. package/dist/extensions/agent/activity-lines.js +76 -0
  81. package/dist/extensions/agent/activity-lines.js.map +1 -0
  82. package/dist/extensions/agent/activity.d.ts +25 -4
  83. package/dist/extensions/agent/activity.d.ts.map +1 -1
  84. package/dist/extensions/agent/activity.js +76 -21
  85. package/dist/extensions/agent/activity.js.map +1 -1
  86. package/dist/extensions/agent/artifacts.d.ts +15 -0
  87. package/dist/extensions/agent/artifacts.d.ts.map +1 -0
  88. package/dist/extensions/agent/artifacts.js +44 -0
  89. package/dist/extensions/agent/artifacts.js.map +1 -0
  90. package/dist/extensions/agent/progress.d.ts +30 -4
  91. package/dist/extensions/agent/progress.d.ts.map +1 -1
  92. package/dist/extensions/agent/progress.js +84 -11
  93. package/dist/extensions/agent/progress.js.map +1 -1
  94. package/dist/extensions/agent/render.d.ts.map +1 -1
  95. package/dist/extensions/agent/render.js +8 -24
  96. package/dist/extensions/agent/render.js.map +1 -1
  97. package/dist/extensions/agent/report.d.ts +11 -3
  98. package/dist/extensions/agent/report.d.ts.map +1 -1
  99. package/dist/extensions/agent/report.js +32 -7
  100. package/dist/extensions/agent/report.js.map +1 -1
  101. package/dist/extensions/agent/tool.d.ts.map +1 -1
  102. package/dist/extensions/agent/tool.js +20 -5
  103. package/dist/extensions/agent/tool.js.map +1 -1
  104. package/dist/extensions/agent/types.d.ts +2 -0
  105. package/dist/extensions/agent/types.d.ts.map +1 -1
  106. package/dist/extensions/agent/types.js.map +1 -1
  107. package/dist/extensions/agent/worker.d.ts +7 -0
  108. package/dist/extensions/agent/worker.d.ts.map +1 -1
  109. package/dist/extensions/agent/worker.js +9 -3
  110. package/dist/extensions/agent/worker.js.map +1 -1
  111. package/dist/extensions/search/index.d.ts.map +1 -1
  112. package/dist/extensions/search/index.js +6 -7
  113. package/dist/extensions/search/index.js.map +1 -1
  114. package/dist/extensions/tasks/tool/actions.d.ts.map +1 -1
  115. package/dist/extensions/tasks/tool/actions.js +15 -11
  116. package/dist/extensions/tasks/tool/actions.js.map +1 -1
  117. package/dist/extensions/tasks/tool/schema.d.ts +1 -1
  118. package/dist/extensions/tasks/tool/schema.d.ts.map +1 -1
  119. package/dist/extensions/tasks/tool/schema.js +5 -5
  120. package/dist/extensions/tasks/tool/schema.js.map +1 -1
  121. package/dist/extensions/tasks/tool/types.d.ts +2 -0
  122. package/dist/extensions/tasks/tool/types.d.ts.map +1 -1
  123. package/dist/extensions/tasks/tool/types.js.map +1 -1
  124. package/dist/extensions/tasks/tool.d.ts.map +1 -1
  125. package/dist/extensions/tasks/tool.js +3 -2
  126. package/dist/extensions/tasks/tool.js.map +1 -1
  127. package/dist/extensions/tasks/ui/completion-render.d.ts.map +1 -1
  128. package/dist/extensions/tasks/ui/completion-render.js +2 -2
  129. package/dist/extensions/tasks/ui/completion-render.js.map +1 -1
  130. package/dist/extensions/tasks/ui/footer.d.ts +37 -0
  131. package/dist/extensions/tasks/ui/footer.d.ts.map +1 -0
  132. package/dist/extensions/tasks/ui/footer.js +129 -0
  133. package/dist/extensions/tasks/ui/footer.js.map +1 -0
  134. package/dist/extensions/tasks/ui/frame.d.ts +36 -0
  135. package/dist/extensions/tasks/ui/frame.d.ts.map +1 -0
  136. package/dist/extensions/tasks/ui/frame.js +33 -0
  137. package/dist/extensions/tasks/ui/frame.js.map +1 -0
  138. package/dist/extensions/tasks/ui/help.d.ts +6 -0
  139. package/dist/extensions/tasks/ui/help.d.ts.map +1 -0
  140. package/dist/extensions/tasks/ui/help.js +53 -0
  141. package/dist/extensions/tasks/ui/help.js.map +1 -0
  142. package/dist/extensions/tasks/ui/inspector.d.ts +4 -3
  143. package/dist/extensions/tasks/ui/inspector.d.ts.map +1 -1
  144. package/dist/extensions/tasks/ui/inspector.js +25 -6
  145. package/dist/extensions/tasks/ui/inspector.js.map +1 -1
  146. package/dist/extensions/tasks/ui/layout.d.ts +15 -18
  147. package/dist/extensions/tasks/ui/layout.d.ts.map +1 -1
  148. package/dist/extensions/tasks/ui/layout.js +162 -255
  149. package/dist/extensions/tasks/ui/layout.js.map +1 -1
  150. package/dist/extensions/tasks/ui/list.d.ts +3 -1
  151. package/dist/extensions/tasks/ui/list.d.ts.map +1 -1
  152. package/dist/extensions/tasks/ui/list.js +19 -14
  153. package/dist/extensions/tasks/ui/list.js.map +1 -1
  154. package/dist/extensions/tasks/ui/manager.d.ts +17 -5
  155. package/dist/extensions/tasks/ui/manager.d.ts.map +1 -1
  156. package/dist/extensions/tasks/ui/manager.js +147 -90
  157. package/dist/extensions/tasks/ui/manager.js.map +1 -1
  158. package/dist/extensions/tasks/ui/model.d.ts +32 -5
  159. package/dist/extensions/tasks/ui/model.d.ts.map +1 -1
  160. package/dist/extensions/tasks/ui/model.js +7 -7
  161. package/dist/extensions/tasks/ui/model.js.map +1 -1
  162. package/dist/extensions/tasks/ui/panes.d.ts +40 -0
  163. package/dist/extensions/tasks/ui/panes.d.ts.map +1 -0
  164. package/dist/extensions/tasks/ui/panes.js +144 -0
  165. package/dist/extensions/tasks/ui/panes.js.map +1 -0
  166. package/dist/extensions/tasks/ui/render.d.ts.map +1 -1
  167. package/dist/extensions/tasks/ui/render.js +11 -7
  168. package/dist/extensions/tasks/ui/render.js.map +1 -1
  169. package/dist/extensions/tasks/ui/viewport.d.ts +12 -4
  170. package/dist/extensions/tasks/ui/viewport.d.ts.map +1 -1
  171. package/dist/extensions/tasks/ui/viewport.js +23 -7
  172. package/dist/extensions/tasks/ui/viewport.js.map +1 -1
  173. package/dist/extensions/tasks/ui/views.d.ts +12 -6
  174. package/dist/extensions/tasks/ui/views.d.ts.map +1 -1
  175. package/dist/extensions/tasks/ui/views.js +39 -32
  176. package/dist/extensions/tasks/ui/views.js.map +1 -1
  177. package/dist/modes/interactive/components/session-selector.d.ts +11 -1
  178. package/dist/modes/interactive/components/session-selector.d.ts.map +1 -1
  179. package/dist/modes/interactive/components/session-selector.js +43 -17
  180. package/dist/modes/interactive/components/session-selector.js.map +1 -1
  181. package/dist/modes/print-mode.d.ts.map +1 -1
  182. package/dist/modes/print-mode.js +37 -2
  183. package/dist/modes/print-mode.js.map +1 -1
  184. package/dist/modes/rpc/rpc-client.d.ts +11 -0
  185. package/dist/modes/rpc/rpc-client.d.ts.map +1 -1
  186. package/dist/modes/rpc/rpc-client.js +48 -0
  187. package/dist/modes/rpc/rpc-client.js.map +1 -1
  188. package/dist/modes/rpc/rpc-mode.d.ts.map +1 -1
  189. package/dist/modes/rpc/rpc-mode.js +1 -0
  190. package/dist/modes/rpc/rpc-mode.js.map +1 -1
  191. package/dist/modes/rpc/rpc-types.d.ts +2 -0
  192. package/dist/modes/rpc/rpc-types.d.ts.map +1 -1
  193. package/dist/modes/rpc/rpc-types.js.map +1 -1
  194. package/docs/bundled/extensions/agent.md +39 -7
  195. package/docs/bundled/tasks.md +25 -21
  196. package/docs/extensions.md +14 -10
  197. package/docs/json.md +3 -0
  198. package/docs/rpc.md +14 -0
  199. package/docs/sdk.md +11 -1
  200. package/docs/session-format.md +10 -4
  201. package/docs/sessions.md +1 -1
  202. package/docs/usage.md +2 -0
  203. package/npm-shrinkwrap.json +2 -2
  204. package/package.json +1 -1
@@ -1 +1 @@
1
- {"version":3,"file":"tool.js","sourceRoot":"","sources":["../../../src/extensions/tasks/tool.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,WAAW,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,mBAAmB,CAAC;AACpF,OAAO,EAAE,uBAAuB,EAAE,oBAAoB,EAAE,sBAAsB,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAEtH,OAAO,EAAE,eAAe,EAAE,iBAAiB,EAAyB,MAAM,gBAAgB,CAAC;AAE3F,MAAM,UAAU,yBAAyB;IACxC,OAAO;QACN,IAAI,EAAE,OAAO;QACb,KAAK,EAAE,OAAO;QACd,WAAW,EAAE,sBAAsB;QACnC,aAAa,EAAE,oBAAoB;QACnC,gBAAgB,EAAE,uBAAuB;QACzC,UAAU,EAAE,WAAW;QACvB,mBAAmB,EAAE,EAAE,IAAI,EAAE,aAAa,EAAE,MAAM,EAAE,QAAQ,EAAE;QAC9D,KAAK,CAAC,OAAO,CAAC,UAAU,EAAE,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,GAAG;YACvD,IAAI,CAAC;gBACJ,QAAQ,MAAM,CAAC,MAAM,EAAE,CAAC;oBACvB,KAAK,MAAM;wBACV,OAAO,MAAM,OAAO,CAAC,GAAG,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC;oBACzC,KAAK,MAAM;wBACV,OAAO,MAAM,OAAO,CAAC,GAAG,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,UAAU,CAAC,CAAC;oBAC7D,KAAK,MAAM;wBACV,OAAO,OAAO,CAAC,GAAG,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC;oBACnC,KAAK,MAAM;wBACV,OAAO,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;gBAC5B,CAAC;YACF,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;gBAChB,uEAAuE;gBACvE,MAAM,OAAO,GAAG,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;gBACvE,MAAM,IAAI,KAAK,CAAC,WAAW,CAAC,OAAO,CAAC,CAAC,CAAC;YACvC,CAAC;QACF,CAAC;QACD,UAAU,EAAE,eAAe;QAC3B,YAAY,EAAE,iBAAiB;KAC/B,CAAC;AACH,CAAC","sourcesContent":["import type { ToolDefinition } from \"../../core/extensions/types.ts\";\nimport { boundedText, runKill, runList, runRead, runWait } from \"./tool/actions.ts\";\nimport { TASKS_PROMPT_GUIDELINES, TASKS_PROMPT_SNIPPET, TASKS_TOOL_DESCRIPTION, tasksSchema } from \"./tool/schema.ts\";\nimport type { TasksDetails } from \"./tool/types.ts\";\nimport { renderTasksCall, renderTasksResult, type TasksRenderState } from \"./ui/render.ts\";\n\nexport function createTasksToolDefinition(): ToolDefinition<typeof tasksSchema, TasksDetails, TasksRenderState> {\n\treturn {\n\t\tname: \"tasks\",\n\t\tlabel: \"tasks\",\n\t\tdescription: TASKS_TOOL_DESCRIPTION,\n\t\tpromptSnippet: TASKS_PROMPT_SNIPPET,\n\t\tpromptGuidelines: TASKS_PROMPT_GUIDELINES,\n\t\tparameters: tasksSchema,\n\t\tconstrainedSampling: { type: \"json_schema\", strict: \"prefer\" },\n\t\tasync execute(toolCallId, params, signal, _onUpdate, ctx) {\n\t\t\ttry {\n\t\t\t\tswitch (params.action) {\n\t\t\t\t\tcase \"read\":\n\t\t\t\t\t\treturn await runRead(ctx.tasks, params);\n\t\t\t\t\tcase \"wait\":\n\t\t\t\t\t\treturn await runWait(ctx.tasks, params, signal, toolCallId);\n\t\t\t\t\tcase \"kill\":\n\t\t\t\t\t\treturn runKill(ctx.tasks, params);\n\t\t\t\t\tcase \"list\":\n\t\t\t\t\t\treturn runList(ctx.tasks);\n\t\t\t\t}\n\t\t\t} catch (error) {\n\t\t\t\t// Lookup failures already carry the current task list from actions.ts.\n\t\t\t\tconst message = error instanceof Error ? error.message : String(error);\n\t\t\t\tthrow new Error(boundedText(message));\n\t\t\t}\n\t\t},\n\t\trenderCall: renderTasksCall,\n\t\trenderResult: renderTasksResult,\n\t};\n}\n"]}
1
+ {"version":3,"file":"tool.js","sourceRoot":"","sources":["../../../src/extensions/tasks/tool.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,eAAe,EAAE,MAAM,2BAA2B,CAAC;AAC5D,OAAO,EAAE,WAAW,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,mBAAmB,CAAC;AACpF,OAAO,EAAE,uBAAuB,EAAE,oBAAoB,EAAE,sBAAsB,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAEtH,OAAO,EAAE,eAAe,EAAE,iBAAiB,EAAyB,MAAM,gBAAgB,CAAC;AAE3F,MAAM,UAAU,yBAAyB;IACxC,OAAO;QACN,IAAI,EAAE,eAAe;QACrB,KAAK,EAAE,eAAe;QACtB,WAAW,EAAE,sBAAsB;QACnC,aAAa,EAAE,oBAAoB;QACnC,gBAAgB,EAAE,uBAAuB;QACzC,UAAU,EAAE,WAAW;QACvB,mBAAmB,EAAE,EAAE,IAAI,EAAE,aAAa,EAAE,MAAM,EAAE,QAAQ,EAAE;QAC9D,KAAK,CAAC,OAAO,CAAC,UAAU,EAAE,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,GAAG;YACvD,IAAI,CAAC;gBACJ,QAAQ,MAAM,CAAC,MAAM,EAAE,CAAC;oBACvB,KAAK,MAAM;wBACV,OAAO,MAAM,OAAO,CAAC,GAAG,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC;oBACzC,KAAK,MAAM;wBACV,OAAO,MAAM,OAAO,CAAC,GAAG,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,UAAU,CAAC,CAAC;oBAC7D,KAAK,MAAM;wBACV,OAAO,OAAO,CAAC,GAAG,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC;oBACnC,KAAK,MAAM;wBACV,OAAO,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;gBAC5B,CAAC;YACF,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;gBAChB,uEAAuE;gBACvE,MAAM,OAAO,GAAG,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;gBACvE,MAAM,IAAI,KAAK,CAAC,WAAW,CAAC,OAAO,CAAC,CAAC,CAAC;YACvC,CAAC;QACF,CAAC;QACD,UAAU,EAAE,eAAe;QAC3B,YAAY,EAAE,iBAAiB;KAC/B,CAAC;AACH,CAAC","sourcesContent":["import type { ToolDefinition } from \"../../core/extensions/types.ts\";\nimport { TASKS_TOOL_NAME } from \"../../core/tasks/types.ts\";\nimport { boundedText, runKill, runList, runRead, runWait } from \"./tool/actions.ts\";\nimport { TASKS_PROMPT_GUIDELINES, TASKS_PROMPT_SNIPPET, TASKS_TOOL_DESCRIPTION, tasksSchema } from \"./tool/schema.ts\";\nimport type { TasksDetails } from \"./tool/types.ts\";\nimport { renderTasksCall, renderTasksResult, type TasksRenderState } from \"./ui/render.ts\";\n\nexport function createTasksToolDefinition(): ToolDefinition<typeof tasksSchema, TasksDetails, TasksRenderState> {\n\treturn {\n\t\tname: TASKS_TOOL_NAME,\n\t\tlabel: TASKS_TOOL_NAME,\n\t\tdescription: TASKS_TOOL_DESCRIPTION,\n\t\tpromptSnippet: TASKS_PROMPT_SNIPPET,\n\t\tpromptGuidelines: TASKS_PROMPT_GUIDELINES,\n\t\tparameters: tasksSchema,\n\t\tconstrainedSampling: { type: \"json_schema\", strict: \"prefer\" },\n\t\tasync execute(toolCallId, params, signal, _onUpdate, ctx) {\n\t\t\ttry {\n\t\t\t\tswitch (params.action) {\n\t\t\t\t\tcase \"read\":\n\t\t\t\t\t\treturn await runRead(ctx.tasks, params);\n\t\t\t\t\tcase \"wait\":\n\t\t\t\t\t\treturn await runWait(ctx.tasks, params, signal, toolCallId);\n\t\t\t\t\tcase \"kill\":\n\t\t\t\t\t\treturn runKill(ctx.tasks, params);\n\t\t\t\t\tcase \"list\":\n\t\t\t\t\t\treturn runList(ctx.tasks);\n\t\t\t\t}\n\t\t\t} catch (error) {\n\t\t\t\t// Lookup failures already carry the current task list from actions.ts.\n\t\t\t\tconst message = error instanceof Error ? error.message : String(error);\n\t\t\t\tthrow new Error(boundedText(message));\n\t\t\t}\n\t\t},\n\t\trenderCall: renderTasksCall,\n\t\trenderResult: renderTasksResult,\n\t};\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"completion-render.d.ts","sourceRoot":"","sources":["../../../../src/extensions/tasks/ui/completion-render.ts"],"names":[],"mappings":"AAAA,sFAAsF;AACtF,OAAO,EAAE,KAAK,SAAS,EAAqC,MAAM,wBAAwB,CAAC;AAC3F,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,mCAAmC,CAAC;AAC9E,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,2BAA2B,CAAC;AAI/D,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,2CAA2C,CAAC;AAoLvE,gGAAgG;AAChG,wBAAgB,oBAAoB,CACnC,OAAO,EAAE,aAAa,CAAC,OAAO,CAAC,EAC/B,OAAO,EAAE,oBAAoB,EAC7B,KAAK,EAAE,KAAK,GACV,SAAS,CAYX"}
1
+ {"version":3,"file":"completion-render.d.ts","sourceRoot":"","sources":["../../../../src/extensions/tasks/ui/completion-render.ts"],"names":[],"mappings":"AAAA,sFAAsF;AACtF,OAAO,EAAE,KAAK,SAAS,EAAqC,MAAM,wBAAwB,CAAC;AAC3F,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,mCAAmC,CAAC;AAC9E,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,2BAA2B,CAAC;AAI/D,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,2CAA2C,CAAC;AAmLvE,gGAAgG;AAChG,wBAAgB,oBAAoB,CACnC,OAAO,EAAE,aAAa,CAAC,OAAO,CAAC,EAC/B,OAAO,EAAE,oBAAoB,EAC7B,KAAK,EAAE,KAAK,GACV,SAAS,CAYX"}
@@ -2,6 +2,7 @@
2
2
  import { truncateToWidth, wrapTextWithAnsi } from "@earendil-works/pi-tui";
3
3
  import { readVersionedTask } from "../../../core/tasks/codec.js";
4
4
  import { runtimeLabel } from "../../../core/tasks/format.js";
5
+ import { TASKS_TOOL_NAME } from "../../../core/tasks/types.js";
5
6
  import { FramedComponent } from "../../../modes/interactive/tool-view/style.js";
6
7
  import { statusName, statusView } from "./status.js";
7
8
  import { cleanTaskText } from "./text.js";
@@ -141,12 +142,11 @@ class CompletionCard {
141
142
  if (id) {
142
143
  lines.push("", ...wrapTextWithAnsi(`Task ID: ${id}`, inner).map((line) => theme.fg("dim", line)));
143
144
  }
144
- lines.push(theme.fg("dim", "Preview of the saved result."));
145
145
  }
146
146
  const bounded = lines.length > CARD_ROWS
147
147
  ? [
148
148
  ...lines.slice(0, CARD_ROWS - 1),
149
- theme.fg("dim", "… card shortened; use tasks read for retained output"),
149
+ theme.fg("dim", `… card shortened; use ${TASKS_TOOL_NAME} read for retained output`),
150
150
  ]
151
151
  : lines;
152
152
  return bounded.map((line) => " ".repeat(padding) + truncateToWidth(line, inner, "…") + " ".repeat(padding));
@@ -1 +1 @@
1
- {"version":3,"file":"completion-render.js","sourceRoot":"","sources":["../../../../src/extensions/tasks/ui/completion-render.ts"],"names":[],"mappings":"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","sourcesContent":["/** Human-facing completion cards. The persisted context message remains untouched. */\nimport { type Component, truncateToWidth, wrapTextWithAnsi } from \"@earendil-works/pi-tui\";\nimport type { MessageRenderOptions } from \"../../../core/extensions/types.ts\";\nimport type { CustomMessage } from \"../../../core/messages.ts\";\nimport { readVersionedTask } from \"../../../core/tasks/codec.ts\";\nimport { runtimeLabel } from \"../../../core/tasks/format.ts\";\nimport type { TaskFact, TaskStatus } from \"../../../core/tasks/types.ts\";\nimport type { Theme } from \"../../../modes/interactive/theme/theme.ts\";\nimport { FramedComponent } from \"../../../modes/interactive/tool-view/style.ts\";\nimport { statusName, statusView } from \"./status.ts\";\nimport { cleanTaskText } from \"./text.ts\";\n\nconst SOURCE_LIMIT = 64 * 1024;\nconst CARD_ROWS = 128;\nconst OUTPUT_ROWS = 20;\n/** A persisted task completion, or the saved text of a message that is unreadable or from an older format. */\ntype CompletionView =\n\t| {\n\t\t\tkind: \"task\";\n\t\t\tlabel: string;\n\t\t\ttitle: string;\n\t\t\tduration: string;\n\t\t\tstatus: TaskStatus;\n\t\t\tid: string;\n\t\t\tinput?: { label: string; text: string };\n\t\t\tfacts: TaskFact[];\n\t\t\tpath?: string;\n\t\t\tdiagnostic?: string;\n\t\t\toutput: string;\n\t\t\t/** A report previews its beginning; a log previews its end. */\n\t\t\treport: boolean;\n\t\t\ttruncated: boolean;\n\t }\n\t| { kind: \"saved\"; body: string; truncated: boolean };\n\nfunction savedText(message: CustomMessage<unknown>): { text: string; clipped: boolean } {\n\t// 64K is a UTF-16 code-unit display budget, not a UTF-8 byte promise; the block\n\t// cap bounds iteration itself so many empty/non-text blocks cannot spin here.\n\tconst MAX_SOURCE_BLOCKS = 256;\n\tconst content = message.content;\n\tlet text = \"\";\n\tlet clipped = false;\n\tif (typeof content === \"string\") {\n\t\ttext = content.slice(0, SOURCE_LIMIT);\n\t\tclipped = content.length > SOURCE_LIMIT;\n\t} else if (Array.isArray(content)) {\n\t\tlet visited = 0;\n\t\tfor (const block of content) {\n\t\t\tif (++visited > MAX_SOURCE_BLOCKS) {\n\t\t\t\tclipped = true;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tif (!block || block.type !== \"text\" || typeof block.text !== \"string\") continue;\n\t\t\tconst next = `${text ? \"\\n\" : \"\"}${block.text.slice(0, SOURCE_LIMIT + 1)}`;\n\t\t\tconst remaining = SOURCE_LIMIT - text.length;\n\t\t\ttext += next.slice(0, remaining);\n\t\t\tif (next.length > remaining || block.text.length > SOURCE_LIMIT) {\n\t\t\t\tclipped = true;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t}\n\treturn { text: cleanTaskText(text), clipped };\n}\n\nfunction completionView(message: CustomMessage<unknown>): CompletionView {\n\tconst task = readVersionedTask(message.details);\n\tif (!task) {\n\t\tconst { text, clipped } = savedText(message);\n\t\treturn { kind: \"saved\", body: text, truncated: clipped };\n\t}\n\treturn {\n\t\tkind: \"task\",\n\t\tlabel: cleanTaskText(task.label),\n\t\ttitle: cleanTaskText(task.title),\n\t\tduration: runtimeLabel(task),\n\t\tstatus: task.status,\n\t\tid: cleanTaskText(task.id),\n\t\tinput: task.input && { label: cleanTaskText(task.input.label), text: cleanTaskText(task.input.text) },\n\t\tfacts: (task.facts ?? []).map(([label, value]) => [cleanTaskText(label), cleanTaskText(value)]),\n\t\tpath: task.outputPath === undefined ? undefined : cleanTaskText(task.outputPath),\n\t\tdiagnostic: task.error === undefined ? undefined : cleanTaskText(task.error),\n\t\toutput: cleanTaskText(task.result?.text ?? \"\"),\n\t\treport: task.output === \"report\",\n\t\ttruncated: task.result?.truncated === true || task.input?.truncated === true,\n\t};\n}\n\nfunction shortId(id: string): string {\n\treturn id.replace(/^(.+)-([0-9a-f]{8})-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i, \"$1-$2\");\n}\n\nclass CompletionCard implements Component {\n\tprivate readonly view: CompletionView;\n\tprivate readonly options: MessageRenderOptions;\n\tprivate readonly theme: Theme;\n\tconstructor(view: CompletionView, options: MessageRenderOptions, theme: Theme) {\n\t\tthis.view = view;\n\t\tthis.options = options;\n\t\tthis.theme = theme;\n\t}\n\tinvalidate(): void {}\n\trender(width: number): string[] {\n\t\tif (width < 1) return [];\n\t\tconst chrome = new FramedComponent(\n\t\t\t{ render: (contentWidth) => this.renderContent(contentWidth), invalidate() {} },\n\t\t\t\"header\",\n\t\t\t() => (this.view.kind === \"task\" ? statusView(this.view.status).color : \"muted\"),\n\t\t);\n\t\t// The gutter reserves its own cells; clip defensively for one-cell terminals.\n\t\treturn chrome.render(width).map((line) => truncateToWidth(line, width, \"\"));\n\t}\n\tprivate renderContent(width: number): string[] {\n\t\t// The chrome clamps to ≥1 already; keep the padding arithmetic safe regardless.\n\t\tif (!Number.isFinite(width) || width < 1) return [];\n\t\tconst view = this.view;\n\t\tconst theme = this.theme;\n\t\tconst options = this.options;\n\t\t// The native dot/rail replaces the default one-cell custom-message inset.\n\t\t// Additional configured padding stays inside the chrome, never shifting the dot.\n\t\tconst padding = Math.min(Math.max(0, Math.floor(options.outputPad || 0) - 1), Math.floor((width - 1) / 2));\n\t\tconst inner = width - padding * 2;\n\t\tconst indent = inner >= 3 ? \" \" : \"\";\n\t\tconst bodyWidth = Math.max(1, inner - indent.length);\n\t\tconst lines: string[] = [];\n\t\tconst status = view.kind === \"task\" ? statusName(view.status) : \"Result received\";\n\t\tconst label = view.kind === \"task\" ? view.label : \"Notification\";\n\t\tconst duration = view.kind === \"task\" && view.duration ? ` · ${view.duration}` : \"\";\n\t\tconst id = view.kind === \"task\" ? view.id : \"\";\n\t\tlines.push(\n\t\t\t`${theme.fg(\"toolTitle\", theme.bold(label))}${theme.fg(\"muted\", ` · Background ${status.toLowerCase()}${duration}`)}${id ? theme.fg(\"dim\", ` · ${shortId(id)}`) : \"\"}`,\n\t\t);\n\t\tif (!options.expanded) {\n\t\t\tif (view.kind === \"task\") {\n\t\t\t\tconst summary = view.title.split(\"\\n\").find((line) => line.trim()) ?? view.title;\n\t\t\t\tif (summary) lines.push(theme.fg(\"toolOutput\", summary));\n\t\t\t\tif (view.diagnostic) lines.push(theme.fg(statusView(view.status).color, view.diagnostic));\n\t\t\t}\n\t\t} else {\n\t\t\tif (view.truncated) {\n\t\t\t\tlines.push(theme.fg(\"warning\", \"The saved result is truncated; this is not the complete original output.\"));\n\t\t\t}\n\t\t\tconst section = (title: string, text: string, limit: number, tail = false, error = false) => {\n\t\t\t\tlines.push(\"\", theme.fg(\"muted\", theme.bold(title)));\n\t\t\t\tconst rendered = wrapTextWithAnsi(text, bodyWidth).map((line) =>\n\t\t\t\t\ttheme.fg(error ? \"error\" : \"toolOutput\", line),\n\t\t\t\t);\n\t\t\t\tconst omitted = Math.max(0, rendered.length - limit);\n\t\t\t\tconst selected = tail ? rendered.slice(-limit) : rendered.slice(0, limit);\n\t\t\t\tif (tail && omitted) lines.push(theme.fg(\"dim\", `${indent}… ${omitted} earlier display lines omitted`));\n\t\t\t\tfor (const line of selected) lines.push(indent + line);\n\t\t\t\tif (!tail && omitted) lines.push(theme.fg(\"dim\", `${indent}… ${omitted} more display lines omitted`));\n\t\t\t};\n\t\t\tif (view.kind === \"saved\") section(\"Details\", view.body || \"No text result.\", 36);\n\t\t\telse {\n\t\t\t\tif (view.input) section(view.input.label, view.input.text, 8);\n\t\t\t\telse if (view.title) section(\"Task\", view.title, 4);\n\t\t\t\tif (view.facts.length) {\n\t\t\t\t\tconst width = Math.max(...view.facts.map(([label]) => label.length)) + 1;\n\t\t\t\t\tsection(\"Details\", view.facts.map(([label, value]) => `${label.padEnd(width)}${value}`).join(\"\\n\"), 8);\n\t\t\t\t}\n\t\t\t\tsection(\n\t\t\t\t\tview.status === \"failed\" || view.status === \"timeout\" ? \"Error\" : \"Result\",\n\t\t\t\t\tview.diagnostic ?? status,\n\t\t\t\t\t4,\n\t\t\t\t\tfalse,\n\t\t\t\t\tview.status === \"failed\",\n\t\t\t\t);\n\t\t\t\tsection(\"Output\", !view.output.trim() ? \"No output.\" : view.output, OUTPUT_ROWS, !view.report);\n\t\t\t\tif (view.path) section(\"Log\", view.path, 4);\n\t\t\t}\n\t\t\tif (id) {\n\t\t\t\tlines.push(\"\", ...wrapTextWithAnsi(`Task ID: ${id}`, inner).map((line) => theme.fg(\"dim\", line)));\n\t\t\t}\n\t\t\tlines.push(theme.fg(\"dim\", \"Preview of the saved result.\"));\n\t\t}\n\t\tconst bounded =\n\t\t\tlines.length > CARD_ROWS\n\t\t\t\t? [\n\t\t\t\t\t\t...lines.slice(0, CARD_ROWS - 1),\n\t\t\t\t\t\ttheme.fg(\"dim\", \"… card shortened; use tasks read for retained output\"),\n\t\t\t\t\t]\n\t\t\t\t: lines;\n\t\treturn bounded.map((line) => \" \".repeat(padding) + truncateToWidth(line, inner, \"…\") + \" \".repeat(padding));\n\t}\n}\n\n/** Always return a compact, replay-safe component, including for unknown historical formats. */\nexport function renderTaskCompletion(\n\tmessage: CustomMessage<unknown>,\n\toptions: MessageRenderOptions,\n\ttheme: Theme,\n): Component {\n\tlet view: CompletionView;\n\ttry {\n\t\tview = completionView(message);\n\t} catch {\n\t\tview = {\n\t\t\tkind: \"saved\",\n\t\t\tbody: \"The saved notification could not be interpreted. Its original message remains in session history.\",\n\t\t\ttruncated: false,\n\t\t};\n\t}\n\treturn new CompletionCard(view, options, theme);\n}\n"]}
1
+ {"version":3,"file":"completion-render.js","sourceRoot":"","sources":["../../../../src/extensions/tasks/ui/completion-render.ts"],"names":[],"mappings":"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","sourcesContent":["/** Human-facing completion cards. The persisted context message remains untouched. */\nimport { type Component, truncateToWidth, wrapTextWithAnsi } from \"@earendil-works/pi-tui\";\nimport type { MessageRenderOptions } from \"../../../core/extensions/types.ts\";\nimport type { CustomMessage } from \"../../../core/messages.ts\";\nimport { readVersionedTask } from \"../../../core/tasks/codec.ts\";\nimport { runtimeLabel } from \"../../../core/tasks/format.ts\";\nimport { TASKS_TOOL_NAME, type TaskFact, type TaskStatus } from \"../../../core/tasks/types.ts\";\nimport type { Theme } from \"../../../modes/interactive/theme/theme.ts\";\nimport { FramedComponent } from \"../../../modes/interactive/tool-view/style.ts\";\nimport { statusName, statusView } from \"./status.ts\";\nimport { cleanTaskText } from \"./text.ts\";\n\nconst SOURCE_LIMIT = 64 * 1024;\nconst CARD_ROWS = 128;\nconst OUTPUT_ROWS = 20;\n/** A persisted task completion, or the saved text of a message that is unreadable or from an older format. */\ntype CompletionView =\n\t| {\n\t\t\tkind: \"task\";\n\t\t\tlabel: string;\n\t\t\ttitle: string;\n\t\t\tduration: string;\n\t\t\tstatus: TaskStatus;\n\t\t\tid: string;\n\t\t\tinput?: { label: string; text: string };\n\t\t\tfacts: TaskFact[];\n\t\t\tpath?: string;\n\t\t\tdiagnostic?: string;\n\t\t\toutput: string;\n\t\t\t/** A report previews its beginning; a log previews its end. */\n\t\t\treport: boolean;\n\t\t\ttruncated: boolean;\n\t }\n\t| { kind: \"saved\"; body: string; truncated: boolean };\n\nfunction savedText(message: CustomMessage<unknown>): { text: string; clipped: boolean } {\n\t// 64K is a UTF-16 code-unit display budget, not a UTF-8 byte promise; the block\n\t// cap bounds iteration itself so many empty/non-text blocks cannot spin here.\n\tconst MAX_SOURCE_BLOCKS = 256;\n\tconst content = message.content;\n\tlet text = \"\";\n\tlet clipped = false;\n\tif (typeof content === \"string\") {\n\t\ttext = content.slice(0, SOURCE_LIMIT);\n\t\tclipped = content.length > SOURCE_LIMIT;\n\t} else if (Array.isArray(content)) {\n\t\tlet visited = 0;\n\t\tfor (const block of content) {\n\t\t\tif (++visited > MAX_SOURCE_BLOCKS) {\n\t\t\t\tclipped = true;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tif (!block || block.type !== \"text\" || typeof block.text !== \"string\") continue;\n\t\t\tconst next = `${text ? \"\\n\" : \"\"}${block.text.slice(0, SOURCE_LIMIT + 1)}`;\n\t\t\tconst remaining = SOURCE_LIMIT - text.length;\n\t\t\ttext += next.slice(0, remaining);\n\t\t\tif (next.length > remaining || block.text.length > SOURCE_LIMIT) {\n\t\t\t\tclipped = true;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t}\n\treturn { text: cleanTaskText(text), clipped };\n}\n\nfunction completionView(message: CustomMessage<unknown>): CompletionView {\n\tconst task = readVersionedTask(message.details);\n\tif (!task) {\n\t\tconst { text, clipped } = savedText(message);\n\t\treturn { kind: \"saved\", body: text, truncated: clipped };\n\t}\n\treturn {\n\t\tkind: \"task\",\n\t\tlabel: cleanTaskText(task.label),\n\t\ttitle: cleanTaskText(task.title),\n\t\tduration: runtimeLabel(task),\n\t\tstatus: task.status,\n\t\tid: cleanTaskText(task.id),\n\t\tinput: task.input && { label: cleanTaskText(task.input.label), text: cleanTaskText(task.input.text) },\n\t\tfacts: (task.facts ?? []).map(([label, value]) => [cleanTaskText(label), cleanTaskText(value)]),\n\t\tpath: task.outputPath === undefined ? undefined : cleanTaskText(task.outputPath),\n\t\tdiagnostic: task.error === undefined ? undefined : cleanTaskText(task.error),\n\t\toutput: cleanTaskText(task.result?.text ?? \"\"),\n\t\treport: task.output === \"report\",\n\t\ttruncated: task.result?.truncated === true || task.input?.truncated === true,\n\t};\n}\n\nfunction shortId(id: string): string {\n\treturn id.replace(/^(.+)-([0-9a-f]{8})-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i, \"$1-$2\");\n}\n\nclass CompletionCard implements Component {\n\tprivate readonly view: CompletionView;\n\tprivate readonly options: MessageRenderOptions;\n\tprivate readonly theme: Theme;\n\tconstructor(view: CompletionView, options: MessageRenderOptions, theme: Theme) {\n\t\tthis.view = view;\n\t\tthis.options = options;\n\t\tthis.theme = theme;\n\t}\n\tinvalidate(): void {}\n\trender(width: number): string[] {\n\t\tif (width < 1) return [];\n\t\tconst chrome = new FramedComponent(\n\t\t\t{ render: (contentWidth) => this.renderContent(contentWidth), invalidate() {} },\n\t\t\t\"header\",\n\t\t\t() => (this.view.kind === \"task\" ? statusView(this.view.status).color : \"muted\"),\n\t\t);\n\t\t// The gutter reserves its own cells; clip defensively for one-cell terminals.\n\t\treturn chrome.render(width).map((line) => truncateToWidth(line, width, \"\"));\n\t}\n\tprivate renderContent(width: number): string[] {\n\t\t// The chrome clamps to ≥1 already; keep the padding arithmetic safe regardless.\n\t\tif (!Number.isFinite(width) || width < 1) return [];\n\t\tconst view = this.view;\n\t\tconst theme = this.theme;\n\t\tconst options = this.options;\n\t\t// The native dot/rail replaces the default one-cell custom-message inset.\n\t\t// Additional configured padding stays inside the chrome, never shifting the dot.\n\t\tconst padding = Math.min(Math.max(0, Math.floor(options.outputPad || 0) - 1), Math.floor((width - 1) / 2));\n\t\tconst inner = width - padding * 2;\n\t\tconst indent = inner >= 3 ? \" \" : \"\";\n\t\tconst bodyWidth = Math.max(1, inner - indent.length);\n\t\tconst lines: string[] = [];\n\t\tconst status = view.kind === \"task\" ? statusName(view.status) : \"Result received\";\n\t\tconst label = view.kind === \"task\" ? view.label : \"Notification\";\n\t\tconst duration = view.kind === \"task\" && view.duration ? ` · ${view.duration}` : \"\";\n\t\tconst id = view.kind === \"task\" ? view.id : \"\";\n\t\tlines.push(\n\t\t\t`${theme.fg(\"toolTitle\", theme.bold(label))}${theme.fg(\"muted\", ` · Background ${status.toLowerCase()}${duration}`)}${id ? theme.fg(\"dim\", ` · ${shortId(id)}`) : \"\"}`,\n\t\t);\n\t\tif (!options.expanded) {\n\t\t\tif (view.kind === \"task\") {\n\t\t\t\tconst summary = view.title.split(\"\\n\").find((line) => line.trim()) ?? view.title;\n\t\t\t\tif (summary) lines.push(theme.fg(\"toolOutput\", summary));\n\t\t\t\tif (view.diagnostic) lines.push(theme.fg(statusView(view.status).color, view.diagnostic));\n\t\t\t}\n\t\t} else {\n\t\t\tif (view.truncated) {\n\t\t\t\tlines.push(theme.fg(\"warning\", \"The saved result is truncated; this is not the complete original output.\"));\n\t\t\t}\n\t\t\tconst section = (title: string, text: string, limit: number, tail = false, error = false) => {\n\t\t\t\tlines.push(\"\", theme.fg(\"muted\", theme.bold(title)));\n\t\t\t\tconst rendered = wrapTextWithAnsi(text, bodyWidth).map((line) =>\n\t\t\t\t\ttheme.fg(error ? \"error\" : \"toolOutput\", line),\n\t\t\t\t);\n\t\t\t\tconst omitted = Math.max(0, rendered.length - limit);\n\t\t\t\tconst selected = tail ? rendered.slice(-limit) : rendered.slice(0, limit);\n\t\t\t\tif (tail && omitted) lines.push(theme.fg(\"dim\", `${indent}… ${omitted} earlier display lines omitted`));\n\t\t\t\tfor (const line of selected) lines.push(indent + line);\n\t\t\t\tif (!tail && omitted) lines.push(theme.fg(\"dim\", `${indent}… ${omitted} more display lines omitted`));\n\t\t\t};\n\t\t\tif (view.kind === \"saved\") section(\"Details\", view.body || \"No text result.\", 36);\n\t\t\telse {\n\t\t\t\tif (view.input) section(view.input.label, view.input.text, 8);\n\t\t\t\telse if (view.title) section(\"Task\", view.title, 4);\n\t\t\t\tif (view.facts.length) {\n\t\t\t\t\tconst width = Math.max(...view.facts.map(([label]) => label.length)) + 1;\n\t\t\t\t\tsection(\"Details\", view.facts.map(([label, value]) => `${label.padEnd(width)}${value}`).join(\"\\n\"), 8);\n\t\t\t\t}\n\t\t\t\tsection(\n\t\t\t\t\tview.status === \"failed\" || view.status === \"timeout\" ? \"Error\" : \"Result\",\n\t\t\t\t\tview.diagnostic ?? status,\n\t\t\t\t\t4,\n\t\t\t\t\tfalse,\n\t\t\t\t\tview.status === \"failed\",\n\t\t\t\t);\n\t\t\t\tsection(\"Output\", !view.output.trim() ? \"No output.\" : view.output, OUTPUT_ROWS, !view.report);\n\t\t\t\tif (view.path) section(\"Log\", view.path, 4);\n\t\t\t}\n\t\t\tif (id) {\n\t\t\t\tlines.push(\"\", ...wrapTextWithAnsi(`Task ID: ${id}`, inner).map((line) => theme.fg(\"dim\", line)));\n\t\t\t}\n\t\t}\n\t\tconst bounded =\n\t\t\tlines.length > CARD_ROWS\n\t\t\t\t? [\n\t\t\t\t\t\t...lines.slice(0, CARD_ROWS - 1),\n\t\t\t\t\t\ttheme.fg(\"dim\", `… card shortened; use ${TASKS_TOOL_NAME} read for retained output`),\n\t\t\t\t\t]\n\t\t\t\t: lines;\n\t\treturn bounded.map((line) => \" \".repeat(padding) + truncateToWidth(line, inner, \"…\") + \" \".repeat(padding));\n\t}\n}\n\n/** Always return a compact, replay-safe component, including for unknown historical formats. */\nexport function renderTaskCompletion(\n\tmessage: CustomMessage<unknown>,\n\toptions: MessageRenderOptions,\n\ttheme: Theme,\n): Component {\n\tlet view: CompletionView;\n\ttry {\n\t\tview = completionView(message);\n\t} catch {\n\t\tview = {\n\t\t\tkind: \"saved\",\n\t\t\tbody: \"The saved notification could not be interpreted. Its original message remains in session history.\",\n\t\t\ttruncated: false,\n\t\t};\n\t}\n\treturn new CompletionCard(view, options, theme);\n}\n"]}
@@ -0,0 +1,37 @@
1
+ import type { Keybinding } from "../../../core/keybindings.ts";
2
+ import type { Theme } from "../../../modes/interactive/theme/theme.ts";
3
+ import type { PanelFocus, TaskHit } from "./model.ts";
4
+ /** An outcome the panel does not already show: the action did not do what was asked. */
5
+ export interface Feedback {
6
+ tone: "warning" | "error";
7
+ text: string;
8
+ }
9
+ export interface FooterEnv {
10
+ width: number;
11
+ /** The footer's screen row. */
12
+ y: number;
13
+ theme: Theme;
14
+ hint: (key: Keybinding) => string;
15
+ feedback?: Feedback;
16
+ }
17
+ export interface Footer {
18
+ line: string;
19
+ hits: TaskHit[];
20
+ }
21
+ export declare function browseFooter(env: FooterEnv, view: {
22
+ focus: PanelFocus;
23
+ split: boolean;
24
+ stoppable: boolean;
25
+ detachable: boolean;
26
+ }): Footer;
27
+ export declare function searchFooter(env: FooterEnv): Footer;
28
+ export declare function helpFooter(env: FooterEnv): Footer;
29
+ /**
30
+ * The stop question borrows only the footer row, so the task stays in view while confirming;
31
+ * `source` and `title` name the target, and the title gives way first on narrow widths.
32
+ */
33
+ export declare function confirmStopFooter(env: FooterEnv, target: {
34
+ source: string;
35
+ title: string;
36
+ }): Footer;
37
+ //# sourceMappingURL=footer.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"footer.d.ts","sourceRoot":"","sources":["../../../../src/extensions/tasks/ui/footer.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,8BAA8B,CAAC;AAC/D,OAAO,KAAK,EAAE,KAAK,EAAc,MAAM,2CAA2C,CAAC;AACnF,OAAO,KAAK,EAAE,UAAU,EAAc,OAAO,EAAE,MAAM,YAAY,CAAC;AAElE,wFAAwF;AACxF,MAAM,WAAW,QAAQ;IACxB,IAAI,EAAE,SAAS,GAAG,OAAO,CAAC;IAC1B,IAAI,EAAE,MAAM,CAAC;CACb;AAqBD,MAAM,WAAW,SAAS;IACzB,KAAK,EAAE,MAAM,CAAC;IACd,+BAA+B;IAC/B,CAAC,EAAE,MAAM,CAAC;IACV,KAAK,EAAE,KAAK,CAAC;IACb,IAAI,EAAE,CAAC,GAAG,EAAE,UAAU,KAAK,MAAM,CAAC;IAClC,QAAQ,CAAC,EAAE,QAAQ,CAAC;CACpB;AAED,MAAM,WAAW,MAAM;IACtB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,OAAO,EAAE,CAAC;CAChB;AA4CD,wBAAgB,YAAY,CAC3B,GAAG,EAAE,SAAS,EACd,IAAI,EAAE;IAAE,KAAK,EAAE,UAAU,CAAC;IAAC,KAAK,EAAE,OAAO,CAAC;IAAC,SAAS,EAAE,OAAO,CAAC;IAAC,UAAU,EAAE,OAAO,CAAA;CAAE,GAClF,MAAM,CA4BR;AAED,wBAAgB,YAAY,CAAC,GAAG,EAAE,SAAS,GAAG,MAAM,CAYnD;AAED,wBAAgB,UAAU,CAAC,GAAG,EAAE,SAAS,GAAG,MAAM,CASjD;AAED;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,SAAS,EAAE,MAAM,EAAE;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,GAAG,MAAM,CAkBnG"}
@@ -0,0 +1,129 @@
1
+ /**
2
+ * The footer row, one builder per panel mode. Context hints sit on the left, the Escape action is
3
+ * pinned to the right edge, and transient feedback temporarily replaces the hints.
4
+ */
5
+ import { truncateToWidth, visibleWidth } from "@earendil-works/pi-tui";
6
+ const FEEDBACK_GLYPH = { warning: "!", error: "×" };
7
+ /** Cells kept between the left content and the pinned Escape action. */
8
+ const GAP = 3;
9
+ /** Drops the lowest-priority items, the later of equals first, until the rest fit. */
10
+ function dropToFit(items, room, separator) {
11
+ const kept = [...items];
12
+ const width = () => kept.reduce((sum, item) => sum + visibleWidth(item.text), 0) + visibleWidth(separator) * (kept.length - 1);
13
+ while (kept.length > 1 && width() > room) {
14
+ let drop = 0;
15
+ for (const [index, item] of kept.entries())
16
+ if (item.priority >= kept[drop].priority)
17
+ drop = index;
18
+ kept.splice(drop, 1);
19
+ }
20
+ return kept;
21
+ }
22
+ function compose(env, spec) {
23
+ const { width, y, theme } = env;
24
+ const hits = [];
25
+ const escapeWidth = visibleWidth(spec.escape.text);
26
+ const room = Math.max(0, width - escapeWidth - GAP);
27
+ let showEscape = true;
28
+ let left;
29
+ if (spec.feedback) {
30
+ const message = `${FEEDBACK_GLYPH[spec.feedback.tone]} ${spec.feedback.text}`;
31
+ // A long message is worth more than the Escape hint while it lasts.
32
+ showEscape = visibleWidth(message) <= room;
33
+ left = theme.fg(spec.feedback.tone, truncateToWidth(message, showEscape ? room : width, "…"));
34
+ }
35
+ else {
36
+ let x = 0;
37
+ const kept = dropToFit(spec.items, room, spec.separator);
38
+ for (const item of kept) {
39
+ if (item.action)
40
+ hits.push({ x, y, width: visibleWidth(item.text), action: item.action });
41
+ x += visibleWidth(item.text) + visibleWidth(spec.separator);
42
+ }
43
+ left = theme.fg(spec.tone, truncateToWidth(kept.map((item) => item.text).join(spec.separator), room, "…"));
44
+ }
45
+ const gap = Math.max(0, width - visibleWidth(left) - (showEscape ? escapeWidth : 0));
46
+ if (showEscape && spec.escape.action)
47
+ hits.push({ x: width - escapeWidth, y, width: escapeWidth, action: spec.escape.action });
48
+ return { line: left + " ".repeat(gap) + (showEscape ? theme.fg(spec.tone, spec.escape.text) : ""), hits };
49
+ }
50
+ const BACK = { kind: "back" };
51
+ export function browseFooter(env, view) {
52
+ const { hint } = env;
53
+ const items = [];
54
+ const add = (text, priority, action) => items.push({ text, priority, action });
55
+ const arrows = `${hint("tui.select.up")} / ${hint("tui.select.down")}`;
56
+ if (view.focus === "list") {
57
+ add(`${arrows} tasks`, 5);
58
+ add(`${hint("tui.select.confirm")} open`, 4, { kind: "open" });
59
+ add(`${hint("app.tasks.search")} find`, 3, { kind: "key", key: "app.tasks.search" });
60
+ }
61
+ else {
62
+ add(`${arrows} scroll`, 5);
63
+ // Without the list beside it, Escape is the way back.
64
+ if (view.split)
65
+ add(`${hint("app.tasks.nextFocus")} tasks`, 6, { kind: "key", key: "app.tasks.nextFocus" });
66
+ add(`${hint("app.tasks.previousTab")} output`, 6, { kind: "tab", tab: "output" });
67
+ add(`${hint("app.tasks.nextTab")} details`, 6, { kind: "tab", tab: "info" });
68
+ }
69
+ if (view.stoppable)
70
+ add(`${hint("app.tasks.kill")} stop`, 2, { kind: "key", key: "app.tasks.kill" });
71
+ if (view.detachable)
72
+ add(`${hint("app.tasks.detachSelected")} background`, 2, { kind: "key", key: "app.tasks.detachSelected" });
73
+ add(`${hint("app.tasks.help")} keys`, 1, { kind: "key", key: "app.tasks.help" });
74
+ const back = !view.split && view.focus === "inspector";
75
+ return compose(env, {
76
+ items,
77
+ separator: " · ",
78
+ escape: { text: `${hint("tui.select.cancel")} ${back ? "back" : "close"}`, priority: 0, action: BACK },
79
+ tone: "dim",
80
+ feedback: env.feedback,
81
+ });
82
+ }
83
+ export function searchFooter(env) {
84
+ const { hint } = env;
85
+ return compose(env, {
86
+ items: [
87
+ { text: `${hint("tui.select.up")} / ${hint("tui.select.down")} choose`, priority: 2 },
88
+ { text: `${hint("tui.select.confirm")} locate`, priority: 1 },
89
+ ],
90
+ separator: " · ",
91
+ escape: { text: `${hint("tui.select.cancel")} cancel`, priority: 0, action: BACK },
92
+ tone: "dim",
93
+ feedback: env.feedback,
94
+ });
95
+ }
96
+ export function helpFooter(env) {
97
+ const { hint } = env;
98
+ return compose(env, {
99
+ items: [{ text: `${hint("tui.select.up")} / ${hint("tui.select.down")} scroll`, priority: 1 }],
100
+ separator: " · ",
101
+ escape: { text: `${hint("tui.select.cancel")} back`, priority: 0, action: BACK },
102
+ tone: "dim",
103
+ feedback: env.feedback,
104
+ });
105
+ }
106
+ /**
107
+ * The stop question borrows only the footer row, so the task stays in view while confirming;
108
+ * `source` and `title` name the target, and the title gives way first on narrow widths.
109
+ */
110
+ export function confirmStopFooter(env, target) {
111
+ const { hint, width } = env;
112
+ const confirm = `${hint("app.tasks.confirmStop")} stop`;
113
+ const cancel = `${hint("tui.select.cancel")} cancel`;
114
+ const separator = " ";
115
+ const room = width - visibleWidth(cancel) - GAP - visibleWidth(separator + confirm);
116
+ const bare = `Stop ${target.source}?`;
117
+ const spare = room - visibleWidth(`Stop ${target.source} ""?`);
118
+ const question = spare >= 4 ? `Stop ${target.source} "${truncateToWidth(target.title, spare, "…")}"?` : bare;
119
+ return compose(env, {
120
+ items: [
121
+ { text: question, priority: 0 },
122
+ { text: confirm, priority: 0, action: { kind: "key", key: "app.tasks.confirmStop" } },
123
+ ],
124
+ separator,
125
+ escape: { text: cancel, priority: 0, action: BACK },
126
+ tone: "warning",
127
+ });
128
+ }
129
+ //# sourceMappingURL=footer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"footer.js","sourceRoot":"","sources":["../../../../src/extensions/tasks/ui/footer.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * The footer row, one builder per panel mode. Context hints sit on the left, the Escape action is\n * pinned to the right edge, and transient feedback temporarily replaces the hints.\n */\nimport { truncateToWidth, visibleWidth } from \"@earendil-works/pi-tui\";\nimport type { Keybinding } from \"../../../core/keybindings.ts\";\nimport type { Theme, ThemeColor } from \"../../../modes/interactive/theme/theme.ts\";\nimport type { PanelFocus, TaskAction, TaskHit } from \"./model.ts\";\n\n/** An outcome the panel does not already show: the action did not do what was asked. */\nexport interface Feedback {\n\ttone: \"warning\" | \"error\";\n\ttext: string;\n}\n\nconst FEEDBACK_GLYPH: Record<Feedback[\"tone\"], string> = { warning: \"!\", error: \"×\" };\n/** Cells kept between the left content and the pinned Escape action. */\nconst GAP = 3;\n\ninterface FooterItem {\n\ttext: string;\n\t/** Higher numbers are dropped first when the row is too narrow. */\n\tpriority: number;\n\taction?: TaskAction;\n}\n\ninterface FooterSpec {\n\titems: FooterItem[];\n\tseparator: string;\n\tescape: FooterItem;\n\ttone: ThemeColor;\n\tfeedback?: Feedback;\n}\n\nexport interface FooterEnv {\n\twidth: number;\n\t/** The footer's screen row. */\n\ty: number;\n\ttheme: Theme;\n\thint: (key: Keybinding) => string;\n\tfeedback?: Feedback;\n}\n\nexport interface Footer {\n\tline: string;\n\thits: TaskHit[];\n}\n\n/** Drops the lowest-priority items, the later of equals first, until the rest fit. */\nfunction dropToFit(items: FooterItem[], room: number, separator: string): FooterItem[] {\n\tconst kept = [...items];\n\tconst width = () =>\n\t\tkept.reduce((sum, item) => sum + visibleWidth(item.text), 0) + visibleWidth(separator) * (kept.length - 1);\n\twhile (kept.length > 1 && width() > room) {\n\t\tlet drop = 0;\n\t\tfor (const [index, item] of kept.entries()) if (item.priority >= kept[drop]!.priority) drop = index;\n\t\tkept.splice(drop, 1);\n\t}\n\treturn kept;\n}\n\nfunction compose(env: FooterEnv, spec: FooterSpec): Footer {\n\tconst { width, y, theme } = env;\n\tconst hits: TaskHit[] = [];\n\tconst escapeWidth = visibleWidth(spec.escape.text);\n\tconst room = Math.max(0, width - escapeWidth - GAP);\n\tlet showEscape = true;\n\tlet left: string;\n\tif (spec.feedback) {\n\t\tconst message = `${FEEDBACK_GLYPH[spec.feedback.tone]} ${spec.feedback.text}`;\n\t\t// A long message is worth more than the Escape hint while it lasts.\n\t\tshowEscape = visibleWidth(message) <= room;\n\t\tleft = theme.fg(spec.feedback.tone, truncateToWidth(message, showEscape ? room : width, \"…\"));\n\t} else {\n\t\tlet x = 0;\n\t\tconst kept = dropToFit(spec.items, room, spec.separator);\n\t\tfor (const item of kept) {\n\t\t\tif (item.action) hits.push({ x, y, width: visibleWidth(item.text), action: item.action });\n\t\t\tx += visibleWidth(item.text) + visibleWidth(spec.separator);\n\t\t}\n\t\tleft = theme.fg(spec.tone, truncateToWidth(kept.map((item) => item.text).join(spec.separator), room, \"…\"));\n\t}\n\tconst gap = Math.max(0, width - visibleWidth(left) - (showEscape ? escapeWidth : 0));\n\tif (showEscape && spec.escape.action)\n\t\thits.push({ x: width - escapeWidth, y, width: escapeWidth, action: spec.escape.action });\n\treturn { line: left + \" \".repeat(gap) + (showEscape ? theme.fg(spec.tone, spec.escape.text) : \"\"), hits };\n}\n\nconst BACK: TaskAction = { kind: \"back\" };\n\nexport function browseFooter(\n\tenv: FooterEnv,\n\tview: { focus: PanelFocus; split: boolean; stoppable: boolean; detachable: boolean },\n): Footer {\n\tconst { hint } = env;\n\tconst items: FooterItem[] = [];\n\tconst add = (text: string, priority: number, action?: TaskAction) => items.push({ text, priority, action });\n\tconst arrows = `${hint(\"tui.select.up\")} / ${hint(\"tui.select.down\")}`;\n\tif (view.focus === \"list\") {\n\t\tadd(`${arrows} tasks`, 5);\n\t\tadd(`${hint(\"tui.select.confirm\")} open`, 4, { kind: \"open\" });\n\t\tadd(`${hint(\"app.tasks.search\")} find`, 3, { kind: \"key\", key: \"app.tasks.search\" });\n\t} else {\n\t\tadd(`${arrows} scroll`, 5);\n\t\t// Without the list beside it, Escape is the way back.\n\t\tif (view.split) add(`${hint(\"app.tasks.nextFocus\")} tasks`, 6, { kind: \"key\", key: \"app.tasks.nextFocus\" });\n\t\tadd(`${hint(\"app.tasks.previousTab\")} output`, 6, { kind: \"tab\", tab: \"output\" });\n\t\tadd(`${hint(\"app.tasks.nextTab\")} details`, 6, { kind: \"tab\", tab: \"info\" });\n\t}\n\tif (view.stoppable) add(`${hint(\"app.tasks.kill\")} stop`, 2, { kind: \"key\", key: \"app.tasks.kill\" });\n\tif (view.detachable)\n\t\tadd(`${hint(\"app.tasks.detachSelected\")} background`, 2, { kind: \"key\", key: \"app.tasks.detachSelected\" });\n\tadd(`${hint(\"app.tasks.help\")} keys`, 1, { kind: \"key\", key: \"app.tasks.help\" });\n\tconst back = !view.split && view.focus === \"inspector\";\n\treturn compose(env, {\n\t\titems,\n\t\tseparator: \" · \",\n\t\tescape: { text: `${hint(\"tui.select.cancel\")} ${back ? \"back\" : \"close\"}`, priority: 0, action: BACK },\n\t\ttone: \"dim\",\n\t\tfeedback: env.feedback,\n\t});\n}\n\nexport function searchFooter(env: FooterEnv): Footer {\n\tconst { hint } = env;\n\treturn compose(env, {\n\t\titems: [\n\t\t\t{ text: `${hint(\"tui.select.up\")} / ${hint(\"tui.select.down\")} choose`, priority: 2 },\n\t\t\t{ text: `${hint(\"tui.select.confirm\")} locate`, priority: 1 },\n\t\t],\n\t\tseparator: \" · \",\n\t\tescape: { text: `${hint(\"tui.select.cancel\")} cancel`, priority: 0, action: BACK },\n\t\ttone: \"dim\",\n\t\tfeedback: env.feedback,\n\t});\n}\n\nexport function helpFooter(env: FooterEnv): Footer {\n\tconst { hint } = env;\n\treturn compose(env, {\n\t\titems: [{ text: `${hint(\"tui.select.up\")} / ${hint(\"tui.select.down\")} scroll`, priority: 1 }],\n\t\tseparator: \" · \",\n\t\tescape: { text: `${hint(\"tui.select.cancel\")} back`, priority: 0, action: BACK },\n\t\ttone: \"dim\",\n\t\tfeedback: env.feedback,\n\t});\n}\n\n/**\n * The stop question borrows only the footer row, so the task stays in view while confirming;\n * `source` and `title` name the target, and the title gives way first on narrow widths.\n */\nexport function confirmStopFooter(env: FooterEnv, target: { source: string; title: string }): Footer {\n\tconst { hint, width } = env;\n\tconst confirm = `${hint(\"app.tasks.confirmStop\")} stop`;\n\tconst cancel = `${hint(\"tui.select.cancel\")} cancel`;\n\tconst separator = \" \";\n\tconst room = width - visibleWidth(cancel) - GAP - visibleWidth(separator + confirm);\n\tconst bare = `Stop ${target.source}?`;\n\tconst spare = room - visibleWidth(`Stop ${target.source} \"\"?`);\n\tconst question = spare >= 4 ? `Stop ${target.source} \"${truncateToWidth(target.title, spare, \"…\")}\"?` : bare;\n\treturn compose(env, {\n\t\titems: [\n\t\t\t{ text: question, priority: 0 },\n\t\t\t{ text: confirm, priority: 0, action: { kind: \"key\", key: \"app.tasks.confirmStop\" } },\n\t\t],\n\t\tseparator,\n\t\tescape: { text: cancel, priority: 0, action: BACK },\n\t\ttone: \"warning\",\n\t});\n}\n"]}
@@ -0,0 +1,36 @@
1
+ import type { Theme } from "../../../modes/interactive/theme/theme.ts";
2
+ export interface Rect {
3
+ x: number;
4
+ y: number;
5
+ width: number;
6
+ height: number;
7
+ }
8
+ /** Columns from a pane's left edge to its title: the corner, one rule cell and a space. */
9
+ export declare const TITLE_INDENT = 3;
10
+ interface FrameOptions {
11
+ width: number;
12
+ theme: Theme;
13
+ /** The focused pane's border is accented. */
14
+ accent: boolean;
15
+ /** Styled title for the top border. */
16
+ title?: string;
17
+ /** Styled text right-aligned in the bottom border, such as a scroll range. */
18
+ status?: string;
19
+ /** Content rows, clipped or padded to the inner width. */
20
+ rows: string[];
21
+ /** Index of the content row drawn as a `├──┤` rule joined to the frame; that row's text is ignored. */
22
+ divider?: number;
23
+ }
24
+ export interface Frame {
25
+ lines: string[];
26
+ /** Where the bottom-border status starts (pane-relative) and how many cells it occupies. */
27
+ status: {
28
+ x: number;
29
+ width: number;
30
+ };
31
+ }
32
+ /** Pads or clips one content row to exactly `width` cells. */
33
+ export declare const fitCell: (text: string, width: number) => string;
34
+ export declare function framePane(options: FrameOptions): Frame;
35
+ export {};
36
+ //# sourceMappingURL=frame.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"frame.d.ts","sourceRoot":"","sources":["../../../../src/extensions/tasks/ui/frame.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,2CAA2C,CAAC;AAEvE,MAAM,WAAW,IAAI;IACpB,CAAC,EAAE,MAAM,CAAC;IACV,CAAC,EAAE,MAAM,CAAC;IACV,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;CACf;AAED,2FAA2F;AAC3F,eAAO,MAAM,YAAY,IAAI,CAAC;AAE9B,UAAU,YAAY;IACrB,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,KAAK,CAAC;IACb,6CAA6C;IAC7C,MAAM,EAAE,OAAO,CAAC;IAChB,uCAAuC;IACvC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,8EAA8E;IAC9E,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,0DAA0D;IAC1D,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,uGAAuG;IACvG,OAAO,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,KAAK;IACrB,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,4FAA4F;IAC5F,MAAM,EAAE;QAAE,CAAC,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC;CACrC;AAED,8DAA8D;AAC9D,eAAO,MAAM,OAAO,GAAI,MAAM,MAAM,EAAE,OAAO,MAAM,KAAG,MAA8D,CAAC;AAErH,wBAAgB,SAAS,CAAC,OAAO,EAAE,YAAY,GAAG,KAAK,CA2BtD"}
@@ -0,0 +1,33 @@
1
+ /** Bordered pane chrome: the title sits in the top border and a status in the bottom one. */
2
+ import { truncateToWidth, visibleWidth } from "@earendil-works/pi-tui";
3
+ /** Columns from a pane's left edge to its title: the corner, one rule cell and a space. */
4
+ export const TITLE_INDENT = 3;
5
+ /** Pads or clips one content row to exactly `width` cells. */
6
+ export const fitCell = (text, width) => truncateToWidth(text, Math.max(1, width), "…", true);
7
+ export function framePane(options) {
8
+ const { width, theme, rows } = options;
9
+ const border = (text) => theme.fg(options.accent ? "borderAccent" : "borderMuted", text);
10
+ const inner = Math.max(1, width - 2);
11
+ // `┌─ title ──┐` and `└── status ─┘`: three cells of corner, rule and space frame the text.
12
+ const room = Math.max(0, width - 5);
13
+ const title = options.title && room > 0 ? truncateToWidth(options.title, room, "…") : "";
14
+ const status = options.status && room > 0 ? truncateToWidth(options.status, room, "…") : "";
15
+ const top = title
16
+ ? border("┌─ ") + title + border(` ${"─".repeat(Math.max(0, width - 5 - visibleWidth(title)))}┐`)
17
+ : border(`┌${"─".repeat(Math.max(0, width - 2))}┐`);
18
+ const statusWidth = visibleWidth(status);
19
+ const bottom = status
20
+ ? border(`└${"─".repeat(Math.max(0, width - 5 - statusWidth))} `) + status + border(" ─┘")
21
+ : border(`└${"─".repeat(Math.max(0, width - 2))}┘`);
22
+ return {
23
+ lines: [
24
+ top,
25
+ ...rows.map((row, index) => index === options.divider
26
+ ? border(`├${"─".repeat(Math.max(0, width - 2))}┤`)
27
+ : border("│") + fitCell(row, inner) + border("│")),
28
+ bottom,
29
+ ],
30
+ status: { x: Math.max(0, width - 3 - statusWidth), width: statusWidth },
31
+ };
32
+ }
33
+ //# sourceMappingURL=frame.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"frame.js","sourceRoot":"","sources":["../../../../src/extensions/tasks/ui/frame.ts"],"names":[],"mappings":"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","sourcesContent":["/** Bordered pane chrome: the title sits in the top border and a status in the bottom one. */\nimport { truncateToWidth, visibleWidth } from \"@earendil-works/pi-tui\";\nimport type { Theme } from \"../../../modes/interactive/theme/theme.ts\";\n\nexport interface Rect {\n\tx: number;\n\ty: number;\n\twidth: number;\n\theight: number;\n}\n\n/** Columns from a pane's left edge to its title: the corner, one rule cell and a space. */\nexport const TITLE_INDENT = 3;\n\ninterface FrameOptions {\n\twidth: number;\n\ttheme: Theme;\n\t/** The focused pane's border is accented. */\n\taccent: boolean;\n\t/** Styled title for the top border. */\n\ttitle?: string;\n\t/** Styled text right-aligned in the bottom border, such as a scroll range. */\n\tstatus?: string;\n\t/** Content rows, clipped or padded to the inner width. */\n\trows: string[];\n\t/** Index of the content row drawn as a `├──┤` rule joined to the frame; that row's text is ignored. */\n\tdivider?: number;\n}\n\nexport interface Frame {\n\tlines: string[];\n\t/** Where the bottom-border status starts (pane-relative) and how many cells it occupies. */\n\tstatus: { x: number; width: number };\n}\n\n/** Pads or clips one content row to exactly `width` cells. */\nexport const fitCell = (text: string, width: number): string => truncateToWidth(text, Math.max(1, width), \"…\", true);\n\nexport function framePane(options: FrameOptions): Frame {\n\tconst { width, theme, rows } = options;\n\tconst border = (text: string) => theme.fg(options.accent ? \"borderAccent\" : \"borderMuted\", text);\n\tconst inner = Math.max(1, width - 2);\n\t// `┌─ title ──┐` and `└── status ─┘`: three cells of corner, rule and space frame the text.\n\tconst room = Math.max(0, width - 5);\n\tconst title = options.title && room > 0 ? truncateToWidth(options.title, room, \"…\") : \"\";\n\tconst status = options.status && room > 0 ? truncateToWidth(options.status, room, \"…\") : \"\";\n\tconst top = title\n\t\t? border(\"┌─ \") + title + border(` ${\"─\".repeat(Math.max(0, width - 5 - visibleWidth(title)))}┐`)\n\t\t: border(`┌${\"─\".repeat(Math.max(0, width - 2))}┐`);\n\tconst statusWidth = visibleWidth(status);\n\tconst bottom = status\n\t\t? border(`└${\"─\".repeat(Math.max(0, width - 5 - statusWidth))} `) + status + border(\" ─┘\")\n\t\t: border(`└${\"─\".repeat(Math.max(0, width - 2))}┘`);\n\treturn {\n\t\tlines: [\n\t\t\ttop,\n\t\t\t...rows.map((row, index) =>\n\t\t\t\tindex === options.divider\n\t\t\t\t\t? border(`├${\"─\".repeat(Math.max(0, width - 2))}┤`)\n\t\t\t\t\t: border(\"│\") + fitCell(row, inner) + border(\"│\"),\n\t\t\t),\n\t\t\tbottom,\n\t\t],\n\t\tstatus: { x: Math.max(0, width - 3 - statusWidth), width: statusWidth },\n\t};\n}\n"]}
@@ -0,0 +1,6 @@
1
+ /** The `?` controls page: every binding grouped by purpose, with the user's current keys. */
2
+ import type { Keybinding } from "../../../core/keybindings.ts";
3
+ import type { Theme } from "../../../modes/interactive/theme/theme.ts";
4
+ /** Controls grouped by purpose; actions sharing a key, such as list and inspector paging, share a row. */
5
+ export declare function helpRows(hint: (key: Keybinding) => string, theme: Theme): string[];
6
+ //# sourceMappingURL=help.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"help.d.ts","sourceRoot":"","sources":["../../../../src/extensions/tasks/ui/help.ts"],"names":[],"mappings":"AAAA,6FAA6F;AAG7F,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,8BAA8B,CAAC;AAC/D,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,2CAA2C,CAAC;AAEvE,0GAA0G;AAC1G,wBAAgB,QAAQ,CAAC,IAAI,EAAE,CAAC,GAAG,EAAE,UAAU,KAAK,MAAM,EAAE,KAAK,EAAE,KAAK,GAAG,MAAM,EAAE,CAiDlF"}
@@ -0,0 +1,53 @@
1
+ /** The `?` controls page: every binding grouped by purpose, with the user's current keys. */
2
+ import { visibleWidth } from "@earendil-works/pi-tui";
3
+ /** Controls grouped by purpose; actions sharing a key, such as list and inspector paging, share a row. */
4
+ export function helpRows(hint, theme) {
5
+ const keys = (...bindings) => [...new Set(bindings.map(hint).filter(Boolean))].join(" / ");
6
+ const listPage = keys("tui.select.pageUp", "tui.select.pageDown");
7
+ const inspectorPage = keys("tui.editor.pageUp", "tui.editor.pageDown");
8
+ const paging = listPage === inspectorPage
9
+ ? [[listPage, "Page the list or inspector"]]
10
+ : [
11
+ [listPage, "Page the list"],
12
+ [inspectorPage, "Page the inspector"],
13
+ ];
14
+ const sections = [
15
+ [
16
+ "Navigate",
17
+ [
18
+ [keys("tui.select.up", "tui.select.down"), "Select a task, or scroll the inspector"],
19
+ ...paging,
20
+ [keys("app.tasks.top", "app.tasks.bottom"), "Top or bottom"],
21
+ [hint("tui.select.confirm"), "Open the selected task"],
22
+ [keys("app.tasks.nextFocus", "app.tasks.previousFocus"), "Switch between list and inspector"],
23
+ ],
24
+ ],
25
+ [
26
+ "View",
27
+ [
28
+ [keys("app.tasks.previousTab", "app.tasks.nextTab"), "Output or Details, in the inspector"],
29
+ [hint("app.tasks.follow"), "Follow the latest output, or load the final output"],
30
+ [hint("app.tasks.search"), "Find a task; Enter locates it, Esc restores the selection"],
31
+ ],
32
+ ],
33
+ [
34
+ "Act",
35
+ [
36
+ [hint("app.tasks.kill"), `Stop the selected task; ${hint("app.tasks.confirmStop")} confirms`],
37
+ [hint("app.tasks.detachSelected"), "Move the selected foreground task to the background"],
38
+ [hint("app.tasks.detach"), "Move every foreground execution to the background"],
39
+ [
40
+ hint("tui.select.cancel"),
41
+ "Close search, help or confirmation; otherwise go back from the inspector in a narrow terminal, or close the panel",
42
+ ],
43
+ ],
44
+ ],
45
+ ];
46
+ const keyWidth = Math.max(...sections.flatMap(([, rows]) => rows.map(([key]) => visibleWidth(key)))) + 2;
47
+ return sections.flatMap(([title, entries], index) => [
48
+ ...(index ? [""] : []),
49
+ theme.fg("accent", title),
50
+ ...entries.map(([key, label]) => ` ${key}${" ".repeat(keyWidth - visibleWidth(key))}${label}`),
51
+ ]);
52
+ }
53
+ //# sourceMappingURL=help.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"help.js","sourceRoot":"","sources":["../../../../src/extensions/tasks/ui/help.ts"],"names":[],"mappings":"AAAA,6FAA6F;AAE7F,OAAO,EAAE,YAAY,EAAE,MAAM,wBAAwB,CAAC;AAItD,0GAA0G;AAC1G,MAAM,UAAU,QAAQ,CAAC,IAAiC,EAAE,KAAY;IACvE,MAAM,IAAI,GAAG,CAAC,GAAG,QAAsB,EAAE,EAAE,CAAC,CAAC,GAAG,IAAI,GAAG,CAAC,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IACzG,MAAM,QAAQ,GAAG,IAAI,CAAC,mBAAmB,EAAE,qBAAqB,CAAC,CAAC;IAClE,MAAM,aAAa,GAAG,IAAI,CAAC,mBAAmB,EAAE,qBAAqB,CAAC,CAAC;IACvE,MAAM,MAAM,GACX,QAAQ,KAAK,aAAa;QACzB,CAAC,CAAC,CAAC,CAAC,QAAQ,EAAE,4BAA4B,CAAC,CAAC;QAC5C,CAAC,CAAC;YACA,CAAC,QAAQ,EAAE,eAAe,CAAC;YAC3B,CAAC,aAAa,EAAE,oBAAoB,CAAC;SACrC,CAAC;IACL,MAAM,QAAQ,GAA6C;QAC1D;YACC,UAAU;YACV;gBACC,CAAC,IAAI,CAAC,eAAe,EAAE,iBAAiB,CAAC,EAAE,wCAAwC,CAAC;gBACpF,GAAG,MAAM;gBACT,CAAC,IAAI,CAAC,eAAe,EAAE,kBAAkB,CAAC,EAAE,eAAe,CAAC;gBAC5D,CAAC,IAAI,CAAC,oBAAoB,CAAC,EAAE,wBAAwB,CAAC;gBACtD,CAAC,IAAI,CAAC,qBAAqB,EAAE,yBAAyB,CAAC,EAAE,mCAAmC,CAAC;aAC7F;SACD;QACD;YACC,MAAM;YACN;gBACC,CAAC,IAAI,CAAC,uBAAuB,EAAE,mBAAmB,CAAC,EAAE,qCAAqC,CAAC;gBAC3F,CAAC,IAAI,CAAC,kBAAkB,CAAC,EAAE,oDAAoD,CAAC;gBAChF,CAAC,IAAI,CAAC,kBAAkB,CAAC,EAAE,2DAA2D,CAAC;aACvF;SACD;QACD;YACC,KAAK;YACL;gBACC,CAAC,IAAI,CAAC,gBAAgB,CAAC,EAAE,2BAA2B,IAAI,CAAC,uBAAuB,CAAC,WAAW,CAAC;gBAC7F,CAAC,IAAI,CAAC,0BAA0B,CAAC,EAAE,qDAAqD,CAAC;gBACzF,CAAC,IAAI,CAAC,kBAAkB,CAAC,EAAE,mDAAmD,CAAC;gBAC/E;oBACC,IAAI,CAAC,mBAAmB,CAAC;oBACzB,mHAAmH;iBACnH;aACD;SACD;KACD,CAAC;IACF,MAAM,QAAQ,GAAG,IAAI,CAAC,GAAG,CAAC,GAAG,QAAQ,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,IAAI,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,EAAE,EAAE,CAAC,YAAY,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC;IACzG,OAAO,QAAQ,CAAC,OAAO,CAAC,CAAC,CAAC,KAAK,EAAE,OAAO,CAAC,EAAE,KAAK,EAAE,EAAE,CAAC;QACpD,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;QACtB,KAAK,CAAC,EAAE,CAAC,QAAQ,EAAE,KAAK,CAAC;QACzB,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,EAAE,KAAK,CAAC,EAAE,EAAE,CAAC,KAAK,GAAG,GAAG,GAAG,CAAC,MAAM,CAAC,QAAQ,GAAG,YAAY,CAAC,GAAG,CAAC,CAAC,GAAG,KAAK,EAAE,CAAC;KAC/F,CAAC,CAAC;AACJ,CAAC","sourcesContent":["/** The `?` controls page: every binding grouped by purpose, with the user's current keys. */\n\nimport { visibleWidth } from \"@earendil-works/pi-tui\";\nimport type { Keybinding } from \"../../../core/keybindings.ts\";\nimport type { Theme } from \"../../../modes/interactive/theme/theme.ts\";\n\n/** Controls grouped by purpose; actions sharing a key, such as list and inspector paging, share a row. */\nexport function helpRows(hint: (key: Keybinding) => string, theme: Theme): 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[\n\t\t\t\t\thint(\"tui.select.cancel\"),\n\t\t\t\t\t\"Close search, help or confirmation; otherwise go back from the inspector in a narrow terminal, or close the panel\",\n\t\t\t\t],\n\t\t\t],\n\t\t],\n\t];\n\tconst keyWidth = Math.max(...sections.flatMap(([, rows]) => rows.map(([key]) => visibleWidth(key)))) + 2;\n\treturn 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}\n"]}
@@ -4,10 +4,12 @@ import { TaskViewport } from "./viewport.ts";
4
4
  interface ReadingPosition {
5
5
  info: TaskViewport;
6
6
  output: TaskViewport;
7
+ /** Whether Output last followed the tail; a change of kind restarts its viewport. */
8
+ tails?: boolean;
7
9
  read?: TaskRead;
8
10
  }
9
11
  interface InspectorOptions {
10
- host: Pick<TasksContext, "read" | "retain">;
12
+ host: Pick<TasksContext, "read" | "retain" | "presentOutput">;
11
13
  theme: Theme;
12
14
  requestRender(): void;
13
15
  }
@@ -29,8 +31,7 @@ export declare class TaskInspector {
29
31
  invalidate(): void;
30
32
  refresh(): Promise<void>;
31
33
  output(width: number): string[];
32
- /** `status` carries the runtime, so Details rebuild at most once per displayed tick. */
33
- details(status: string): string[];
34
+ details(width: number): string[];
34
35
  dispose(): void;
35
36
  }
36
37
  export {};
@@ -1 +1 @@
1
- {"version":3,"file":"inspector.d.ts","sourceRoot":"","sources":["../../../../src/extensions/tasks/ui/inspector.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,8BAA8B,CAAC;AAEzF,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,2CAA2C,CAAC;AAEvE,OAAO,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAG7C,UAAU,eAAe;IACxB,IAAI,EAAE,YAAY,CAAC;IACnB,MAAM,EAAE,YAAY,CAAC;IACrB,IAAI,CAAC,EAAE,QAAQ,CAAC;CAChB;AACD,UAAU,gBAAgB;IACzB,IAAI,EAAE,IAAI,CAAC,YAAY,EAAE,MAAM,GAAG,QAAQ,CAAC,CAAC;IAC5C,KAAK,EAAE,KAAK,CAAC;IACb,aAAa,IAAI,IAAI,CAAC;CACtB;AAED,8FAA8F;AAC9F,qBAAa,aAAa;IACzB,IAAI,CAAC,EAAE,YAAY,CAAC;IACpB,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAmB;IAC3C,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAsC;IAChE,OAAO,CAAC,OAAO,CAAC,CAAa;IAC7B,OAAO,CAAC,OAAO,CAAS;IACxB,OAAO,CAAC,QAAQ,CAAS;IACzB,iFAAiF;IACjF,OAAO,CAAC,WAAW,CAAC,CAA0E;IAC9F,OAAO,CAAC,WAAW,CAAC,CAA0D;gBAClE,OAAO,EAAE,gBAAgB;IAGrC,IAAI,QAAQ,IAAI,eAAe,CAQ9B;IACD,MAAM,CAAC,IAAI,EAAE,YAAY,GAAG,SAAS,GAAG,IAAI;IAU5C,KAAK,CAAC,GAAG,EAAE,WAAW,CAAC,MAAM,CAAC,GAAG,IAAI;IAGrC,UAAU,IAAI,IAAI;IAIZ,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;IA0B9B,MAAM,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE;IAS/B,wFAAwF;IACxF,OAAO,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE;IAQjC,OAAO,IAAI,IAAI;CAOf"}
1
+ {"version":3,"file":"inspector.d.ts","sourceRoot":"","sources":["../../../../src/extensions/tasks/ui/inspector.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,8BAA8B,CAAC;AAEzF,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,2CAA2C,CAAC;AAEvE,OAAO,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAG7C,UAAU,eAAe;IACxB,IAAI,EAAE,YAAY,CAAC;IACnB,MAAM,EAAE,YAAY,CAAC;IACrB,qFAAqF;IACrF,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,IAAI,CAAC,EAAE,QAAQ,CAAC;CAChB;AACD,UAAU,gBAAgB;IACzB,IAAI,EAAE,IAAI,CAAC,YAAY,EAAE,MAAM,GAAG,QAAQ,GAAG,eAAe,CAAC,CAAC;IAC9D,KAAK,EAAE,KAAK,CAAC;IACb,aAAa,IAAI,IAAI,CAAC;CACtB;AAOD,8FAA8F;AAC9F,qBAAa,aAAa;IACzB,IAAI,CAAC,EAAE,YAAY,CAAC;IACpB,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAmB;IAC3C,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAsC;IAChE,OAAO,CAAC,OAAO,CAAC,CAAa;IAC7B,OAAO,CAAC,OAAO,CAAS;IACxB,OAAO,CAAC,QAAQ,CAAS;IACzB,iFAAiF;IACjF,OAAO,CAAC,WAAW,CAAC,CAA0E;IAC9F,OAAO,CAAC,WAAW,CAAC,CAAyD;gBACjE,OAAO,EAAE,gBAAgB;IAGrC,IAAI,QAAQ,IAAI,eAAe,CAQ9B;IACD,MAAM,CAAC,IAAI,EAAE,YAAY,GAAG,SAAS,GAAG,IAAI;IAiB5C,KAAK,CAAC,GAAG,EAAE,WAAW,CAAC,MAAM,CAAC,GAAG,IAAI;IAGrC,UAAU,IAAI,IAAI;IAIZ,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;IA4B9B,MAAM,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE;IAc/B,OAAO,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE;IAQhC,OAAO,IAAI,IAAI;CAOf"}
@@ -1,7 +1,11 @@
1
1
  import { isTaskTerminal } from "../../../core/tasks/types.js";
2
2
  import { cleanTaskText } from "./text.js";
3
3
  import { TaskViewport } from "./viewport.js";
4
- import { detailLines, outputLines } from "./views.js";
4
+ import { detailLines, outputLines, streamsLog } from "./views.js";
5
+ /** Output follows the tail while it is a log or still live; a settled report reads from its first line. */
6
+ function tailsOutput(task) {
7
+ return task.output === "log" || !isTaskTerminal(task.status);
8
+ }
5
9
  /** Owns the selected task's bounded reads. Reading never acknowledges completion delivery. */
6
10
  export class TaskInspector {
7
11
  constructor(options) {
@@ -30,6 +34,14 @@ export class TaskInspector {
30
34
  previous?.();
31
35
  }
32
36
  this.task = task;
37
+ if (!task)
38
+ return;
39
+ const position = this.position;
40
+ const tails = tailsOutput(task);
41
+ if (position.tails !== tails) {
42
+ position.tails = tails;
43
+ position.output.retarget(tails);
44
+ }
33
45
  }
34
46
  prune(ids) {
35
47
  for (const id of this.positions.keys())
@@ -42,7 +54,9 @@ export class TaskInspector {
42
54
  }
43
55
  async refresh() {
44
56
  const task = this.task;
45
- if (this.disposed || !task || this.reading)
57
+ // Only an attached log streams. A terminal file-less log is read once for its expiry diagnostic.
58
+ const terminalFallback = task?.output === "log" && isTaskTerminal(task.status);
59
+ if (this.disposed || !task || (!streamsLog(task) && !terminalFallback) || this.reading)
46
60
  return;
47
61
  const position = this.position;
48
62
  if (position.read && (!position.output.follow || isTaskTerminal(position.read.task.status)))
@@ -77,20 +91,25 @@ export class TaskInspector {
77
91
  const task = this.task;
78
92
  if (!task)
79
93
  return [];
94
+ // A live presentation changes with time, not with the snapshot, so it is never cached.
95
+ const live = isTaskTerminal(task.status)
96
+ ? undefined
97
+ : this.options.host.presentOutput(task.id, width, this.options.theme);
98
+ if (live)
99
+ return outputLines(task, undefined, this.options.theme, width, live);
80
100
  const read = this.position.read;
81
101
  const cache = this.outputCache;
82
102
  if (cache?.task !== task || cache.read !== read || cache.width !== width)
83
103
  this.outputCache = { task, read, width, lines: outputLines(task, read, this.options.theme, width) };
84
104
  return this.outputCache.lines;
85
105
  }
86
- /** `status` carries the runtime, so Details rebuild at most once per displayed tick. */
87
- details(status) {
106
+ details(width) {
88
107
  const task = this.task;
89
108
  if (!task)
90
109
  return [];
91
110
  const cache = this.detailCache;
92
- if (cache?.task !== task || cache.status !== status)
93
- this.detailCache = { task, status, lines: detailLines(task, status, this.options.theme) };
111
+ if (cache?.task !== task || cache.width !== width)
112
+ this.detailCache = { task, width, lines: detailLines(task, this.options.theme, width) };
94
113
  return this.detailCache.lines;
95
114
  }
96
115
  dispose() {