pi-ui-extend 0.1.1

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 (420) hide show
  1. package/README.md +307 -0
  2. package/bin/pix.mjs +219 -0
  3. package/dist/app/app.d.ts +96 -0
  4. package/dist/app/app.js +871 -0
  5. package/dist/app/blink-controller.d.ts +23 -0
  6. package/dist/app/blink-controller.js +82 -0
  7. package/dist/app/cli.d.ts +8 -0
  8. package/dist/app/cli.js +83 -0
  9. package/dist/app/clipboard.d.ts +1 -0
  10. package/dist/app/clipboard.js +24 -0
  11. package/dist/app/command-controller.d.ts +18 -0
  12. package/dist/app/command-controller.js +58 -0
  13. package/dist/app/command-host.d.ts +44 -0
  14. package/dist/app/command-host.js +1 -0
  15. package/dist/app/command-model-actions.d.ts +12 -0
  16. package/dist/app/command-model-actions.js +176 -0
  17. package/dist/app/command-navigation-actions.d.ts +19 -0
  18. package/dist/app/command-navigation-actions.js +267 -0
  19. package/dist/app/command-registry.d.ts +32 -0
  20. package/dist/app/command-registry.js +225 -0
  21. package/dist/app/command-runtime.d.ts +5 -0
  22. package/dist/app/command-runtime.js +32 -0
  23. package/dist/app/command-session-actions.d.ts +20 -0
  24. package/dist/app/command-session-actions.js +295 -0
  25. package/dist/app/constants.d.ts +52 -0
  26. package/dist/app/constants.js +103 -0
  27. package/dist/app/conversation-entry-renderer.d.ts +21 -0
  28. package/dist/app/conversation-entry-renderer.js +81 -0
  29. package/dist/app/conversation-shell-renderer.d.ts +5 -0
  30. package/dist/app/conversation-shell-renderer.js +43 -0
  31. package/dist/app/conversation-tool-renderer.d.ts +16 -0
  32. package/dist/app/conversation-tool-renderer.js +216 -0
  33. package/dist/app/conversation-viewport.d.ts +55 -0
  34. package/dist/app/conversation-viewport.js +252 -0
  35. package/dist/app/dcp-stats.d.ts +2 -0
  36. package/dist/app/dcp-stats.js +116 -0
  37. package/dist/app/editor-layout-renderer.d.ts +31 -0
  38. package/dist/app/editor-layout-renderer.js +211 -0
  39. package/dist/app/editor-panels.d.ts +4 -0
  40. package/dist/app/editor-panels.js +130 -0
  41. package/dist/app/extension-actions-controller.d.ts +22 -0
  42. package/dist/app/extension-actions-controller.js +60 -0
  43. package/dist/app/extension-event-bus.d.ts +3 -0
  44. package/dist/app/extension-event-bus.js +23 -0
  45. package/dist/app/extension-ui-controller.d.ts +57 -0
  46. package/dist/app/extension-ui-controller.js +532 -0
  47. package/dist/app/file-link-opener.d.ts +2 -0
  48. package/dist/app/file-link-opener.js +66 -0
  49. package/dist/app/file-links.d.ts +10 -0
  50. package/dist/app/file-links.js +117 -0
  51. package/dist/app/guards.d.ts +3 -0
  52. package/dist/app/guards.js +9 -0
  53. package/dist/app/icons.d.ts +37 -0
  54. package/dist/app/icons.js +97 -0
  55. package/dist/app/id.d.ts +1 -0
  56. package/dist/app/id.js +4 -0
  57. package/dist/app/image-click-targets.d.ts +5 -0
  58. package/dist/app/image-click-targets.js +32 -0
  59. package/dist/app/image-opener.d.ts +2 -0
  60. package/dist/app/image-opener.js +64 -0
  61. package/dist/app/input-action-controller.d.ts +47 -0
  62. package/dist/app/input-action-controller.js +209 -0
  63. package/dist/app/input-controller.d.ts +60 -0
  64. package/dist/app/input-controller.js +425 -0
  65. package/dist/app/input-paste-handler.d.ts +27 -0
  66. package/dist/app/input-paste-handler.js +146 -0
  67. package/dist/app/menu-items-controller.d.ts +32 -0
  68. package/dist/app/menu-items-controller.js +182 -0
  69. package/dist/app/message-content.d.ts +8 -0
  70. package/dist/app/message-content.js +115 -0
  71. package/dist/app/model-ref.d.ts +13 -0
  72. package/dist/app/model-ref.js +50 -0
  73. package/dist/app/model-usage-controller.d.ts +35 -0
  74. package/dist/app/model-usage-controller.js +99 -0
  75. package/dist/app/model-usage-status.d.ts +125 -0
  76. package/dist/app/model-usage-status.js +749 -0
  77. package/dist/app/mouse-controller.d.ts +182 -0
  78. package/dist/app/mouse-controller.js +968 -0
  79. package/dist/app/native-modifiers.d.ts +3 -0
  80. package/dist/app/native-modifiers.js +60 -0
  81. package/dist/app/nerd-font-controller.d.ts +11 -0
  82. package/dist/app/nerd-font-controller.js +90 -0
  83. package/dist/app/popup-action-controller.d.ts +44 -0
  84. package/dist/app/popup-action-controller.js +278 -0
  85. package/dist/app/popup-menu-controller.d.ts +183 -0
  86. package/dist/app/popup-menu-controller.js +1070 -0
  87. package/dist/app/prompt-enhancer-controller.d.ts +40 -0
  88. package/dist/app/prompt-enhancer-controller.js +215 -0
  89. package/dist/app/queued-message-controller.d.ts +62 -0
  90. package/dist/app/queued-message-controller.js +377 -0
  91. package/dist/app/render-controller.d.ts +41 -0
  92. package/dist/app/render-controller.js +378 -0
  93. package/dist/app/render-text.d.ts +19 -0
  94. package/dist/app/render-text.js +67 -0
  95. package/dist/app/request-history.d.ts +23 -0
  96. package/dist/app/request-history.js +131 -0
  97. package/dist/app/runtime.d.ts +39 -0
  98. package/dist/app/runtime.js +195 -0
  99. package/dist/app/screen-selection.d.ts +6 -0
  100. package/dist/app/screen-selection.js +9 -0
  101. package/dist/app/screen-styler.d.ts +34 -0
  102. package/dist/app/screen-styler.js +168 -0
  103. package/dist/app/scroll-controller.d.ts +51 -0
  104. package/dist/app/scroll-controller.js +207 -0
  105. package/dist/app/session-event-controller.d.ts +69 -0
  106. package/dist/app/session-event-controller.js +338 -0
  107. package/dist/app/session-history.d.ts +23 -0
  108. package/dist/app/session-history.js +164 -0
  109. package/dist/app/session-lifecycle-controller.d.ts +55 -0
  110. package/dist/app/session-lifecycle-controller.js +116 -0
  111. package/dist/app/session-search.d.ts +24 -0
  112. package/dist/app/session-search.js +215 -0
  113. package/dist/app/shell-command.d.ts +27 -0
  114. package/dist/app/shell-command.js +176 -0
  115. package/dist/app/shell-controller.d.ts +28 -0
  116. package/dist/app/shell-controller.js +124 -0
  117. package/dist/app/slash-commands.d.ts +6 -0
  118. package/dist/app/slash-commands.js +75 -0
  119. package/dist/app/startup-checks.d.ts +8 -0
  120. package/dist/app/startup-checks.js +59 -0
  121. package/dist/app/startup-info.d.ts +3 -0
  122. package/dist/app/startup-info.js +176 -0
  123. package/dist/app/status-controller.d.ts +35 -0
  124. package/dist/app/status-controller.js +105 -0
  125. package/dist/app/status-line-renderer.d.ts +68 -0
  126. package/dist/app/status-line-renderer.js +453 -0
  127. package/dist/app/subagents-files.d.ts +10 -0
  128. package/dist/app/subagents-files.js +193 -0
  129. package/dist/app/subagents-model.d.ts +23 -0
  130. package/dist/app/subagents-model.js +224 -0
  131. package/dist/app/subagents-widget-controller.d.ts +43 -0
  132. package/dist/app/subagents-widget-controller.js +311 -0
  133. package/dist/app/tab-line-renderer.d.ts +26 -0
  134. package/dist/app/tab-line-renderer.js +222 -0
  135. package/dist/app/tabs-controller.d.ts +100 -0
  136. package/dist/app/tabs-controller.js +885 -0
  137. package/dist/app/terminal-controller.d.ts +40 -0
  138. package/dist/app/terminal-controller.js +135 -0
  139. package/dist/app/terminal-edit-shortcuts.d.ts +23 -0
  140. package/dist/app/terminal-edit-shortcuts.js +138 -0
  141. package/dist/app/terminal-output-buffer.d.ts +20 -0
  142. package/dist/app/terminal-output-buffer.js +52 -0
  143. package/dist/app/toast-controller.d.ts +13 -0
  144. package/dist/app/toast-controller.js +40 -0
  145. package/dist/app/toast-renderer.d.ts +9 -0
  146. package/dist/app/toast-renderer.js +79 -0
  147. package/dist/app/todo-model.d.ts +22 -0
  148. package/dist/app/todo-model.js +179 -0
  149. package/dist/app/todo-widget-controller.d.ts +21 -0
  150. package/dist/app/todo-widget-controller.js +59 -0
  151. package/dist/app/tool-block-renderer.d.ts +26 -0
  152. package/dist/app/tool-block-renderer.js +439 -0
  153. package/dist/app/types.d.ts +550 -0
  154. package/dist/app/types.js +1 -0
  155. package/dist/app/update.d.ts +36 -0
  156. package/dist/app/update.js +315 -0
  157. package/dist/app/voice-controller.d.ts +52 -0
  158. package/dist/app/voice-controller.js +600 -0
  159. package/dist/app/workspace-actions-controller.d.ts +40 -0
  160. package/dist/app/workspace-actions-controller.js +215 -0
  161. package/dist/app/workspace-undo.d.ts +44 -0
  162. package/dist/app/workspace-undo.js +215 -0
  163. package/dist/config.d.ts +62 -0
  164. package/dist/config.js +527 -0
  165. package/dist/context-progress-bar.d.ts +17 -0
  166. package/dist/context-progress-bar.js +48 -0
  167. package/dist/default-pix-config.d.ts +1 -0
  168. package/dist/default-pix-config.js +375 -0
  169. package/dist/fuzzy.d.ts +23 -0
  170. package/dist/fuzzy.js +165 -0
  171. package/dist/input-editor-files.d.ts +15 -0
  172. package/dist/input-editor-files.js +62 -0
  173. package/dist/input-editor.d.ts +186 -0
  174. package/dist/input-editor.js +835 -0
  175. package/dist/main.d.ts +1 -0
  176. package/dist/main.js +12 -0
  177. package/dist/markdown-format.d.ts +22 -0
  178. package/dist/markdown-format.js +542 -0
  179. package/dist/sdk.d.ts +3 -0
  180. package/dist/sdk.js +1 -0
  181. package/dist/syntax-highlight.d.ts +22 -0
  182. package/dist/syntax-highlight.js +528 -0
  183. package/dist/terminal-width.d.ts +6 -0
  184. package/dist/terminal-width.js +245 -0
  185. package/dist/theme.d.ts +56 -0
  186. package/dist/theme.js +118 -0
  187. package/dist/tool-renderers/apply-patch.d.ts +2 -0
  188. package/dist/tool-renderers/apply-patch.js +36 -0
  189. package/dist/tool-renderers/ast.d.ts +2 -0
  190. package/dist/tool-renderers/ast.js +5 -0
  191. package/dist/tool-renderers/compress.d.ts +2 -0
  192. package/dist/tool-renderers/compress.js +126 -0
  193. package/dist/tool-renderers/index.d.ts +3 -0
  194. package/dist/tool-renderers/index.js +44 -0
  195. package/dist/tool-renderers/question.d.ts +2 -0
  196. package/dist/tool-renderers/question.js +88 -0
  197. package/dist/tool-renderers/read.d.ts +2 -0
  198. package/dist/tool-renderers/read.js +36 -0
  199. package/dist/tool-renderers/repo.d.ts +2 -0
  200. package/dist/tool-renderers/repo.js +13 -0
  201. package/dist/tool-renderers/shell.d.ts +3 -0
  202. package/dist/tool-renderers/shell.js +27 -0
  203. package/dist/tool-renderers/skill.d.ts +2 -0
  204. package/dist/tool-renderers/skill.js +132 -0
  205. package/dist/tool-renderers/subagents.d.ts +2 -0
  206. package/dist/tool-renderers/subagents.js +51 -0
  207. package/dist/tool-renderers/todo.d.ts +2 -0
  208. package/dist/tool-renderers/todo.js +9 -0
  209. package/dist/tool-renderers/types.d.ts +42 -0
  210. package/dist/tool-renderers/types.js +1 -0
  211. package/dist/tool-renderers/utils.d.ts +31 -0
  212. package/dist/tool-renderers/utils.js +230 -0
  213. package/dist/tool-renderers/web.d.ts +3 -0
  214. package/dist/tool-renderers/web.js +9 -0
  215. package/dist/tool-renderers/write.d.ts +2 -0
  216. package/dist/tool-renderers/write.js +42 -0
  217. package/dist/ui.d.ts +56 -0
  218. package/dist/ui.js +94 -0
  219. package/docs/release.md +81 -0
  220. package/extensions/question/contract.ts +100 -0
  221. package/extensions/question/index.ts +34 -0
  222. package/extensions/question/render.ts +28 -0
  223. package/extensions/question/result.ts +86 -0
  224. package/extensions/question/tool-description.ts +11 -0
  225. package/extensions/question/tui.ts +629 -0
  226. package/extensions/question/types.ts +123 -0
  227. package/extensions/session-title/config.ts +169 -0
  228. package/extensions/session-title/index.ts +459 -0
  229. package/extensions/terminal-bell/index.ts +315 -0
  230. package/external/pi-tools-suite/README.md +242 -0
  231. package/external/pi-tools-suite/index.ts +1 -0
  232. package/external/pi-tools-suite/licenses/ollama-pi-web-search.MIT +21 -0
  233. package/external/pi-tools-suite/licenses/opencode-mystatus-MIT.txt +21 -0
  234. package/external/pi-tools-suite/package.json +53 -0
  235. package/external/pi-tools-suite/src/antigravity-auth/auth-store.ts +194 -0
  236. package/external/pi-tools-suite/src/antigravity-auth/commands.ts +80 -0
  237. package/external/pi-tools-suite/src/antigravity-auth/constants.ts +26 -0
  238. package/external/pi-tools-suite/src/antigravity-auth/headers.ts +20 -0
  239. package/external/pi-tools-suite/src/antigravity-auth/index.ts +104 -0
  240. package/external/pi-tools-suite/src/antigravity-auth/models.ts +86 -0
  241. package/external/pi-tools-suite/src/antigravity-auth/oauth.ts +305 -0
  242. package/external/pi-tools-suite/src/antigravity-auth/payload.ts +423 -0
  243. package/external/pi-tools-suite/src/antigravity-auth/status.ts +78 -0
  244. package/external/pi-tools-suite/src/antigravity-auth/stream.ts +302 -0
  245. package/external/pi-tools-suite/src/antigravity-auth/types.ts +113 -0
  246. package/external/pi-tools-suite/src/ast-grep/index.ts +6 -0
  247. package/external/pi-tools-suite/src/ast-grep/render.ts +70 -0
  248. package/external/pi-tools-suite/src/ast-grep/schema.ts +109 -0
  249. package/external/pi-tools-suite/src/ast-grep/tool.ts +345 -0
  250. package/external/pi-tools-suite/src/ast-grep/types.ts +55 -0
  251. package/external/pi-tools-suite/src/ast-grep/utils.ts +65 -0
  252. package/external/pi-tools-suite/src/async-subagents/async-subagents.sample.jsonc +222 -0
  253. package/external/pi-tools-suite/src/async-subagents/commands.ts +518 -0
  254. package/external/pi-tools-suite/src/async-subagents/constants.ts +11 -0
  255. package/external/pi-tools-suite/src/async-subagents/core/agent-strategy.ts +74 -0
  256. package/external/pi-tools-suite/src/async-subagents/core/attachment-bridge.ts +133 -0
  257. package/external/pi-tools-suite/src/async-subagents/core/cleanup.ts +66 -0
  258. package/external/pi-tools-suite/src/async-subagents/core/concurrency.ts +90 -0
  259. package/external/pi-tools-suite/src/async-subagents/core/config.ts +819 -0
  260. package/external/pi-tools-suite/src/async-subagents/core/log-limits.ts +166 -0
  261. package/external/pi-tools-suite/src/async-subagents/core/model-fallback.ts +133 -0
  262. package/external/pi-tools-suite/src/async-subagents/core/paths.ts +47 -0
  263. package/external/pi-tools-suite/src/async-subagents/core/pi-invocation.ts +35 -0
  264. package/external/pi-tools-suite/src/async-subagents/core/presets.ts +67 -0
  265. package/external/pi-tools-suite/src/async-subagents/core/process.ts +15 -0
  266. package/external/pi-tools-suite/src/async-subagents/core/prompt.ts +66 -0
  267. package/external/pi-tools-suite/src/async-subagents/core/registry.ts +224 -0
  268. package/external/pi-tools-suite/src/async-subagents/core/retry.ts +191 -0
  269. package/external/pi-tools-suite/src/async-subagents/core/routing.ts +259 -0
  270. package/external/pi-tools-suite/src/async-subagents/core/sessions.ts +138 -0
  271. package/external/pi-tools-suite/src/async-subagents/core/spawn.ts +688 -0
  272. package/external/pi-tools-suite/src/async-subagents/core/state.ts +281 -0
  273. package/external/pi-tools-suite/src/async-subagents/core/stop.ts +131 -0
  274. package/external/pi-tools-suite/src/async-subagents/core/structured-result.ts +237 -0
  275. package/external/pi-tools-suite/src/async-subagents/core/tool-guard.ts +34 -0
  276. package/external/pi-tools-suite/src/async-subagents/core/types.ts +150 -0
  277. package/external/pi-tools-suite/src/async-subagents/core/ultrawork-auto.ts +184 -0
  278. package/external/pi-tools-suite/src/async-subagents/core/utils.ts +11 -0
  279. package/external/pi-tools-suite/src/async-subagents/format.ts +41 -0
  280. package/external/pi-tools-suite/src/async-subagents/index.ts +422 -0
  281. package/external/pi-tools-suite/src/async-subagents/lib.ts +88 -0
  282. package/external/pi-tools-suite/src/async-subagents/live.ts +10 -0
  283. package/external/pi-tools-suite/src/async-subagents/polling.ts +83 -0
  284. package/external/pi-tools-suite/src/async-subagents/render.ts +230 -0
  285. package/external/pi-tools-suite/src/async-subagents/subagent-overlay.ts +77 -0
  286. package/external/pi-tools-suite/src/async-subagents/tasks.ts +120 -0
  287. package/external/pi-tools-suite/src/async-subagents/tools/cleanup.ts +99 -0
  288. package/external/pi-tools-suite/src/async-subagents/tools/result.ts +179 -0
  289. package/external/pi-tools-suite/src/async-subagents/tools/spawn.ts +372 -0
  290. package/external/pi-tools-suite/src/async-subagents/tools/status.ts +60 -0
  291. package/external/pi-tools-suite/src/async-subagents/tools/stop.ts +79 -0
  292. package/external/pi-tools-suite/src/async-subagents/tools/subagents.ts +152 -0
  293. package/external/pi-tools-suite/src/async-subagents/tools/wait.ts +67 -0
  294. package/external/pi-tools-suite/src/async-subagents/types.ts +45 -0
  295. package/external/pi-tools-suite/src/async-subagents/ui.ts +5 -0
  296. package/external/pi-tools-suite/src/compress/commands.ts +440 -0
  297. package/external/pi-tools-suite/src/compress/compress-tool.ts +368 -0
  298. package/external/pi-tools-suite/src/compress/compression-blocks.ts +524 -0
  299. package/external/pi-tools-suite/src/compress/config.ts +310 -0
  300. package/external/pi-tools-suite/src/compress/dcp-tui-filter.ts +498 -0
  301. package/external/pi-tools-suite/src/compress/index.ts +397 -0
  302. package/external/pi-tools-suite/src/compress/prompts.ts +269 -0
  303. package/external/pi-tools-suite/src/compress/pruner-candidates.ts +176 -0
  304. package/external/pi-tools-suite/src/compress/pruner-compression-blocks.ts +260 -0
  305. package/external/pi-tools-suite/src/compress/pruner-message-ids.ts +147 -0
  306. package/external/pi-tools-suite/src/compress/pruner-metadata.ts +268 -0
  307. package/external/pi-tools-suite/src/compress/pruner-nudge.ts +315 -0
  308. package/external/pi-tools-suite/src/compress/pruner-tools.ts +263 -0
  309. package/external/pi-tools-suite/src/compress/pruner-types.ts +25 -0
  310. package/external/pi-tools-suite/src/compress/pruner.ts +92 -0
  311. package/external/pi-tools-suite/src/compress/state.ts +486 -0
  312. package/external/pi-tools-suite/src/compress/ui.ts +308 -0
  313. package/external/pi-tools-suite/src/config.ts +176 -0
  314. package/external/pi-tools-suite/src/context-usage.ts +31 -0
  315. package/external/pi-tools-suite/src/default-pi-tools-suite-config.ts +355 -0
  316. package/external/pi-tools-suite/src/index.ts +46 -0
  317. package/external/pi-tools-suite/src/lib/lsp.ts +62 -0
  318. package/external/pi-tools-suite/src/lib/project.ts +42 -0
  319. package/external/pi-tools-suite/src/lib/tool-args.ts +137 -0
  320. package/external/pi-tools-suite/src/lsp/_shared/config.ts +156 -0
  321. package/external/pi-tools-suite/src/lsp/_shared/glob.ts +60 -0
  322. package/external/pi-tools-suite/src/lsp/_shared/output.ts +102 -0
  323. package/external/pi-tools-suite/src/lsp/_shared/paths.ts +138 -0
  324. package/external/pi-tools-suite/src/lsp/_shared/runner.ts +64 -0
  325. package/external/pi-tools-suite/src/lsp/_shared/template.ts +23 -0
  326. package/external/pi-tools-suite/src/lsp/_shared/trust.ts +116 -0
  327. package/external/pi-tools-suite/src/lsp/_shared/types.ts +98 -0
  328. package/external/pi-tools-suite/src/lsp/async.ts +29 -0
  329. package/external/pi-tools-suite/src/lsp/child-process.ts +81 -0
  330. package/external/pi-tools-suite/src/lsp/client.ts +340 -0
  331. package/external/pi-tools-suite/src/lsp/constants.ts +9 -0
  332. package/external/pi-tools-suite/src/lsp/diagnostics-store.ts +64 -0
  333. package/external/pi-tools-suite/src/lsp/documents.ts +24 -0
  334. package/external/pi-tools-suite/src/lsp/index.ts +31 -0
  335. package/external/pi-tools-suite/src/lsp/lsp-utils.ts +37 -0
  336. package/external/pi-tools-suite/src/lsp/manager.ts +190 -0
  337. package/external/pi-tools-suite/src/lsp/mutation-events.ts +78 -0
  338. package/external/pi-tools-suite/src/lsp/renderer.ts +1 -0
  339. package/external/pi-tools-suite/src/lsp/tool-result.ts +6 -0
  340. package/external/pi-tools-suite/src/lsp/tsserver.ts +107 -0
  341. package/external/pi-tools-suite/src/lsp/types.ts +15 -0
  342. package/external/pi-tools-suite/src/model-tools/apply-patch.ts +590 -0
  343. package/external/pi-tools-suite/src/model-tools/index.ts +430 -0
  344. package/external/pi-tools-suite/src/model-tools/path-utils.ts +6 -0
  345. package/external/pi-tools-suite/src/model-tools/tool-args.ts +11 -0
  346. package/external/pi-tools-suite/src/prompt-commands/index.ts +349 -0
  347. package/external/pi-tools-suite/src/repo-discovery/index.ts +384 -0
  348. package/external/pi-tools-suite/src/repo-discovery/project.ts +7 -0
  349. package/external/pi-tools-suite/src/startup-section.ts +13 -0
  350. package/external/pi-tools-suite/src/terminal-bell/index.ts +309 -0
  351. package/external/pi-tools-suite/src/todo/index.ts +201 -0
  352. package/external/pi-tools-suite/src/todo/state/auto-clear.ts +13 -0
  353. package/external/pi-tools-suite/src/todo/state/invariants.ts +21 -0
  354. package/external/pi-tools-suite/src/todo/state/persistence.ts +94 -0
  355. package/external/pi-tools-suite/src/todo/state/replay.ts +38 -0
  356. package/external/pi-tools-suite/src/todo/state/selectors.ts +49 -0
  357. package/external/pi-tools-suite/src/todo/state/state-reducer.ts +362 -0
  358. package/external/pi-tools-suite/src/todo/state/state.ts +18 -0
  359. package/external/pi-tools-suite/src/todo/state/store.ts +52 -0
  360. package/external/pi-tools-suite/src/todo/state/task-graph.ts +57 -0
  361. package/external/pi-tools-suite/src/todo/todo.ts +487 -0
  362. package/external/pi-tools-suite/src/todo/tool/response-envelope.ts +143 -0
  363. package/external/pi-tools-suite/src/todo/tool/types.ts +188 -0
  364. package/external/pi-tools-suite/src/todo/view/format.ts +18 -0
  365. package/external/pi-tools-suite/src/todo/view/labels.ts +13 -0
  366. package/external/pi-tools-suite/src/tool-descriptions.ts +369 -0
  367. package/external/pi-tools-suite/src/usage/index.ts +152 -0
  368. package/external/pi-tools-suite/src/usage/lib/copilot.ts +535 -0
  369. package/external/pi-tools-suite/src/usage/lib/google.ts +478 -0
  370. package/external/pi-tools-suite/src/usage/lib/openai.ts +268 -0
  371. package/external/pi-tools-suite/src/usage/lib/types.ts +114 -0
  372. package/external/pi-tools-suite/src/usage/lib/utils.ts +134 -0
  373. package/external/pi-tools-suite/src/usage/lib/zhipu.ts +228 -0
  374. package/external/pi-tools-suite/src/web-search/index.ts +397 -0
  375. package/package.json +89 -0
  376. package/skills/context7/SKILL.md +69 -0
  377. package/skills/context7/scripts/context7.sh +73 -0
  378. package/skills/handoff/SKILL.md +15 -0
  379. package/skills/pdf/LICENSE.txt +30 -0
  380. package/skills/pdf/SKILL.md +314 -0
  381. package/skills/pdf/forms.md +294 -0
  382. package/skills/pdf/reference.md +612 -0
  383. package/skills/pdf/scripts/check_bounding_boxes.py +65 -0
  384. package/skills/pdf/scripts/check_fillable_fields.py +11 -0
  385. package/skills/pdf/scripts/convert_pdf_to_images.py +33 -0
  386. package/skills/pdf/scripts/create_validation_image.py +37 -0
  387. package/skills/pdf/scripts/extract_form_field_info.py +122 -0
  388. package/skills/pdf/scripts/extract_form_structure.py +115 -0
  389. package/skills/pdf/scripts/fill_fillable_fields.py +98 -0
  390. package/skills/pdf/scripts/fill_pdf_form_with_annotations.py +107 -0
  391. package/skills/playwright-cli/SKILL.md +388 -0
  392. package/skills/playwright-cli/references/element-attributes.md +23 -0
  393. package/skills/playwright-cli/references/playwright-tests.md +39 -0
  394. package/skills/playwright-cli/references/request-mocking.md +87 -0
  395. package/skills/playwright-cli/references/running-code.md +241 -0
  396. package/skills/playwright-cli/references/session-management.md +225 -0
  397. package/skills/playwright-cli/references/spec-driven-testing.md +305 -0
  398. package/skills/playwright-cli/references/storage-state.md +275 -0
  399. package/skills/playwright-cli/references/test-generation.md +134 -0
  400. package/skills/playwright-cli/references/tracing.md +139 -0
  401. package/skills/playwright-cli/references/video-recording.md +143 -0
  402. package/skills/simplify/SKILL.md +51 -0
  403. package/skills/skill-creator/LICENSE.txt +202 -0
  404. package/skills/skill-creator/SKILL.md +485 -0
  405. package/skills/skill-creator/agents/analyzer.md +274 -0
  406. package/skills/skill-creator/agents/comparator.md +202 -0
  407. package/skills/skill-creator/agents/grader.md +223 -0
  408. package/skills/skill-creator/assets/eval_review.html +146 -0
  409. package/skills/skill-creator/eval-viewer/generate_review.py +471 -0
  410. package/skills/skill-creator/eval-viewer/viewer.html +1325 -0
  411. package/skills/skill-creator/references/schemas.md +430 -0
  412. package/skills/skill-creator/scripts/__init__.py +0 -0
  413. package/skills/skill-creator/scripts/aggregate_benchmark.py +401 -0
  414. package/skills/skill-creator/scripts/generate_report.py +326 -0
  415. package/skills/skill-creator/scripts/improve_description.py +247 -0
  416. package/skills/skill-creator/scripts/package_skill.py +136 -0
  417. package/skills/skill-creator/scripts/quick_validate.py +103 -0
  418. package/skills/skill-creator/scripts/run_eval.py +310 -0
  419. package/skills/skill-creator/scripts/run_loop.py +328 -0
  420. package/skills/skill-creator/scripts/utils.py +47 -0
