@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.
- package/CHANGELOG.md +54 -0
- package/dist/bundle/chunks/{chunk-3PKXUKTB.js → chunk-6GTUUUCJ.js} +123 -123
- package/dist/bundle/chunks/{chunk-BAGHIP5J.js → chunk-MF3T3ETJ.js} +3 -3
- package/dist/bundle/chunks/{chunk-F66AY3WR.js → chunk-UCH27AVQ.js} +1 -1
- package/dist/bundle/chunks/{execute-P22VMM2H.js → execute-FQKRO4XJ.js} +1 -1
- package/dist/bundle/chunks/{virtual-modules-VKMBQTR2.js → virtual-modules-6IJ6V2CG.js} +1 -1
- package/dist/bundle/cli.js +1 -1
- package/dist/bundle/index.js +1 -1
- package/dist/bundle/rpc-entry.js +1 -1
- package/dist/core/agent-session.d.ts +34 -0
- package/dist/core/agent-session.d.ts.map +1 -1
- package/dist/core/agent-session.js +99 -2
- package/dist/core/agent-session.js.map +1 -1
- package/dist/core/isolated-session.d.ts +7 -2
- package/dist/core/isolated-session.d.ts.map +1 -1
- package/dist/core/isolated-session.js +7 -3
- package/dist/core/isolated-session.js.map +1 -1
- package/dist/core/session-manager.d.ts +17 -1
- package/dist/core/session-manager.d.ts.map +1 -1
- package/dist/core/session-manager.js +29 -0
- package/dist/core/session-manager.js.map +1 -1
- package/dist/core/tasks/delivery.d.ts +42 -7
- package/dist/core/tasks/delivery.d.ts.map +1 -1
- package/dist/core/tasks/delivery.js +129 -15
- package/dist/core/tasks/delivery.js.map +1 -1
- package/dist/core/tasks/execution.d.ts +27 -14
- package/dist/core/tasks/execution.d.ts.map +1 -1
- package/dist/core/tasks/execution.js +90 -86
- package/dist/core/tasks/execution.js.map +1 -1
- package/dist/core/tasks/limits.d.ts +13 -1
- package/dist/core/tasks/limits.d.ts.map +1 -1
- package/dist/core/tasks/limits.js +14 -1
- package/dist/core/tasks/limits.js.map +1 -1
- package/dist/core/tasks/output.d.ts +9 -1
- package/dist/core/tasks/output.d.ts.map +1 -1
- package/dist/core/tasks/output.js +23 -10
- package/dist/core/tasks/output.js.map +1 -1
- package/dist/core/tasks/presentation.d.ts +1 -1
- package/dist/core/tasks/presentation.d.ts.map +1 -1
- package/dist/core/tasks/presentation.js +6 -4
- package/dist/core/tasks/presentation.js.map +1 -1
- package/dist/core/tasks/record.d.ts +41 -29
- package/dist/core/tasks/record.d.ts.map +1 -1
- package/dist/core/tasks/record.js +82 -37
- package/dist/core/tasks/record.js.map +1 -1
- package/dist/core/tasks/reminder.js +2 -2
- package/dist/core/tasks/reminder.js.map +1 -1
- package/dist/core/tasks/runtime.d.ts +17 -14
- package/dist/core/tasks/runtime.d.ts.map +1 -1
- package/dist/core/tasks/runtime.js +68 -116
- package/dist/core/tasks/runtime.js.map +1 -1
- package/dist/core/tasks/scheduler.d.ts +3 -1
- package/dist/core/tasks/scheduler.d.ts.map +1 -1
- package/dist/core/tasks/scheduler.js +16 -12
- package/dist/core/tasks/scheduler.js.map +1 -1
- package/dist/core/tasks/session.d.ts +24 -3
- package/dist/core/tasks/session.d.ts.map +1 -1
- package/dist/core/tasks/session.js +73 -9
- package/dist/core/tasks/session.js.map +1 -1
- package/dist/core/tasks/store.d.ts +17 -4
- package/dist/core/tasks/store.d.ts.map +1 -1
- package/dist/core/tasks/store.js +45 -48
- package/dist/core/tasks/store.js.map +1 -1
- package/dist/core/tasks/types.d.ts +72 -29
- package/dist/core/tasks/types.d.ts.map +1 -1
- package/dist/core/tasks/types.js +7 -0
- package/dist/core/tasks/types.js.map +1 -1
- package/dist/core/tasks/wait.d.ts +19 -0
- package/dist/core/tasks/wait.d.ts.map +1 -0
- package/dist/core/tasks/wait.js +45 -0
- package/dist/core/tasks/wait.js.map +1 -0
- package/dist/core/tools/shell-execution.d.ts.map +1 -1
- package/dist/core/tools/shell-execution.js +2 -1
- package/dist/core/tools/shell-execution.js.map +1 -1
- package/dist/core/tools/shell-tool.d.ts.map +1 -1
- package/dist/core/tools/shell-tool.js +20 -13
- package/dist/core/tools/shell-tool.js.map +1 -1
- package/dist/extensions/agent/activity-lines.d.ts +10 -0
- package/dist/extensions/agent/activity-lines.d.ts.map +1 -0
- package/dist/extensions/agent/activity-lines.js +76 -0
- package/dist/extensions/agent/activity-lines.js.map +1 -0
- package/dist/extensions/agent/activity.d.ts +25 -4
- package/dist/extensions/agent/activity.d.ts.map +1 -1
- package/dist/extensions/agent/activity.js +76 -21
- package/dist/extensions/agent/activity.js.map +1 -1
- package/dist/extensions/agent/artifacts.d.ts +15 -0
- package/dist/extensions/agent/artifacts.d.ts.map +1 -0
- package/dist/extensions/agent/artifacts.js +44 -0
- package/dist/extensions/agent/artifacts.js.map +1 -0
- package/dist/extensions/agent/progress.d.ts +30 -4
- package/dist/extensions/agent/progress.d.ts.map +1 -1
- package/dist/extensions/agent/progress.js +84 -11
- package/dist/extensions/agent/progress.js.map +1 -1
- package/dist/extensions/agent/render.d.ts.map +1 -1
- package/dist/extensions/agent/render.js +8 -24
- package/dist/extensions/agent/render.js.map +1 -1
- package/dist/extensions/agent/report.d.ts +11 -3
- package/dist/extensions/agent/report.d.ts.map +1 -1
- package/dist/extensions/agent/report.js +32 -7
- package/dist/extensions/agent/report.js.map +1 -1
- package/dist/extensions/agent/tool.d.ts.map +1 -1
- package/dist/extensions/agent/tool.js +20 -5
- package/dist/extensions/agent/tool.js.map +1 -1
- package/dist/extensions/agent/types.d.ts +2 -0
- package/dist/extensions/agent/types.d.ts.map +1 -1
- package/dist/extensions/agent/types.js.map +1 -1
- package/dist/extensions/agent/worker.d.ts +7 -0
- package/dist/extensions/agent/worker.d.ts.map +1 -1
- package/dist/extensions/agent/worker.js +9 -3
- package/dist/extensions/agent/worker.js.map +1 -1
- package/dist/extensions/search/index.d.ts.map +1 -1
- package/dist/extensions/search/index.js +6 -7
- package/dist/extensions/search/index.js.map +1 -1
- package/dist/extensions/tasks/tool/actions.d.ts.map +1 -1
- package/dist/extensions/tasks/tool/actions.js +15 -11
- package/dist/extensions/tasks/tool/actions.js.map +1 -1
- package/dist/extensions/tasks/tool/schema.d.ts +1 -1
- package/dist/extensions/tasks/tool/schema.d.ts.map +1 -1
- package/dist/extensions/tasks/tool/schema.js +5 -5
- package/dist/extensions/tasks/tool/schema.js.map +1 -1
- package/dist/extensions/tasks/tool/types.d.ts +2 -0
- package/dist/extensions/tasks/tool/types.d.ts.map +1 -1
- package/dist/extensions/tasks/tool/types.js.map +1 -1
- package/dist/extensions/tasks/tool.d.ts.map +1 -1
- package/dist/extensions/tasks/tool.js +3 -2
- package/dist/extensions/tasks/tool.js.map +1 -1
- package/dist/extensions/tasks/ui/completion-render.d.ts.map +1 -1
- package/dist/extensions/tasks/ui/completion-render.js +2 -2
- package/dist/extensions/tasks/ui/completion-render.js.map +1 -1
- package/dist/extensions/tasks/ui/footer.d.ts +37 -0
- package/dist/extensions/tasks/ui/footer.d.ts.map +1 -0
- package/dist/extensions/tasks/ui/footer.js +129 -0
- package/dist/extensions/tasks/ui/footer.js.map +1 -0
- package/dist/extensions/tasks/ui/frame.d.ts +36 -0
- package/dist/extensions/tasks/ui/frame.d.ts.map +1 -0
- package/dist/extensions/tasks/ui/frame.js +33 -0
- package/dist/extensions/tasks/ui/frame.js.map +1 -0
- package/dist/extensions/tasks/ui/help.d.ts +6 -0
- package/dist/extensions/tasks/ui/help.d.ts.map +1 -0
- package/dist/extensions/tasks/ui/help.js +53 -0
- package/dist/extensions/tasks/ui/help.js.map +1 -0
- package/dist/extensions/tasks/ui/inspector.d.ts +4 -3
- package/dist/extensions/tasks/ui/inspector.d.ts.map +1 -1
- package/dist/extensions/tasks/ui/inspector.js +25 -6
- package/dist/extensions/tasks/ui/inspector.js.map +1 -1
- package/dist/extensions/tasks/ui/layout.d.ts +15 -18
- package/dist/extensions/tasks/ui/layout.d.ts.map +1 -1
- package/dist/extensions/tasks/ui/layout.js +162 -255
- package/dist/extensions/tasks/ui/layout.js.map +1 -1
- package/dist/extensions/tasks/ui/list.d.ts +3 -1
- package/dist/extensions/tasks/ui/list.d.ts.map +1 -1
- package/dist/extensions/tasks/ui/list.js +19 -14
- package/dist/extensions/tasks/ui/list.js.map +1 -1
- package/dist/extensions/tasks/ui/manager.d.ts +17 -5
- package/dist/extensions/tasks/ui/manager.d.ts.map +1 -1
- package/dist/extensions/tasks/ui/manager.js +147 -90
- package/dist/extensions/tasks/ui/manager.js.map +1 -1
- package/dist/extensions/tasks/ui/model.d.ts +32 -5
- package/dist/extensions/tasks/ui/model.d.ts.map +1 -1
- package/dist/extensions/tasks/ui/model.js +7 -7
- package/dist/extensions/tasks/ui/model.js.map +1 -1
- package/dist/extensions/tasks/ui/panes.d.ts +40 -0
- package/dist/extensions/tasks/ui/panes.d.ts.map +1 -0
- package/dist/extensions/tasks/ui/panes.js +144 -0
- package/dist/extensions/tasks/ui/panes.js.map +1 -0
- package/dist/extensions/tasks/ui/render.d.ts.map +1 -1
- package/dist/extensions/tasks/ui/render.js +11 -7
- package/dist/extensions/tasks/ui/render.js.map +1 -1
- package/dist/extensions/tasks/ui/viewport.d.ts +12 -4
- package/dist/extensions/tasks/ui/viewport.d.ts.map +1 -1
- package/dist/extensions/tasks/ui/viewport.js +23 -7
- package/dist/extensions/tasks/ui/viewport.js.map +1 -1
- package/dist/extensions/tasks/ui/views.d.ts +12 -6
- package/dist/extensions/tasks/ui/views.d.ts.map +1 -1
- package/dist/extensions/tasks/ui/views.js +39 -32
- package/dist/extensions/tasks/ui/views.js.map +1 -1
- package/dist/modes/interactive/components/session-selector.d.ts +11 -1
- package/dist/modes/interactive/components/session-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/session-selector.js +43 -17
- package/dist/modes/interactive/components/session-selector.js.map +1 -1
- package/dist/modes/print-mode.d.ts.map +1 -1
- package/dist/modes/print-mode.js +37 -2
- package/dist/modes/print-mode.js.map +1 -1
- package/dist/modes/rpc/rpc-client.d.ts +11 -0
- package/dist/modes/rpc/rpc-client.d.ts.map +1 -1
- package/dist/modes/rpc/rpc-client.js +48 -0
- package/dist/modes/rpc/rpc-client.js.map +1 -1
- package/dist/modes/rpc/rpc-mode.d.ts.map +1 -1
- package/dist/modes/rpc/rpc-mode.js +1 -0
- package/dist/modes/rpc/rpc-mode.js.map +1 -1
- package/dist/modes/rpc/rpc-types.d.ts +2 -0
- package/dist/modes/rpc/rpc-types.d.ts.map +1 -1
- package/dist/modes/rpc/rpc-types.js.map +1 -1
- package/docs/bundled/extensions/agent.md +39 -7
- package/docs/bundled/tasks.md +25 -21
- package/docs/extensions.md +14 -10
- package/docs/json.md +3 -0
- package/docs/rpc.md +14 -0
- package/docs/sdk.md +11 -1
- package/docs/session-format.md +10 -4
- package/docs/sessions.md +1 -1
- package/docs/usage.md +2 -0
- package/npm-shrinkwrap.json +2 -2
- 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":"AACA,OAAO,EAAE,cAAc,EAAE,MAAM,8BAA8B,CAAC;AAE9D,OAAO,EAAE,aAAa,EAAE,MAAM,WAAW,CAAC;AAC1C,OAAO,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAC7C,OAAO,EAAE,WAAW,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAelE,2GAA2G;AAC3G,SAAS,WAAW,CAAC,IAAkB;IACtC,OAAO,IAAI,CAAC,MAAM,KAAK,KAAK,IAAI,CAAC,cAAc,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;AAC9D,CAAC;AAED,8FAA8F;AAC9F,MAAM,OAAO,aAAa;IAUzB,YAAY,OAAyB;QAPpB,cAAS,GAAG,IAAI,GAAG,EAA2B,CAAC;QAExD,YAAO,GAAG,KAAK,CAAC;QAChB,aAAQ,GAAG,KAAK,CAAC;QAKxB,IAAI,CAAC,OAAO,GAAG,OAAO,CAAC;IACxB,CAAC;IACD,IAAI,QAAQ;QACX,MAAM,EAAE,GAAG,IAAI,CAAC,IAAI,EAAE,EAAE,CAAC;QACzB,IAAI,QAAQ,GAAG,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;QACvD,IAAI,CAAC,QAAQ,EAAE,CAAC;YACf,QAAQ,GAAG,EAAE,IAAI,EAAE,IAAI,YAAY,EAAE,EAAE,MAAM,EAAE,IAAI,YAAY,CAAC,IAAI,CAAC,EAAE,CAAC;YACxE,IAAI,EAAE;gBAAE,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,EAAE,EAAE,QAAQ,CAAC,CAAC;QAC1C,CAAC;QACD,OAAO,QAAQ,CAAC;IACjB,CAAC;IACD,MAAM,CAAC,IAA8B;QACpC,IAAI,IAAI,CAAC,QAAQ;YAAE,OAAO;QAC1B,IAAI,IAAI,CAAC,IAAI,EAAE,EAAE,KAAK,IAAI,EAAE,EAAE,EAAE,CAAC;YAChC,MAAM,OAAO,GAAG,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;YACrE,MAAM,QAAQ,GAAG,IAAI,CAAC,OAAO,CAAC;YAC9B,IAAI,CAAC,OAAO,GAAG,OAAO,CAAC;YACvB,QAAQ,EAAE,EAAE,CAAC;QACd,CAAC;QACD,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;QACjB,IAAI,CAAC,IAAI;YAAE,OAAO;QAClB,MAAM,QAAQ,GAAG,IAAI,CAAC,QAAQ,CAAC;QAC/B,MAAM,KAAK,GAAG,WAAW,CAAC,IAAI,CAAC,CAAC;QAChC,IAAI,QAAQ,CAAC,KAAK,KAAK,KAAK,EAAE,CAAC;YAC9B,QAAQ,CAAC,KAAK,GAAG,KAAK,CAAC;YACvB,QAAQ,CAAC,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;QACjC,CAAC;IACF,CAAC;IACD,KAAK,CAAC,GAAwB;QAC7B,KAAK,MAAM,EAAE,IAAI,IAAI,CAAC,SAAS,CAAC,IAAI,EAAE;YAAE,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC;gBAAE,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;IACrF,CAAC;IACD,UAAU;QACT,IAAI,CAAC,WAAW,GAAG,SAAS,CAAC;QAC7B,IAAI,CAAC,WAAW,GAAG,SAAS,CAAC;IAC9B,CAAC;IACD,KAAK,CAAC,OAAO;QACZ,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC;QACvB,iGAAiG;QACjG,MAAM,gBAAgB,GAAG,IAAI,EAAE,MAAM,KAAK,KAAK,IAAI,cAAc,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QAC/E,IAAI,IAAI,CAAC,QAAQ,IAAI,CAAC,IAAI,IAAI,CAAC,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,gBAAgB,CAAC,IAAI,IAAI,CAAC,OAAO;YAAE,OAAO;QAC/F,MAAM,QAAQ,GAAG,IAAI,CAAC,QAAQ,CAAC;QAC/B,IAAI,QAAQ,CAAC,IAAI,IAAI,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC,MAAM,IAAI,cAAc,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;YAAE,OAAO;QACpG,IAAI,CAAC,OAAO,GAAG,IAAI,CAAC;QACpB,IAAI,CAAC;YACJ,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,EAAE,GAAG,IAAI,EAAE,CAAC,CAAC;YACvF,IAAI,IAAI,CAAC,QAAQ,IAAI,IAAI,CAAC,IAAI,EAAE,EAAE,KAAK,IAAI,CAAC,EAAE,IAAI,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC,MAAM,IAAI,QAAQ,CAAC,IAAI,CAAC;gBAAE,OAAO;YACrG,QAAQ,CAAC,IAAI,GAAG,IAAI,CAAC;QACtB,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YAChB,IAAI,CAAC,IAAI,CAAC,QAAQ,IAAI,IAAI,CAAC,IAAI,EAAE,EAAE,KAAK,IAAI,CAAC,EAAE,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,MAAM,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC;gBAC/F,QAAQ,CAAC,IAAI,GAAG;oBACf,IAAI;oBACJ,IAAI,EAAE,EAAE;oBACR,SAAS,EAAE,uBAAuB,aAAa,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,EAAE;oBAChE,UAAU,EAAE,CAAC;oBACb,SAAS,EAAE,KAAK;iBAChB,CAAC;YACH,CAAC;QACF,CAAC;gBAAS,CAAC;YACV,IAAI,CAAC,OAAO,GAAG,KAAK,CAAC;YACrB,IAAI,CAAC,IAAI,CAAC,QAAQ;gBAAE,IAAI,CAAC,OAAO,CAAC,aAAa,EAAE,CAAC;YACjD,IAAI,CAAC,IAAI,CAAC,QAAQ,IAAI,IAAI,CAAC,IAAI,EAAE,EAAE,KAAK,IAAI,CAAC,EAAE;gBAAE,MAAM,IAAI,CAAC,OAAO,EAAE,CAAC;QACvE,CAAC;IACF,CAAC;IACD,MAAM,CAAC,KAAa;QACnB,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC;QACvB,IAAI,CAAC,IAAI;YAAE,OAAO,EAAE,CAAC;QACrB,uFAAuF;QACvF,MAAM,IAAI,GAAG,cAAc,CAAC,IAAI,CAAC,MAAM,CAAC;YACvC,CAAC,CAAC,SAAS;YACX,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,aAAa,CAAC,IAAI,CAAC,EAAE,EAAE,KAAK,EAAE,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;QACvE,IAAI,IAAI;YAAE,OAAO,WAAW,CAAC,IAAI,EAAE,SAAS,EAAE,IAAI,CAAC,OAAO,CAAC,KAAK,EAAE,KAAK,EAAE,IAAI,CAAC,CAAC;QAC/E,MAAM,IAAI,GAAG,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC;QAChC,MAAM,KAAK,GAAG,IAAI,CAAC,WAAW,CAAC;QAC/B,IAAI,KAAK,EAAE,IAAI,KAAK,IAAI,IAAI,KAAK,CAAC,IAAI,KAAK,IAAI,IAAI,KAAK,CAAC,KAAK,KAAK,KAAK;YACvE,IAAI,CAAC,WAAW,GAAG,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,KAAK,EAAE,WAAW,CAAC,IAAI,EAAE,IAAI,EAAE,IAAI,CAAC,OAAO,CAAC,KAAK,EAAE,KAAK,CAAC,EAAE,CAAC;QACrG,OAAO,IAAI,CAAC,WAAY,CAAC,KAAK,CAAC;IAChC,CAAC;IACD,OAAO,CAAC,KAAa;QACpB,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC;QACvB,IAAI,CAAC,IAAI;YAAE,OAAO,EAAE,CAAC;QACrB,MAAM,KAAK,GAAG,IAAI,CAAC,WAAW,CAAC;QAC/B,IAAI,KAAK,EAAE,IAAI,KAAK,IAAI,IAAI,KAAK,CAAC,KAAK,KAAK,KAAK;YAChD,IAAI,CAAC,WAAW,GAAG,EAAE,IAAI,EAAE,KAAK,EAAE,KAAK,EAAE,WAAW,CAAC,IAAI,EAAE,IAAI,CAAC,OAAO,CAAC,KAAK,EAAE,KAAK,CAAC,EAAE,CAAC;QACzF,OAAO,IAAI,CAAC,WAAY,CAAC,KAAK,CAAC;IAChC,CAAC;IACD,OAAO;QACN,IAAI,IAAI,CAAC,QAAQ;YAAE,OAAO;QAC1B,IAAI,CAAC,QAAQ,GAAG,IAAI,CAAC;QACrB,IAAI,CAAC,OAAO,EAAE,EAAE,CAAC;QACjB,IAAI,CAAC,OAAO,GAAG,SAAS,CAAC;QACzB,IAAI,CAAC,SAAS,CAAC,KAAK,EAAE,CAAC;IACxB,CAAC;CACD","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 {
|
|
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 {
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
/**
|
|
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":"
|
|
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
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
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 {
|
|
12
|
+
import { renderInspectorPane, renderListPane } from "./panes.js";
|
|
8
13
|
import { cleanTaskText, firstCommandLine } from "./text.js";
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
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,
|
|
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
|
|
19
|
-
const
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
const
|
|
23
|
-
const
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
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
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
54
|
+
case "search": {
|
|
55
|
+
body = renderPanes(options, hint, rects);
|
|
56
|
+
footer = searchFooter(env);
|
|
57
|
+
break;
|
|
65
58
|
}
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
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
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
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
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
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
|
-
|
|
196
|
-
|
|
197
|
-
|
|
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
|