@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":"inspector.js","sourceRoot":"","sources":["../../../../src/extensions/tasks/ui/inspector.ts"],"names":[],"mappings":"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","sourcesContent":["import type { TaskRead, TaskSnapshot, TasksContext } from \"../../../core/tasks/types.ts\";\nimport { isTaskTerminal } from \"../../../core/tasks/types.ts\";\nimport type { Theme } from \"../../../modes/interactive/theme/theme.ts\";\nimport { cleanTaskText } from \"./text.ts\";\nimport { TaskViewport } from \"./viewport.ts\";\nimport { detailLines, outputLines } from \"./views.ts\";\n\ninterface ReadingPosition {\n\tinfo: TaskViewport;\n\toutput: TaskViewport;\n\tread?: TaskRead;\n}\ninterface InspectorOptions {\n\thost: Pick<TasksContext, \"read\" | \"retain\">;\n\ttheme: Theme;\n\trequestRender(): void;\n}\n\n/** Owns the selected task's bounded reads. Reading never acknowledges completion delivery. */\nexport class TaskInspector {\n\ttask?: TaskSnapshot;\n\tprivate readonly options: InspectorOptions;\n\tprivate readonly positions = new Map<string, ReadingPosition>();\n\tprivate release?: () => void;\n\tprivate reading = false;\n\tprivate disposed = false;\n\t/** Snapshots are immutable, so identity tells when the lines must be rebuilt. */\n\tprivate outputCache?: { task: TaskSnapshot; read?: TaskRead; width: number; lines: string[] };\n\tprivate detailCache?: { task: TaskSnapshot; status: string; lines: string[] };\n\tconstructor(options: InspectorOptions) {\n\t\tthis.options = options;\n\t}\n\tget position(): ReadingPosition {\n\t\tconst id = this.task?.id;\n\t\tlet position = id ? this.positions.get(id) : undefined;\n\t\tif (!position) {\n\t\t\tposition = { info: new TaskViewport(), output: new TaskViewport(true) };\n\t\t\tif (id) this.positions.set(id, position);\n\t\t}\n\t\treturn position;\n\t}\n\tselect(task: TaskSnapshot | undefined): void {\n\t\tif (this.disposed) return;\n\t\tif (this.task?.id !== task?.id) {\n\t\t\tconst release = task ? this.options.host.retain(task.id) : undefined;\n\t\t\tconst previous = this.release;\n\t\t\tthis.release = release;\n\t\t\tprevious?.();\n\t\t}\n\t\tthis.task = task;\n\t}\n\tprune(ids: ReadonlySet<string>): void {\n\t\tfor (const id of this.positions.keys()) if (!ids.has(id)) this.positions.delete(id);\n\t}\n\tinvalidate(): void {\n\t\tthis.outputCache = undefined;\n\t\tthis.detailCache = undefined;\n\t}\n\tasync refresh(): Promise<void> {\n\t\tconst task = this.task;\n\t\tif (this.disposed || !task || this.reading) return;\n\t\tconst position = this.position;\n\t\tif (position.read && (!position.output.follow || isTaskTerminal(position.read.task.status))) return;\n\t\tthis.reading = true;\n\t\ttry {\n\t\t\tconst read = await this.options.host.read(task.id, { mode: \"tail\", bytes: 48 * 1024 });\n\t\t\tif (this.disposed || this.task?.id !== task.id || (!position.output.follow && position.read)) return;\n\t\t\tposition.read = read;\n\t\t} catch (error) {\n\t\t\tif (!this.disposed && this.task?.id === task.id && (position.output.follow || !position.read)) {\n\t\t\t\tposition.read = {\n\t\t\t\t\ttask,\n\t\t\t\t\ttext: \"\",\n\t\t\t\t\treadError: `Cannot read output: ${cleanTaskText(String(error))}`,\n\t\t\t\t\ttotalBytes: 0,\n\t\t\t\t\ttruncated: false,\n\t\t\t\t};\n\t\t\t}\n\t\t} finally {\n\t\t\tthis.reading = false;\n\t\t\tif (!this.disposed) this.options.requestRender();\n\t\t\tif (!this.disposed && this.task?.id !== task.id) await this.refresh();\n\t\t}\n\t}\n\toutput(width: number): string[] {\n\t\tconst task = this.task;\n\t\tif (!task) return [];\n\t\tconst read = this.position.read;\n\t\tconst cache = this.outputCache;\n\t\tif (cache?.task !== task || cache.read !== read || cache.width !== width)\n\t\t\tthis.outputCache = { task, read, width, lines: outputLines(task, read, this.options.theme, width) };\n\t\treturn this.outputCache!.lines;\n\t}\n\t/** `status` carries the runtime, so Details rebuild at most once per displayed tick. */\n\tdetails(status: string): string[] {\n\t\tconst task = this.task;\n\t\tif (!task) return [];\n\t\tconst cache = this.detailCache;\n\t\tif (cache?.task !== task || cache.status !== status)\n\t\t\tthis.detailCache = { task, status, lines: detailLines(task, status, this.options.theme) };\n\t\treturn this.detailCache!.lines;\n\t}\n\tdispose(): void {\n\t\tif (this.disposed) return;\n\t\tthis.disposed = true;\n\t\tthis.release?.();\n\t\tthis.release = undefined;\n\t\tthis.positions.clear();\n\t}\n}\n"]}
1
+ {"version":3,"file":"inspector.js","sourceRoot":"","sources":["../../../../src/extensions/tasks/ui/inspector.ts"],"names":[],"mappings":"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","sourcesContent":["import type { TaskRead, TaskSnapshot, TasksContext } from \"../../../core/tasks/types.ts\";\nimport { isTaskTerminal } from \"../../../core/tasks/types.ts\";\nimport type { Theme } from \"../../../modes/interactive/theme/theme.ts\";\nimport { cleanTaskText } from \"./text.ts\";\nimport { TaskViewport } from \"./viewport.ts\";\nimport { detailLines, outputLines, streamsLog } from \"./views.ts\";\n\ninterface ReadingPosition {\n\tinfo: TaskViewport;\n\toutput: TaskViewport;\n\t/** Whether Output last followed the tail; a change of kind restarts its viewport. */\n\ttails?: boolean;\n\tread?: TaskRead;\n}\ninterface InspectorOptions {\n\thost: Pick<TasksContext, \"read\" | \"retain\" | \"presentOutput\">;\n\ttheme: Theme;\n\trequestRender(): void;\n}\n\n/** Output follows the tail while it is a log or still live; a settled report reads from its first line. */\nfunction tailsOutput(task: TaskSnapshot): boolean {\n\treturn task.output === \"log\" || !isTaskTerminal(task.status);\n}\n\n/** Owns the selected task's bounded reads. Reading never acknowledges completion delivery. */\nexport class TaskInspector {\n\ttask?: TaskSnapshot;\n\tprivate readonly options: InspectorOptions;\n\tprivate readonly positions = new Map<string, ReadingPosition>();\n\tprivate release?: () => void;\n\tprivate reading = false;\n\tprivate disposed = false;\n\t/** Snapshots are immutable, so identity tells when the lines must be rebuilt. */\n\tprivate outputCache?: { task: TaskSnapshot; read?: TaskRead; width: number; lines: string[] };\n\tprivate detailCache?: { task: TaskSnapshot; width: number; lines: string[] };\n\tconstructor(options: InspectorOptions) {\n\t\tthis.options = options;\n\t}\n\tget position(): ReadingPosition {\n\t\tconst id = this.task?.id;\n\t\tlet position = id ? this.positions.get(id) : undefined;\n\t\tif (!position) {\n\t\t\tposition = { info: new TaskViewport(), output: new TaskViewport(true) };\n\t\t\tif (id) this.positions.set(id, position);\n\t\t}\n\t\treturn position;\n\t}\n\tselect(task: TaskSnapshot | undefined): void {\n\t\tif (this.disposed) return;\n\t\tif (this.task?.id !== task?.id) {\n\t\t\tconst release = task ? this.options.host.retain(task.id) : undefined;\n\t\t\tconst previous = this.release;\n\t\t\tthis.release = release;\n\t\t\tprevious?.();\n\t\t}\n\t\tthis.task = task;\n\t\tif (!task) return;\n\t\tconst position = this.position;\n\t\tconst tails = tailsOutput(task);\n\t\tif (position.tails !== tails) {\n\t\t\tposition.tails = tails;\n\t\t\tposition.output.retarget(tails);\n\t\t}\n\t}\n\tprune(ids: ReadonlySet<string>): void {\n\t\tfor (const id of this.positions.keys()) if (!ids.has(id)) this.positions.delete(id);\n\t}\n\tinvalidate(): void {\n\t\tthis.outputCache = undefined;\n\t\tthis.detailCache = undefined;\n\t}\n\tasync refresh(): Promise<void> {\n\t\tconst task = this.task;\n\t\t// Only an attached log streams. A terminal file-less log is read once for its expiry diagnostic.\n\t\tconst terminalFallback = task?.output === \"log\" && isTaskTerminal(task.status);\n\t\tif (this.disposed || !task || (!streamsLog(task) && !terminalFallback) || this.reading) return;\n\t\tconst position = this.position;\n\t\tif (position.read && (!position.output.follow || isTaskTerminal(position.read.task.status))) return;\n\t\tthis.reading = true;\n\t\ttry {\n\t\t\tconst read = await this.options.host.read(task.id, { mode: \"tail\", bytes: 48 * 1024 });\n\t\t\tif (this.disposed || this.task?.id !== task.id || (!position.output.follow && position.read)) return;\n\t\t\tposition.read = read;\n\t\t} catch (error) {\n\t\t\tif (!this.disposed && this.task?.id === task.id && (position.output.follow || !position.read)) {\n\t\t\t\tposition.read = {\n\t\t\t\t\ttask,\n\t\t\t\t\ttext: \"\",\n\t\t\t\t\treadError: `Cannot read output: ${cleanTaskText(String(error))}`,\n\t\t\t\t\ttotalBytes: 0,\n\t\t\t\t\ttruncated: false,\n\t\t\t\t};\n\t\t\t}\n\t\t} finally {\n\t\t\tthis.reading = false;\n\t\t\tif (!this.disposed) this.options.requestRender();\n\t\t\tif (!this.disposed && this.task?.id !== task.id) await this.refresh();\n\t\t}\n\t}\n\toutput(width: number): string[] {\n\t\tconst task = this.task;\n\t\tif (!task) return [];\n\t\t// A live presentation changes with time, not with the snapshot, so it is never cached.\n\t\tconst live = isTaskTerminal(task.status)\n\t\t\t? undefined\n\t\t\t: this.options.host.presentOutput(task.id, width, this.options.theme);\n\t\tif (live) return outputLines(task, undefined, this.options.theme, width, live);\n\t\tconst read = this.position.read;\n\t\tconst cache = this.outputCache;\n\t\tif (cache?.task !== task || cache.read !== read || cache.width !== width)\n\t\t\tthis.outputCache = { task, read, width, lines: outputLines(task, read, this.options.theme, width) };\n\t\treturn this.outputCache!.lines;\n\t}\n\tdetails(width: number): string[] {\n\t\tconst task = this.task;\n\t\tif (!task) return [];\n\t\tconst cache = this.detailCache;\n\t\tif (cache?.task !== task || cache.width !== width)\n\t\t\tthis.detailCache = { task, width, lines: detailLines(task, this.options.theme, width) };\n\t\treturn this.detailCache!.lines;\n\t}\n\tdispose(): void {\n\t\tif (this.disposed) return;\n\t\tthis.disposed = true;\n\t\tthis.release?.();\n\t\tthis.release = undefined;\n\t\tthis.positions.clear();\n\t}\n}\n"]}
@@ -1,26 +1,25 @@
1
- import type { AppKeybinding, KeybindingsManager } from "../../../core/keybindings.ts";
1
+ import type { KeybindingsManager } from "../../../core/keybindings.ts";
2
2
  import type { Theme } from "../../../modes/interactive/theme/theme.ts";
