@astralyn/pi 1.4.9 → 1.4.11

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 (230) hide show
  1. package/CHANGELOG.md +69 -0
  2. package/dist/bundle/chunks/{chunk-BAGHIP5J.js → chunk-NUQMVNKQ.js} +3 -3
  3. package/dist/bundle/chunks/{chunk-3PKXUKTB.js → chunk-PUU43PDV.js} +121 -136
  4. package/dist/bundle/chunks/{chunk-F66AY3WR.js → chunk-TH3QDDPD.js} +1 -1
  5. package/dist/bundle/chunks/{execute-P22VMM2H.js → execute-VLAIP26K.js} +1 -1
  6. package/dist/bundle/chunks/{virtual-modules-VKMBQTR2.js → virtual-modules-63XNTSVN.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 -5
  13. package/dist/core/agent-session.js.map +1 -1
  14. package/dist/core/extensions/index.d.ts +1 -1
  15. package/dist/core/extensions/index.d.ts.map +1 -1
  16. package/dist/core/extensions/index.js.map +1 -1
  17. package/dist/core/extensions/loader.d.ts.map +1 -1
  18. package/dist/core/extensions/loader.js +14 -7
  19. package/dist/core/extensions/loader.js.map +1 -1
  20. package/dist/core/isolated-session.d.ts +7 -2
  21. package/dist/core/isolated-session.d.ts.map +1 -1
  22. package/dist/core/isolated-session.js +7 -3
  23. package/dist/core/isolated-session.js.map +1 -1
  24. package/dist/core/session-manager.d.ts +17 -1
  25. package/dist/core/session-manager.d.ts.map +1 -1
  26. package/dist/core/session-manager.js +29 -0
  27. package/dist/core/session-manager.js.map +1 -1
  28. package/dist/core/tasks/delivery.d.ts +31 -12
  29. package/dist/core/tasks/delivery.d.ts.map +1 -1
  30. package/dist/core/tasks/delivery.js +97 -36
  31. package/dist/core/tasks/delivery.js.map +1 -1
  32. package/dist/core/tasks/execution.d.ts +27 -14
  33. package/dist/core/tasks/execution.d.ts.map +1 -1
  34. package/dist/core/tasks/execution.js +90 -86
  35. package/dist/core/tasks/execution.js.map +1 -1
  36. package/dist/core/tasks/limits.d.ts +7 -11
  37. package/dist/core/tasks/limits.d.ts.map +1 -1
  38. package/dist/core/tasks/limits.js +8 -16
  39. package/dist/core/tasks/limits.js.map +1 -1
  40. package/dist/core/tasks/output.d.ts +9 -1
  41. package/dist/core/tasks/output.d.ts.map +1 -1
  42. package/dist/core/tasks/output.js +23 -10
  43. package/dist/core/tasks/output.js.map +1 -1
  44. package/dist/core/tasks/presentation.d.ts +1 -1
  45. package/dist/core/tasks/presentation.d.ts.map +1 -1
  46. package/dist/core/tasks/presentation.js +6 -4
  47. package/dist/core/tasks/presentation.js.map +1 -1
  48. package/dist/core/tasks/record.d.ts +41 -29
  49. package/dist/core/tasks/record.d.ts.map +1 -1
  50. package/dist/core/tasks/record.js +82 -37
  51. package/dist/core/tasks/record.js.map +1 -1
  52. package/dist/core/tasks/reminder.js +2 -2
  53. package/dist/core/tasks/reminder.js.map +1 -1
  54. package/dist/core/tasks/runtime.d.ts +15 -23
  55. package/dist/core/tasks/runtime.d.ts.map +1 -1
  56. package/dist/core/tasks/runtime.js +50 -178
  57. package/dist/core/tasks/runtime.js.map +1 -1
  58. package/dist/core/tasks/scheduler.d.ts +3 -1
  59. package/dist/core/tasks/scheduler.d.ts.map +1 -1
  60. package/dist/core/tasks/scheduler.js +16 -12
  61. package/dist/core/tasks/scheduler.js.map +1 -1
  62. package/dist/core/tasks/session.d.ts +24 -6
  63. package/dist/core/tasks/session.d.ts.map +1 -1
  64. package/dist/core/tasks/session.js +70 -22
  65. package/dist/core/tasks/session.js.map +1 -1
  66. package/dist/core/tasks/store.d.ts +17 -4
  67. package/dist/core/tasks/store.d.ts.map +1 -1
  68. package/dist/core/tasks/store.js +45 -48
  69. package/dist/core/tasks/store.js.map +1 -1
  70. package/dist/core/tasks/types.d.ts +64 -93
  71. package/dist/core/tasks/types.d.ts.map +1 -1
  72. package/dist/core/tasks/types.js +7 -0
  73. package/dist/core/tasks/types.js.map +1 -1
  74. package/dist/core/tools/shell-execution.d.ts.map +1 -1
  75. package/dist/core/tools/shell-execution.js +2 -1
  76. package/dist/core/tools/shell-execution.js.map +1 -1
  77. package/dist/core/tools/shell-tool.d.ts.map +1 -1
  78. package/dist/core/tools/shell-tool.js +20 -13
  79. package/dist/core/tools/shell-tool.js.map +1 -1
  80. package/dist/extensions/agent/activity-lines.d.ts +10 -0
  81. package/dist/extensions/agent/activity-lines.d.ts.map +1 -0
  82. package/dist/extensions/agent/activity-lines.js +76 -0
  83. package/dist/extensions/agent/activity-lines.js.map +1 -0
  84. package/dist/extensions/agent/activity.d.ts +25 -4
  85. package/dist/extensions/agent/activity.d.ts.map +1 -1
  86. package/dist/extensions/agent/activity.js +76 -21
  87. package/dist/extensions/agent/activity.js.map +1 -1
  88. package/dist/extensions/agent/artifacts.d.ts +15 -0
  89. package/dist/extensions/agent/artifacts.d.ts.map +1 -0
  90. package/dist/extensions/agent/artifacts.js +44 -0
  91. package/dist/extensions/agent/artifacts.js.map +1 -0
  92. package/dist/extensions/agent/progress.d.ts +30 -4
  93. package/dist/extensions/agent/progress.d.ts.map +1 -1
  94. package/dist/extensions/agent/progress.js +84 -11
  95. package/dist/extensions/agent/progress.js.map +1 -1
  96. package/dist/extensions/agent/render.d.ts.map +1 -1
  97. package/dist/extensions/agent/render.js +8 -24
  98. package/dist/extensions/agent/render.js.map +1 -1
  99. package/dist/extensions/agent/report.d.ts +11 -3
  100. package/dist/extensions/agent/report.d.ts.map +1 -1
  101. package/dist/extensions/agent/report.js +32 -7
  102. package/dist/extensions/agent/report.js.map +1 -1
  103. package/dist/extensions/agent/tool.d.ts.map +1 -1
  104. package/dist/extensions/agent/tool.js +20 -5
  105. package/dist/extensions/agent/tool.js.map +1 -1
  106. package/dist/extensions/agent/types.d.ts +2 -0
  107. package/dist/extensions/agent/types.d.ts.map +1 -1
  108. package/dist/extensions/agent/types.js.map +1 -1
  109. package/dist/extensions/agent/worker.d.ts +7 -0
  110. package/dist/extensions/agent/worker.d.ts.map +1 -1
  111. package/dist/extensions/agent/worker.js +9 -3
  112. package/dist/extensions/agent/worker.js.map +1 -1
  113. package/dist/extensions/cached-compaction/summary.d.ts +1 -1
  114. package/dist/extensions/cached-compaction/summary.d.ts.map +1 -1
  115. package/dist/extensions/cached-compaction/summary.js +13 -16
  116. package/dist/extensions/cached-compaction/summary.js.map +1 -1
  117. package/dist/extensions/search/index.d.ts.map +1 -1
  118. package/dist/extensions/search/index.js +6 -7
  119. package/dist/extensions/search/index.js.map +1 -1
  120. package/dist/extensions/tasks/tool/actions.d.ts +1 -2
  121. package/dist/extensions/tasks/tool/actions.d.ts.map +1 -1
  122. package/dist/extensions/tasks/tool/actions.js +12 -80
  123. package/dist/extensions/tasks/tool/actions.js.map +1 -1
  124. package/dist/extensions/tasks/tool/constants.d.ts +1 -3
  125. package/dist/extensions/tasks/tool/constants.d.ts.map +1 -1
  126. package/dist/extensions/tasks/tool/constants.js +1 -3
  127. package/dist/extensions/tasks/tool/constants.js.map +1 -1
  128. package/dist/extensions/tasks/tool/schema.d.ts +2 -3
  129. package/dist/extensions/tasks/tool/schema.d.ts.map +1 -1
  130. package/dist/extensions/tasks/tool/schema.js +10 -18
  131. package/dist/extensions/tasks/tool/schema.js.map +1 -1
  132. package/dist/extensions/tasks/tool/types.d.ts +1 -17
  133. package/dist/extensions/tasks/tool/types.d.ts.map +1 -1
  134. package/dist/extensions/tasks/tool/types.js.map +1 -1
  135. package/dist/extensions/tasks/tool.d.ts +1 -2
  136. package/dist/extensions/tasks/tool.d.ts.map +1 -1
  137. package/dist/extensions/tasks/tool.js +5 -6
  138. package/dist/extensions/tasks/tool.js.map +1 -1
  139. package/dist/extensions/tasks/ui/completion-render.d.ts.map +1 -1
  140. package/dist/extensions/tasks/ui/completion-render.js +2 -2
  141. package/dist/extensions/tasks/ui/completion-render.js.map +1 -1
  142. package/dist/extensions/tasks/ui/footer.d.ts +37 -0
  143. package/dist/extensions/tasks/ui/footer.d.ts.map +1 -0
  144. package/dist/extensions/tasks/ui/footer.js +129 -0
  145. package/dist/extensions/tasks/ui/footer.js.map +1 -0
  146. package/dist/extensions/tasks/ui/frame.d.ts +36 -0
  147. package/dist/extensions/tasks/ui/frame.d.ts.map +1 -0
  148. package/dist/extensions/tasks/ui/frame.js +33 -0
  149. package/dist/extensions/tasks/ui/frame.js.map +1 -0
  150. package/dist/extensions/tasks/ui/help.d.ts +6 -0
  151. package/dist/extensions/tasks/ui/help.d.ts.map +1 -0
  152. package/dist/extensions/tasks/ui/help.js +53 -0
  153. package/dist/extensions/tasks/ui/help.js.map +1 -0
  154. package/dist/extensions/tasks/ui/inspector.d.ts +4 -3
  155. package/dist/extensions/tasks/ui/inspector.d.ts.map +1 -1
  156. package/dist/extensions/tasks/ui/inspector.js +25 -6
  157. package/dist/extensions/tasks/ui/inspector.js.map +1 -1
  158. package/dist/extensions/tasks/ui/layout.d.ts +15 -18
  159. package/dist/extensions/tasks/ui/layout.d.ts.map +1 -1
  160. package/dist/extensions/tasks/ui/layout.js +162 -255
  161. package/dist/extensions/tasks/ui/layout.js.map +1 -1
  162. package/dist/extensions/tasks/ui/list.d.ts +3 -1
  163. package/dist/extensions/tasks/ui/list.d.ts.map +1 -1
  164. package/dist/extensions/tasks/ui/list.js +19 -14
  165. package/dist/extensions/tasks/ui/list.js.map +1 -1
  166. package/dist/extensions/tasks/ui/manager.d.ts +17 -5
  167. package/dist/extensions/tasks/ui/manager.d.ts.map +1 -1
  168. package/dist/extensions/tasks/ui/manager.js +147 -90
  169. package/dist/extensions/tasks/ui/manager.js.map +1 -1
  170. package/dist/extensions/tasks/ui/model.d.ts +32 -5
  171. package/dist/extensions/tasks/ui/model.d.ts.map +1 -1
  172. package/dist/extensions/tasks/ui/model.js +7 -7
  173. package/dist/extensions/tasks/ui/model.js.map +1 -1
  174. package/dist/extensions/tasks/ui/panes.d.ts +40 -0
  175. package/dist/extensions/tasks/ui/panes.d.ts.map +1 -0
  176. package/dist/extensions/tasks/ui/panes.js +144 -0
  177. package/dist/extensions/tasks/ui/panes.js.map +1 -0
  178. package/dist/extensions/tasks/ui/render.d.ts +2 -16
  179. package/dist/extensions/tasks/ui/render.d.ts.map +1 -1
  180. package/dist/extensions/tasks/ui/render.js +13 -85
  181. package/dist/extensions/tasks/ui/render.js.map +1 -1
  182. package/dist/extensions/tasks/ui/viewport.d.ts +12 -4
  183. package/dist/extensions/tasks/ui/viewport.d.ts.map +1 -1
  184. package/dist/extensions/tasks/ui/viewport.js +23 -7
  185. package/dist/extensions/tasks/ui/viewport.js.map +1 -1
  186. package/dist/extensions/tasks/ui/views.d.ts +12 -6
  187. package/dist/extensions/tasks/ui/views.d.ts.map +1 -1
  188. package/dist/extensions/tasks/ui/views.js +39 -32
  189. package/dist/extensions/tasks/ui/views.js.map +1 -1
  190. package/dist/index.d.ts +1 -1
  191. package/dist/index.d.ts.map +1 -1
  192. package/dist/index.js.map +1 -1
  193. package/dist/modes/interactive/components/session-selector.d.ts +11 -1
  194. package/dist/modes/interactive/components/session-selector.d.ts.map +1 -1
  195. package/dist/modes/interactive/components/session-selector.js +43 -17
  196. package/dist/modes/interactive/components/session-selector.js.map +1 -1
  197. package/dist/modes/print-mode.d.ts.map +1 -1
  198. package/dist/modes/print-mode.js +38 -2
  199. package/dist/modes/print-mode.js.map +1 -1
  200. package/dist/modes/rpc/rpc-client.d.ts +11 -0
  201. package/dist/modes/rpc/rpc-client.d.ts.map +1 -1
  202. package/dist/modes/rpc/rpc-client.js +48 -0
  203. package/dist/modes/rpc/rpc-client.js.map +1 -1
  204. package/dist/modes/rpc/rpc-mode.d.ts.map +1 -1
  205. package/dist/modes/rpc/rpc-mode.js +2 -0
  206. package/dist/modes/rpc/rpc-mode.js.map +1 -1
  207. package/dist/modes/rpc/rpc-types.d.ts +2 -0
  208. package/dist/modes/rpc/rpc-types.d.ts.map +1 -1
  209. package/dist/modes/rpc/rpc-types.js.map +1 -1
  210. package/docs/bundled/extensions/agent.md +39 -7
  211. package/docs/bundled/extensions/statusline.md +1 -1
  212. package/docs/bundled/tasks.md +28 -37
  213. package/docs/bundled/tool-presentation.md +1 -1
  214. package/docs/extensions.md +15 -13
  215. package/docs/json.md +3 -0
  216. package/docs/models-configuration.md +1 -1
  217. package/docs/models.md +7 -85
  218. package/docs/rpc.md +14 -0
  219. package/docs/sdk.md +12 -2
  220. package/docs/session-format.md +11 -5
  221. package/docs/sessions.md +1 -1
  222. package/docs/usage.md +2 -0
  223. package/package.json +3 -4
  224. package/dist/modes/interactive/tool-view/tool-execution.d.ts +0 -14
  225. package/dist/modes/interactive/tool-view/tool-execution.d.ts.map +0 -1
  226. package/dist/modes/interactive/tool-view/tool-execution.js +0 -13
  227. package/dist/modes/interactive/tool-view/tool-execution.js.map +0 -1
  228. package/docs/images/doom-extension.png +0 -0
  229. package/docs/images/exy.png +0 -0
  230. package/npm-shrinkwrap.json +0 -4207
@@ -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
@@ -1 +1 @@
1
- {"version":3,"file":"layout.js","sourceRoot":"","sources":["../../../../src/extensions/tasks/ui/layout.ts"],"names":[],"mappings":"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","sourcesContent":["import { truncateToWidth, visibleWidth, wrapTextWithAnsi } from \"@earendil-works/pi-tui\";\nimport type { AppKeybinding, Keybinding, KeybindingsManager } from \"../../../core/keybindings.ts\";\nimport { runtimeLabel } from \"../../../core/tasks/format.ts\";\nimport { isTaskTerminal } from \"../../../core/tasks/types.ts\";\nimport { formatSize } from \"../../../core/tools/truncate.ts\";\nimport { keyLabel } from \"../../../modes/interactive/components/keybinding-hints.ts\";\nimport type { Theme } from \"../../../modes/interactive/theme/theme.ts\";\nimport type { TaskInspector } from \"./inspector.ts\";\nimport type { TaskList } from \"./list.ts\";\nimport { isStoppable, type PanelFocus, type PanelMode, type TaskSelection, type TasksTab } from \"./model.ts\";\nimport { modeName, statusName, statusView } from \"./status.ts\";\nimport { cleanTaskText, firstCommandLine } from \"./text.ts\";\nimport type { TaskViewport } from \"./viewport.ts\";\n\nexport interface TaskHit {\n\tx: number;\n\ty: number;\n\twidth: number;\n\tkey?: AppKeybinding;\n\ttaskId?: string;\n\ttab?: TasksTab;\n\tcancel?: boolean;\n}\nexport interface TasksLayout {\n\thits: TaskHit[];\n\tlistWidth: number;\n\tlistPageSize: number;\n\tlines: string[];\n\t/** The terminal is below the supported size; only a full-screen resize notice is shown. */\n\ttooSmall?: boolean;\n}\ninterface LayoutOptions {\n\twidth: number;\n\theight: number;\n\ttheme: Theme;\n\tkeybindings: Pick<KeybindingsManager, \"getKeys\">;\n\tselection: TaskSelection;\n\tinspector: TaskInspector;\n\tlist: TaskList;\n\tfocus: PanelFocus;\n\tmode: PanelMode;\n\t/** Renders the search input line; shown only in search mode. */\n\tsearchLine: (width: number) => string;\n\tfeedback?: string;\n\t/** Whether the host would move this task to the background now. */\n\tcanDetach: (id: string) => boolean;\n}\nconst pad = (text: string, width: number) => truncateToWidth(text, Math.max(1, width), \"…\", true);\n/** Visible width of \"Output Details\". */\nconst TABS_WIDTH = 15;\n\n/** Cell geometry and mouse targets are produced together, so resize cannot separate them. */\nexport function renderTasksLayout(options: LayoutOptions): TasksLayout {\n\tconst { width, height, theme, selection, inspector, list, focus, keybindings, mode } = options;\n\tconst hint = (key: Keybinding) => keyLabel(key, { keybindings });\n\tconst result: TasksLayout = { hits: [], listWidth: 0, listPageSize: 1, lines: [] };\n\tif (width < 1 || height < 1) return result;\n\tconst wide = width >= 100;\n\tconst listWidth = wide ? Math.min(42, Math.max(32, Math.floor(width * 0.32))) : width;\n\tconst rightWidth = wide ? width - listWidth - 1 : width;\n\tconst rightX = wide ? listWidth + 1 : 0;\n\tconst bodyHeight = height - 5;\n\tconst hits = result.hits;\n\tresult.listWidth = listWidth;\n\t// One row per task, less the group headings.\n\tresult.listPageSize = Math.max(1, bodyHeight - 2);\n\tconst pane = (lines: string[], size: number, active: boolean): string[] => {\n\t\tconst border = (text: string) => theme.fg(wide && active ? \"borderAccent\" : \"borderMuted\", text);\n\t\treturn [\n\t\t\tborder(`┌${\"─\".repeat(size - 2)}┐`),\n\t\t\t...lines.map((line) => border(\"│\") + pad(line, size - 2) + border(\"│\")),\n\t\t\tborder(`└${\"─\".repeat(size - 2)}┘`),\n\t\t];\n\t};\n\tif (width < 60 || height < 14) {\n\t\t// The panel is modal: a floating notice would leave the transcript visible yet\n\t\t// unscrollable, so the notice owns every row as a centered dialog. Only a truly\n\t\t// tiny terminal, where even the dialog does not fit, gets bare covering lines.\n\t\tresult.tooSmall = true;\n\t\tconst message = \"Terminal too small for /tasks. Enlarge the window to continue.\";\n\t\tconst close = `${hint(\"tui.select.cancel\")} close`;\n\t\tconst boxWidth = Math.min(width, 46);\n\t\tconst inner = boxWidth - 2;\n\t\tconst wrapped = wrapTextWithAnsi(message, inner).map((line) => theme.fg(\"warning\", line));\n\t\tconst spacious = [theme.fg(\"text\", \"Tasks\"), \"\", ...wrapped, \"\", theme.fg(\"dim\", close)];\n\t\tconst tight = [theme.fg(\"text\", \"Tasks\"), ...wrapped, theme.fg(\"dim\", close)];\n\t\tconst body = height >= spacious.length + 2 ? spacious : tight;\n\t\tif (inner >= 16 && height >= body.length + 2) {\n\t\t\tconst box = pane(body, boxWidth, false);\n\t\t\tconst left = Math.floor((width - boxWidth) / 2);\n\t\t\tconst top = Math.max(0, Math.floor((height - box.length) / 2));\n\t\t\tconst blank = \" \".repeat(width);\n\t\t\tresult.lines = [\n\t\t\t\t...Array.from({ length: top }, () => blank),\n\t\t\t\t...box.map((line) => \" \".repeat(left) + line + \" \".repeat(width - left - boxWidth)),\n\t\t\t\t...Array.from({ length: height - top - box.length }, () => blank),\n\t\t\t];\n\t\t\thits.push({ x: left + 1, y: top + body.length, width: visibleWidth(close), cancel: true });\n\t\t} else {\n\t\t\tresult.lines = [\"Tasks\", message, close].slice(0, height).map((line) => pad(line, width));\n\t\t\twhile (result.lines.length < height) result.lines.push(\" \".repeat(width));\n\t\t}\n\t\treturn result;\n\t}\n\tconst visible = list.render(selection, theme, listWidth - 2, bodyHeight);\n\tlet listHeader = theme.fg(\"text\", \"Tasks\");\n\tconst searchLabel = `${hint(\"app.tasks.search\")} find`;\n\tif (mode.kind === \"search\") listHeader = options.searchLine(listWidth - 2);\n\telse {\n\t\tconst x = Math.max(6, listWidth - 2 - visibleWidth(searchLabel));\n\t\tlistHeader += \" \".repeat(x - 5) + theme.fg(\"dim\", searchLabel);\n\t\tif (wide || focus === \"list\")\n\t\t\thits.push({ x: x + 1, y: 1, width: visibleWidth(searchLabel), key: \"app.tasks.search\" });\n\t}\n\tif (wide || focus === \"list\")\n\t\tvisible.forEach((row, index) => {\n\t\t\tif (row.taskId) hits.push({ x: 1, y: 2 + index, width: listWidth - 2, taskId: row.taskId });\n\t\t});\n\tconst left = [\n\t\tlistHeader,\n\t\t...Array.from({ length: bodyHeight }, (_, i) => visible[i]?.text ?? \"\"),\n\t\ttheme.fg(\"dim\", list.range),\n\t];\n\tconst task = inspector.task;\n\tconst tab = selection.state.tab;\n\tconst tabLabel = (id: TasksTab, label: string) => theme.style(label, { fg: \"text\", underline: tab === id });\n\t// One header row in every layout: the selected task's source and status, with the tabs on the right.\n\tconst tabsX = rightWidth - 2 - TABS_WIDTH;\n\tconst summary = task\n\t\t? [\n\t\t\t\ttheme.fg(\"muted\", cleanTaskText(task.label)),\n\t\t\t\ttheme.fg(statusView(task.status).color, statusName(task.status)),\n\t\t\t\ttheme.fg(\"muted\", runtimeLabel(task)),\n\t\t\t].join(theme.fg(\"dim\", \" · \"))\n\t\t: \"\";\n\tconst shownSummary = truncateToWidth(summary, Math.max(0, tabsX - 1), \"…\");\n\tconst right = [\n\t\t`${shownSummary}${\" \".repeat(tabsX - visibleWidth(shownSummary))}${tabLabel(\"output\", \"Output\")} ${tabLabel(\"info\", \"Details\")}`,\n\t];\n\tif (wide || focus === \"inspector\")\n\t\thits.push(\n\t\t\t{ x: rightX + 1 + tabsX, y: 1, width: 6, tab: \"output\" },\n\t\t\t{ x: rightX + 9 + tabsX, y: 1, width: 7, tab: \"info\" },\n\t\t);\n\tconst content: string[] = [];\n\tlet reading = \"\";\n\tif (task) {\n\t\t// Without the list beside it, the inspector names the task it shows.\n\t\tif (!wide) content.push(theme.fg(\"text\", cleanTaskText(firstCommandLine(task.title))));\n\t\tconst position = inspector.position;\n\t\tconst read = position.read;\n\t\tconst terminal = isTaskTerminal(task.status);\n\t\tconst diagnostics = [task.error, read?.readError].filter((text): text is string => !!text).map(cleanTaskText);\n\t\tif (tab === \"output\" && diagnostics.length)\n\t\t\tcontent.push(theme.fg(\"warning\", diagnostics.join(\" · \").replace(/\\n/g, \" \")));\n\t\tconst source =\n\t\t\ttab === \"info\"\n\t\t\t\t? inspector.details(`${statusName(task.status)} · ${modeName(task.mode)} · ${runtimeLabel(task)}`)\n\t\t\t\t: inspector.output(rightWidth - 2);\n\t\tcontent.push(...position[tab].layout(source, rightWidth - 2, bodyHeight - content.length));\n\t\tif (tab === \"output\") {\n\t\t\t// Only a log holds a browsed snapshot; other output is always the current result.\n\t\t\tconst settled = terminal && task.outputPath === undefined;\n\t\t\treading =\n\t\t\t\tposition.output.follow || settled\n\t\t\t\t\t? terminal\n\t\t\t\t\t\t? \"Finished\"\n\t\t\t\t\t\t: \"Live\"\n\t\t\t\t\t: terminal\n\t\t\t\t\t\t? `Finished · ${hint(\"app.tasks.follow\")} load final output`\n\t\t\t\t\t\t: `Browsing · ${hint(\"app.tasks.follow\")} follow output`;\n\t\t\tif (!position.output.follow && !settled && (wide || focus === \"inspector\"))\n\t\t\t\thits.push({\n\t\t\t\t\tx: rightX + 1,\n\t\t\t\t\ty: height - 3,\n\t\t\t\t\twidth: Math.min(rightWidth - 2, visibleWidth(reading)),\n\t\t\t\t\tkey: \"app.tasks.follow\",\n\t\t\t\t});\n\t\t\tif (task.outputPath !== undefined && read?.truncated)\n\t\t\t\treading += ` · Tail preview: ${formatSize(Buffer.byteLength(read.text))} of ${formatSize(read.totalBytes)}`;\n\t\t} else if (tab === \"info\") reading = `Details ${position.info.range}`;\n\t} else content.push(theme.fg(\"muted\", \"Select a task to inspect its result.\"));\n\tright.push(...Array.from({ length: bodyHeight }, (_, i) => content[i] ?? \"\"), theme.fg(\"dim\", reading));\n\tconst rightPane = pane(right, rightWidth, focus === \"inspector\");\n\tlet panels = wide\n\t\t? pane(left, listWidth, focus === \"list\").map((line, i) => `${line} ${rightPane[i]}`)\n\t\t: pane(focus === \"list\" ? left : right, width, false);\n\n\tlet footer = \"\";\n\tconst addHint = (key: Keybinding, label: string, target?: Omit<TaskHit, \"x\" | \"y\" | \"width\">) => {\n\t\tconst text = `${hint(key)} ${label}`;\n\t\tconst x = visibleWidth(footer) + (footer ? 3 : 0);\n\t\tif (x + visibleWidth(text) > width) return;\n\t\tfooter += (footer ? \" · \" : \"\") + text;\n\t\tif (target) hits.push({ x, y: height - 1, width: visibleWidth(text), ...target });\n\t};\n\tif (mode.kind === \"help\") {\n\t\thits.length = 0;\n\t\tpanels = pane(helpLines(mode.viewport, hint, theme, width, height), width, false);\n\t\taddHint(\"tui.select.up\", `/ ${hint(\"tui.select.down\")} scroll`);\n\t\taddHint(\"tui.select.cancel\", \"back\", { cancel: true });\n\t} else if (mode.kind === \"confirmStop\") {\n\t\t// The question borrows only the footer row, so the task stays in view while confirming.\n\t\thits.length = 0;\n\t\tconst target = selection.all.find((task) => task.id === mode.taskId);\n\t\tconst source = cleanTaskText(target?.label ?? \"task\");\n\t\tconst confirm = `${hint(\"app.tasks.confirmStop\")} stop`;\n\t\tconst cancel = `${hint(\"tui.select.cancel\")} cancel`;\n\t\tconst actions = `${confirm} · ${cancel}`;\n\t\tconst room = width - visibleWidth(`Stop ${source} \"\"? ${actions}`);\n\t\tconst title = cleanTaskText(firstCommandLine(target?.title ?? mode.taskId));\n\t\tconst question = room >= 4 ? `Stop ${source} \"${truncateToWidth(title, room, \"…\")}\"? ` : `Stop ${source}? `;\n\t\tfooter = question + actions;\n\t\tconst x = visibleWidth(question);\n\t\thits.push(\n\t\t\t{ x, y: height - 1, width: visibleWidth(confirm), key: \"app.tasks.confirmStop\" },\n\t\t\t{ x: x + visibleWidth(confirm) + 3, y: height - 1, width: visibleWidth(cancel), cancel: true },\n\t\t);\n\t} else if (mode.kind === \"search\") {\n\t\t// Only search candidates and cancellation are clickable while locating a task.\n\t\tfor (let i = hits.length - 1; i >= 0; i--) if (!hits[i]!.taskId) hits.splice(i, 1);\n\t\taddHint(\"tui.select.up\", `/ ${hint(\"tui.select.down\")} choose`);\n\t\taddHint(\"tui.select.confirm\", \"locate\");\n\t\taddHint(\"tui.select.cancel\", \"cancel\", { cancel: true });\n\t} else {\n\t\taddHint(\"tui.select.up\", `/ ${hint(\"tui.select.down\")}${focus === \"list\" ? \" tasks\" : \" scroll\"}`);\n\t\tif (focus === \"list\") addHint(\"tui.select.confirm\", \"open\", { tab });\n\t\telse addHint(\"app.tasks.nextFocus\", \"tasks\", { key: \"app.tasks.nextFocus\" });\n\t\taddHint(\"tui.select.cancel\", \"close\", { cancel: true });\n\t\tif (focus === \"list\") addHint(\"app.tasks.search\", \"find\", { key: \"app.tasks.search\" });\n\t\telse {\n\t\t\taddHint(\"app.tasks.previousTab\", \"output\", { tab: \"output\" });\n\t\t\taddHint(\"app.tasks.nextTab\", \"details\", { tab: \"info\" });\n\t\t}\n\t\tif (task && isStoppable(task)) addHint(\"app.tasks.kill\", \"stop\", { key: \"app.tasks.kill\" });\n\t\tif (task && options.canDetach(task.id))\n\t\t\taddHint(\"app.tasks.detachSelected\", \"background\", { key: \"app.tasks.detachSelected\" });\n\t\taddHint(\"app.tasks.help\", \"keys\", { key: \"app.tasks.help\" });\n\t\t// Feedback borrows the hint row, so the hint hit targets stay live and the click that\n\t\t// dismisses it can act in the same gesture.\n\t\tif (options.feedback) footer = options.feedback;\n\t}\n\tresult.lines = [...panels, theme.fg(mode.kind === \"confirmStop\" ? \"warning\" : \"dim\", pad(footer, width))];\n\treturn result;\n}\n\n/** Controls grouped by purpose; actions sharing a key, such as list and inspector paging, share a row. */\nfunction helpLines(\n\thelp: TaskViewport,\n\thint: (key: Keybinding) => string,\n\ttheme: Theme,\n\twidth: number,\n\theight: number,\n): string[] {\n\tconst keys = (...bindings: Keybinding[]) => [...new Set(bindings.map(hint).filter(Boolean))].join(\" / \");\n\tconst listPage = keys(\"tui.select.pageUp\", \"tui.select.pageDown\");\n\tconst inspectorPage = keys(\"tui.editor.pageUp\", \"tui.editor.pageDown\");\n\tconst paging: Array<[string, string]> =\n\t\tlistPage === inspectorPage\n\t\t\t? [[listPage, \"Page the list or inspector\"]]\n\t\t\t: [\n\t\t\t\t\t[listPage, \"Page the list\"],\n\t\t\t\t\t[inspectorPage, \"Page the inspector\"],\n\t\t\t\t];\n\tconst sections: Array<[string, Array<[string, string]>]> = [\n\t\t[\n\t\t\t\"Navigate\",\n\t\t\t[\n\t\t\t\t[keys(\"tui.select.up\", \"tui.select.down\"), \"Select a task, or scroll the inspector\"],\n\t\t\t\t...paging,\n\t\t\t\t[keys(\"app.tasks.top\", \"app.tasks.bottom\"), \"Top or bottom\"],\n\t\t\t\t[hint(\"tui.select.confirm\"), \"Open the selected task\"],\n\t\t\t\t[keys(\"app.tasks.nextFocus\", \"app.tasks.previousFocus\"), \"Switch between list and inspector\"],\n\t\t\t],\n\t\t],\n\t\t[\n\t\t\t\"View\",\n\t\t\t[\n\t\t\t\t[keys(\"app.tasks.previousTab\", \"app.tasks.nextTab\"), \"Output or Details, in the inspector\"],\n\t\t\t\t[hint(\"app.tasks.follow\"), \"Follow the latest output, or load the final output\"],\n\t\t\t\t[hint(\"app.tasks.search\"), \"Find a task; Enter locates it, Esc restores the selection\"],\n\t\t\t],\n\t\t],\n\t\t[\n\t\t\t\"Act\",\n\t\t\t[\n\t\t\t\t[hint(\"app.tasks.kill\"), `Stop the selected task; ${hint(\"app.tasks.confirmStop\")} confirms`],\n\t\t\t\t[hint(\"app.tasks.detachSelected\"), \"Move the selected foreground task to the background\"],\n\t\t\t\t[hint(\"app.tasks.detach\"), \"Move every foreground execution to the background\"],\n\t\t\t\t[hint(\"tui.select.cancel\"), \"Close search, help or confirmation; otherwise the panel\"],\n\t\t\t],\n\t\t],\n\t];\n\tconst keyWidth = Math.max(...sections.flatMap(([, rows]) => rows.map(([key]) => visibleWidth(key)))) + 2;\n\tconst rows = sections.flatMap(([title, entries], index) => [\n\t\t...(index ? [\"\"] : []),\n\t\ttheme.fg(\"accent\", title),\n\t\t...entries.map(([key, label]) => ` ${key}${\" \".repeat(keyWidth - visibleWidth(key))}${label}`),\n\t]);\n\tconst body = [theme.fg(\"text\", \"Task controls\"), ...help.layout(rows, width - 2, height - 5), \"\"];\n\twhile (body.length < height - 3) body.push(\"\");\n\treturn body;\n}\n"]}
1
+ {"version":3,"file":"layout.js","sourceRoot":"","sources":["../../../../src/extensions/tasks/ui/layout.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Screen layout. This is the only place that knows widths and row positions: it returns the\n * rendered lines together with the clickable cells and the pane regions the manager routes\n * wheel and focus gestures by, so a resize cannot separate geometry from targets.\n */\nimport { visibleWidth, wrapTextWithAnsi } from \"@earendil-works/pi-tui\";\nimport type { Keybinding, KeybindingsManager } from \"../../../core/keybindings.ts\";\nimport { keyLabel } from \"../../../modes/interactive/components/keybinding-hints.ts\";\nimport type { Theme } from \"../../../modes/interactive/theme/theme.ts\";\nimport {\n\tbrowseFooter,\n\tconfirmStopFooter,\n\ttype Feedback,\n\ttype Footer,\n\ttype FooterEnv,\n\thelpFooter,\n\tsearchFooter,\n} from \"./footer.ts\";\nimport { fitCell, framePane, type Rect } from \"./frame.ts\";\nimport { helpRows } from \"./help.ts\";\nimport type { TaskInspector } from \"./inspector.ts\";\nimport type { TaskList } from \"./list.ts\";\nimport type { PanelFocus, PanelMode, TaskHit, TaskSelection } from \"./model.ts\";\nimport { isStoppable } from \"./model.ts\";\nimport { type PaneRender, renderInspectorPane, renderListPane } from \"./panes.ts\";\nimport { cleanTaskText, firstCommandLine } from \"./text.ts\";\n\n/** A terminal at least this wide shows the list beside the inspector. */\nconst SPLIT_MIN_WIDTH = 100;\n/** The minimum supported terminal; below it a resize notice covers the screen. */\nconst MIN_WIDTH = 60;\nconst MIN_HEIGHT = 14;\n\n/** A pane the pointer can act on: wheel scrolls it and a click on it focuses it. */\nexport interface PaneRegion extends Rect {\n\tpane: PanelFocus | \"help\";\n}\n\nexport interface TasksLayout {\n\tlines: string[];\n\thits: TaskHit[];\n\tregions: PaneRegion[];\n\t/** Both panes are visible; otherwise one pane at a time fills the width. */\n\tsplit: boolean;\n\tlistPageSize: number;\n\t/** The terminal is below the supported size; only a full-screen resize notice is shown. */\n\ttooSmall?: boolean;\n}\n\nexport interface LayoutOptions {\n\twidth: number;\n\theight: number;\n\ttheme: Theme;\n\tkeybindings: Pick<KeybindingsManager, \"getKeys\">;\n\tselection: TaskSelection;\n\tinspector: TaskInspector;\n\tlist: TaskList;\n\tfocus: PanelFocus;\n\tmode: PanelMode;\n\tfeedback?: Feedback;\n\t/** Whether the host would move this task to the background now. */\n\tcanDetach: (id: string) => boolean;\n}\n\n/** The list takes 40% of a split terminal, between 34 and 60 columns. */\nexport function listWidthFor(width: number): number {\n\treturn Math.min(60, Math.max(34, Math.round(width * 0.4)));\n}\n\ninterface PaneRects {\n\tsplit: boolean;\n\tlist: Rect;\n\tinspector: Rect;\n}\n\n/** Pane rectangles above the single footer row; without room for two, both panes share the full width. */\nfunction paneRects(width: number, height: number): PaneRects {\n\tconst rows = height - 1;\n\tif (width < SPLIT_MIN_WIDTH) {\n\t\tconst full = { x: 0, y: 0, width, height: rows };\n\t\treturn { split: false, list: full, inspector: full };\n\t}\n\tconst listWidth = listWidthFor(width);\n\treturn {\n\t\tsplit: true,\n\t\tlist: { x: 0, y: 0, width: listWidth, height: rows },\n\t\tinspector: { x: listWidth + 1, y: 0, width: width - listWidth - 1, height: rows },\n\t};\n}\n\nexport function renderTasksLayout(options: LayoutOptions): TasksLayout {\n\tconst { width, height, theme, mode } = options;\n\tif (width < 1 || height < 1) return { lines: [], hits: [], regions: [], split: false, listPageSize: 1 };\n\tconst hint = (key: Keybinding) => keyLabel(key, { keybindings: options.keybindings });\n\tif (width < MIN_WIDTH || height < MIN_HEIGHT) return renderTooSmall(options, hint);\n\tconst rects = paneRects(width, height);\n\tconst env: FooterEnv = { width, y: height - 1, theme, hint, feedback: options.feedback };\n\tlet body: PaneRender & { regions: PaneRegion[]; pageSize?: number };\n\tlet footer: Footer;\n\tswitch (mode.kind) {\n\t\tcase \"help\": {\n\t\t\tbody = renderHelpPane(options, hint, { x: 0, y: 0, width, height: height - 1 });\n\t\t\tfooter = helpFooter(env);\n\t\t\tbreak;\n\t\t}\n\t\tcase \"search\": {\n\t\t\tbody = renderPanes(options, hint, rects);\n\t\t\tfooter = searchFooter(env);\n\t\t\tbreak;\n\t\t}\n\t\tcase \"confirmStop\": {\n\t\t\tbody = renderPanes(options, hint, rects);\n\t\t\tconst target = options.selection.all.find((task) => task.id === mode.taskId);\n\t\t\tfooter = confirmStopFooter(env, {\n\t\t\t\tsource: cleanTaskText(target?.label ?? \"task\"),\n\t\t\t\ttitle: cleanTaskText(firstCommandLine(target?.title ?? mode.taskId)),\n\t\t\t});\n\t\t\tbreak;\n\t\t}\n\t\tcase \"browse\": {\n\t\t\tbody = renderPanes(options, hint, rects);\n\t\t\tconst task = options.inspector.task;\n\t\t\tfooter = browseFooter(env, {\n\t\t\t\tfocus: options.focus,\n\t\t\t\tsplit: rects.split,\n\t\t\t\tstoppable: !!task && isStoppable(task),\n\t\t\t\tdetachable: !!task && options.canDetach(task.id),\n\t\t\t});\n\t\t\tbreak;\n\t\t}\n\t}\n\treturn {\n\t\tlines: [...body.lines, footer.line],\n\t\thits: [...body.hits, ...footer.hits],\n\t\tregions: body.regions,\n\t\tsplit: rects.split,\n\t\tlistPageSize: body.pageSize ?? Math.max(1, rects.list.height - 4),\n\t};\n}\n\n/**\n * Browse, search and confirmation share the two-pane frame; the mode decides which panes\n * answer to the pointer, so each mode exposes only its own targets.\n */\nfunction renderPanes(\n\toptions: LayoutOptions,\n\thint: (key: Keybinding) => string,\n\trects: PaneRects,\n): PaneRender & { regions: PaneRegion[]; pageSize: number } {\n\tconst { theme, selection, inspector, list, focus, mode } = options;\n\tconst showList = rects.split || focus === \"list\";\n\tconst showInspector = rects.split || focus === \"inspector\";\n\tconst browsing = mode.kind === \"browse\";\n\tconst searching = mode.kind === \"search\";\n\tconst listPane = showList\n\t\t? renderListPane({ theme, selection, list }, rects.list, {\n\t\t\t\tactive: rects.split && focus === \"list\",\n\t\t\t\tinteractive: browsing || searching,\n\t\t\t\tsearchRow: mode.kind === \"search\" ? mode.input.render(rects.list.width - 2).join(\"\") : undefined,\n\t\t\t\tstopTargetId: mode.kind === \"confirmStop\" ? mode.taskId : undefined,\n\t\t\t})\n\t\t: undefined;\n\tconst inspectorPane = showInspector\n\t\t? renderInspectorPane({ theme, selection, inspector, hint }, rects.inspector, {\n\t\t\t\tactive: rects.split && focus === \"inspector\",\n\t\t\t\tinteractive: browsing,\n\t\t\t})\n\t\t: undefined;\n\tconst regions: PaneRegion[] = [];\n\tif (listPane && (browsing || searching)) regions.push({ pane: \"list\", ...rects.list });\n\tif (inspectorPane && browsing) regions.push({ pane: \"inspector\", ...rects.inspector });\n\tconst lines =\n\t\tlistPane && inspectorPane\n\t\t\t? listPane.lines.map((line, i) => `${line} ${inspectorPane.lines[i]}`)\n\t\t\t: (listPane ?? inspectorPane)!.lines;\n\treturn {\n\t\tlines,\n\t\thits: [...(listPane?.hits ?? []), ...(inspectorPane?.hits ?? [])],\n\t\tregions,\n\t\tpageSize: listPane?.pageSize ?? Math.max(1, rects.list.height - 4),\n\t};\n}\n\nfunction renderHelpPane(\n\toptions: LayoutOptions,\n\thint: (key: Keybinding) => string,\n\trect: Rect,\n): PaneRender & { regions: PaneRegion[] } {\n\tconst { theme, mode } = options;\n\tif (mode.kind !== \"help\") throw new Error(\"help pane requires help mode\");\n\tconst inner = rect.width - 2;\n\tconst rows = mode.viewport.layout(helpRows(hint, theme), inner, rect.height - 2);\n\tconst frame = framePane({\n\t\twidth: rect.width,\n\t\ttheme,\n\t\taccent: false,\n\t\ttitle: theme.fg(\"text\", \"Task controls\"),\n\t\tstatus: mode.viewport.overflows ? theme.fg(\"muted\", mode.viewport.range) : undefined,\n\t\trows: Array.from({ length: rect.height - 2 }, (_, i) => rows[i] ?? \"\"),\n\t});\n\treturn { lines: frame.lines, hits: [], regions: [{ pane: \"help\", ...rect }] };\n}\n\n/**\n * The panel is modal: a floating notice would leave the transcript visible yet unscrollable,\n * so the notice owns every row as a centered dialog. Only a truly tiny terminal, where even\n * the dialog does not fit, gets bare covering lines.\n */\nfunction renderTooSmall(options: LayoutOptions, hint: (key: Keybinding) => string): TasksLayout {\n\tconst { width, height, theme } = options;\n\tconst result: TasksLayout = { lines: [], hits: [], regions: [], split: false, listPageSize: 1, tooSmall: true };\n\tconst message = \"Terminal too small for /tasks. Enlarge the window to continue.\";\n\tconst close = `${hint(\"tui.select.cancel\")} close`;\n\tconst boxWidth = Math.min(width, 46);\n\tconst inner = boxWidth - 2;\n\tconst wrapped = wrapTextWithAnsi(message, inner).map((line) => theme.fg(\"warning\", line));\n\tconst spacious = [theme.fg(\"text\", \"Tasks\"), \"\", ...wrapped, \"\", theme.fg(\"dim\", close)];\n\tconst tight = [theme.fg(\"text\", \"Tasks\"), ...wrapped, theme.fg(\"dim\", close)];\n\tconst body = height >= spacious.length + 2 ? spacious : tight;\n\tif (inner >= 16 && height >= body.length + 2) {\n\t\tconst box = framePane({ width: boxWidth, theme, accent: false, rows: body }).lines;\n\t\tconst left = Math.floor((width - boxWidth) / 2);\n\t\tconst top = Math.max(0, Math.floor((height - box.length) / 2));\n\t\tconst blank = \" \".repeat(width);\n\t\tresult.lines = [\n\t\t\t...Array.from({ length: top }, () => blank),\n\t\t\t...box.map((line) => \" \".repeat(left) + line + \" \".repeat(width - left - boxWidth)),\n\t\t\t...Array.from({ length: height - top - box.length }, () => blank),\n\t\t];\n\t\tresult.hits.push({ x: left + 1, y: top + body.length, width: visibleWidth(close), action: { kind: \"back\" } });\n\t} else {\n\t\tresult.lines = [\"Tasks\", message, close].slice(0, height).map((line) => fitCell(line, width));\n\t\twhile (result.lines.length < height) result.lines.push(\" \".repeat(width));\n\t}\n\treturn result;\n}\n"]}
@@ -14,7 +14,9 @@ export declare class TaskList {
14
14
  private reveal;
15
15
  showSelection(): void;
16
16
  scroll(delta: number): void;
17
- render(selection: TaskSelection, theme: Theme, width: number, height: number): ListRow[];
17
+ /** `stopTargetId` marks the row a pending stop question is about. */
18
+ render(selection: TaskSelection, theme: Theme, width: number, height: number, stopTargetId?: string): ListRow[];
19
+ /** The visible task span, such as `1-12 of 40`; empty while every row fits. */
18
20
  get range(): string;
19
21
  }
20
22
  export {};
@@ -1 +1 @@
1
- {"version":3,"file":"list.d.ts","sourceRoot":"","sources":["../../../../src/extensions/tasks/ui/list.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,2CAA2C,CAAC;AACvE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAIhD,UAAU,OAAO;IAChB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,CAAC,EAAE,MAAM,CAAC;CAChB;AAgBD,4FAA4F;AAC5F,qBAAa,QAAQ;IACpB,OAAO,CAAC,KAAK,CAAK;IAClB,OAAO,CAAC,MAAM,CAAK;IACnB,OAAO,CAAC,IAAI,CAAiB;IAC7B,OAAO,CAAC,UAAU,CAAC,CAAS;IAC5B,OAAO,CAAC,YAAY,CAAM;IAC1B,OAAO,CAAC,MAAM,CAAS;IACvB,aAAa,IAAI,IAAI;IAGrB,MAAM,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI;IAG3B,MAAM,CAAC,SAAS,EAAE,aAAa,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,EAAE;IAgExF,IAAI,KAAK,IAAI,MAAM,CAOlB;CACD"}
1
+ {"version":3,"file":"list.d.ts","sourceRoot":"","sources":["../../../../src/extensions/tasks/ui/list.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,2CAA2C,CAAC;AACvE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAIhD,UAAU,OAAO;IAChB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,CAAC,EAAE,MAAM,CAAC;CAChB;AAmBD,4FAA4F;AAC5F,qBAAa,QAAQ;IACpB,OAAO,CAAC,KAAK,CAAK;IAClB,OAAO,CAAC,MAAM,CAAK;IACnB,OAAO,CAAC,IAAI,CAAiB;IAC7B,OAAO,CAAC,UAAU,CAAC,CAAS;IAC5B,OAAO,CAAC,YAAY,CAAM;IAC1B,OAAO,CAAC,MAAM,CAAS;IACvB,aAAa,IAAI,IAAI;IAGrB,MAAM,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI;IAG3B,qEAAqE;IACrE,MAAM,CAAC,SAAS,EAAE,aAAa,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,YAAY,CAAC,EAAE,MAAM,GAAG,OAAO,EAAE;IAgE/G,+EAA+E;IAC/E,IAAI,KAAK,IAAI,MAAM,CAOlB;CACD"}