@@ -0,0 +1,1070 @@
1
+ import { resolve } from "node:path";
2
+ import { fuzzySearch } from "../fuzzy.js";
3
+ import { colorLine } from "../theme.js";
4
+ import { PopupMenu } from "../ui.js";
5
+ import { padOrTrimPlain, ellipsizeDisplay, sanitizeText } from "./render-text.js";
6
+ import { stringDisplayWidth } from "../terminal-width.js";
7
+ import { RESUME_MENU_INITIAL_SESSION_ROWS, RESUME_MENU_LOAD_BATCH_ROWS, RESUME_MENU_LOAD_THRESHOLD_ROWS, RESUME_MENU_MAX_ROWS, SLASH_COMMAND_DESCRIPTION_COLUMN, SLASH_COMMAND_MENU_MAX_ROWS, THINKING_MENU_MAX_ROWS, } from "./constants.js";
8
+ import { APP_ICONS } from "./icons.js";
9
+ const POPUP_MENU_ESCAPE_BUTTON = "Esc";
10
+ export class AppPopupMenuController {
11
+ host;
12
+ menuController = {
13
+ show: (items, options) => this.showSdkMenu(items, options),
14
+ select: (title, options, menuOptions) => this.selectSdkMenu(title, options, menuOptions),
15
+ close: () => {
16
+ this.closeSdkMenu(undefined);
17
+ },
18
+ };
19
+ slashCommandMenu = new PopupMenu({ maxVisibleRows: SLASH_COMMAND_MENU_MAX_ROWS });
20
+ modelMenu = new PopupMenu({ maxVisibleRows: SLASH_COMMAND_MENU_MAX_ROWS });
21
+ thinkingMenu = new PopupMenu({ maxVisibleRows: THINKING_MENU_MAX_ROWS });
22
+ resumeMenu = new PopupMenu({ maxVisibleRows: RESUME_MENU_MAX_ROWS });
23
+ userMessageMenu = new PopupMenu({ maxVisibleRows: 4 });
24
+ userMessageJumpMenu = new PopupMenu({ maxVisibleRows: RESUME_MENU_MAX_ROWS });
25
+ queueMessageMenu = new PopupMenu({ maxVisibleRows: 4 });
26
+ sdkMenu = new PopupMenu({ maxVisibleRows: SLASH_COMMAND_MENU_MAX_ROWS });
27
+ sdkMenuRequest;
28
+ slashCommandMenuQuery = "";
29
+ dismissedSlashCommandMenuInput;
30
+ modelMenuQuery = "";
31
+ dismissedModelMenuInput;
32
+ thinkingMenuQuery = "";
33
+ dismissedThinkingMenuInput;
34
+ directPopupMenu;
35
+ directPopupMenuPreserveStatus = false;
36
+ directPopupMenuPlacement = "default";
37
+ directPopupMenuQuery = "";
38
+ resumeMenuQuery = "";
39
+ resumeMenuSessionLimit = RESUME_MENU_INITIAL_SESSION_ROWS;
40
+ resumeMenuAllSessionsLoaded = false;
41
+ activeUserMessageEntryId;
42
+ activeQueuedMessageEntryId;
43
+ constructor(host) {
44
+ this.host = host;
45
+ }
46
+ get directMenu() {
47
+ return this.directPopupMenu;
48
+ }
49
+ get directQuery() {
50
+ return this.directPopupMenuQuery;
51
+ }
52
+ setDirectMenu(menu) {
53
+ this.directPopupMenu = menu;
54
+ if (!menu)
55
+ this.directPopupMenuPlacement = "default";
56
+ }
57
+ setDirectPreserveStatus(preserveStatus) {
58
+ this.directPopupMenuPreserveStatus = preserveStatus;
59
+ }
60
+ setDirectQuery(query) {
61
+ this.directPopupMenuQuery = query;
62
+ if (this.directPopupMenu === "resume")
63
+ this.resetResumeMenuLazyState(query);
64
+ }
65
+ resetInputMenuDismissals() {
66
+ this.dismissedSlashCommandMenuInput = undefined;
67
+ this.dismissedModelMenuInput = undefined;
68
+ this.dismissedThinkingMenuInput = undefined;
69
+ }
70
+ showSdkMenu(items, options) {
71
+ if (!this.host.isRunning())
72
+ return Promise.resolve(undefined);
73
+ this.closeSdkMenu(undefined, { render: false, restoreStatus: false });
74
+ const requestItems = items.map((item) => item);
75
+ return new Promise((resolveResult) => {
76
+ this.sdkMenuRequest = {
77
+ items: requestItems,
78
+ options,
79
+ resolve: (value) => {
80
+ resolveResult(value);
81
+ },
82
+ };
83
+ this.openDirectPopupMenu("sdk-menu", options.preserveStatus === undefined ? {} : { preserveStatus: options.preserveStatus });
84
+ this.host.render();
85
+ });
86
+ }
87
+ selectSdkMenu(title, options, menuOptions = {}) {
88
+ return this.showSdkMenu(options.map((option) => ({ value: option, label: option })), { title, ...menuOptions });
89
+ }
90
+ closeSdkMenu(value, options = {}) {
91
+ const request = this.sdkMenuRequest;
92
+ this.sdkMenuRequest = undefined;
93
+ this.sdkMenu.close();
94
+ if (this.directPopupMenu === "sdk-menu") {
95
+ this.directPopupMenu = undefined;
96
+ this.directPopupMenuPreserveStatus = false;
97
+ this.directPopupMenuQuery = "";
98
+ }
99
+ if (request && options.restoreStatus !== false && request.options.preserveStatus !== true)
100
+ this.host.restoreSessionStatus();
101
+ request?.resolve(value);
102
+ if (options.render !== false && this.host.isRunning())
103
+ this.host.render();
104
+ }
105
+ getActivePopupMenu(active) {
106
+ switch (active) {
107
+ case "sdk-menu":
108
+ return this.sdkMenu;
109
+ case "queue-message":
110
+ return this.queueMessageMenu;
111
+ case "user-message-jump":
112
+ return this.userMessageJumpMenu;
113
+ case "user-message":
114
+ return this.userMessageMenu;
115
+ case "model":
116
+ return this.modelMenu;
117
+ case "thinking":
118
+ return this.thinkingMenu;
119
+ case "resume":
120
+ return this.resumeMenu;
121
+ case "slash":
122
+ return this.slashCommandMenu;
123
+ }
124
+ throw new Error(`Unknown popup menu: ${active}`);
125
+ }
126
+ moveActivePopupMenuSelection(delta) {
127
+ const active = this.syncActivePopupMenu();
128
+ if (!active)
129
+ return false;
130
+ this.getActivePopupMenu(active).moveSelection(delta);
131
+ this.host.render();
132
+ return true;
133
+ }
134
+ scrollActivePopupMenu(delta) {
135
+ const active = this.syncActivePopupMenu();
136
+ if (!active)
137
+ return false;
138
+ this.getActivePopupMenu(active).scroll(delta);
139
+ this.host.render();
140
+ return true;
141
+ }
142
+ handleDirectPopupInput(char) {
143
+ const active = this.directPopupMenu;
144
+ if (!active)
145
+ return false;
146
+ if (active === "user-message")
147
+ return char >= " " || char === "\u007f" || char === "\b";
148
+ if (char === "\u007f" || char === "\b") {
149
+ this.directPopupMenuQuery = this.directPopupMenuQuery.slice(0, -1);
150
+ this.resetPopupMenuSelection(this.getActivePopupMenu(active));
151
+ this.host.render();
152
+ return true;
153
+ }
154
+ if (char >= " ") {
155
+ this.directPopupMenuQuery += char;
156
+ this.resetPopupMenuSelection(this.getActivePopupMenu(active));
157
+ this.host.render();
158
+ return true;
159
+ }
160
+ return false;
161
+ }
162
+ openDirectPopupMenu(menu, options = {}) {
163
+ if (this.directPopupMenu === "sdk-menu" && menu !== "sdk-menu") {
164
+ this.closeSdkMenu(undefined, { render: false, restoreStatus: false });
165
+ }
166
+ this.directPopupMenu = menu;
167
+ this.directPopupMenuPreserveStatus = options.preserveStatus === true;
168
+ this.directPopupMenuPlacement = options.placement ?? "default";
169
+ this.directPopupMenuQuery = "";
170
+ if (menu !== "user-message")
171
+ this.activeUserMessageEntryId = undefined;
172
+ if (menu !== "queue-message")
173
+ this.activeQueuedMessageEntryId = undefined;
174
+ if (menu === "resume")
175
+ this.resetResumeMenuLazyState();
176
+ this.dismissedModelMenuInput = undefined;
177
+ this.dismissedThinkingMenuInput = undefined;
178
+ this.slashCommandMenu.close();
179
+ this.modelMenu.close();
180
+ this.thinkingMenu.close();
181
+ this.resumeMenu.close();
182
+ this.userMessageMenu.close();
183
+ this.queueMessageMenu.close();
184
+ this.sdkMenu.close();
185
+ const popup = this.getActivePopupMenu(menu);
186
+ this.resetPopupMenuSelection(popup);
187
+ }
188
+ popupMenuPlacement() {
189
+ return this.directPopupMenu ? this.directPopupMenuPlacement : "default";
190
+ }
191
+ openUserMessageMenu(entryId) {
192
+ if (!this.host.hasUserEntry(entryId))
193
+ return false;
194
+ this.activeUserMessageEntryId = entryId;
195
+ this.openDirectPopupMenu("user-message", { preserveStatus: true });
196
+ return true;
197
+ }
198
+ openQueueMessageMenu(entryId) {
199
+ if (!this.host.hasQueuedEntry(entryId))
200
+ return false;
201
+ this.activeQueuedMessageEntryId = entryId;
202
+ this.openDirectPopupMenu("queue-message", { preserveStatus: true });
203
+ return true;
204
+ }
205
+ openResumeMenuWithQuery(query) {
206
+ this.resetPopupMenuSelection(this.resumeMenu);
207
+ this.resetResumeMenuLazyState(query);
208
+ this.resumeMenu.openWithItems(this.withoutCloseMenuItems(this.host.getResumeMenuItems(query, this.resumeMenuSessionLimit)));
209
+ this.updateResumeMenuLoadedState();
210
+ }
211
+ closeResumeMenu() {
212
+ this.resumeMenu.close();
213
+ }
214
+ closeUserMessageMenu() {
215
+ this.directPopupMenu = undefined;
216
+ this.directPopupMenuQuery = "";
217
+ this.directPopupMenuPreserveStatus = false;
218
+ this.activeUserMessageEntryId = undefined;
219
+ this.userMessageMenu.close();
220
+ this.host.restoreSessionStatus();
221
+ }
222
+ closeUserMessageJumpMenu() {
223
+ this.directPopupMenu = undefined;
224
+ this.directPopupMenuQuery = "";
225
+ this.directPopupMenuPreserveStatus = false;
226
+ this.userMessageJumpMenu.close();
227
+ this.host.restoreSessionStatus();
228
+ }
229
+ closeQueueMessageMenu() {
230
+ this.directPopupMenu = undefined;
231
+ this.directPopupMenuQuery = "";
232
+ this.directPopupMenuPreserveStatus = false;
233
+ this.activeQueuedMessageEntryId = undefined;
234
+ this.queueMessageMenu.close();
235
+ this.host.restoreSessionStatus();
236
+ }
237
+ resetConversationMenuState() {
238
+ this.activeUserMessageEntryId = undefined;
239
+ this.activeQueuedMessageEntryId = undefined;
240
+ this.userMessageMenu.close();
241
+ this.userMessageJumpMenu.close();
242
+ this.queueMessageMenu.close();
243
+ if (this.directPopupMenu === "user-message" ||
244
+ this.directPopupMenu === "user-message-jump" ||
245
+ this.directPopupMenu === "queue-message") {
246
+ this.directPopupMenu = undefined;
247
+ this.directPopupMenuQuery = "";
248
+ this.directPopupMenuPreserveStatus = false;
249
+ }
250
+ }
251
+ closeModelSelection() {
252
+ this.modelMenu.close();
253
+ this.directPopupMenu = undefined;
254
+ this.directPopupMenuPreserveStatus = false;
255
+ this.directPopupMenuQuery = "";
256
+ this.dismissedModelMenuInput = undefined;
257
+ }
258
+ closeThinkingSelection() {
259
+ this.thinkingMenu.close();
260
+ this.directPopupMenu = undefined;
261
+ this.directPopupMenuPreserveStatus = false;
262
+ this.directPopupMenuQuery = "";
263
+ this.dismissedThinkingMenuInput = undefined;
264
+ }
265
+ closeSlashCommandSelection() {
266
+ this.slashCommandMenu.close();
267
+ this.dismissedSlashCommandMenuInput = undefined;
268
+ }
269
+ cancelActivePopupMenu() {
270
+ const active = this.syncActivePopupMenu();
271
+ if (this.directPopupMenu === "sdk-menu") {
272
+ this.closeSdkMenu(undefined);
273
+ return;
274
+ }
275
+ if (this.directPopupMenu) {
276
+ this.directPopupMenu = undefined;
277
+ this.directPopupMenuQuery = "";
278
+ this.activeUserMessageEntryId = undefined;
279
+ this.activeQueuedMessageEntryId = undefined;
280
+ this.modelMenu.close();
281
+ this.thinkingMenu.close();
282
+ this.resumeMenu.close();
283
+ this.userMessageMenu.close();
284
+ this.userMessageJumpMenu.close();
285
+ this.queueMessageMenu.close();
286
+ this.sdkMenu.close();
287
+ const preserveStatus = this.directPopupMenuPreserveStatus;
288
+ this.directPopupMenuPreserveStatus = false;
289
+ if (!preserveStatus)
290
+ this.host.restoreSessionStatus();
291
+ this.host.render();
292
+ return;
293
+ }
294
+ if (active === "model") {
295
+ this.dismissedModelMenuInput = this.host.getInput();
296
+ this.modelMenu.close();
297
+ }
298
+ else if (active === "thinking") {
299
+ this.dismissedThinkingMenuInput = this.host.getInput();
300
+ this.thinkingMenu.close();
301
+ }
302
+ else if (active === "slash") {
303
+ this.dismissedSlashCommandMenuInput = this.host.getInput();
304
+ this.slashCommandMenu.close();
305
+ }
306
+ this.host.render();
307
+ }
308
+ autocompleteSlashCommand() {
309
+ if (!this.syncSlashCommandMenu())
310
+ return;
311
+ const selected = this.selectedSlashCommand();
312
+ if (!selected)
313
+ return;
314
+ this.host.setInput(`/${selected.name}`);
315
+ this.host.render();
316
+ }
317
+ autocompleteModel() {
318
+ if (!this.syncModelMenu())
319
+ return false;
320
+ const selected = this.selectedModel();
321
+ if (!selected)
322
+ return true;
323
+ if (selected.direct)
324
+ return true;
325
+ this.host.setInput(`/model ${selected.value.ref}`);
326
+ this.host.render();
327
+ return true;
328
+ }
329
+ autocompleteThinking() {
330
+ if (!this.syncThinkingMenu())
331
+ return false;
332
+ const selected = this.selectedThinking();
333
+ if (!selected)
334
+ return true;
335
+ if (selected.direct)
336
+ return true;
337
+ this.host.setInput(`/thinking ${selected.value.level}`);
338
+ this.host.render();
339
+ return true;
340
+ }
341
+ selectedSlashCommand() {
342
+ if (!this.syncSlashCommandMenu())
343
+ return undefined;
344
+ return this.slashCommandMenu.selectedItem()?.value;
345
+ }
346
+ selectedModel() {
347
+ if (!this.syncModelMenu())
348
+ return undefined;
349
+ const value = this.modelMenu.selectedItem()?.value;
350
+ if (!value)
351
+ return undefined;
352
+ return { value, direct: this.directPopupMenu === "model" };
353
+ }
354
+ selectedThinking() {
355
+ if (!this.syncThinkingMenu())
356
+ return undefined;
357
+ const value = this.thinkingMenu.selectedItem()?.value;
358
+ if (!value)
359
+ return undefined;
360
+ return { value, direct: this.directPopupMenu === "thinking" };
361
+ }
362
+ selectedResume() {
363
+ if (!this.syncResumeMenu())
364
+ return undefined;
365
+ return this.resumeMenu.selectedItem()?.value;
366
+ }
367
+ selectedUserMessageAction() {
368
+ if (!this.syncUserMessageMenu())
369
+ return undefined;
370
+ const selected = this.userMessageMenu.selectedItem();
371
+ const entryId = this.activeUserMessageEntryId;
372
+ if (!selected || !entryId)
373
+ return undefined;
374
+ return { value: selected.value, label: selected.label, entryId };
375
+ }
376
+ selectedUserMessageJump() {
377
+ if (!this.syncUserMessageJumpMenu())
378
+ return undefined;
379
+ return this.userMessageJumpMenu.selectedItem()?.value;
380
+ }
381
+ selectedQueueMessageAction() {
382
+ if (!this.syncQueueMessageMenu())
383
+ return undefined;
384
+ const selected = this.queueMessageMenu.selectedItem();
385
+ const entryId = this.activeQueuedMessageEntryId;
386
+ if (!selected || !entryId)
387
+ return undefined;
388
+ return { value: selected.value, label: selected.label, entryId };
389
+ }
390
+ submitSelectedSdkMenu() {
391
+ if (!this.syncSdkMenu())
392
+ return false;
393
+ this.closeSdkMenu(this.sdkMenu.selectedItem()?.value.value);
394
+ return true;
395
+ }
396
+ syncActivePopupMenu() {
397
+ if (this.syncQueueMessageMenu())
398
+ return "queue-message";
399
+ if (this.syncUserMessageMenu())
400
+ return "user-message";
401
+ if (this.syncUserMessageJumpMenu())
402
+ return "user-message-jump";
403
+ if (this.syncResumeMenu())
404
+ return "resume";
405
+ if (this.syncSdkMenu())
406
+ return "sdk-menu";
407
+ if (this.syncModelMenu())
408
+ return "model";
409
+ if (this.syncThinkingMenu())
410
+ return "thinking";
411
+ if (this.syncSlashCommandMenu())
412
+ return "slash";
413
+ return undefined;
414
+ }
415
+ renderActivePopupMenu(width) {
416
+ if (this.syncQueueMessageMenu())
417
+ return this.renderQueueMessageMenu(width);
418
+ // User-message actions are rendered inline inside the selected message block.
419
+ // They must never also appear as the global popup above the input editor.
420
+ if (this.syncUserMessageMenu())
421
+ return [];
422
+ if (this.syncUserMessageJumpMenu())
423
+ return this.renderUserMessageJumpMenu(width);
424
+ if (this.syncResumeMenu())
425
+ return this.renderResumeMenu(width);
426
+ if (this.syncSdkMenu())
427
+ return this.renderSdkMenu(width);
428
+ if (this.syncModelMenu())
429
+ return this.renderModelMenu(width);
430
+ if (this.syncThinkingMenu())
431
+ return this.renderThinkingMenu(width);
432
+ return this.renderSlashCommandMenu(width);
433
+ }
434
+ popupMenuWidth(columns) {
435
+ return columns;
436
+ }
437
+ popupMenuMargin(columns) {
438
+ return columns > 44 ? 2 : 0;
439
+ }
440
+ effectivePopupMenuWidth(columns) {
441
+ const sideMargin = this.popupMenuMargin(columns);
442
+ return Math.min(this.popupMenuWidth(columns), Math.max(1, columns - sideMargin * 2));
443
+ }
444
+ styleOverlayLine(row, line, width) {
445
+ const colors = this.host.theme.colors;
446
+ const margin = this.popupMenuMargin(width);
447
+ const menuWidth = this.effectivePopupMenuWidth(width);
448
+ const rightMargin = Math.max(0, width - margin - menuWidth);
449
+ const activeMenuName = this.syncActivePopupMenu() ?? "slash";
450
+ const activeMenu = this.getActivePopupMenu(activeMenuName);
451
+ const selected = line.target?.kind === "popup-menu" && activeMenu.selectedIndex === line.target.index;
452
+ const foreground = this.popupLineForeground(line, selected);
453
+ const background = this.popupLineBackground(line, selected);
454
+ const plain = `${" ".repeat(margin)}${padOrTrimPlain(line.text, menuWidth)}${" ".repeat(rightMargin)}`;
455
+ if (this.host.screenStyler.selectionRangeForRow(row, width)) {
456
+ return this.host.screenStyler.styleLine(row, plain, width, { foreground, background });
457
+ }
458
+ return [
459
+ colorLine("", margin, { background: colors.background }),
460
+ line.segments && line.segments.length > 0
461
+ ? this.host.screenStyler.styleLineSegments(row, line.text, menuWidth, { foreground, background, bold: selected }, line.segments)
462
+ : colorLine(line.text, menuWidth, { foreground, background, bold: selected }),
463
+ colorLine("", rightMargin, { background: colors.background }),
464
+ ].join("");
465
+ }
466
+ overlayPlainText(line, width) {
467
+ const margin = this.popupMenuMargin(width);
468
+ const menuWidth = this.effectivePopupMenuWidth(width);
469
+ const rightMargin = Math.max(0, width - margin - menuWidth);
470
+ return `${" ".repeat(margin)}${padOrTrimPlain(line.text, menuWidth)}${" ".repeat(rightMargin)}`;
471
+ }
472
+ isDynamicConversationBlock(entry) {
473
+ return entry.kind === "user" && this.directPopupMenu === "user-message" && this.activeUserMessageEntryId === entry.id;
474
+ }
475
+ hasDynamicConversationBlock() {
476
+ return this.directPopupMenu === "user-message" && this.activeUserMessageEntryId !== undefined;
477
+ }
478
+ renderInlineUserMessageMenu(entry, options) {
479
+ if (!(this.directPopupMenu === "user-message" && this.activeUserMessageEntryId === entry.id && this.syncUserMessageMenu()))
480
+ return [];
481
+ const headerLine = options.userLine(formatPopupMenuHeader("Message actions", options.userContentWidth));
482
+ headerLine.target = { kind: "popup-menu-close" };
483
+ headerLine.segments = [{
484
+ start: options.userContentLeft,
485
+ end: options.userContentLeft + options.userContentWidth,
486
+ foreground: this.host.theme.colors.accent,
487
+ background: this.host.theme.colors.popupHeaderBackground,
488
+ bold: true,
489
+ }];
490
+ const lines = [headerLine];
491
+ for (const item of this.userMessageMenu.visibleItems()) {
492
+ const label = item.label.padEnd(18, " ");
493
+ const description = item.description ?? "";
494
+ const marker = item.selected ? "›" : " ";
495
+ const rawText = `${marker} ${label}${description}`;
496
+ const text = ellipsizeDisplay(rawText, options.userContentWidth);
497
+ const line = options.userLine(text);
498
+ line.target = { kind: "popup-menu", index: item.index };
499
+ const contentStart = options.userContentLeft;
500
+ const labelStart = contentStart + 2;
501
+ const labelEnd = Math.min(contentStart + text.length, labelStart + item.label.length);
502
+ const descriptionStart = contentStart + 2 + label.length;
503
+ line.segments = [
504
+ ...(item.selected ? [{ start: contentStart, end: contentStart + 1, foreground: this.host.theme.colors.accent, bold: true }] : []),
505
+ {
506
+ start: labelStart,
507
+ end: labelEnd,
508
+ foreground: this.userMessageActionForeground(item.selected, item.value),
509
+ bold: item.selected,
510
+ },
511
+ ...(descriptionStart < contentStart + text.length
512
+ ? [{ start: descriptionStart, end: contentStart + text.length, foreground: this.host.theme.colors.muted }]
513
+ : []),
514
+ ];
515
+ lines.push(line);
516
+ }
517
+ return lines;
518
+ }
519
+ hasPopupActionItems(items) {
520
+ return items.length > 0;
521
+ }
522
+ withoutCloseMenuItems(items) {
523
+ return items.filter((item) => item.label.trim().toLowerCase() !== "cancel");
524
+ }
525
+ resetPopupMenuSelection(menu) {
526
+ menu.selectedIndex = 0;
527
+ menu.scrollOffset = 0;
528
+ }
529
+ resetResumeMenuLazyState(query = this.directPopupMenuQuery) {
530
+ this.resumeMenuQuery = query;
531
+ this.resumeMenuSessionLimit = RESUME_MENU_INITIAL_SESSION_ROWS;
532
+ this.resumeMenuAllSessionsLoaded = false;
533
+ }
534
+ maybeGrowResumeMenuWindow(query) {
535
+ if (this.resumeMenuQuery !== query) {
536
+ this.resetResumeMenuLazyState(query);
537
+ return;
538
+ }
539
+ if (this.resumeMenuAllSessionsLoaded || this.resumeMenu.items.length === 0)
540
+ return;
541
+ const loadThresholdIndex = Math.max(0, this.resumeMenu.items.length - RESUME_MENU_LOAD_THRESHOLD_ROWS);
542
+ const lastVisibleIndex = this.resumeMenu.scrollOffset + this.resumeMenu.maxVisibleRows - 1;
543
+ if (this.resumeMenu.selectedIndex >= loadThresholdIndex || lastVisibleIndex >= loadThresholdIndex) {
544
+ this.resumeMenuSessionLimit += RESUME_MENU_LOAD_BATCH_ROWS;
545
+ }
546
+ }
547
+ updateResumeMenuLoadedState() {
548
+ const loadedSessions = this.resumeMenu.items.filter((item) => item.value.kind === "session").length;
549
+ this.resumeMenuAllSessionsLoaded = loadedSessions < this.resumeMenuSessionLimit;
550
+ }
551
+ resumeMenuLoadedSessionCount() {
552
+ return this.resumeMenu.items.filter((item) => item.value.kind === "session").length;
553
+ }
554
+ userMessageActionForeground(selected, value) {
555
+ if (selected)
556
+ return this.host.theme.colors.accent;
557
+ if (value === "undo")
558
+ return this.host.theme.colors.error;
559
+ return this.host.theme.colors.inputForeground;
560
+ }
561
+ selectableItemVariant(selected, value) {
562
+ if (selected)
563
+ return "accent";
564
+ return value.current ? "muted" : "normal";
565
+ }
566
+ queueMessageItemVariant(selected, value) {
567
+ if (selected)
568
+ return "accent";
569
+ return value === "cancel" ? "error" : "normal";
570
+ }
571
+ sdkItemVariant(selected, value) {
572
+ if (selected)
573
+ return "accent";
574
+ return value.variant ?? "normal";
575
+ }
576
+ resumeMenuItemSegments(value, label, description, text) {
577
+ if (value.kind !== "session")
578
+ return undefined;
579
+ const sessionLabel = value.session.name ?? value.session.firstMessage.slice(0, 50);
580
+ const sessionLabelStart = Math.max(0, label.length - sessionLabel.length);
581
+ const muted = this.host.theme.colors.popupMuted;
582
+ const segments = [];
583
+ if (sessionLabelStart > 0)
584
+ segments.push({ start: 0, end: sessionLabelStart, foreground: muted });
585
+ if (description.length > 0)
586
+ segments.push({ start: label.length, end: text.length, foreground: muted });
587
+ return segments.length > 0 ? segments : undefined;
588
+ }
589
+ popupMenuHeader(title, width) {
590
+ return {
591
+ text: formatPopupMenuHeader(title, width),
592
+ variant: "accent",
593
+ backgroundOverride: this.host.theme.colors.popupHeaderBackground,
594
+ target: { kind: "popup-menu-close" },
595
+ };
596
+ }
597
+ syncModelMenu() {
598
+ if (this.directPopupMenu === "model") {
599
+ this.closeMenusExcept("model");
600
+ this.modelMenu.openWithItems(this.withoutCloseMenuItems(this.host.getModelMenuItems(this.directPopupMenuQuery)));
601
+ return true;
602
+ }
603
+ const parsed = this.host.parseSlashInput(this.host.getInput());
604
+ if (parsed?.commandName.toLowerCase() !== "model" || this.dismissedModelMenuInput === this.host.getInput()) {
605
+ this.modelMenu.close();
606
+ return false;
607
+ }
608
+ const query = parsed.hasArguments ? parsed.arguments : "";
609
+ if (this.modelMenuQuery !== query) {
610
+ this.resetPopupMenuSelection(this.modelMenu);
611
+ this.modelMenuQuery = query;
612
+ }
613
+ this.closeMenusExcept("model");
614
+ this.modelMenu.openWithItems(this.withoutCloseMenuItems(this.host.getModelMenuItems(query)));
615
+ return true;
616
+ }
617
+ syncThinkingMenu() {
618
+ if (this.directPopupMenu === "thinking") {
619
+ this.closeMenusExcept("thinking");
620
+ this.thinkingMenu.openWithItems(this.withoutCloseMenuItems(this.host.getThinkingMenuItems(this.directPopupMenuQuery)));
621
+ return true;
622
+ }
623
+ const parsed = this.host.parseSlashInput(this.host.getInput());
624
+ if (parsed?.commandName.toLowerCase() !== "thinking" || this.dismissedThinkingMenuInput === this.host.getInput()) {
625
+ this.thinkingMenu.close();
626
+ return false;
627
+ }
628
+ const query = parsed.hasArguments ? parsed.arguments : "";
629
+ if (this.thinkingMenuQuery !== query) {
630
+ this.resetPopupMenuSelection(this.thinkingMenu);
631
+ this.thinkingMenuQuery = query;
632
+ }
633
+ this.closeMenusExcept("thinking");
634
+ this.thinkingMenu.openWithItems(this.withoutCloseMenuItems(this.host.getThinkingMenuItems(query)));
635
+ return true;
636
+ }
637
+ syncResumeMenu() {
638
+ if (this.directPopupMenu !== "resume") {
639
+ this.resumeMenu.close();
640
+ return false;
641
+ }
642
+ this.closeMenusExcept("resume");
643
+ this.maybeGrowResumeMenuWindow(this.directPopupMenuQuery);
644
+ this.resumeMenu.openWithItems(this.withoutCloseMenuItems(this.host.getResumeMenuItems(this.directPopupMenuQuery, this.resumeMenuSessionLimit)));
645
+ this.updateResumeMenuLoadedState();
646
+ return true;
647
+ }
648
+ syncUserMessageMenu() {
649
+ if (this.directPopupMenu !== "user-message" || !this.activeUserMessageEntryId) {
650
+ this.userMessageMenu.close();
651
+ if (this.directPopupMenu === "user-message")
652
+ this.directPopupMenu = undefined;
653
+ return false;
654
+ }
655
+ if (!this.host.hasUserEntry(this.activeUserMessageEntryId)) {
656
+ this.userMessageMenu.close();
657
+ this.activeUserMessageEntryId = undefined;
658
+ this.directPopupMenu = undefined;
659
+ return false;
660
+ }
661
+ this.closeMenusExcept("user-message");
662
+ this.userMessageMenu.openWithItems(this.withoutCloseMenuItems(this.host.getUserMessageMenuItems()));
663
+ return true;
664
+ }
665
+ syncUserMessageJumpMenu() {
666
+ if (this.directPopupMenu !== "user-message-jump") {
667
+ this.userMessageJumpMenu.close();
668
+ return false;
669
+ }
670
+ this.closeMenusExcept("user-message-jump");
671
+ this.userMessageJumpMenu.openWithItems(this.withoutCloseMenuItems(this.host.getUserMessageJumpMenuItems(this.directPopupMenuQuery)));
672
+ return true;
673
+ }
674
+ syncQueueMessageMenu() {
675
+ if (this.directPopupMenu !== "queue-message" || !this.activeQueuedMessageEntryId) {
676
+ this.queueMessageMenu.close();
677
+ if (this.directPopupMenu === "queue-message")
678
+ this.directPopupMenu = undefined;
679
+ return false;
680
+ }
681
+ if (!this.host.hasQueuedEntry(this.activeQueuedMessageEntryId)) {
682
+ this.queueMessageMenu.close();
683
+ this.activeQueuedMessageEntryId = undefined;
684
+ this.directPopupMenu = undefined;
685
+ return false;
686
+ }
687
+ this.closeMenusExcept("queue-message");
688
+ this.queueMessageMenu.openWithItems(this.withoutCloseMenuItems(this.host.getQueueMessageMenuItems()));
689
+ return true;
690
+ }
691
+ syncSdkMenu() {
692
+ if (this.directPopupMenu !== "sdk-menu" || !this.sdkMenuRequest) {
693
+ this.sdkMenu.close();
694
+ if (this.directPopupMenu === "sdk-menu") {
695
+ this.directPopupMenu = undefined;
696
+ this.directPopupMenuPreserveStatus = false;
697
+ this.directPopupMenuQuery = "";
698
+ }
699
+ return false;
700
+ }
701
+ this.closeMenusExcept("sdk-menu");
702
+ this.sdkMenu.openWithItems(this.getSdkMenuItems(this.directPopupMenuQuery));
703
+ return true;
704
+ }
705
+ syncSlashCommandMenu() {
706
+ const parsed = this.host.parseSlashInput(this.host.getInput());
707
+ if (!parsed || parsed.hasArguments || this.dismissedSlashCommandMenuInput === this.host.getInput()) {
708
+ this.slashCommandMenu.close();
709
+ return false;
710
+ }
711
+ if (this.slashCommandMenuQuery !== parsed.commandName) {
712
+ this.resetPopupMenuSelection(this.slashCommandMenu);
713
+ this.slashCommandMenuQuery = parsed.commandName;
714
+ }
715
+ this.slashCommandMenu.openWithItems(this.withoutCloseMenuItems(this.host.getSlashCommandMenuItems(parsed.commandName)));
716
+ this.closeMenusExcept("slash");
717
+ return true;
718
+ }
719
+ closeMenusExcept(active) {
720
+ if (active !== "slash")
721
+ this.slashCommandMenu.close();
722
+ if (active !== "model")
723
+ this.modelMenu.close();
724
+ if (active !== "thinking")
725
+ this.thinkingMenu.close();
726
+ if (active !== "resume")
727
+ this.resumeMenu.close();
728
+ if (active !== "user-message")
729
+ this.userMessageMenu.close();
730
+ if (active !== "user-message-jump")
731
+ this.userMessageJumpMenu.close();
732
+ if (active !== "queue-message")
733
+ this.queueMessageMenu.close();
734
+ if (active !== "sdk-menu")
735
+ this.sdkMenu.close();
736
+ }
737
+ getSdkMenuItems(query) {
738
+ const request = this.sdkMenuRequest;
739
+ if (!request)
740
+ return [];
741
+ const items = request.options.searchable === false || query.trim().length === 0
742
+ ? request.items
743
+ : fuzzySearch(request.items.map((item) => ({
744
+ value: item,
745
+ label: item.label,
746
+ ...(item.keywords === undefined ? {} : { keywords: item.keywords }),
747
+ })), query).map((match) => match.value);
748
+ return this.withoutCloseMenuItems(items.map((item) => ({
749
+ value: item,
750
+ label: item.label,
751
+ ...(item.description === undefined ? {} : { description: item.description }),
752
+ })));
753
+ }
754
+ renderSlashCommandMenu(_width) {
755
+ if (!this.syncSlashCommandMenu())
756
+ return [];
757
+ const lines = [this.popupMenuHeader("Commands", _width)];
758
+ const visibleItems = this.slashCommandMenu.visibleItems();
759
+ if (!this.hasPopupActionItems(this.slashCommandMenu.items)) {
760
+ lines.push({ text: " No matching slash commands", variant: "muted" });
761
+ }
762
+ for (const item of visibleItems) {
763
+ const command = item.label.padEnd(SLASH_COMMAND_DESCRIPTION_COLUMN, " ");
764
+ const description = item.description ?? "";
765
+ lines.push({
766
+ text: `${command}${description}`,
767
+ variant: item.selected ? "accent" : "normal",
768
+ target: { kind: "popup-menu", index: item.index },
769
+ });
770
+ }
771
+ return lines;
772
+ }
773
+ renderModelMenu(_width) {
774
+ if (!this.syncModelMenu())
775
+ return [];
776
+ const lines = [this.popupMenuHeader("Select model", _width)];
777
+ const visibleItems = this.modelMenu.visibleItems();
778
+ if (!this.hasPopupActionItems(this.modelMenu.items)) {
779
+ lines.push({
780
+ text: this.host.session ? " No matching favorite models" : " Model menu unavailable",
781
+ variant: "muted",
782
+ });
783
+ }
784
+ for (const item of visibleItems) {
785
+ const model = item.label.padEnd(SLASH_COMMAND_DESCRIPTION_COLUMN, " ");
786
+ const description = item.description ?? "";
787
+ lines.push({
788
+ text: `${model}${description}`,
789
+ variant: this.selectableItemVariant(item.selected, item.value),
790
+ target: { kind: "popup-menu", index: item.index },
791
+ });
792
+ }
793
+ return lines;
794
+ }
795
+ renderThinkingMenu(_width) {
796
+ if (!this.syncThinkingMenu())
797
+ return [];
798
+ const lines = [this.popupMenuHeader("Thinking level", _width)];
799
+ const visibleItems = this.thinkingMenu.visibleItems();
800
+ if (!this.hasPopupActionItems(this.thinkingMenu.items)) {
801
+ lines.push({ text: " No matching thinking levels", variant: "muted" });
802
+ }
803
+ for (const item of visibleItems) {
804
+ const level = item.label.padEnd(SLASH_COMMAND_DESCRIPTION_COLUMN, " ");
805
+ const description = item.description ?? "";
806
+ lines.push({
807
+ text: `${level}${description}`,
808
+ variant: this.selectableItemVariant(item.selected, item.value),
809
+ target: { kind: "popup-menu", index: item.index },
810
+ });
811
+ }
812
+ return lines;
813
+ }
814
+ renderResumeMenu(_width) {
815
+ if (!this.syncResumeMenu())
816
+ return [];
817
+ const title = this.host.resumeLoading ? `Resume session ${APP_ICONS.timerSand}` : "Resume session";
818
+ const lines = [this.popupMenuHeader(title, _width)];
819
+ const visibleItems = this.resumeMenu.visibleItems();
820
+ if (!this.host.resumeLoading && !this.hasPopupActionItems(this.resumeMenu.items)) {
821
+ lines.push({
822
+ text: this.host.resumeSessionCount === 0 ? " No sessions found" : " No matching sessions",
823
+ variant: "muted",
824
+ });
825
+ }
826
+ for (const item of visibleItems) {
827
+ const label = item.label;
828
+ const description = item.description ?? "";
829
+ const text = `${label} ${description}`;
830
+ const segments = this.resumeMenuItemSegments(item.value, label, description, text);
831
+ lines.push({
832
+ text,
833
+ variant: item.selected ? "accent" : "normal",
834
+ ...(segments ? { segments } : {}),
835
+ target: { kind: "popup-menu", index: item.index },
836
+ });
837
+ }
838
+ if (!this.resumeMenuAllSessionsLoaded && this.resumeMenuLoadedSessionCount() > 0) {
839
+ lines.push({ text: ` Loaded ${this.resumeMenuLoadedSessionCount()} sessions · scroll for more`, variant: "muted" });
840
+ }
841
+ if (this.directPopupMenuQuery) {
842
+ lines.push({ text: ` Search: ${this.directPopupMenuQuery}`, variant: "muted" });
843
+ }
844
+ return lines;
845
+ }
846
+ renderUserMessageJumpMenu(width) {
847
+ if (!this.syncUserMessageJumpMenu())
848
+ return [];
849
+ const lines = [this.popupMenuHeader("Jump to user message", width)];
850
+ if (!this.hasPopupActionItems(this.userMessageJumpMenu.items)) {
851
+ lines.push({
852
+ text: this.host.entries.some((entry) => entry.kind === "user") ? " No matching user messages" : " No user messages yet",
853
+ variant: "muted",
854
+ });
855
+ }
856
+ const labelWidth = Math.max(1, width);
857
+ for (const item of this.userMessageJumpMenu.visibleItems()) {
858
+ const label = ellipsizeDisplay(item.label, labelWidth);
859
+ lines.push({
860
+ text: label,
861
+ variant: item.selected ? "accent" : "normal",
862
+ target: { kind: "popup-menu", index: item.index },
863
+ });
864
+ }
865
+ if (this.directPopupMenuQuery) {
866
+ lines.push({ text: ` Search: ${this.directPopupMenuQuery}`, variant: "muted" });
867
+ }
868
+ return lines;
869
+ }
870
+ renderQueueMessageMenu(_width) {
871
+ if (!this.syncQueueMessageMenu())
872
+ return [];
873
+ const lines = [this.popupMenuHeader("Queued message", _width)];
874
+ for (const item of this.queueMessageMenu.visibleItems()) {
875
+ const label = item.label.padEnd(18, " ");
876
+ const description = item.description ?? "";
877
+ lines.push({
878
+ text: `${label}${description}`,
879
+ variant: this.queueMessageItemVariant(item.selected, item.value),
880
+ target: { kind: "popup-menu", index: item.index },
881
+ });
882
+ }
883
+ return lines;
884
+ }
885
+ renderSdkMenu(_width) {
886
+ if (!this.syncSdkMenu())
887
+ return [];
888
+ const request = this.sdkMenuRequest;
889
+ const lines = [this.popupMenuHeader(request?.options.title ?? "Menu", _width)];
890
+ if (!this.hasPopupActionItems(this.sdkMenu.items)) {
891
+ lines.push({ text: ` ${request?.options.emptyText ?? "No matching items"}`, variant: "muted" });
892
+ }
893
+ for (const item of this.sdkMenu.visibleItems()) {
894
+ const label = item.label.padEnd(SLASH_COMMAND_DESCRIPTION_COLUMN, " ");
895
+ const description = item.description ?? "";
896
+ lines.push({
897
+ text: `${label}${description}`,
898
+ variant: this.sdkItemVariant(item.selected, item.value),
899
+ target: { kind: "popup-menu", index: item.index },
900
+ });
901
+ }
902
+ if (request?.options.searchable !== false && this.directPopupMenuQuery) {
903
+ lines.push({ text: ` ${request?.options.placeholder ?? "Search"}: ${this.directPopupMenuQuery}`, variant: "muted" });
904
+ }
905
+ return lines;
906
+ }
907
+ popupLineForeground(line, selected) {
908
+ const colors = this.host.theme.colors;
909
+ if (selected)
910
+ return colors.popupSelectedForeground;
911
+ if (line.colorOverride)
912
+ return line.colorOverride;
913
+ switch (line.variant) {
914
+ case "accent":
915
+ return colors.accent;
916
+ case "muted":
917
+ return colors.popupMuted;
918
+ case "error":
919
+ return colors.error;
920
+ case "normal":
921
+ case undefined:
922
+ return colors.popupForeground;
923
+ }
924
+ return colors.popupForeground;
925
+ }
926
+ popupLineBackground(line, selected) {
927
+ const colors = this.host.theme.colors;
928
+ if (selected)
929
+ return colors.popupSelectedBackground;
930
+ return line.backgroundOverride ?? colors.popupBackground;
931
+ }
932
+ }
933
+ export function formatPopupMenuHeader(title, width) {
934
+ const safeWidth = Math.max(1, width);
935
+ const sanitizedTitle = sanitizeText(title).replace(/\s+/g, " ").trim() || "Menu";
936
+ const buttonWidth = stringDisplayWidth(POPUP_MENU_ESCAPE_BUTTON);
937
+ if (safeWidth <= buttonWidth + 1)
938
+ return padOrTrimPlain(POPUP_MENU_ESCAPE_BUTTON, safeWidth);
939
+ const titleWidth = safeWidth - buttonWidth - 1;
940
+ const titleText = ellipsizeDisplay(sanitizedTitle, titleWidth);
941
+ const gapWidth = Math.max(1, safeWidth - stringDisplayWidth(titleText) - buttonWidth);
942
+ return `${titleText}${" ".repeat(gapWidth)}${POPUP_MENU_ESCAPE_BUTTON}`;
943
+ }
944
+ function canonicalSessionPath(sessionPath) {
945
+ return sessionPath ? resolve(sessionPath) : undefined;
946
+ }
947
+ function buildSessionInfoTree(sessions) {
948
+ const byPath = new Map();
949
+ for (const session of sessions) {
950
+ byPath.set(canonicalSessionPath(session.path) ?? session.path, { session, children: [] });
951
+ }
952
+ const roots = [];
953
+ for (const session of sessions) {
954
+ const sessionPath = canonicalSessionPath(session.path) ?? session.path;
955
+ const node = byPath.get(sessionPath);
956
+ if (!node)
957
+ continue;
958
+ const parentPath = canonicalSessionPath(session.parentSessionPath);
959
+ const parent = parentPath ? byPath.get(parentPath) : undefined;
960
+ if (parent)
961
+ parent.children.push(node);
962
+ else
963
+ roots.push(node);
964
+ }
965
+ const sortNodes = (nodes) => {
966
+ nodes.sort((left, right) => right.session.modified.getTime() - left.session.modified.getTime());
967
+ for (const node of nodes)
968
+ sortNodes(node.children);
969
+ };
970
+ sortNodes(roots);
971
+ return roots;
972
+ }
973
+ function flattenSessionInfoTree(roots) {
974
+ const result = [];
975
+ const walk = (node, depth, ancestorContinues, isLast) => {
976
+ result.push({ session: node.session, depth, isLast, ancestorContinues });
977
+ for (let index = 0; index < node.children.length; index++) {
978
+ const child = node.children[index];
979
+ if (!child)
980
+ continue;
981
+ walk(child, depth + 1, [...ancestorContinues, depth > 0 && !isLast], index === node.children.length - 1);
982
+ }
983
+ };
984
+ for (let index = 0; index < roots.length; index++) {
985
+ const root = roots[index];
986
+ if (!root)
987
+ continue;
988
+ walk(root, 0, [], index === roots.length - 1);
989
+ }
990
+ return result;
991
+ }
992
+ function sessionTreePrefix(node) {
993
+ if (node.depth === 0)
994
+ return "";
995
+ return `${node.ancestorContinues.map((continues) => (continues ? "│ " : " ")).join("")}${node.isLast ? "└─ " : "├─ "}`;
996
+ }
997
+ function formatSessionMenuDateTime(dateTime) {
998
+ return {
999
+ date: dateTime.toLocaleDateString("ru-RU", { day: "2-digit", month: "2-digit", year: "numeric" }),
1000
+ time: dateTime.toLocaleTimeString("ru-RU", { hour: "2-digit", minute: "2-digit", hourCycle: "h23" }),
1001
+ };
1002
+ }
1003
+ function formatSessionInfoMenuItem(session, labelPrefix = "") {
1004
+ const { date, time } = formatSessionMenuDateTime(session.modified);
1005
+ const messages = `${session.messageCount} msg${session.messageCount !== 1 ? "s" : ""}`;
1006
+ const label = session.name ?? session.firstMessage.slice(0, 50);
1007
+ return {
1008
+ value: session,
1009
+ label: `${labelPrefix}${label}`,
1010
+ description: `${date} ${time} · ${messages} · ${session.id.slice(0, 8)}`,
1011
+ };
1012
+ }
1013
+ function buildSessionInfoMenuSource(sessions, currentSessionFile, query) {
1014
+ const visibleSessions = sessions.filter((session) => canonicalSessionPath(session.path) !== currentSessionFile);
1015
+ const trimmedQuery = query.trim();
1016
+ if (!trimmedQuery) {
1017
+ return flattenSessionInfoTree(buildSessionInfoTree(visibleSessions)).map((node) => ({
1018
+ session: node.session,
1019
+ labelPrefix: sessionTreePrefix(node),
1020
+ }));
1021
+ }
1022
+ const items = visibleSessions
1023
+ .map((session) => ({
1024
+ value: session,
1025
+ label: session.name ?? session.firstMessage.slice(0, 60),
1026
+ keywords: [
1027
+ session.cwd.split("/").pop() ?? session.cwd,
1028
+ session.id,
1029
+ ],
1030
+ }));
1031
+ return fuzzySearch(items, query).map((match) => ({ session: match.value, labelPrefix: "" }));
1032
+ }
1033
+ export function createSessionInfoMenuItemsLoader(sessions, currentSessionFile, query) {
1034
+ const source = buildSessionInfoMenuSource(sessions, currentSessionFile, query);
1035
+ const cachedItems = new Map();
1036
+ return {
1037
+ get total() {
1038
+ return source.length;
1039
+ },
1040
+ items(limit) {
1041
+ const effectiveLimit = limit === undefined ? source.length : Math.max(0, Math.min(limit, source.length));
1042
+ const cached = cachedItems.get(effectiveLimit);
1043
+ if (cached)
1044
+ return cached;
1045
+ const result = source.slice(0, effectiveLimit).map((item) => formatSessionInfoMenuItem(item.session, item.labelPrefix));
1046
+ cachedItems.set(effectiveLimit, result);
1047
+ return result;
1048
+ },
1049
+ };
1050
+ }
1051
+ export function formatSessionInfoMenuItems(sessions, currentSessionFile, query, options = {}) {
1052
+ return createSessionInfoMenuItemsLoader(sessions, currentSessionFile, query).items(options.limit);
1053
+ }
1054
+ export function buildUserMessageJumpItems(entries, query) {
1055
+ const userEntries = entries.filter((entry) => entry.kind === "user");
1056
+ const items = userEntries.map((entry, index) => {
1057
+ const preview = sanitizeText(entry.text).replace(/\s+/g, " ").trim();
1058
+ const label = `${index + 1}. ${preview || "(empty message)"}`;
1059
+ return {
1060
+ value: { entryId: entry.id },
1061
+ label,
1062
+ aliases: [entry.sessionEntryId ?? "", entry.id],
1063
+ keywords: [entry.text],
1064
+ };
1065
+ });
1066
+ return fuzzySearch(items, query).map((match) => ({
1067
+ value: match.value,
1068
+ label: match.label,
1069
+ }));
1070
+ }