3
+ import { type Feedback } from "./footer.ts";
4
+ import { type Rect } from "./frame.ts";
3
5
  import type { TaskInspector } from "./inspector.ts";
4
6
  import type { TaskList } from "./list.ts";
5
- import { type PanelFocus, type PanelMode, type TaskSelection, type TasksTab } from "./model.ts";
6
- export interface TaskHit {
7
- x: number;
8
- y: number;
9
- width: number;
10
- key?: AppKeybinding;
11
- taskId?: string;
12
- tab?: TasksTab;
13
- cancel?: boolean;
7
+ import type { PanelFocus, PanelMode, TaskHit, TaskSelection } from "./model.ts";
8
+ /** A pane the pointer can act on: wheel scrolls it and a click on it focuses it. */
9
+ export interface PaneRegion extends Rect {
10
+ pane: PanelFocus | "help";
14
11
  }
15
12
  export interface TasksLayout {
13
+ lines: string[];
16
14
  hits: TaskHit[];
17
- listWidth: number;
15
+ regions: PaneRegion[];
16
+ /** Both panes are visible; otherwise one pane at a time fills the width. */
17
+ split: boolean;
18
18
  listPageSize: number;
19
- lines: string[];
20
19
  /** The terminal is below the supported size; only a full-screen resize notice is shown. */
21
20
  tooSmall?: boolean;
22
21
  }
23
- interface LayoutOptions {
22
+ export interface LayoutOptions {
24
23
  width: number;
25
24
  height: number;
26
25
  theme: Theme;
@@ -30,13 +29,11 @@ interface LayoutOptions {
30
29
  list: TaskList;
31
30
  focus: PanelFocus;
32
31
  mode: PanelMode;
33
- /** Renders the search input line; shown only in search mode. */
34
- searchLine: (width: number) => string;
35
- feedback?: string;
32
+ feedback?: Feedback;
36
33
  /** Whether the host would move this task to the background now. */
37
34
  canDetach: (id: string) => boolean;
38
35
  }
39
- /** Cell geometry and mouse targets are produced together, so resize cannot separate them. */
36
+ /** The list takes 40% of a split terminal, between 34 and 60 columns. */
37
+ export declare function listWidthFor(width: number): number;
40
38
  export declare function renderTasksLayout(options: LayoutOptions): TasksLayout;
41
- export {};
42
39
  //# sourceMappingURL=layout.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"layout.d.ts","sourceRoot":"","sources":["../../../../src/extensions/tasks/ui/layout.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,aAAa,EAAc,kBAAkB,EAAE,MAAM,8BAA8B,CAAC;AAKlG,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,2CAA2C,CAAC;AACvE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;AACpD,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAC;AAC1C,OAAO,EAAe,KAAK,UAAU,EAAE,KAAK,SAAS,EAAE,KAAK,aAAa,EAAE,KAAK,QAAQ,EAAE,MAAM,YAAY,CAAC;AAK7G,MAAM,WAAW,OAAO;IACvB,CAAC,EAAE,MAAM,CAAC;IACV,CAAC,EAAE,MAAM,CAAC;IACV,KAAK,EAAE,MAAM,CAAC;IACd,GAAG,CAAC,EAAE,aAAa,CAAC;IACpB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,GAAG,CAAC,EAAE,QAAQ,CAAC;IACf,MAAM,CAAC,EAAE,OAAO,CAAC;CACjB;AACD,MAAM,WAAW,WAAW;IAC3B,IAAI,EAAE,OAAO,EAAE,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,YAAY,EAAE,MAAM,CAAC;IACrB,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,2FAA2F;IAC3F,QAAQ,CAAC,EAAE,OAAO,CAAC;CACnB;AACD,UAAU,aAAa;IACtB,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,KAAK,CAAC;IACb,WAAW,EAAE,IAAI,CAAC,kBAAkB,EAAE,SAAS,CAAC,CAAC;IACjD,SAAS,EAAE,aAAa,CAAC;IACzB,SAAS,EAAE,aAAa,CAAC;IACzB,IAAI,EAAE,QAAQ,CAAC;IACf,KAAK,EAAE,UAAU,CAAC;IAClB,IAAI,EAAE,SAAS,CAAC;IAChB,gEAAgE;IAChE,UAAU,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,MAAM,CAAC;IACtC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,mEAAmE;IACnE,SAAS,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,OAAO,CAAC;CACnC;AAKD,6FAA6F;AAC7F,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,aAAa,GAAG,WAAW,CAgMrE"}
1
+ {"version":3,"file":"layout.d.ts","sourceRoot":"","sources":["../../../../src/extensions/tasks/ui/layout.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAc,kBAAkB,EAAE,MAAM,8BAA8B,CAAC;AAEnF,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,2CAA2C,CAAC;AACvE,OAAO,EAGN,KAAK,QAAQ,EAKb,MAAM,aAAa,CAAC;AACrB,OAAO,EAAsB,KAAK,IAAI,EAAE,MAAM,YAAY,CAAC;AAE3D,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;AACpD,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAC;AAC1C,OAAO,KAAK,EAAE,UAAU,EAAE,SAAS,EAAE,OAAO,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAWhF,oFAAoF;AACpF,MAAM,WAAW,UAAW,SAAQ,IAAI;IACvC,IAAI,EAAE,UAAU,GAAG,MAAM,CAAC;CAC1B;AAED,MAAM,WAAW,WAAW;IAC3B,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,IAAI,EAAE,OAAO,EAAE,CAAC;IAChB,OAAO,EAAE,UAAU,EAAE,CAAC;IACtB,4EAA4E;IAC5E,KAAK,EAAE,OAAO,CAAC;IACf,YAAY,EAAE,MAAM,CAAC;IACrB,2FAA2F;IAC3F,QAAQ,CAAC,EAAE,OAAO,CAAC;CACnB;AAED,MAAM,WAAW,aAAa;IAC7B,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,KAAK,CAAC;IACb,WAAW,EAAE,IAAI,CAAC,kBAAkB,EAAE,SAAS,CAAC,CAAC;IACjD,SAAS,EAAE,aAAa,CAAC;IACzB,SAAS,EAAE,aAAa,CAAC;IACzB,IAAI,EAAE,QAAQ,CAAC;IACf,KAAK,EAAE,UAAU,CAAC;IAClB,IAAI,EAAE,SAAS,CAAC;IAChB,QAAQ,CAAC,EAAE,QAAQ,CAAC;IACpB,mEAAmE;IACnE,SAAS,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,OAAO,CAAC;CACnC;AAED,yEAAyE;AACzE,wBAAgB,YAAY,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAElD;AAuBD,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,aAAa,GAAG,WAAW,CAgDrE"}
@@ -1,271 +1,178 @@
1
- import { truncateToWidth, visibleWidth, wrapTextWithAnsi } from "@earendil-works/pi-tui";
2
- import { runtimeLabel } from "../../../core/tasks/format.js";
3
- import { isTaskTerminal } from "../../../core/tasks/types.js";
4
- import { formatSize } from "../../../core/tools/truncate.js";
1
+ /**
2
+ * Screen layout. This is the only place that knows widths and row positions: it returns the
3
+ * rendered lines together with the clickable cells and the pane regions the manager routes
4
+ * wheel and focus gestures by, so a resize cannot separate geometry from targets.
5
+ */
6
+ import { visibleWidth, wrapTextWithAnsi } from "@earendil-works/pi-tui";
5
7
  import { keyLabel } from "../../../modes/interactive/components/keybinding-hints.js";
8
+ import { browseFooter, confirmStopFooter, helpFooter, searchFooter, } from "./footer.js";
9
+ import { fitCell, framePane } from "./frame.js";
10
+ import { helpRows } from "./help.js";
6
11
  import { isStoppable } from "./model.js";
7
- import { modeName, statusName, statusView } from "./status.js";
12
+ import { renderInspectorPane, renderListPane } from "./panes.js";
8
13
  import { cleanTaskText, firstCommandLine } from "./text.js";
9
- const pad = (text, width) => truncateToWidth(text, Math.max(1, width), "…", true);
10
- /** Visible width of "Output Details". */
11
- const TABS_WIDTH = 15;
12
- /** Cell geometry and mouse targets are produced together, so resize cannot separate them. */
14
+ /** A terminal at least this wide shows the list beside the inspector. */
15
+ const SPLIT_MIN_WIDTH = 100;
16
+ /** The minimum supported terminal; below it a resize notice covers the screen. */
17
+ const MIN_WIDTH = 60;
18
+ const MIN_HEIGHT = 14;
19
+ /** The list takes 40% of a split terminal, between 34 and 60 columns. */
20
+ export function listWidthFor(width) {
21
+ return Math.min(60, Math.max(34, Math.round(width * 0.4)));
22
+ }
23
+ /** Pane rectangles above the single footer row; without room for two, both panes share the full width. */
24
+ function paneRects(width, height) {
25
+ const rows = height - 1;
26
+ if (width < SPLIT_MIN_WIDTH) {
27
+ const full = { x: 0, y: 0, width, height: rows };
28
+ return { split: false, list: full, inspector: full };
29
+ }
30
+ const listWidth = listWidthFor(width);
31
+ return {
32
+ split: true,
33
+ list: { x: 0, y: 0, width: listWidth, height: rows },
34
+ inspector: { x: listWidth + 1, y: 0, width: width - listWidth - 1, height: rows },
35
+ };
36
+ }
13
37
  export function renderTasksLayout(options) {
14
- const { width, height, theme, selection, inspector, list, focus, keybindings, mode } = options;
15
- const hint = (key) => keyLabel(key, { keybindings });
16
- const result = { hits: [], listWidth: 0, listPageSize: 1, lines: [] };
38
+ const { width, height, theme, mode } = options;
17
39
  if (width < 1 || height < 1)
18
- return result;
19
- const wide = width >= 100;
20
- const listWidth = wide ? Math.min(42, Math.max(32, Math.floor(width * 0.32))) : width;
21
- const rightWidth = wide ? width - listWidth - 1 : width;
22
- const rightX = wide ? listWidth + 1 : 0;
23
- const bodyHeight = height - 5;
24
- const hits = result.hits;
25
- result.listWidth = listWidth;
26
- // One row per task, less the group headings.
27
- result.listPageSize = Math.max(1, bodyHeight - 2);
28
- const pane = (lines, size, active) => {
29
- const border = (text) => theme.fg(wide && active ? "borderAccent" : "borderMuted", text);
30
- return [
31
- border(`┌${"─".repeat(size - 2)}┐`),
32
- ...lines.map((line) => border("│") + pad(line, size - 2) + border("│")),
33
- border(`└${"─".repeat(size - 2)}┘`),
34
- ];
35
- };
36
- if (width < 60 || height < 14) {
37
- // The panel is modal: a floating notice would leave the transcript visible yet
38
- // unscrollable, so the notice owns every row as a centered dialog. Only a truly
39
- // tiny terminal, where even the dialog does not fit, gets bare covering lines.
40
- result.tooSmall = true;
41
- const message = "Terminal too small for /tasks. Enlarge the window to continue.";
42
- const close = `${hint("tui.select.cancel")} close`;
43
- const boxWidth = Math.min(width, 46);
44
- const inner = boxWidth - 2;
45
- const wrapped = wrapTextWithAnsi(message, inner).map((line) => theme.fg("warning", line));
46
- const spacious = [theme.fg("text", "Tasks"), "", ...wrapped, "", theme.fg("dim", close)];
47
- const tight = [theme.fg("text", "Tasks"), ...wrapped, theme.fg("dim", close)];
48
- const body = height >= spacious.length + 2 ? spacious : tight;
49
- if (inner >= 16 && height >= body.length + 2) {
50
- const box = pane(body, boxWidth, false);
51
- const left = Math.floor((width - boxWidth) / 2);
52
- const top = Math.max(0, Math.floor((height - box.length) / 2));
53
- const blank = " ".repeat(width);
54
- result.lines = [
55
- ...Array.from({ length: top }, () => blank),
56
- ...box.map((line) => " ".repeat(left) + line + " ".repeat(width - left - boxWidth)),
57
- ...Array.from({ length: height - top - box.length }, () => blank),
58
- ];
59
- hits.push({ x: left + 1, y: top + body.length, width: visibleWidth(close), cancel: true });
40
+ return { lines: [], hits: [], regions: [], split: false, listPageSize: 1 };
41
+ const hint = (key) => keyLabel(key, { keybindings: options.keybindings });
42
+ if (width < MIN_WIDTH || height < MIN_HEIGHT)
43
+ return renderTooSmall(options, hint);
44
+ const rects = paneRects(width, height);
45
+ const env = { width, y: height - 1, theme, hint, feedback: options.feedback };
46
+ let body;
47
+ let footer;
48
+ switch (mode.kind) {
49
+ case "help": {
50
+ body = renderHelpPane(options, hint, { x: 0, y: 0, width, height: height - 1 });
51
+ footer = helpFooter(env);
52
+ break;
60
53
  }
61
- else {
62
- result.lines = ["Tasks", message, close].slice(0, height).map((line) => pad(line, width));
63
- while (result.lines.length < height)
64
- result.lines.push(" ".repeat(width));
54
+ case "search": {
55
+ body = renderPanes(options, hint, rects);
56
+ footer = searchFooter(env);
57
+ break;
65
58
  }
66
- return result;
67
- }
68
- const visible = list.render(selection, theme, listWidth - 2, bodyHeight);
69
- let listHeader = theme.fg("text", "Tasks");
70
- const searchLabel = `${hint("app.tasks.search")} find`;
71
- if (mode.kind === "search")
72
- listHeader = options.searchLine(listWidth - 2);
73
- else {
74
- const x = Math.max(6, listWidth - 2 - visibleWidth(searchLabel));
75
- listHeader += " ".repeat(x - 5) + theme.fg("dim", searchLabel);
76
- if (wide || focus === "list")
77
- hits.push({ x: x + 1, y: 1, width: visibleWidth(searchLabel), key: "app.tasks.search" });
78
- }
79
- if (wide || focus === "list")
80
- visible.forEach((row, index) => {
81
- if (row.taskId)
82
- hits.push({ x: 1, y: 2 + index, width: listWidth - 2, taskId: row.taskId });
83
- });
84
- const left = [
85
- listHeader,
86
- ...Array.from({ length: bodyHeight }, (_, i) => visible[i]?.text ?? ""),
87
- theme.fg("dim", list.range),
88
- ];
89
- const task = inspector.task;
90
- const tab = selection.state.tab;
91
- const tabLabel = (id, label) => theme.style(label, { fg: "text", underline: tab === id });
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
- ];
105
- if (wide || focus === "inspector")
106
- hits.push({ x: rightX + 1 + tabsX, y: 1, width: 6, tab: "output" }, { x: rightX + 9 + tabsX, y: 1, width: 7, tab: "info" });
107
- const content = [];
108
- let reading = "";
109
- if (task) {
110
- // Without the list beside it, the inspector names the task it shows.
111
- if (!wide)
112
- content.push(theme.fg("text", cleanTaskText(firstCommandLine(task.title))));
113
- const position = inspector.position;
114
- const read = position.read;
115
- const terminal = isTaskTerminal(task.status);
116
- const diagnostics = [task.error, read?.readError].filter((text) => !!text).map(cleanTaskText);
117
- if (tab === "output" && diagnostics.length)
118
- content.push(theme.fg("warning", diagnostics.join(" · ").replace(/\n/g, " ")));
119
- const source = tab === "info"
120
- ? inspector.details(`${statusName(task.status)} · ${modeName(task.mode)} · ${runtimeLabel(task)}`)
121
- : inspector.output(rightWidth - 2);
122
- content.push(...position[tab].layout(source, rightWidth - 2, bodyHeight - content.length));
123
- if (tab === "output") {
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"))
135
- hits.push({
136
- x: rightX + 1,
137
- y: height - 3,
138
- width: Math.min(rightWidth - 2, visibleWidth(reading)),
139
- key: "app.tasks.follow",
140
- });
141
- if (task.outputPath !== undefined && read?.truncated)
142
- reading += ` · Tail preview: ${formatSize(Buffer.byteLength(read.text))} of ${formatSize(read.totalBytes)}`;
59
+ case "confirmStop": {
60
+ body = renderPanes(options, hint, rects);
61
+ const target = options.selection.all.find((task) => task.id === mode.taskId);
62
+ footer = confirmStopFooter(env, {
63
+ source: cleanTaskText(target?.label ?? "task"),
64
+ title: cleanTaskText(firstCommandLine(target?.title ?? mode.taskId)),
65
+ });
66
+ break;
67
+ }
68
+ case "browse": {
69
+ body = renderPanes(options, hint, rects);
70
+ const task = options.inspector.task;
71
+ footer = browseFooter(env, {
72
+ focus: options.focus,
73
+ split: rects.split,
74
+ stoppable: !!task && isStoppable(task),
75
+ detachable: !!task && options.canDetach(task.id),
76
+ });
77
+ break;
143
78
  }
144
- else if (tab === "info")
145
- reading = `Details ${position.info.range}`;
146
79
  }
147
- else
148
- content.push(theme.fg("muted", "Select a task to inspect its result."));
149
- right.push(...Array.from({ length: bodyHeight }, (_, i) => content[i] ?? ""), theme.fg("dim", reading));
150
- const rightPane = pane(right, rightWidth, focus === "inspector");
151
- let panels = wide
152
- ? pane(left, listWidth, focus === "list").map((line, i) => `${line} ${rightPane[i]}`)
153
- : pane(focus === "list" ? left : right, width, false);
154
- let footer = "";
155
- const addHint = (key, label, target) => {
156
- const text = `${hint(key)} ${label}`;
157
- const x = visibleWidth(footer) + (footer ? 3 : 0);
158
- if (x + visibleWidth(text) > width)
159
- return;
160
- footer += (footer ? " · " : "") + text;
161
- if (target)
162
- hits.push({ x, y: height - 1, width: visibleWidth(text), ...target });
80
+ return {
81
+ lines: [...body.lines, footer.line],
82
+ hits: [...body.hits, ...footer.hits],
83
+ regions: body.regions,
84
+ split: rects.split,
85
+ listPageSize: body.pageSize ?? Math.max(1, rects.list.height - 4),
163
86
  };
164
- if (mode.kind === "help") {
165
- hits.length = 0;
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 });
169
- }
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") {
186
- // Only search candidates and cancellation are clickable while locating a task.
187
- for (let i = hits.length - 1; i >= 0; i--)
188
- if (!hits[i].taskId)
189
- hits.splice(i, 1);
190
- addHint("tui.select.up", `/ ${hint("tui.select.down")} choose`);
191
- addHint("tui.select.confirm", "locate");
192
- addHint("tui.select.cancel", "cancel", { cancel: true });
87
+ }
88
+ /**
89
+ * Browse, search and confirmation share the two-pane frame; the mode decides which panes
90
+ * answer to the pointer, so each mode exposes only its own targets.
91
+ */
92
+ function renderPanes(options, hint, rects) {
93
+ const { theme, selection, inspector, list, focus, mode } = options;
94
+ const showList = rects.split || focus === "list";
95
+ const showInspector = rects.split || focus === "inspector";
96
+ const browsing = mode.kind === "browse";
97
+ const searching = mode.kind === "search";
98
+ const listPane = showList
99
+ ? renderListPane({ theme, selection, list }, rects.list, {
100
+ active: rects.split && focus === "list",
101
+ interactive: browsing || searching,
102
+ searchRow: mode.kind === "search" ? mode.input.render(rects.list.width - 2).join("") : undefined,
103
+ stopTargetId: mode.kind === "confirmStop" ? mode.taskId : undefined,
104
+ })
105
+ : undefined;
106
+ const inspectorPane = showInspector
107
+ ? renderInspectorPane({ theme, selection, inspector, hint }, rects.inspector, {
108
+ active: rects.split && focus === "inspector",
109
+ interactive: browsing,
110
+ })
111
+ : undefined;
112
+ const regions = [];
113
+ if (listPane && (browsing || searching))
114
+ regions.push({ pane: "list", ...rects.list });
115
+ if (inspectorPane && browsing)
116
+ regions.push({ pane: "inspector", ...rects.inspector });
117
+ const lines = listPane && inspectorPane
118
+ ? listPane.lines.map((line, i) => `${line} ${inspectorPane.lines[i]}`)
119
+ : (listPane ?? inspectorPane).lines;
120
+ return {
121
+ lines,
122
+ hits: [...(listPane?.hits ?? []), ...(inspectorPane?.hits ?? [])],
123
+ regions,
124
+ pageSize: listPane?.pageSize ?? Math.max(1, rects.list.height - 4),
125
+ };
126
+ }
127
+ function renderHelpPane(options, hint, rect) {
128
+ const { theme, mode } = options;
129
+ if (mode.kind !== "help")
130
+ throw new Error("help pane requires help mode");
131
+ const inner = rect.width - 2;
132
+ const rows = mode.viewport.layout(helpRows(hint, theme), inner, rect.height - 2);
133
+ const frame = framePane({
134
+ width: rect.width,
135
+ theme,
136
+ accent: false,
137
+ title: theme.fg("text", "Task controls"),
138
+ status: mode.viewport.overflows ? theme.fg("muted", mode.viewport.range) : undefined,
139
+ rows: Array.from({ length: rect.height - 2 }, (_, i) => rows[i] ?? ""),
140
+ });
141
+ return { lines: frame.lines, hits: [], regions: [{ pane: "help", ...rect }] };
142
+ }
143
+ /**
144
+ * The panel is modal: a floating notice would leave the transcript visible yet unscrollable,
145
+ * so the notice owns every row as a centered dialog. Only a truly tiny terminal, where even
146
+ * the dialog does not fit, gets bare covering lines.
147
+ */
148
+ function renderTooSmall(options, hint) {
149
+ const { width, height, theme } = options;
150
+ const result = { lines: [], hits: [], regions: [], split: false, listPageSize: 1, tooSmall: true };
151
+ const message = "Terminal too small for /tasks. Enlarge the window to continue.";
152
+ const close = `${hint("tui.select.cancel")} close`;
153
+ const boxWidth = Math.min(width, 46);
154
+ const inner = boxWidth - 2;
155
+ const wrapped = wrapTextWithAnsi(message, inner).map((line) => theme.fg("warning", line));
156
+ const spacious = [theme.fg("text", "Tasks"), "", ...wrapped, "", theme.fg("dim", close)];
157
+ const tight = [theme.fg("text", "Tasks"), ...wrapped, theme.fg("dim", close)];
158
+ const body = height >= spacious.length + 2 ? spacious : tight;
159
+ if (inner >= 16 && height >= body.length + 2) {
160
+ const box = framePane({ width: boxWidth, theme, accent: false, rows: body }).lines;
161
+ const left = Math.floor((width - boxWidth) / 2);
162
+ const top = Math.max(0, Math.floor((height - box.length) / 2));
163
+ const blank = " ".repeat(width);
164
+ result.lines = [
165
+ ...Array.from({ length: top }, () => blank),
166
+ ...box.map((line) => " ".repeat(left) + line + " ".repeat(width - left - boxWidth)),
167
+ ...Array.from({ length: height - top - box.length }, () => blank),
168
+ ];
169
+ result.hits.push({ x: left + 1, y: top + body.length, width: visibleWidth(close), action: { kind: "back" } });
193
170
  }
194
171
  else {
195
- addHint("tui.select.up", `/ ${hint("tui.select.down")}${focus === "list" ? " tasks" : " scroll"}`);
196
- if (focus === "list")
197
- addHint("tui.select.confirm", "open", { tab });
198
- else
199
- addHint("app.tasks.nextFocus", "tasks", { key: "app.tasks.nextFocus" });
200
- addHint("tui.select.cancel", "close", { cancel: true });
201
- if (focus === "list")
202
- addHint("app.tasks.search", "find", { key: "app.tasks.search" });
203
- else {
204
- addHint("app.tasks.previousTab", "output", { tab: "output" });
205
- addHint("app.tasks.nextTab", "details", { tab: "info" });
206
- }
207
- if (task && isStoppable(task))
208
- addHint("app.tasks.kill", "stop", { key: "app.tasks.kill" });
209
- if (task && options.canDetach(task.id))
210
- addHint("app.tasks.detachSelected", "background", { key: "app.tasks.detachSelected" });
211
- addHint("app.tasks.help", "keys", { key: "app.tasks.help" });
212
- // Feedback borrows the hint row, so the hint hit targets stay live and the click that
213
- // dismisses it can act in the same gesture.
214
- if (options.feedback)
215
- footer = options.feedback;
172
+ result.lines = ["Tasks", message, close].slice(0, height).map((line) => fitCell(line, width));
173
+ while (result.lines.length < height)
174
+ result.lines.push(" ".repeat(width));
216
175
  }
217
- result.lines = [...panels, theme.fg(mode.kind === "confirmStop" ? "warning" : "dim", pad(footer, width))];
218
176
  return result;
219
177
  }
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
- }
271
178
  //# sourceMappingURL=layout.js.map