@bastani/atomic 0.9.21-alpha.1 → 0.9.21-alpha.2

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 (514) hide show
  1. package/CHANGELOG.md +27 -0
  2. package/dist/builtin/intercom/package.json +1 -1
  3. package/dist/builtin/mcp/package.json +1 -1
  4. package/dist/builtin/subagents/CHANGELOG.md +11 -0
  5. package/dist/builtin/subagents/package.json +1 -1
  6. package/dist/builtin/subagents/src/extension/index.bundle.mjs +292 -60
  7. package/dist/builtin/web-access/package.json +1 -1
  8. package/dist/builtin/workflows/CHANGELOG.md +11 -0
  9. package/dist/builtin/workflows/builtin/adversarial-verification.js +3 -3
  10. package/dist/builtin/workflows/builtin/{chunk-cw86srkf.js → chunk-632fxeb8.js} +1 -1
  11. package/dist/builtin/workflows/builtin/{chunk-w18kbz63.js → chunk-94f8zfz1.js} +1 -1
  12. package/dist/builtin/workflows/builtin/{chunk-kspvwc24.js → chunk-9cm2ckq2.js} +1 -1
  13. package/dist/builtin/workflows/builtin/{chunk-jaj27rqc.js → chunk-a1ar035d.js} +1 -1
  14. package/dist/builtin/workflows/builtin/{chunk-wh19f5ee.js → chunk-d3xhj946.js} +5 -4
  15. package/dist/builtin/workflows/builtin/{chunk-crw7q5g1.js → chunk-gyjck9z8.js} +1 -1
  16. package/dist/builtin/workflows/builtin/{chunk-maqv731g.js → chunk-hj8mh07f.js} +10 -6
  17. package/dist/builtin/workflows/builtin/{chunk-0nrgfbag.js → chunk-kb7s3gx2.js} +1 -1
  18. package/dist/builtin/workflows/builtin/{chunk-73c5tayt.js → chunk-kgn5wz1c.js} +19 -1
  19. package/dist/builtin/workflows/builtin/{chunk-2hak3b7g.js → chunk-nkj4qhj6.js} +1 -1
  20. package/dist/builtin/workflows/builtin/{chunk-bzdvx2xr.js → chunk-r97ndfvt.js} +1 -1
  21. package/dist/builtin/workflows/builtin/{chunk-vkmpdat4.js → chunk-wf876n16.js} +1 -1
  22. package/dist/builtin/workflows/builtin/classify-and-act.js +3 -3
  23. package/dist/builtin/workflows/builtin/fan-out-and-synthesize.js +3 -3
  24. package/dist/builtin/workflows/builtin/generate-and-filter.js +3 -3
  25. package/dist/builtin/workflows/builtin/goal.js +4 -4
  26. package/dist/builtin/workflows/builtin/index.js +12 -12
  27. package/dist/builtin/workflows/builtin/loop-until-done.js +3 -3
  28. package/dist/builtin/workflows/builtin/open-claude-design.js +3 -3
  29. package/dist/builtin/workflows/builtin/ralph.js +4 -4
  30. package/dist/builtin/workflows/builtin/steering-context.js +1 -1
  31. package/dist/builtin/workflows/builtin/tournament.js +3 -3
  32. package/dist/builtin/workflows/package.json +1 -1
  33. package/dist/builtin/workflows/src/extension/index.bundle.mjs +40 -11
  34. package/dist/builtin/workflows/src/index.js +9 -3
  35. package/dist/builtin/workflows/src/shared/authoring-contract-stage.d.ts +6 -1
  36. package/dist/cli/auth-command.js +5 -0
  37. package/dist/cli/auth-command.js.map +1 -1
  38. package/dist/cli/credential-print.js +2 -0
  39. package/dist/cli/credential-print.js.map +1 -1
  40. package/dist/cli/experimental/command.js +5 -2
  41. package/dist/cli/experimental/command.js.map +1 -1
  42. package/dist/cli.js +1 -1
  43. package/dist/cli.js.map +1 -1
  44. package/dist/config.d.ts +4 -4
  45. package/dist/config.js +4 -4
  46. package/dist/config.js.map +1 -1
  47. package/dist/core/agent-session-extension-bindings.d.ts.map +1 -1
  48. package/dist/core/agent-session-extension-bindings.js +9 -4
  49. package/dist/core/agent-session-extension-bindings.js.map +1 -1
  50. package/dist/core/agent-session-methods.d.ts +4 -4
  51. package/dist/core/agent-session-methods.d.ts.map +1 -1
  52. package/dist/core/agent-session-methods.js.map +1 -1
  53. package/dist/core/agent-session-prompt.d.ts +4 -4
  54. package/dist/core/agent-session-prompt.d.ts.map +1 -1
  55. package/dist/core/agent-session-prompt.js +38 -15
  56. package/dist/core/agent-session-prompt.js.map +1 -1
  57. package/dist/core/agent-session-runtime.d.ts.map +1 -1
  58. package/dist/core/agent-session-runtime.js +14 -9
  59. package/dist/core/agent-session-runtime.js.map +1 -1
  60. package/dist/core/agent-session-types.d.ts +4 -1
  61. package/dist/core/agent-session-types.d.ts.map +1 -1
  62. package/dist/core/agent-session-types.js.map +1 -1
  63. package/dist/core/agent-session.d.ts +1 -1
  64. package/dist/core/agent-session.d.ts.map +1 -1
  65. package/dist/core/agent-session.js +140 -95
  66. package/dist/core/agent-session.js.map +1 -1
  67. package/dist/core/auth-storage-backends.js +2 -3
  68. package/dist/core/auth-storage-backends.js.map +1 -1
  69. package/dist/core/auth-storage.js +4 -1
  70. package/dist/core/auth-storage.js.map +1 -1
  71. package/dist/core/builtin-resource-loader.js +9 -0
  72. package/dist/core/builtin-resource-loader.js.map +1 -1
  73. package/dist/core/cache-warmer.js +8 -1
  74. package/dist/core/cache-warmer.js.map +1 -1
  75. package/dist/core/compaction/range-planner.js +6 -0
  76. package/dist/core/compaction/range-planner.js.map +1 -1
  77. package/dist/core/execution-model-router.d.ts +13 -3
  78. package/dist/core/execution-model-router.d.ts.map +1 -1
  79. package/dist/core/execution-model-router.js +227 -131
  80. package/dist/core/execution-model-router.js.map +1 -1
  81. package/dist/core/export-html/template-js/entries-navigation.js +16 -6
  82. package/dist/core/export-html/template-js/initialization.js +34 -11
  83. package/dist/core/export-html/template.css +10 -0
  84. package/dist/core/export-html/template.js +50 -17
  85. package/dist/core/extensions/context-types.d.ts +2 -0
  86. package/dist/core/extensions/context-types.d.ts.map +1 -1
  87. package/dist/core/extensions/context-types.js.map +1 -1
  88. package/dist/core/extensions/host-input.js +8 -4
  89. package/dist/core/extensions/host-input.js.map +1 -1
  90. package/dist/core/extensions/reactive-widget.js +1 -0
  91. package/dist/core/extensions/reactive-widget.js.map +1 -1
  92. package/dist/core/extensions/runner-context.d.ts +1 -0
  93. package/dist/core/extensions/runner-context.d.ts.map +1 -1
  94. package/dist/core/extensions/runner-context.js +4 -0
  95. package/dist/core/extensions/runner-context.js.map +1 -1
  96. package/dist/core/extensions/runner.d.ts +1 -0
  97. package/dist/core/extensions/runner.d.ts.map +1 -1
  98. package/dist/core/extensions/runner.js +48 -30
  99. package/dist/core/extensions/runner.js.map +1 -1
  100. package/dist/core/extensions/runtime-types.d.ts +1 -0
  101. package/dist/core/extensions/runtime-types.d.ts.map +1 -1
  102. package/dist/core/extensions/runtime-types.js.map +1 -1
  103. package/dist/core/extensions/workflow-activity-hub.js +9 -7
  104. package/dist/core/extensions/workflow-activity-hub.js.map +1 -1
  105. package/dist/core/footer-data-provider.js +24 -23
  106. package/dist/core/footer-data-provider.js.map +1 -1
  107. package/dist/core/keybindings.js +1 -0
  108. package/dist/core/keybindings.js.map +1 -1
  109. package/dist/core/mandatory-resource-loader.js +4 -1
  110. package/dist/core/mandatory-resource-loader.js.map +1 -1
  111. package/dist/core/model-config.js +2 -0
  112. package/dist/core/model-config.js.map +1 -1
  113. package/dist/core/model-registry.js +1 -0
  114. package/dist/core/model-registry.js.map +1 -1
  115. package/dist/core/{model-routing-task.d.ts → model-routing-bytes.d.ts} +1 -4
  116. package/dist/core/model-routing-bytes.d.ts.map +1 -0
  117. package/dist/core/model-routing-bytes.js +33 -0
  118. package/dist/core/model-routing-bytes.js.map +1 -0
  119. package/dist/core/model-routing-candidates.d.ts +42 -0
  120. package/dist/core/model-routing-candidates.d.ts.map +1 -0
  121. package/dist/core/model-routing-candidates.js +309 -0
  122. package/dist/core/model-routing-candidates.js.map +1 -0
  123. package/dist/core/model-routing-constraints.d.ts +4 -0
  124. package/dist/core/model-routing-constraints.d.ts.map +1 -1
  125. package/dist/core/model-routing-constraints.js +13 -1
  126. package/dist/core/model-routing-constraints.js.map +1 -1
  127. package/dist/core/model-routing-debug.d.ts +9 -0
  128. package/dist/core/model-routing-debug.d.ts.map +1 -0
  129. package/dist/core/model-routing-debug.js +14 -0
  130. package/dist/core/model-routing-debug.js.map +1 -0
  131. package/dist/core/model-routing-evals.d.ts +50 -0
  132. package/dist/core/model-routing-evals.d.ts.map +1 -0
  133. package/dist/core/model-routing-evals.js +190 -0
  134. package/dist/core/model-routing-evals.js.map +1 -0
  135. package/dist/core/model-routing-needs.d.ts +75 -0
  136. package/dist/core/model-routing-needs.d.ts.map +1 -0
  137. package/dist/core/model-routing-needs.js +176 -0
  138. package/dist/core/model-routing-needs.js.map +1 -0
  139. package/dist/core/model-runtime-streaming.js +3 -0
  140. package/dist/core/model-runtime-streaming.js.map +1 -1
  141. package/dist/core/model-runtime.js +26 -14
  142. package/dist/core/model-runtime.js.map +1 -1
  143. package/dist/core/models-store.js +2 -3
  144. package/dist/core/models-store.js.map +1 -1
  145. package/dist/core/output-guard.js +1 -0
  146. package/dist/core/output-guard.js.map +1 -1
  147. package/dist/core/package-manager-paths.d.ts +2 -1
  148. package/dist/core/package-manager-paths.d.ts.map +1 -1
  149. package/dist/core/package-manager-paths.js +4 -3
  150. package/dist/core/package-manager-paths.js.map +1 -1
  151. package/dist/core/package-manager.js +1 -0
  152. package/dist/core/package-manager.js.map +1 -1
  153. package/dist/core/resource-loader-assets.d.ts.map +1 -1
  154. package/dist/core/resource-loader-assets.js +9 -7
  155. package/dist/core/resource-loader-assets.js.map +1 -1
  156. package/dist/core/resource-loader-core.js +49 -0
  157. package/dist/core/resource-loader-core.js.map +1 -1
  158. package/dist/core/runtime-credentials.js +2 -1
  159. package/dist/core/runtime-credentials.js.map +1 -1
  160. package/dist/core/session-cwd.js +1 -0
  161. package/dist/core/session-cwd.js.map +1 -1
  162. package/dist/core/session-manager-archive.d.ts.map +1 -1
  163. package/dist/core/session-manager-archive.js +4 -2
  164. package/dist/core/session-manager-archive.js.map +1 -1
  165. package/dist/core/session-manager-core.js +11 -7
  166. package/dist/core/session-manager-core.js.map +1 -1
  167. package/dist/core/session-manager-storage.d.ts +10 -4
  168. package/dist/core/session-manager-storage.d.ts.map +1 -1
  169. package/dist/core/session-manager-storage.js +12 -6
  170. package/dist/core/session-manager-storage.js.map +1 -1
  171. package/dist/core/settings-manager-basic-accessors.d.ts +2 -1
  172. package/dist/core/settings-manager-basic-accessors.d.ts.map +1 -1
  173. package/dist/core/settings-manager-basic-accessors.js +21 -0
  174. package/dist/core/settings-manager-basic-accessors.js.map +1 -1
  175. package/dist/core/settings-manager-core.js +13 -7
  176. package/dist/core/settings-manager-core.js.map +1 -1
  177. package/dist/core/settings-manager.d.ts +1 -1
  178. package/dist/core/settings-manager.d.ts.map +1 -1
  179. package/dist/core/settings-manager.js.map +1 -1
  180. package/dist/core/settings-storage.js +6 -0
  181. package/dist/core/settings-storage.js.map +1 -1
  182. package/dist/core/settings-types.d.ts +8 -0
  183. package/dist/core/settings-types.d.ts.map +1 -1
  184. package/dist/core/settings-types.js.map +1 -1
  185. package/dist/core/structured-output/index.d.ts.map +1 -1
  186. package/dist/core/structured-output/index.js +4 -1
  187. package/dist/core/structured-output/index.js.map +1 -1
  188. package/dist/core/structured-output/invalid-output.js +1 -0
  189. package/dist/core/structured-output/invalid-output.js.map +1 -1
  190. package/dist/core/tasks/agent-adapter.js +5 -3
  191. package/dist/core/tasks/agent-adapter.js.map +1 -1
  192. package/dist/core/tasks/child-command-owner.js +3 -7
  193. package/dist/core/tasks/child-command-owner.js.map +1 -1
  194. package/dist/core/tasks/completion.js +5 -1
  195. package/dist/core/tasks/completion.js.map +1 -1
  196. package/dist/core/tasks/owner-store.js +12 -8
  197. package/dist/core/tasks/owner-store.js.map +1 -1
  198. package/dist/core/tasks/supervisor.js +14 -35
  199. package/dist/core/tasks/supervisor.js.map +1 -1
  200. package/dist/core/tools/ask-user-question/state/build-questionnaire.js +13 -4
  201. package/dist/core/tools/ask-user-question/state/build-questionnaire.js.map +1 -1
  202. package/dist/core/tools/ask-user-question/state/questionnaire-session.js +11 -1
  203. package/dist/core/tools/ask-user-question/state/questionnaire-session.js.map +1 -1
  204. package/dist/core/tools/ask-user-question/view/components/chat-row-view.js +1 -0
  205. package/dist/core/tools/ask-user-question/view/components/chat-row-view.js.map +1 -1
  206. package/dist/core/tools/ask-user-question/view/components/multi-select-view.js +1 -0
  207. package/dist/core/tools/ask-user-question/view/components/multi-select-view.js.map +1 -1
  208. package/dist/core/tools/ask-user-question/view/components/option-list-view.js +1 -0
  209. package/dist/core/tools/ask-user-question/view/components/option-list-view.js.map +1 -1
  210. package/dist/core/tools/ask-user-question/view/components/preview/markdown-content-cache.js +5 -0
  211. package/dist/core/tools/ask-user-question/view/components/preview/markdown-content-cache.js.map +1 -1
  212. package/dist/core/tools/ask-user-question/view/components/preview/preview-block-renderer.js +2 -0
  213. package/dist/core/tools/ask-user-question/view/components/preview/preview-block-renderer.js.map +1 -1
  214. package/dist/core/tools/ask-user-question/view/components/preview/preview-pane.js +13 -8
  215. package/dist/core/tools/ask-user-question/view/components/preview/preview-pane.js.map +1 -1
  216. package/dist/core/tools/ask-user-question/view/components/submit-picker.js +1 -0
  217. package/dist/core/tools/ask-user-question/view/components/submit-picker.js.map +1 -1
  218. package/dist/core/tools/ask-user-question/view/components/tab-bar.js +1 -0
  219. package/dist/core/tools/ask-user-question/view/components/tab-bar.js.map +1 -1
  220. package/dist/core/tools/ask-user-question/view/components/wrapping-select.js +30 -24
  221. package/dist/core/tools/ask-user-question/view/components/wrapping-select.js.map +1 -1
  222. package/dist/core/tools/ask-user-question/view/dialog-builder.js +5 -0
  223. package/dist/core/tools/ask-user-question/view/dialog-builder.js.map +1 -1
  224. package/dist/core/tools/ask-user-question/view/props-adapter.js +8 -0
  225. package/dist/core/tools/ask-user-question/view/props-adapter.js.map +1 -1
  226. package/dist/core/tools/ask-user-question/view/tab-content-strategy.js +4 -4
  227. package/dist/core/tools/ask-user-question/view/tab-content-strategy.js.map +1 -1
  228. package/dist/core/tools/bash.js +5 -8
  229. package/dist/core/tools/bash.js.map +1 -1
  230. package/dist/core/tools/edit-batch.js +4 -0
  231. package/dist/core/tools/edit-batch.js.map +1 -1
  232. package/dist/core/tools/edit.js +2 -0
  233. package/dist/core/tools/edit.js.map +1 -1
  234. package/dist/core/tools/file-mutation-coordinator.js +9 -0
  235. package/dist/core/tools/file-mutation-coordinator.js.map +1 -1
  236. package/dist/core/tools/find.js +1 -1
  237. package/dist/core/tools/find.js.map +1 -1
  238. package/dist/core/tools/hashline-engine/fs.js +1 -1
  239. package/dist/core/tools/hashline-engine/fs.js.map +1 -1
  240. package/dist/core/tools/hashline-engine/input.js +4 -0
  241. package/dist/core/tools/hashline-engine/input.js.map +1 -1
  242. package/dist/core/tools/hashline-engine/mismatch.js +6 -0
  243. package/dist/core/tools/hashline-engine/mismatch.js.map +1 -1
  244. package/dist/core/tools/hashline-engine/patcher.js +13 -0
  245. package/dist/core/tools/hashline-engine/patcher.js.map +1 -1
  246. package/dist/core/tools/hashline-engine/recovery.js +1 -0
  247. package/dist/core/tools/hashline-engine/recovery.js.map +1 -1
  248. package/dist/core/tools/output-accumulator.js +20 -13
  249. package/dist/core/tools/output-accumulator.js.map +1 -1
  250. package/dist/core/tools/persisted-output-file.js +9 -3
  251. package/dist/core/tools/persisted-output-file.js.map +1 -1
  252. package/dist/core/tools/resource-selectors.js +1 -0
  253. package/dist/core/tools/resource-selectors.js.map +1 -1
  254. package/dist/core/tools/write.js +1 -0
  255. package/dist/core/tools/write.js.map +1 -1
  256. package/dist/core/trust-manager.js +1 -0
  257. package/dist/core/trust-manager.js.map +1 -1
  258. package/dist/core/workflow-stage-admission.js +17 -10
  259. package/dist/core/workflow-stage-admission.js.map +1 -1
  260. package/dist/extensions/llama/client.js +2 -0
  261. package/dist/extensions/llama/client.js.map +1 -1
  262. package/dist/extensions/llama/huggingface-ui.js +17 -9
  263. package/dist/extensions/llama/huggingface-ui.js.map +1 -1
  264. package/dist/extensions/llama/huggingface.js +2 -0
  265. package/dist/extensions/llama/huggingface.js.map +1 -1
  266. package/dist/extensions/llama/ui.js +11 -3
  267. package/dist/extensions/llama/ui.js.map +1 -1
  268. package/dist/index.d.ts +4 -2
  269. package/dist/index.d.ts.map +1 -1
  270. package/dist/index.js +3 -1
  271. package/dist/index.js.map +1 -1
  272. package/dist/modes/interactive/components/armin.js +10 -6
  273. package/dist/modes/interactive/components/armin.js.map +1 -1
  274. package/dist/modes/interactive/components/assistant-message.js +10 -2
  275. package/dist/modes/interactive/components/assistant-message.js.map +1 -1
  276. package/dist/modes/interactive/components/atomic-working-status.js +10 -2
  277. package/dist/modes/interactive/components/atomic-working-status.js.map +1 -1
  278. package/dist/modes/interactive/components/bash-execution.js +9 -4
  279. package/dist/modes/interactive/components/bash-execution.js.map +1 -1
  280. package/dist/modes/interactive/components/bordered-loader.js +3 -0
  281. package/dist/modes/interactive/components/bordered-loader.js.map +1 -1
  282. package/dist/modes/interactive/components/branch-summary-message.js +4 -1
  283. package/dist/modes/interactive/components/branch-summary-message.js.map +1 -1
  284. package/dist/modes/interactive/components/chat-message-renderer.js +2 -1
  285. package/dist/modes/interactive/components/chat-message-renderer.js.map +1 -1
  286. package/dist/modes/interactive/components/chat-session-host-state.js +63 -31
  287. package/dist/modes/interactive/components/chat-session-host-state.js.map +1 -1
  288. package/dist/modes/interactive/components/chat-session-host.js +8 -1
  289. package/dist/modes/interactive/components/chat-session-host.js.map +1 -1
  290. package/dist/modes/interactive/components/chat-transcript.js +24 -14
  291. package/dist/modes/interactive/components/chat-transcript.js.map +1 -1
  292. package/dist/modes/interactive/components/compaction-boundary-message.js +2 -1
  293. package/dist/modes/interactive/components/compaction-boundary-message.js.map +1 -1
  294. package/dist/modes/interactive/components/config-selector-list.js +15 -6
  295. package/dist/modes/interactive/components/config-selector-list.js.map +1 -1
  296. package/dist/modes/interactive/components/config-selector.js +7 -1
  297. package/dist/modes/interactive/components/config-selector.js.map +1 -1
  298. package/dist/modes/interactive/components/countdown-timer.js +2 -0
  299. package/dist/modes/interactive/components/countdown-timer.js.map +1 -1
  300. package/dist/modes/interactive/components/custom-editor.js +21 -1
  301. package/dist/modes/interactive/components/custom-editor.js.map +1 -1
  302. package/dist/modes/interactive/components/custom-entry.js +4 -1
  303. package/dist/modes/interactive/components/custom-entry.js.map +1 -1
  304. package/dist/modes/interactive/components/custom-message.js +9 -1
  305. package/dist/modes/interactive/components/custom-message.js.map +1 -1
  306. package/dist/modes/interactive/components/daxnuts.js +8 -6
  307. package/dist/modes/interactive/components/daxnuts.js.map +1 -1
  308. package/dist/modes/interactive/components/dynamic-border.js +1 -0
  309. package/dist/modes/interactive/components/dynamic-border.js.map +1 -1
  310. package/dist/modes/interactive/components/extension-editor.js +7 -1
  311. package/dist/modes/interactive/components/extension-editor.js.map +1 -1
  312. package/dist/modes/interactive/components/extension-input.js +8 -2
  313. package/dist/modes/interactive/components/extension-input.js.map +1 -1
  314. package/dist/modes/interactive/components/extension-selector.js +9 -1
  315. package/dist/modes/interactive/components/extension-selector.js.map +1 -1
  316. package/dist/modes/interactive/components/first-time-setup.js +4 -2
  317. package/dist/modes/interactive/components/first-time-setup.js.map +1 -1
  318. package/dist/modes/interactive/components/footer.js +5 -2
  319. package/dist/modes/interactive/components/footer.js.map +1 -1
  320. package/dist/modes/interactive/components/host-input-form.js +13 -2
  321. package/dist/modes/interactive/components/host-input-form.js.map +1 -1
  322. package/dist/modes/interactive/components/login-dialog.js +9 -3
  323. package/dist/modes/interactive/components/login-dialog.js.map +1 -1
  324. package/dist/modes/interactive/components/model-selector.js +24 -12
  325. package/dist/modes/interactive/components/model-selector.js.map +1 -1
  326. package/dist/modes/interactive/components/oauth-selector.js +12 -3
  327. package/dist/modes/interactive/components/oauth-selector.js.map +1 -1
  328. package/dist/modes/interactive/components/reserved-bottom-overlay.js +11 -2
  329. package/dist/modes/interactive/components/reserved-bottom-overlay.js.map +1 -1
  330. package/dist/modes/interactive/components/scoped-models-selector.js +13 -8
  331. package/dist/modes/interactive/components/scoped-models-selector.js.map +1 -1
  332. package/dist/modes/interactive/components/scroll-widget.js +4 -0
  333. package/dist/modes/interactive/components/scroll-widget.js.map +1 -1
  334. package/dist/modes/interactive/components/session-selector-header.js +11 -7
  335. package/dist/modes/interactive/components/session-selector-header.js.map +1 -1
  336. package/dist/modes/interactive/components/session-selector-list.js +25 -12
  337. package/dist/modes/interactive/components/session-selector-list.js.map +1 -1
  338. package/dist/modes/interactive/components/session-selector.js +23 -15
  339. package/dist/modes/interactive/components/session-selector.js.map +1 -1
  340. package/dist/modes/interactive/components/settings-selector-submenus.js +19 -1
  341. package/dist/modes/interactive/components/settings-selector-submenus.js.map +1 -1
  342. package/dist/modes/interactive/components/settings-selector.js +1 -0
  343. package/dist/modes/interactive/components/settings-selector.js.map +1 -1
  344. package/dist/modes/interactive/components/show-images-selector.js +1 -0
  345. package/dist/modes/interactive/components/show-images-selector.js.map +1 -1
  346. package/dist/modes/interactive/components/skill-invocation-message.js +4 -1
  347. package/dist/modes/interactive/components/skill-invocation-message.js.map +1 -1
  348. package/dist/modes/interactive/components/startup-identity.js +8 -3
  349. package/dist/modes/interactive/components/startup-identity.js.map +1 -1
  350. package/dist/modes/interactive/components/task-completion-message.js +2 -0
  351. package/dist/modes/interactive/components/task-completion-message.js.map +1 -1
  352. package/dist/modes/interactive/components/task-detail.js +2 -0
  353. package/dist/modes/interactive/components/task-detail.js.map +1 -1
  354. package/dist/modes/interactive/components/task-inspector.js +22 -12
  355. package/dist/modes/interactive/components/task-inspector.js.map +1 -1
  356. package/dist/modes/interactive/components/task-list.js +2 -0
  357. package/dist/modes/interactive/components/task-list.js.map +1 -1
  358. package/dist/modes/interactive/components/task-live-transcript.js +8 -3
  359. package/dist/modes/interactive/components/task-live-transcript.js.map +1 -1
  360. package/dist/modes/interactive/components/task-navigation.js +5 -2
  361. package/dist/modes/interactive/components/task-navigation.js.map +1 -1
  362. package/dist/modes/interactive/components/task-row.js +2 -0
  363. package/dist/modes/interactive/components/task-row.js.map +1 -1
  364. package/dist/modes/interactive/components/theme-selector.js +2 -0
  365. package/dist/modes/interactive/components/theme-selector.js.map +1 -1
  366. package/dist/modes/interactive/components/thinking-selector.js +7 -1
  367. package/dist/modes/interactive/components/thinking-selector.js.map +1 -1
  368. package/dist/modes/interactive/components/tool-execution.js +25 -10
  369. package/dist/modes/interactive/components/tool-execution.js.map +1 -1
  370. package/dist/modes/interactive/components/transcript-follow-indicator.js +1 -0
  371. package/dist/modes/interactive/components/transcript-follow-indicator.js.map +1 -1
  372. package/dist/modes/interactive/components/tree-selector-component.js +7 -3
  373. package/dist/modes/interactive/components/tree-selector-component.js.map +1 -1
  374. package/dist/modes/interactive/components/tree-selector-help.js +1 -0
  375. package/dist/modes/interactive/components/tree-selector-help.js.map +1 -1
  376. package/dist/modes/interactive/components/tree-selector-label-input.js +6 -2
  377. package/dist/modes/interactive/components/tree-selector-label-input.js.map +1 -1
  378. package/dist/modes/interactive/components/tree-selector-list.js +4 -0
  379. package/dist/modes/interactive/components/tree-selector-list.js.map +1 -1
  380. package/dist/modes/interactive/components/trust-selector.js +6 -0
  381. package/dist/modes/interactive/components/trust-selector.js.map +1 -1
  382. package/dist/modes/interactive/components/user-message-selector.js +6 -3
  383. package/dist/modes/interactive/components/user-message-selector.js.map +1 -1
  384. package/dist/modes/interactive/components/user-message.js +5 -0
  385. package/dist/modes/interactive/components/user-message.js.map +1 -1
  386. package/dist/modes/interactive/components/working-status.js +1 -0
  387. package/dist/modes/interactive/components/working-status.js.map +1 -1
  388. package/dist/modes/interactive/interactive-autocomplete.js +2 -0
  389. package/dist/modes/interactive/interactive-autocomplete.js.map +1 -1
  390. package/dist/modes/interactive/interactive-extension-runtime.js +1 -0
  391. package/dist/modes/interactive/interactive-extension-runtime.js.map +1 -1
  392. package/dist/modes/interactive/interactive-mode-base.js +162 -120
  393. package/dist/modes/interactive/interactive-mode-base.js.map +1 -1
  394. package/dist/modes/interactive/interactive-mode-helpers.js +3 -0
  395. package/dist/modes/interactive/interactive-mode-helpers.js.map +1 -1
  396. package/dist/modes/interactive/interactive-mode-surface.d.ts +0 -6
  397. package/dist/modes/interactive/interactive-mode-surface.d.ts.map +1 -1
  398. package/dist/modes/interactive/interactive-mode-surface.js.map +1 -1
  399. package/dist/modes/interactive/interactive-resource-disclosure.js +0 -4
  400. package/dist/modes/interactive/interactive-resource-disclosure.js.map +1 -1
  401. package/dist/modes/interactive/interactive-resource-rendering.js +0 -12
  402. package/dist/modes/interactive/interactive-resource-rendering.js.map +1 -1
  403. package/dist/modes/interactive/interactive-startup-chat-container.js +1 -4
  404. package/dist/modes/interactive/interactive-startup-chat-container.js.map +1 -1
  405. package/dist/modes/interactive/model-catalog-refresh.js +1 -3
  406. package/dist/modes/interactive/model-catalog-refresh.js.map +1 -1
  407. package/dist/modes/interactive/theme/color-utils.d.ts +5 -0
  408. package/dist/modes/interactive/theme/color-utils.d.ts.map +1 -1
  409. package/dist/modes/interactive/theme/color-utils.js +11 -0
  410. package/dist/modes/interactive/theme/color-utils.js.map +1 -1
  411. package/dist/modes/interactive/theme/global-theme.d.ts.map +1 -1
  412. package/dist/modes/interactive/theme/global-theme.js +4 -3
  413. package/dist/modes/interactive/theme/global-theme.js.map +1 -1
  414. package/dist/modes/interactive/theme/theme-class.js +7 -0
  415. package/dist/modes/interactive/theme/theme-class.js.map +1 -1
  416. package/dist/modes/interactive/theme/theme-controller.js +9 -2
  417. package/dist/modes/interactive/theme/theme-controller.js.map +1 -1
  418. package/dist/modes/interactive-engine/activity-watchdog.js +8 -3
  419. package/dist/modes/interactive-engine/activity-watchdog.js.map +1 -1
  420. package/dist/modes/interactive-engine/engine-custom-ui.js +12 -8
  421. package/dist/modes/interactive-engine/engine-custom-ui.js.map +1 -1
  422. package/dist/modes/interactive-engine/engine-dialog-host.js +7 -3
  423. package/dist/modes/interactive-engine/engine-dialog-host.js.map +1 -1
  424. package/dist/modes/interactive-engine/engine-health.js +23 -13
  425. package/dist/modes/interactive-engine/engine-health.js.map +1 -1
  426. package/dist/modes/interactive-engine/engine-input-form.js +3 -2
  427. package/dist/modes/interactive-engine/engine-input-form.js.map +1 -1
  428. package/dist/modes/interactive-engine/engine-monitor.js +13 -1
  429. package/dist/modes/interactive-engine/engine-monitor.js.map +1 -1
  430. package/dist/modes/interactive-engine/engine-project-trust.js +2 -1
  431. package/dist/modes/interactive-engine/engine-project-trust.js.map +1 -1
  432. package/dist/modes/interactive-engine/engine-render-service.js +7 -4
  433. package/dist/modes/interactive-engine/engine-render-service.js.map +1 -1
  434. package/dist/modes/interactive-engine/engine-session-picker.js +3 -2
  435. package/dist/modes/interactive-engine/engine-session-picker.js.map +1 -1
  436. package/dist/modes/interactive-engine/input-form-host.js +5 -1
  437. package/dist/modes/interactive-engine/input-form-host.js.map +1 -1
  438. package/dist/modes/interactive-engine/isolated-runtime.js +37 -23
  439. package/dist/modes/interactive-engine/isolated-runtime.js.map +1 -1
  440. package/dist/modes/interactive-engine/remote-command-catalog.js +4 -3
  441. package/dist/modes/interactive-engine/remote-command-catalog.js.map +1 -1
  442. package/dist/modes/interactive-engine/remote-component.js +24 -12
  443. package/dist/modes/interactive-engine/remote-component.js.map +1 -1
  444. package/dist/modes/interactive-engine/remote-frame-clamp.js +3 -3
  445. package/dist/modes/interactive-engine/remote-frame-clamp.js.map +1 -1
  446. package/dist/modes/interactive-engine/remote-model-catalog.js +5 -4
  447. package/dist/modes/interactive-engine/remote-model-catalog.js.map +1 -1
  448. package/dist/modes/interactive-engine/remote-queue-pause.js +3 -1
  449. package/dist/modes/interactive-engine/remote-queue-pause.js.map +1 -1
  450. package/dist/modes/interactive-engine/remote-renderer.js +24 -13
  451. package/dist/modes/interactive-engine/remote-renderer.js.map +1 -1
  452. package/dist/modes/interactive-engine/resource-readiness-error.js +1 -0
  453. package/dist/modes/interactive-engine/resource-readiness-error.js.map +1 -1
  454. package/dist/modes/interactive-engine/session-picker-host.js +5 -1
  455. package/dist/modes/interactive-engine/session-picker-host.js.map +1 -1
  456. package/dist/modes/interactive-engine/terminal-mode-controller.js +2 -1
  457. package/dist/modes/interactive-engine/terminal-mode-controller.js.map +1 -1
  458. package/dist/modes/rpc/queued-writer.js +13 -4
  459. package/dist/modes/rpc/queued-writer.js.map +1 -1
  460. package/dist/modes/rpc/rpc-bash-request-owners.js +2 -1
  461. package/dist/modes/rpc/rpc-bash-request-owners.js.map +1 -1
  462. package/dist/modes/rpc/rpc-client-api.d.ts +8 -4
  463. package/dist/modes/rpc/rpc-client-api.d.ts.map +1 -1
  464. package/dist/modes/rpc/rpc-client-api.js +10 -3
  465. package/dist/modes/rpc/rpc-client-api.js.map +1 -1
  466. package/dist/modes/rpc/rpc-client.d.ts.map +1 -1
  467. package/dist/modes/rpc/rpc-client.js +35 -22
  468. package/dist/modes/rpc/rpc-client.js.map +1 -1
  469. package/dist/modes/rpc/rpc-command-handler.js +6 -6
  470. package/dist/modes/rpc/rpc-command-handler.js.map +1 -1
  471. package/dist/modes/rpc/rpc-event-buffer.js +3 -1
  472. package/dist/modes/rpc/rpc-event-buffer.js.map +1 -1
  473. package/dist/modes/rpc/rpc-generation-buffer.js +1 -3
  474. package/dist/modes/rpc/rpc-generation-buffer.js.map +1 -1
  475. package/dist/modes/rpc/rpc-keybindings-reload.js +4 -1
  476. package/dist/modes/rpc/rpc-keybindings-reload.js.map +1 -1
  477. package/dist/modes/rpc/rpc-output-buffer.js +3 -4
  478. package/dist/modes/rpc/rpc-output-buffer.js.map +1 -1
  479. package/dist/modes/rpc/rpc-pending-requests.js +1 -3
  480. package/dist/modes/rpc/rpc-pending-requests.js.map +1 -1
  481. package/dist/modes/rpc/rpc-provider-auth.js +3 -1
  482. package/dist/modes/rpc/rpc-provider-auth.js.map +1 -1
  483. package/dist/modes/rpc/rpc-resource-readiness.js +4 -5
  484. package/dist/modes/rpc/rpc-resource-readiness.js.map +1 -1
  485. package/dist/modes/rpc/rpc-session-binding.js +15 -0
  486. package/dist/modes/rpc/rpc-session-binding.js.map +1 -1
  487. package/dist/modes/rpc/rpc-terminal-drain.js +3 -0
  488. package/dist/modes/rpc/rpc-terminal-drain.js.map +1 -1
  489. package/dist/modes/rpc/rpc-types.d.ts +10 -1
  490. package/dist/modes/rpc/rpc-types.d.ts.map +1 -1
  491. package/dist/modes/rpc/rpc-types.js.map +1 -1
  492. package/docs/models/evals.md +883 -39
  493. package/docs/models/model-selection.md +7 -8
  494. package/docs/rpc/examples.md +1 -1
  495. package/docs/rpc/protocol.md +9 -3
  496. package/docs/sdk/structured-decisions.md +1 -1
  497. package/docs/sdk.md +3 -3
  498. package/docs/settings.md +24 -3
  499. package/docs/subagents/reference.md +22 -4
  500. package/docs/subagents.md +2 -0
  501. package/docs/usage.md +3 -3
  502. package/docs/workflows/api-reference.md +1 -1
  503. package/docs/workflows/authoring.md +1 -7
  504. package/docs/workflows/builtins.md +1 -1
  505. package/docs/workflows/reliable-design.md +1 -1
  506. package/examples/extensions/custom-provider-gitlab-duo/test.ts +5 -5
  507. package/examples/extensions/gondolin/package.json +1 -1
  508. package/examples/rpc-client.ts +1 -1
  509. package/examples/rpc-extension-ui.ts +1 -1
  510. package/npm-shrinkwrap.json +70 -33
  511. package/package.json +6 -7
  512. package/dist/core/model-routing-task.d.ts.map +0 -1
  513. package/dist/core/model-routing-task.js +0 -120
  514. package/dist/core/model-routing-task.js.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"file":"questionnaire-session.js","sourceRoot":"","sources":["../../../../../src/core/tools/ask-user-question/state/questionnaire-session.ts"],"names":[],"mappings":"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","sourcesContent":["import { decodeKittyPrintable, getKeybindings, type Input } from \"@earendil-works/pi-tui\";\nimport type { Theme } from \"../../../../modes/interactive/theme/theme.js\";\nimport type { QuestionData, QuestionnaireResult, QuestionParams } from \"../tool/types.js\";\nimport type { WrappingSelectItem } from \"../view/components/wrapping-select.ts\";\nimport type { QuestionnairePropsAdapter } from \"../view/props-adapter.ts\";\nimport { buildQuestionnaire } from \"./build-questionnaire.ts\";\nimport { readInlineCaret, readInlineDraft, withInlineDraft } from \"./inline-input.ts\";\nimport { type QuestionnaireAction, routeKey } from \"./key-router.ts\";\nimport { ROW_INTENT_META } from \"./row-intent.ts\";\nimport { computeFocusedOptionHasPreview } from \"./selectors/derivations.ts\";\nimport type { QuestionnaireRuntime, QuestionnaireState } from \"./state.ts\";\nimport { type ApplyContext, type Effect, reduce } from \"./state-reducer.ts\";\n\nfunction readInputCursor(input: Input): number {\n\tconst value = input.getValue();\n\tconst raw = Reflect.get(input, \"cursor\");\n\treturn typeof raw === \"number\" && Number.isFinite(raw) ? Math.max(0, Math.min(raw, value.length)) : value.length;\n}\n\nfunction writeInputCursor(input: Input, cursor: number): void {\n\tconst value = input.getValue();\n\tReflect.set(input, \"cursor\", Math.max(0, Math.min(cursor, value.length)));\n}\n\nfunction inlineInputHandles(data: string): boolean {\n\tif (data.includes(\"\\x1b[200~\")) return true;\n\tif (data === \"\\n\") return true;\n\tconst keybindings = getKeybindings();\n\tconst inputActions: Array<Parameters<typeof keybindings.matches>[1]> = [\n\t\t\"tui.select.cancel\",\n\t\t\"tui.editor.undo\",\n\t\t\"tui.input.submit\",\n\t\t\"tui.editor.deleteCharBackward\",\n\t\t\"tui.editor.deleteCharForward\",\n\t\t\"tui.editor.deleteWordBackward\",\n\t\t\"tui.editor.deleteWordForward\",\n\t\t\"tui.editor.deleteToLineStart\",\n\t\t\"tui.editor.deleteToLineEnd\",\n\t\t\"tui.editor.yank\",\n\t\t\"tui.editor.yankPop\",\n\t\t\"tui.editor.cursorLeft\",\n\t\t\"tui.editor.cursorRight\",\n\t\t\"tui.editor.cursorLineStart\",\n\t\t\"tui.editor.cursorLineEnd\",\n\t\t\"tui.editor.cursorWordLeft\",\n\t\t\"tui.editor.cursorWordRight\",\n\t];\n\tif (inputActions.some((action) => keybindings.matches(data, action))) {\n\t\treturn true;\n\t}\n\tif (decodeKittyPrintable(data) !== undefined) return true;\n\treturn [...data].every((character) => {\n\t\tconst code = character.charCodeAt(0);\n\t\treturn code >= 32 && code !== 0x7f && !(code >= 0x80 && code <= 0x9f);\n\t});\n}\n\nexport interface QuestionnaireSessionConfig {\n\ttui: { terminal: { columns: number }; requestRender(): void };\n\ttheme: Theme;\n\tparams: QuestionParams;\n\titemsByTab: WrappingSelectItem[][];\n\tdone: (result: QuestionnaireResult) => void;\n\t/** When true, Chat about this is a plain option and does not open an inline editor. */\n\tchatAsOption?: boolean;\n}\n\nexport interface QuestionnaireSessionComponent {\n\trender(width: number): string[];\n\tinvalidate(): void;\n\thandleInput(data: string): boolean;\n}\n\nfunction initialState(): QuestionnaireState {\n\treturn {\n\t\tcurrentTab: 0,\n\t\toptionIndex: 0,\n\t\tinputMode: false,\n\t\tinlineInputOwner: null,\n\t\tnotesVisible: false,\n\t\tchatFocused: false,\n\t\tanswers: new Map(),\n\t\tmultiSelectChecked: new Set(),\n\t\tnotesByTab: new Map(),\n\t\tfocusedOptionHasPreview: false,\n\t\tsubmitChoiceIndex: 0,\n\t\tnotesDraft: \"\",\n\t\tcustomDraftByTab: new Map(),\n\t\tcustomCaretByTab: new Map(),\n\t\tchatDraftByTab: new Map(),\n\t\tchatCaretByTab: new Map(),\n\t};\n}\n\n/**\n * Slim runtime: owns the canonical state cell, the input-buffer cell, the\n * two-pass `notesVisible` dispatch loop, and the effect runner. State\n * transitions go through the pure `reduce` reducer; UI fan-out goes through\n * the `QuestionnairePropsAdapter` produced by `buildQuestionnaire`.\n */\nexport class QuestionnaireSession {\n\tprivate state: QuestionnaireState = initialState();\n\n\tprivate readonly questions: readonly QuestionData[];\n\tprivate readonly isMulti: boolean;\n\tprivate readonly itemsByTab: WrappingSelectItem[][];\n\tprivate readonly chatAsOption: boolean;\n\n\tprivate readonly notesInput: Input;\n\tprivate readonly inlineInput: Input;\n\tprivate readonly viewAdapter: QuestionnairePropsAdapter;\n\n\tprivate readonly tui: QuestionnaireSessionConfig[\"tui\"];\n\tprivate readonly done: QuestionnaireSessionConfig[\"done\"];\n\n\treadonly component: QuestionnaireSessionComponent;\n\n\tconstructor(config: QuestionnaireSessionConfig) {\n\t\tthis.tui = config.tui;\n\t\tthis.done = config.done;\n\t\tthis.questions = config.params.questions;\n\t\tthis.isMulti = this.questions.length > 1;\n\t\tthis.itemsByTab = config.itemsByTab;\n\t\tthis.chatAsOption = config.chatAsOption === true;\n\t\t// Seed from the focused option at start; the reducer keeps it in sync via withFocusedOptionHasPreview.\n\t\tthis.state = { ...this.state, focusedOptionHasPreview: computeFocusedOptionHasPreview(this.questions, 0, 0) };\n\n\t\tconst built = buildQuestionnaire({\n\t\t\ttui: this.tui,\n\t\t\ttheme: config.theme,\n\t\t\tquestions: this.questions,\n\t\t\titemsByTab: this.itemsByTab,\n\t\t\tisMulti: this.isMulti,\n\t\t\tinitialState: this.state,\n\t\t\tgetCurrentTab: () => this.state.currentTab,\n\t\t\t...(this.chatAsOption ? { chatAsOption: true } : {}),\n\t\t});\n\n\t\tthis.notesInput = built.notesInput;\n\t\tthis.inlineInput = built.inlineInput;\n\t\tthis.viewAdapter = built.adapter;\n\n\t\tthis.component = {\n\t\t\trender: built.render,\n\t\t\tinvalidate: built.invalidate,\n\t\t\thandleInput: (data) => this.dispatch(data),\n\t\t};\n\n\t\tthis.viewAdapter.apply(this.state);\n\t}\n\n\tdispatch(data: string): boolean {\n\t\tconst action = routeKey(data, this.state, this.runtime());\n\t\tif (action.kind === \"ignore\") {\n\t\t\tconst hasInlineInput = this.state.inputMode && this.state.inlineInputOwner !== null;\n\t\t\tthis.handleIgnoreInline(data);\n\t\t\treturn hasInlineInput && inlineInputHandles(data);\n\t\t}\n\t\tthis.commit(action);\n\t\treturn true;\n\t}\n\n\tprivate commit(action: QuestionnaireAction): void {\n\t\tthis.state = this.mirrorInlineInputDraft(this.state);\n\t\tconst result = reduce(this.state, action, this.applyContext());\n\t\tthis.state = result.state;\n\t\tfor (const effect of result.effects) this.runEffect(effect);\n\t\tthis.state = this.mirrorNotesDraft(this.state);\n\t\tthis.viewAdapter.apply(this.state);\n\t}\n\n\tprivate mirrorNotesDraft(s: QuestionnaireState): QuestionnaireState {\n\t\tconst draft = this.notesInput.getValue();\n\t\treturn s.notesDraft === draft ? s : { ...s, notesDraft: draft };\n\t}\n\n\tprivate mirrorInlineInputDraft(s: QuestionnaireState): QuestionnaireState {\n\t\tif (!s.inputMode || !s.inlineInputOwner) return s;\n\t\tconst owner = s.inlineInputOwner;\n\t\tconst value = this.inlineInput.getValue();\n\t\tconst caret = readInputCursor(this.inlineInput);\n\t\tif (readInlineDraft(s, owner) === value && readInlineCaret(s, owner) === caret) {\n\t\t\treturn s;\n\t\t}\n\t\treturn withInlineDraft(s, owner, value, caret);\n\t}\n\n\tprivate runEffect(effect: Effect): void {\n\t\tswitch (effect.kind) {\n\t\t\tcase \"set_input_buffer\":\n\t\t\t\tthis.inlineInput.setValue(effect.value);\n\t\t\t\twriteInputCursor(this.inlineInput, effect.caret);\n\t\t\t\treturn;\n\t\t\tcase \"set_notes_value\":\n\t\t\t\tthis.notesInput.setValue(effect.value);\n\t\t\t\treturn;\n\t\t\tcase \"set_notes_focused\":\n\t\t\t\tthis.notesInput.focused = effect.focused;\n\t\t\t\treturn;\n\t\t\tcase \"forward_notes_keystroke\":\n\t\t\t\tthis.notesInput.handleInput(effect.data);\n\t\t\t\treturn;\n\t\t\tcase \"done\":\n\t\t\t\tthis.done(effect.result);\n\t\t\t\treturn;\n\t\t}\n\t}\n\n\t/**\n\t * Per-keystroke `ignore` fast path: delegates to the headless `inlineInput`\n\t * Input so bracketed-paste accumulator (`input.js:33-63`) and Kitty CSI-u\n\t * decode (`input.js:155-163`) take effect. Cursor is NOT force-reset here —\n\t * doing so would corrupt split-chunk pastes (a `\\x05` byte mid-paste lands\n\t * verbatim in `pasteBuffer` and survives `handlePaste`'s narrow strip).\n\t * Cursor advances naturally via `insertCharacter` on typing/paste; cursor-\n\t * movement keys (Left/Right/Home/End/word-jumps) are now functional, and\n\t * the live buffer/caret are mirrored into canonical state so rendering can\n\t * draw the same thick cursor at the editing caret. `viewAdapter.apply` is\n\t * called directly without a reducer round-trip — preserves the D3 fast-path\n\t * latency profile from Phase 11.\n\t */\n\tprivate handleIgnoreInline(data: string): void {\n\t\tif (!this.state.inputMode || !this.state.inlineInputOwner) return;\n\t\tthis.inlineInput.handleInput(data);\n\t\tthis.state = this.mirrorInlineInputDraft(this.state);\n\t\tthis.viewAdapter.apply(this.state);\n\t}\n\n\tprivate runtime(): QuestionnaireRuntime {\n\t\treturn {\n\t\t\tkeybindings: getKeybindings(),\n\t\t\tinputBuffer: this.inlineInput.getValue(),\n\t\t\tquestions: this.questions,\n\t\t\tisMulti: this.isMulti,\n\t\t\tcurrentItem: this.currentItem(),\n\t\t\titems: this.itemsByTab[this.state.currentTab] ?? [],\n\t\t};\n\t}\n\n\tprivate applyContext(): ApplyContext {\n\t\treturn {\n\t\t\tquestions: this.questions,\n\t\t\titemsByTab: this.itemsByTab,\n\t\t\t...(this.chatAsOption ? { chatAsOption: true } : {}),\n\t\t};\n\t}\n\n\tprivate currentItem(): WrappingSelectItem | undefined {\n\t\tif (this.state.chatFocused) return { kind: \"chat\", label: ROW_INTENT_META.chat.label };\n\t\tconst arr = this.itemsByTab[this.state.currentTab] ?? [];\n\t\tif (this.state.optionIndex < arr.length) return arr[this.state.optionIndex];\n\t\treturn { kind: \"chat\", label: ROW_INTENT_META.chat.label };\n\t}\n}\n"]}
1
+ {"version":3,"file":"questionnaire-session.js","sourceRoot":"","sources":["../../../../../src/core/tools/ask-user-question/state/questionnaire-session.ts"],"names":[],"mappings":"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","sourcesContent":["import { decodeKittyPrintable, getKeybindings, type Input } from \"@earendil-works/pi-tui\";\nimport type { Theme } from \"../../../../modes/interactive/theme/theme.js\";\nimport type { QuestionData, QuestionnaireResult, QuestionParams } from \"../tool/types.js\";\nimport type { WrappingSelectItem } from \"../view/components/wrapping-select.ts\";\nimport type { QuestionnairePropsAdapter } from \"../view/props-adapter.ts\";\nimport { buildQuestionnaire } from \"./build-questionnaire.ts\";\nimport { readInlineCaret, readInlineDraft, withInlineDraft } from \"./inline-input.ts\";\nimport { type QuestionnaireAction, routeKey } from \"./key-router.ts\";\nimport { ROW_INTENT_META } from \"./row-intent.ts\";\nimport { computeFocusedOptionHasPreview } from \"./selectors/derivations.ts\";\nimport type { QuestionnaireRuntime, QuestionnaireState } from \"./state.ts\";\nimport { type ApplyContext, type Effect, reduce } from \"./state-reducer.ts\";\n\nfunction readInputCursor(input: Input): number {\n\tconst value = input.getValue();\n\tconst raw = Reflect.get(input, \"cursor\");\n\treturn typeof raw === \"number\" && Number.isFinite(raw) ? Math.max(0, Math.min(raw, value.length)) : value.length;\n}\n\nfunction writeInputCursor(input: Input, cursor: number): void {\n\tconst value = input.getValue();\n\tReflect.set(input, \"cursor\", Math.max(0, Math.min(cursor, value.length)));\n}\n\nfunction inlineInputHandles(data: string): boolean {\n\tif (data.includes(\"\\x1b[200~\")) return true;\n\tif (data === \"\\n\") return true;\n\tconst keybindings = getKeybindings();\n\tconst inputActions: Array<Parameters<typeof keybindings.matches>[1]> = [\n\t\t\"tui.select.cancel\",\n\t\t\"tui.editor.undo\",\n\t\t\"tui.input.submit\",\n\t\t\"tui.editor.deleteCharBackward\",\n\t\t\"tui.editor.deleteCharForward\",\n\t\t\"tui.editor.deleteWordBackward\",\n\t\t\"tui.editor.deleteWordForward\",\n\t\t\"tui.editor.deleteToLineStart\",\n\t\t\"tui.editor.deleteToLineEnd\",\n\t\t\"tui.editor.yank\",\n\t\t\"tui.editor.yankPop\",\n\t\t\"tui.editor.cursorLeft\",\n\t\t\"tui.editor.cursorRight\",\n\t\t\"tui.editor.cursorLineStart\",\n\t\t\"tui.editor.cursorLineEnd\",\n\t\t\"tui.editor.cursorWordLeft\",\n\t\t\"tui.editor.cursorWordRight\",\n\t];\n\tif (inputActions.some((action) => keybindings.matches(data, action))) {\n\t\treturn true;\n\t}\n\tif (decodeKittyPrintable(data) !== undefined) return true;\n\treturn [...data].every((character) => {\n\t\tconst code = character.charCodeAt(0);\n\t\treturn code >= 32 && code !== 0x7f && !(code >= 0x80 && code <= 0x9f);\n\t});\n}\n\nexport interface QuestionnaireSessionConfig {\n\ttui: { terminal: { columns: number }; requestRender(): void };\n\ttheme: Theme;\n\tparams: QuestionParams;\n\titemsByTab: WrappingSelectItem[][];\n\tdone: (result: QuestionnaireResult) => void;\n\t/** When true, Chat about this is a plain option and does not open an inline editor. */\n\tchatAsOption?: boolean;\n}\n\nexport interface QuestionnaireSessionComponent {\n\trender(width: number): string[];\n\tinvalidate(): void;\n\thandleInput(data: string): boolean;\n}\n\nfunction initialState(): QuestionnaireState {\n\treturn {\n\t\tcurrentTab: 0,\n\t\toptionIndex: 0,\n\t\tinputMode: false,\n\t\tinlineInputOwner: null,\n\t\tnotesVisible: false,\n\t\tchatFocused: false,\n\t\tanswers: new Map(),\n\t\tmultiSelectChecked: new Set(),\n\t\tnotesByTab: new Map(),\n\t\tfocusedOptionHasPreview: false,\n\t\tsubmitChoiceIndex: 0,\n\t\tnotesDraft: \"\",\n\t\tcustomDraftByTab: new Map(),\n\t\tcustomCaretByTab: new Map(),\n\t\tchatDraftByTab: new Map(),\n\t\tchatCaretByTab: new Map(),\n\t};\n}\n\n/**\n * Slim runtime: owns the canonical state cell, the input-buffer cell, the\n * two-pass `notesVisible` dispatch loop, and the effect runner. State\n * transitions go through the pure `reduce` reducer; UI fan-out goes through\n * the `QuestionnairePropsAdapter` produced by `buildQuestionnaire`.\n */\nexport class QuestionnaireSession {\n\tprivate state: QuestionnaireState = initialState();\n\n\tprivate readonly questions: readonly QuestionData[];\n\tprivate readonly isMulti: boolean;\n\tprivate readonly itemsByTab: WrappingSelectItem[][];\n\tprivate readonly chatAsOption: boolean;\n\n\tprivate readonly notesInput: Input;\n\tprivate readonly inlineInput: Input;\n\tprivate readonly viewAdapter: QuestionnairePropsAdapter;\n\n\tprivate readonly tui: QuestionnaireSessionConfig[\"tui\"];\n\tprivate readonly done: QuestionnaireSessionConfig[\"done\"];\n\n\treadonly component: QuestionnaireSessionComponent;\n\n\tconstructor(config: QuestionnaireSessionConfig) {\n\t\tthis.tui = config.tui;\n\t\tthis.done = config.done;\n\t\tthis.questions = config.params.questions;\n\t\tthis.isMulti = this.questions.length > 1;\n\t\tthis.itemsByTab = config.itemsByTab;\n\t\tthis.chatAsOption = config.chatAsOption === true;\n\t\t// Seed from the focused option at start; the reducer keeps it in sync via withFocusedOptionHasPreview.\n\t\tthis.state = { ...this.state, focusedOptionHasPreview: computeFocusedOptionHasPreview(this.questions, 0, 0) };\n\n\t\tconst built = buildQuestionnaire({\n\t\t\ttui: this.tui,\n\t\t\ttheme: config.theme,\n\t\t\tquestions: this.questions,\n\t\t\titemsByTab: this.itemsByTab,\n\t\t\tisMulti: this.isMulti,\n\t\t\tinitialState: this.state,\n\t\t\tgetCurrentTab: () => this.state.currentTab,\n\t\t\t...(this.chatAsOption ? { chatAsOption: true } : {}),\n\t\t});\n\n\t\tthis.notesInput = built.notesInput;\n\t\tthis.inlineInput = built.inlineInput;\n\t\tthis.viewAdapter = built.adapter;\n\n\t\tthis.component = {\n\t\t\trender: built.render,\n\t\t\tinvalidate: built.invalidate,\n\t\t\thandleInput: (data) => this.dispatch(data),\n\t\t};\n\n\t\tthis.viewAdapter.apply(this.state);\n\t}\n\n\tdispatch(data: string): boolean {\n\t\tconst action = routeKey(data, this.state, this.runtime());\n\t\tif (action.kind === \"ignore\") {\n\t\t\tconst hasInlineInput = this.state.inputMode && this.state.inlineInputOwner !== null;\n\t\t\tthis.handleIgnoreInline(data);\n\t\t\treturn hasInlineInput && inlineInputHandles(data);\n\t\t}\n\t\tthis.commit(action);\n\t\treturn true;\n\t}\n\n\tprivate commit(action: QuestionnaireAction): void {\n\t\tthis.state = this.mirrorInlineInputDraft(this.state);\n\t\tconst result = reduce(this.state, action, this.applyContext());\n\t\tthis.state = result.state;\n\t\tfor (const effect of result.effects) this.runEffect(effect);\n\t\tthis.state = this.mirrorNotesDraft(this.state);\n\t\tthis.viewAdapter.apply(this.state);\n\t}\n\n\tprivate mirrorNotesDraft(s: QuestionnaireState): QuestionnaireState {\n\t\tconst draft = this.notesInput.getValue();\n\t\treturn s.notesDraft === draft ? s : { ...s, notesDraft: draft };\n\t}\n\n\tprivate mirrorInlineInputDraft(s: QuestionnaireState): QuestionnaireState {\n\t\tif (!s.inputMode || !s.inlineInputOwner) return s;\n\t\tconst owner = s.inlineInputOwner;\n\t\tconst value = this.inlineInput.getValue();\n\t\tconst caret = readInputCursor(this.inlineInput);\n\t\tif (readInlineDraft(s, owner) === value && readInlineCaret(s, owner) === caret) {\n\t\t\treturn s;\n\t\t}\n\t\treturn withInlineDraft(s, owner, value, caret);\n\t}\n\n\tprivate runEffect(effect: Effect): void {\n\t\tswitch (effect.kind) {\n\t\t\tcase \"set_input_buffer\":\n\t\t\t\tthis.inlineInput.setValue(effect.value);\n\t\t\t\twriteInputCursor(this.inlineInput, effect.caret);\n\t\t\t\treturn;\n\t\t\tcase \"set_notes_value\":\n\t\t\t\tthis.notesInput.setValue(effect.value);\n\t\t\t\treturn;\n\t\t\tcase \"set_notes_focused\":\n\t\t\t\tthis.notesInput.focused = effect.focused;\n\t\t\t\treturn;\n\t\t\tcase \"forward_notes_keystroke\":\n\t\t\t\tthis.notesInput.handleInput(effect.data);\n\t\t\t\treturn;\n\t\t\tcase \"done\":\n\t\t\t\tthis.done(effect.result);\n\t\t\t\treturn;\n\t\t}\n\t}\n\n\t/**\n\t * Per-keystroke `ignore` fast path: delegates to the headless `inlineInput`\n\t * Input so bracketed-paste accumulator (`input.js:33-63`) and Kitty CSI-u\n\t * decode (`input.js:155-163`) take effect. Cursor is NOT force-reset here —\n\t * doing so would corrupt split-chunk pastes (a `\\x05` byte mid-paste lands\n\t * verbatim in `pasteBuffer` and survives `handlePaste`'s narrow strip).\n\t * Cursor advances naturally via `insertCharacter` on typing/paste; cursor-\n\t * movement keys (Left/Right/Home/End/word-jumps) are now functional, and\n\t * the live buffer/caret are mirrored into canonical state so rendering can\n\t * draw the same thick cursor at the editing caret. `viewAdapter.apply` is\n\t * called directly without a reducer round-trip — preserves the D3 fast-path\n\t * latency profile from Phase 11.\n\t */\n\tprivate handleIgnoreInline(data: string): void {\n\t\tif (!this.state.inputMode || !this.state.inlineInputOwner) return;\n\t\tthis.inlineInput.handleInput(data);\n\t\tthis.state = this.mirrorInlineInputDraft(this.state);\n\t\tthis.viewAdapter.apply(this.state);\n\t}\n\n\tprivate runtime(): QuestionnaireRuntime {\n\t\treturn {\n\t\t\tkeybindings: getKeybindings(),\n\t\t\tinputBuffer: this.inlineInput.getValue(),\n\t\t\tquestions: this.questions,\n\t\t\tisMulti: this.isMulti,\n\t\t\tcurrentItem: this.currentItem(),\n\t\t\titems: this.itemsByTab[this.state.currentTab] ?? [],\n\t\t};\n\t}\n\n\tprivate applyContext(): ApplyContext {\n\t\treturn {\n\t\t\tquestions: this.questions,\n\t\t\titemsByTab: this.itemsByTab,\n\t\t\t...(this.chatAsOption ? { chatAsOption: true } : {}),\n\t\t};\n\t}\n\n\tprivate currentItem(): WrappingSelectItem | undefined {\n\t\tif (this.state.chatFocused) return { kind: \"chat\", label: ROW_INTENT_META.chat.label };\n\t\tconst arr = this.itemsByTab[this.state.currentTab] ?? [];\n\t\tif (this.state.optionIndex < arr.length) return arr[this.state.optionIndex];\n\t\treturn { kind: \"chat\", label: ROW_INTENT_META.chat.label };\n\t}\n}\n"]}
@@ -10,6 +10,7 @@ import { WrappingSelect } from "./wrapping-select.js";
10
10
  * triplet forwards.
11
11
  */
12
12
  export class ChatRowView {
13
+ select;
13
14
  constructor(config) {
14
15
  this.select = new WrappingSelect([config.item], 1, config.theme, {
15
16
  numberStartOffset: 0,
@@ -1 +1 @@
1
- {"version":3,"file":"chat-row-view.js","sourceRoot":"","sources":["../../../../../../src/core/tools/ask-user-question/view/components/chat-row-view.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,cAAc,EAAqD,MAAM,sBAAsB,CAAC;AAwBzG;;;;;;;;;GASG;AACH,MAAM,OAAO,WAAW;IAGvB,YAAY,MAAyB;QACpC,IAAI,CAAC,MAAM,GAAG,IAAI,cAAc,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC,EAAE,MAAM,CAAC,KAAK,EAAE;YAChE,iBAAiB,EAAE,CAAC;YACpB,sBAAsB,EAAE,CAAC;YACzB,kBAAkB,EAAE,MAAM,CAAC,kBAAkB;SAC7C,CAAC,CAAC;IACJ,CAAC;IAED,QAAQ,CAAC,KAAuB;QAC/B,IAAI,CAAC,MAAM,CAAC,UAAU,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;QACtC,IAAI,CAAC,MAAM,CAAC,YAAY,CAAC,KAAK,CAAC,SAAS,CAAC,MAAM,EAAE,KAAK,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC;QACxE,IAAI,CAAC,MAAM,CAAC,cAAc,CAAC,KAAK,CAAC,WAAW,CAAC,CAAC;QAC9C,IAAI,CAAC,MAAM,CAAC,cAAc,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC;IAC9C,CAAC;IAED,WAAW,CAAC,KAAa,IAAS,CAAC;IAEnC,UAAU;QACT,IAAI,CAAC,MAAM,CAAC,UAAU,EAAE,CAAC;IAC1B,CAAC;IAED,MAAM,CAAC,KAAa;QACnB,OAAO,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;IAClC,CAAC;CACD","sourcesContent":["import type { Component } from \"@earendil-works/pi-tui\";\nimport type { StatefulView } from \"../stateful-view.ts\";\nimport { WrappingSelect, type WrappingSelectItem, type WrappingSelectTheme } from \"./wrapping-select.ts\";\n\n/**\n * Per-tick projection of chat-row state. The chat row is a single-item\n * `WrappingSelect` rendered in the question-tab footer; it owns no per-tab\n * state — only `focused` (whether the chat row is the active focus target),\n * `numbering` (display number aligned with the active tab's items), and the\n * owner-specific inline input projection supplied by the session.\n */\nexport interface ChatRowViewProps {\n\tfocused: boolean;\n\tnumbering: { offset: number; total: number };\n\tinputBuffer: string;\n\tinputCaret: number;\n}\n\nexport interface ChatRowViewConfig {\n\t/** The single chat sentinel row — `{kind: \"chat\", label: SENTINEL_LABELS.chat}`. */\n\titem: WrappingSelectItem;\n\ttheme: WrappingSelectTheme;\n\t/** When false, Chat about this renders as a plain option instead of an inline editor. */\n\tinlineInputEnabled?: boolean;\n}\n\n/**\n * Typed wrapper around the chat-row `WrappingSelect`. Replaces the prior\n * raw-primitive consumption at `props-adapter.ts:106, :120` and removes the\n * accidental surface area (8 unused `WrappingSelect` setters) noted in\n * research Q4.\n *\n * Pattern modeled after `OptionListView` (`option-list-view.ts:27-93`):\n * mirror-then-delegate `setProps`; render is pure delegation; `Component`\n * triplet forwards.\n */\nexport class ChatRowView implements StatefulView<ChatRowViewProps>, Component {\n\tprivate readonly select: WrappingSelect;\n\n\tconstructor(config: ChatRowViewConfig) {\n\t\tthis.select = new WrappingSelect([config.item], 1, config.theme, {\n\t\t\tnumberStartOffset: 0,\n\t\t\ttotalItemsForNumbering: 1,\n\t\t\tinlineInputEnabled: config.inlineInputEnabled,\n\t\t});\n\t}\n\n\tsetProps(props: ChatRowViewProps): void {\n\t\tthis.select.setFocused(props.focused);\n\t\tthis.select.setNumbering(props.numbering.offset, props.numbering.total);\n\t\tthis.select.setInputBuffer(props.inputBuffer);\n\t\tthis.select.setInputCursor(props.inputCaret);\n\t}\n\n\thandleInput(_data: string): void {}\n\n\tinvalidate(): void {\n\t\tthis.select.invalidate();\n\t}\n\n\trender(width: number): string[] {\n\t\treturn this.select.render(width);\n\t}\n}\n"]}
1
+ {"version":3,"file":"chat-row-view.js","sourceRoot":"","sources":["../../../../../../src/core/tools/ask-user-question/view/components/chat-row-view.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,cAAc,EAAqD,MAAM,sBAAsB,CAAC;AAwBzG;;;;;;;;;GASG;AACH,MAAM,OAAO,WAAW;IACN,MAAM,CAAiB;IAExC,YAAY,MAAyB;QACpC,IAAI,CAAC,MAAM,GAAG,IAAI,cAAc,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC,EAAE,MAAM,CAAC,KAAK,EAAE;YAChE,iBAAiB,EAAE,CAAC;YACpB,sBAAsB,EAAE,CAAC;YACzB,kBAAkB,EAAE,MAAM,CAAC,kBAAkB;SAC7C,CAAC,CAAC;IACJ,CAAC;IAED,QAAQ,CAAC,KAAuB;QAC/B,IAAI,CAAC,MAAM,CAAC,UAAU,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;QACtC,IAAI,CAAC,MAAM,CAAC,YAAY,CAAC,KAAK,CAAC,SAAS,CAAC,MAAM,EAAE,KAAK,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC;QACxE,IAAI,CAAC,MAAM,CAAC,cAAc,CAAC,KAAK,CAAC,WAAW,CAAC,CAAC;QAC9C,IAAI,CAAC,MAAM,CAAC,cAAc,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC;IAC9C,CAAC;IAED,WAAW,CAAC,KAAa,IAAS,CAAC;IAEnC,UAAU;QACT,IAAI,CAAC,MAAM,CAAC,UAAU,EAAE,CAAC;IAC1B,CAAC;IAED,MAAM,CAAC,KAAa;QACnB,OAAO,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;IAClC,CAAC;CACD","sourcesContent":["import type { Component } from \"@earendil-works/pi-tui\";\nimport type { StatefulView } from \"../stateful-view.ts\";\nimport { WrappingSelect, type WrappingSelectItem, type WrappingSelectTheme } from \"./wrapping-select.ts\";\n\n/**\n * Per-tick projection of chat-row state. The chat row is a single-item\n * `WrappingSelect` rendered in the question-tab footer; it owns no per-tab\n * state — only `focused` (whether the chat row is the active focus target),\n * `numbering` (display number aligned with the active tab's items), and the\n * owner-specific inline input projection supplied by the session.\n */\nexport interface ChatRowViewProps {\n\tfocused: boolean;\n\tnumbering: { offset: number; total: number };\n\tinputBuffer: string;\n\tinputCaret: number;\n}\n\nexport interface ChatRowViewConfig {\n\t/** The single chat sentinel row — `{kind: \"chat\", label: SENTINEL_LABELS.chat}`. */\n\titem: WrappingSelectItem;\n\ttheme: WrappingSelectTheme;\n\t/** When false, Chat about this renders as a plain option instead of an inline editor. */\n\tinlineInputEnabled?: boolean;\n}\n\n/**\n * Typed wrapper around the chat-row `WrappingSelect`. Replaces the prior\n * raw-primitive consumption at `props-adapter.ts:106, :120` and removes the\n * accidental surface area (8 unused `WrappingSelect` setters) noted in\n * research Q4.\n *\n * Pattern modeled after `OptionListView` (`option-list-view.ts:27-93`):\n * mirror-then-delegate `setProps`; render is pure delegation; `Component`\n * triplet forwards.\n */\nexport class ChatRowView implements StatefulView<ChatRowViewProps>, Component {\n\tprivate readonly select: WrappingSelect;\n\n\tconstructor(config: ChatRowViewConfig) {\n\t\tthis.select = new WrappingSelect([config.item], 1, config.theme, {\n\t\t\tnumberStartOffset: 0,\n\t\t\ttotalItemsForNumbering: 1,\n\t\t\tinlineInputEnabled: config.inlineInputEnabled,\n\t\t});\n\t}\n\n\tsetProps(props: ChatRowViewProps): void {\n\t\tthis.select.setFocused(props.focused);\n\t\tthis.select.setNumbering(props.numbering.offset, props.numbering.total);\n\t\tthis.select.setInputBuffer(props.inputBuffer);\n\t\tthis.select.setInputCursor(props.inputCaret);\n\t}\n\n\thandleInput(_data: string): void {}\n\n\tinvalidate(): void {\n\t\tthis.select.invalidate();\n\t}\n\n\trender(width: number): string[] {\n\t\treturn this.select.render(width);\n\t}\n}\n"]}
@@ -22,6 +22,7 @@ export const MULTI_SUBMIT_LABEL = "Submit";
22
22
  * `setProps(props)` is a pure field reassignment — no render, no invalidate side effects.
23
23
  */
24
24
  export class MultiSelectView {
25
+ props;
25
26
  constructor(theme, question) {
26
27
  this.theme = theme;
27
28
  this.question = question;
@@ -1 +1 @@
1
- {"version":3,"file":"multi-select-view.js","sourceRoot":"","sources":["../../../../../../src/core/tools/ask-user-question/view/components/multi-select-view.ts"],"names":[],"mappings":"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","sourcesContent":["import { truncateToWidth, visibleWidth, wrapTextWithAnsi } from \"@earendil-works/pi-tui\";\nimport type { Theme } from \"../../../../../modes/interactive/theme/theme.js\";\nimport { OVERLAY_ACTIVE_ROW_MARKER } from \"../../../../extensions/ui-types.js\";\nimport { ROW_INTENT_META } from \"../../state/row-intent.ts\";\nimport type { QuestionData } from \"../../tool/types.js\";\nimport type { StatefulView } from \"../stateful-view.ts\";\n\nconst ACTIVE_POINTER = \"❯ \";\nconst INACTIVE_POINTER = \" \";\nconst CHECKED = \"[✔]\";\nconst UNCHECKED = \"[ ]\";\nconst NUMBER_SEPARATOR = \". \";\nconst BOX_LABEL_GAP = \" \";\n// CC parity: description continuation indents to col 2 (past the pointer slot), NOT to the\n// full prefix column. Wrap width still uses prefixVisibleWidth so naturalHeight matches render.\nconst CONTINUATION_INDENT = \" \";\n\nexport const MULTI_SUBMIT_LABEL = \"Submit\";\n\nexport interface MultiSelectViewProps {\n\trows: ReadonlyArray<{ checked: boolean; active: boolean }>;\n\tnextActive: boolean;\n\tnextLabel: string;\n}\n\n/**\n * Renders the multi-select option list (one row per option — pointer + checkbox + label —\n * plus zero or more wrapped continuation lines per description).\n *\n * `naturalHeight(width)` is state-INDEPENDENT (depends only on theme glyph widths,\n * question.options, and width) so the host can compute a stable globalContentHeight\n * without rendering. `naturalHeight(w) === render(w).length` for every props.\n *\n * `setProps(props)` is a pure field reassignment — no render, no invalidate side effects.\n */\nexport class MultiSelectView implements StatefulView<MultiSelectViewProps> {\n\tprivate props: MultiSelectViewProps;\n\n\tprivate declare readonly theme: Theme;\n\tprivate declare readonly question: QuestionData;\n\n\tconstructor(theme: Theme, question: QuestionData) {\n\t\tthis.theme = theme;\n\t\tthis.question = question;\n\t\tthis.props = { rows: [], nextActive: false, nextLabel: ROW_INTENT_META.next.label };\n\t}\n\n\tsetProps(props: MultiSelectViewProps): void {\n\t\tthis.props = props;\n\t}\n\n\thandleInput(_data: string): void {}\n\n\tinvalidate(): void {}\n\n\trender(width: number): string[] {\n\t\tconst lines: string[] = [];\n\t\tconst prefixWidth = this.prefixVisibleWidth();\n\t\tconst contentWidth = Math.max(1, width - prefixWidth);\n\t\tconst numberWidth = String(Math.max(1, this.question.options.length)).length;\n\t\tfor (let i = 0; i < this.question.options.length; i++) {\n\t\t\tconst opt = this.question.options[i];\n\t\t\tconst row = this.props.rows[i];\n\t\t\tif (!opt || !row) continue;\n\t\t\tconst pointer = row.active ? this.theme.fg(\"accent\", ACTIVE_POINTER) : INACTIVE_POINTER;\n\t\t\t// Checked uses the same `accent` hue as the active-row label so checked rows read\n\t\t\t// as \"selected\" rather than \"success\" — matches the visual rhythm of the rest of\n\t\t\t// the dialog (active pointer, label, picker rows are all accent).\n\t\t\tconst box = row.checked ? this.theme.fg(\"accent\", CHECKED) : this.theme.fg(\"muted\", UNCHECKED);\n\t\t\tconst label = truncateToWidth(opt.label, contentWidth, \"…\");\n\t\t\tconst styledLabel = row.active ? this.theme.fg(\"accent\", this.theme.bold(label)) : label;\n\t\t\tconst num = String(i + 1).padStart(numberWidth, \" \");\n\t\t\tconst line = `${pointer}${num}${NUMBER_SEPARATOR}${box}${BOX_LABEL_GAP}${styledLabel}`;\n\t\t\tconst rendered = truncateToWidth(line, width, \"\");\n\t\t\tlines.push(`${rendered}${row.active ? OVERLAY_ACTIVE_ROW_MARKER : \"\"}`);\n\t\t\tif (opt.description) {\n\t\t\t\tconst wrapped = wrapTextWithAnsi(opt.description, contentWidth);\n\t\t\t\tfor (const segment of wrapped) {\n\t\t\t\t\tlines.push(CONTINUATION_INDENT + this.theme.fg(\"muted\", segment));\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tconst nextPointer = this.props.nextActive ? this.theme.fg(\"accent\", ACTIVE_POINTER) : INACTIVE_POINTER;\n\t\tconst nextLabel = this.props.nextActive\n\t\t\t? this.theme.fg(\"accent\", this.theme.bold(this.props.nextLabel))\n\t\t\t: this.props.nextLabel;\n\t\tconst rendered = truncateToWidth(`${nextPointer}${nextLabel}`, width, \"\");\n\t\tlines.push(`${rendered}${this.props.nextActive ? OVERLAY_ACTIVE_ROW_MARKER : \"\"}`);\n\t\treturn lines;\n\t}\n\n\tnaturalHeight(width: number): number {\n\t\tconst contentWidth = Math.max(1, width - this.prefixVisibleWidth());\n\t\tlet total = 0;\n\t\tfor (const opt of this.question.options) {\n\t\t\tif (!opt) continue;\n\t\t\ttotal += 1; // row line\n\t\t\tif (opt.description) {\n\t\t\t\ttotal += wrapTextWithAnsi(opt.description, contentWidth).length;\n\t\t\t}\n\t\t}\n\t\treturn total + 1; // Next sentinel row (no description; never wraps).\n\t}\n\n\tprivate prefixVisibleWidth(): number {\n\t\t// Canonical prefix for OPTION rows: INACTIVE_POINTER + numberWidth digits + NUMBER_SEPARATOR\n\t\t// + UNCHECKED + BOX_LABEL_GAP. State-independent because ACTIVE/INACTIVE pointer share\n\t\t// visibleWidth, CHECKED/UNCHECKED share visibleWidth, and numberWidth is constant per question.\n\t\t// The Next sentinel uses a bare `pointer + \"Next\"` shape — its width never exceeds this prefix\n\t\t// at any reasonable terminal width, so it's safe to leave it out of the canonical computation.\n\t\tconst numberWidth = String(Math.max(1, this.question.options.length)).length;\n\t\treturn (\n\t\t\tvisibleWidth(INACTIVE_POINTER) + numberWidth + visibleWidth(`${NUMBER_SEPARATOR}${UNCHECKED}${BOX_LABEL_GAP}`)\n\t\t);\n\t}\n}\n"]}
1
+ {"version":3,"file":"multi-select-view.js","sourceRoot":"","sources":["../../../../../../src/core/tools/ask-user-question/view/components/multi-select-view.ts"],"names":[],"mappings":"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","sourcesContent":["import { truncateToWidth, visibleWidth, wrapTextWithAnsi } from \"@earendil-works/pi-tui\";\nimport type { Theme } from \"../../../../../modes/interactive/theme/theme.js\";\nimport { OVERLAY_ACTIVE_ROW_MARKER } from \"../../../../extensions/ui-types.js\";\nimport { ROW_INTENT_META } from \"../../state/row-intent.ts\";\nimport type { QuestionData } from \"../../tool/types.js\";\nimport type { StatefulView } from \"../stateful-view.ts\";\n\nconst ACTIVE_POINTER = \"❯ \";\nconst INACTIVE_POINTER = \" \";\nconst CHECKED = \"[✔]\";\nconst UNCHECKED = \"[ ]\";\nconst NUMBER_SEPARATOR = \". \";\nconst BOX_LABEL_GAP = \" \";\n// CC parity: description continuation indents to col 2 (past the pointer slot), NOT to the\n// full prefix column. Wrap width still uses prefixVisibleWidth so naturalHeight matches render.\nconst CONTINUATION_INDENT = \" \";\n\nexport const MULTI_SUBMIT_LABEL = \"Submit\";\n\nexport interface MultiSelectViewProps {\n\trows: ReadonlyArray<{ checked: boolean; active: boolean }>;\n\tnextActive: boolean;\n\tnextLabel: string;\n}\n\n/**\n * Renders the multi-select option list (one row per option — pointer + checkbox + label —\n * plus zero or more wrapped continuation lines per description).\n *\n * `naturalHeight(width)` is state-INDEPENDENT (depends only on theme glyph widths,\n * question.options, and width) so the host can compute a stable globalContentHeight\n * without rendering. `naturalHeight(w) === render(w).length` for every props.\n *\n * `setProps(props)` is a pure field reassignment — no render, no invalidate side effects.\n */\nexport class MultiSelectView implements StatefulView<MultiSelectViewProps> {\n\tprivate props: MultiSelectViewProps;\n\n\tprivate declare readonly theme: Theme;\n\tprivate declare readonly question: QuestionData;\n\n\tconstructor(theme: Theme, question: QuestionData) {\n\t\tthis.theme = theme;\n\t\tthis.question = question;\n\t\tthis.props = { rows: [], nextActive: false, nextLabel: ROW_INTENT_META.next.label };\n\t}\n\n\tsetProps(props: MultiSelectViewProps): void {\n\t\tthis.props = props;\n\t}\n\n\thandleInput(_data: string): void {}\n\n\tinvalidate(): void {}\n\n\trender(width: number): string[] {\n\t\tconst lines: string[] = [];\n\t\tconst prefixWidth = this.prefixVisibleWidth();\n\t\tconst contentWidth = Math.max(1, width - prefixWidth);\n\t\tconst numberWidth = String(Math.max(1, this.question.options.length)).length;\n\t\tfor (let i = 0; i < this.question.options.length; i++) {\n\t\t\tconst opt = this.question.options[i];\n\t\t\tconst row = this.props.rows[i];\n\t\t\tif (!opt || !row) continue;\n\t\t\tconst pointer = row.active ? this.theme.fg(\"accent\", ACTIVE_POINTER) : INACTIVE_POINTER;\n\t\t\t// Checked uses the same `accent` hue as the active-row label so checked rows read\n\t\t\t// as \"selected\" rather than \"success\" — matches the visual rhythm of the rest of\n\t\t\t// the dialog (active pointer, label, picker rows are all accent).\n\t\t\tconst box = row.checked ? this.theme.fg(\"accent\", CHECKED) : this.theme.fg(\"muted\", UNCHECKED);\n\t\t\tconst label = truncateToWidth(opt.label, contentWidth, \"…\");\n\t\t\tconst styledLabel = row.active ? this.theme.fg(\"accent\", this.theme.bold(label)) : label;\n\t\t\tconst num = String(i + 1).padStart(numberWidth, \" \");\n\t\t\tconst line = `${pointer}${num}${NUMBER_SEPARATOR}${box}${BOX_LABEL_GAP}${styledLabel}`;\n\t\t\tconst rendered = truncateToWidth(line, width, \"\");\n\t\t\tlines.push(`${rendered}${row.active ? OVERLAY_ACTIVE_ROW_MARKER : \"\"}`);\n\t\t\tif (opt.description) {\n\t\t\t\tconst wrapped = wrapTextWithAnsi(opt.description, contentWidth);\n\t\t\t\tfor (const segment of wrapped) {\n\t\t\t\t\tlines.push(CONTINUATION_INDENT + this.theme.fg(\"muted\", segment));\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tconst nextPointer = this.props.nextActive ? this.theme.fg(\"accent\", ACTIVE_POINTER) : INACTIVE_POINTER;\n\t\tconst nextLabel = this.props.nextActive\n\t\t\t? this.theme.fg(\"accent\", this.theme.bold(this.props.nextLabel))\n\t\t\t: this.props.nextLabel;\n\t\tconst rendered = truncateToWidth(`${nextPointer}${nextLabel}`, width, \"\");\n\t\tlines.push(`${rendered}${this.props.nextActive ? OVERLAY_ACTIVE_ROW_MARKER : \"\"}`);\n\t\treturn lines;\n\t}\n\n\tnaturalHeight(width: number): number {\n\t\tconst contentWidth = Math.max(1, width - this.prefixVisibleWidth());\n\t\tlet total = 0;\n\t\tfor (const opt of this.question.options) {\n\t\t\tif (!opt) continue;\n\t\t\ttotal += 1; // row line\n\t\t\tif (opt.description) {\n\t\t\t\ttotal += wrapTextWithAnsi(opt.description, contentWidth).length;\n\t\t\t}\n\t\t}\n\t\treturn total + 1; // Next sentinel row (no description; never wraps).\n\t}\n\n\tprivate prefixVisibleWidth(): number {\n\t\t// Canonical prefix for OPTION rows: INACTIVE_POINTER + numberWidth digits + NUMBER_SEPARATOR\n\t\t// + UNCHECKED + BOX_LABEL_GAP. State-independent because ACTIVE/INACTIVE pointer share\n\t\t// visibleWidth, CHECKED/UNCHECKED share visibleWidth, and numberWidth is constant per question.\n\t\t// The Next sentinel uses a bare `pointer + \"Next\"` shape — its width never exceeds this prefix\n\t\t// at any reasonable terminal width, so it's safe to leave it out of the canonical computation.\n\t\tconst numberWidth = String(Math.max(1, this.question.options.length)).length;\n\t\treturn (\n\t\t\tvisibleWidth(INACTIVE_POINTER) + numberWidth + visibleWidth(`${NUMBER_SEPARATOR}${UNCHECKED}${BOX_LABEL_GAP}`)\n\t\t);\n\t}\n}\n"]}
@@ -11,6 +11,7 @@ export const MAX_VISIBLE_OPTIONS = 10;
11
11
  * received.
12
12
  */
13
13
  export class OptionListView {
14
+ select;
14
15
  constructor(config) {
15
16
  // Reserve a slot for the chat row in the WrappingSelect's number-padding so
16
17
  // the column width is identical whether or not the user navigates into chat
@@ -1 +1 @@
1
- {"version":3,"file":"option-list-view.js","sourceRoot":"","sources":["../../../../../../src/core/tools/ask-user-question/view/components/option-list-view.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,cAAc,EAAqD,MAAM,sBAAsB,CAAC;AAEzG;;;GAGG;AACH,MAAM,CAAC,MAAM,mBAAmB,GAAG,EAAE,CAAC;AAuBtC;;;;;GAKG;AACH,MAAM,OAAO,cAAc;IAG1B,YAAY,MAA4B;QACvC,4EAA4E;QAC5E,4EAA4E;QAC5E,oCAAoC;QACpC,IAAI,CAAC,MAAM,GAAG,IAAI,cAAc,CAAC,MAAM,CAAC,KAAK,EAAE,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,KAAK,CAAC,MAAM,EAAE,mBAAmB,CAAC,EAAE,MAAM,CAAC,KAAK,EAAE;YAChH,iBAAiB,EAAE,CAAC;YACpB,sBAAsB,EAAE,MAAM,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC;SAC/C,CAAC,CAAC;IACJ,CAAC;IAED,QAAQ,CAAC,KAA0B;QAClC,IAAI,CAAC,MAAM,CAAC,gBAAgB,CAAC,KAAK,CAAC,aAAa,CAAC,CAAC;QAClD,IAAI,CAAC,MAAM,CAAC,UAAU,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;QACtC,IAAI,CAAC,MAAM,CAAC,iBAAiB,CAAC,KAAK,CAAC,SAAS,EAAE,KAAK,EAAE,KAAK,CAAC,SAAS,EAAE,aAAa,CAAC,CAAC;QACtF,IAAI,CAAC,MAAM,CAAC,cAAc,CAAC,KAAK,CAAC,WAAW,CAAC,CAAC;QAC9C,IAAI,CAAC,MAAM,CAAC,cAAc,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC;IAC9C,CAAC;IAED,WAAW,CAAC,KAAa,IAAS,CAAC;IAEnC,UAAU;QACT,IAAI,CAAC,MAAM,CAAC,UAAU,EAAE,CAAC;IAC1B,CAAC;IAED,MAAM,CAAC,KAAa;QACnB,OAAO,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;IAClC,CAAC;CACD","sourcesContent":["import type { StatefulView } from \"../stateful-view.ts\";\nimport { WrappingSelect, type WrappingSelectItem, type WrappingSelectTheme } from \"./wrapping-select.ts\";\n\n/**\n * Maximum number of option rows visible in the WrappingSelect window. Lifted here from\n * `preview-pane.ts` so the cap travels with the option-list owner.\n */\nexport const MAX_VISIBLE_OPTIONS = 10;\n\nexport interface OptionListViewConfig {\n\titems: readonly WrappingSelectItem[];\n\ttheme: WrappingSelectTheme;\n}\n\n/**\n * Per-tick projection of OptionListView state. After Phase 11b, `inputBuffer`\n * is part of the props bag — the session-owned `inlineInput` (a headless\n * `pi-tui` Input instance) supplies its current `getValue()` here per tick.\n * `OptionListView` is purely props-driven; the imperative buffer surface and\n * read-back getters are gone.\n */\nexport interface OptionListViewProps {\n\tselectedIndex: number;\n\tfocused: boolean;\n\tinputBuffer: string;\n\tinputCaret: number;\n\t/** Optional previously-confirmed indicator. Omit when no marker should be drawn. */\n\tconfirmed?: { index: number; labelOverride?: string };\n}\n\n/**\n * Sole owner of the option list's interactive state. Wraps a single\n * `WrappingSelect`. Implements `StatefulView<OptionListViewProps>`:\n * `setProps` is the only mutator; render output reflects the last props\n * received.\n */\nexport class OptionListView implements StatefulView<OptionListViewProps> {\n\tprivate readonly select: WrappingSelect;\n\n\tconstructor(config: OptionListViewConfig) {\n\t\t// Reserve a slot for the chat row in the WrappingSelect's number-padding so\n\t\t// the column width is identical whether or not the user navigates into chat\n\t\t// (chat row uses items.length + 1).\n\t\tthis.select = new WrappingSelect(config.items, Math.min(config.items.length, MAX_VISIBLE_OPTIONS), config.theme, {\n\t\t\tnumberStartOffset: 0,\n\t\t\ttotalItemsForNumbering: config.items.length + 1,\n\t\t});\n\t}\n\n\tsetProps(props: OptionListViewProps): void {\n\t\tthis.select.setSelectedIndex(props.selectedIndex);\n\t\tthis.select.setFocused(props.focused);\n\t\tthis.select.setConfirmedIndex(props.confirmed?.index, props.confirmed?.labelOverride);\n\t\tthis.select.setInputBuffer(props.inputBuffer);\n\t\tthis.select.setInputCursor(props.inputCaret);\n\t}\n\n\thandleInput(_data: string): void {}\n\n\tinvalidate(): void {\n\t\tthis.select.invalidate();\n\t}\n\n\trender(width: number): string[] {\n\t\treturn this.select.render(width);\n\t}\n}\n"]}
1
+ {"version":3,"file":"option-list-view.js","sourceRoot":"","sources":["../../../../../../src/core/tools/ask-user-question/view/components/option-list-view.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,cAAc,EAAqD,MAAM,sBAAsB,CAAC;AAEzG;;;GAGG;AACH,MAAM,CAAC,MAAM,mBAAmB,GAAG,EAAE,CAAC;AAuBtC;;;;;GAKG;AACH,MAAM,OAAO,cAAc;IACT,MAAM,CAAiB;IAExC,YAAY,MAA4B;QACvC,4EAA4E;QAC5E,4EAA4E;QAC5E,oCAAoC;QACpC,IAAI,CAAC,MAAM,GAAG,IAAI,cAAc,CAAC,MAAM,CAAC,KAAK,EAAE,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,KAAK,CAAC,MAAM,EAAE,mBAAmB,CAAC,EAAE,MAAM,CAAC,KAAK,EAAE;YAChH,iBAAiB,EAAE,CAAC;YACpB,sBAAsB,EAAE,MAAM,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC;SAC/C,CAAC,CAAC;IACJ,CAAC;IAED,QAAQ,CAAC,KAA0B;QAClC,IAAI,CAAC,MAAM,CAAC,gBAAgB,CAAC,KAAK,CAAC,aAAa,CAAC,CAAC;QAClD,IAAI,CAAC,MAAM,CAAC,UAAU,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;QACtC,IAAI,CAAC,MAAM,CAAC,iBAAiB,CAAC,KAAK,CAAC,SAAS,EAAE,KAAK,EAAE,KAAK,CAAC,SAAS,EAAE,aAAa,CAAC,CAAC;QACtF,IAAI,CAAC,MAAM,CAAC,cAAc,CAAC,KAAK,CAAC,WAAW,CAAC,CAAC;QAC9C,IAAI,CAAC,MAAM,CAAC,cAAc,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC;IAC9C,CAAC;IAED,WAAW,CAAC,KAAa,IAAS,CAAC;IAEnC,UAAU;QACT,IAAI,CAAC,MAAM,CAAC,UAAU,EAAE,CAAC;IAC1B,CAAC;IAED,MAAM,CAAC,KAAa;QACnB,OAAO,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;IAClC,CAAC;CACD","sourcesContent":["import type { StatefulView } from \"../stateful-view.ts\";\nimport { WrappingSelect, type WrappingSelectItem, type WrappingSelectTheme } from \"./wrapping-select.ts\";\n\n/**\n * Maximum number of option rows visible in the WrappingSelect window. Lifted here from\n * `preview-pane.ts` so the cap travels with the option-list owner.\n */\nexport const MAX_VISIBLE_OPTIONS = 10;\n\nexport interface OptionListViewConfig {\n\titems: readonly WrappingSelectItem[];\n\ttheme: WrappingSelectTheme;\n}\n\n/**\n * Per-tick projection of OptionListView state. After Phase 11b, `inputBuffer`\n * is part of the props bag — the session-owned `inlineInput` (a headless\n * `pi-tui` Input instance) supplies its current `getValue()` here per tick.\n * `OptionListView` is purely props-driven; the imperative buffer surface and\n * read-back getters are gone.\n */\nexport interface OptionListViewProps {\n\tselectedIndex: number;\n\tfocused: boolean;\n\tinputBuffer: string;\n\tinputCaret: number;\n\t/** Optional previously-confirmed indicator. Omit when no marker should be drawn. */\n\tconfirmed?: { index: number; labelOverride?: string };\n}\n\n/**\n * Sole owner of the option list's interactive state. Wraps a single\n * `WrappingSelect`. Implements `StatefulView<OptionListViewProps>`:\n * `setProps` is the only mutator; render output reflects the last props\n * received.\n */\nexport class OptionListView implements StatefulView<OptionListViewProps> {\n\tprivate readonly select: WrappingSelect;\n\n\tconstructor(config: OptionListViewConfig) {\n\t\t// Reserve a slot for the chat row in the WrappingSelect's number-padding so\n\t\t// the column width is identical whether or not the user navigates into chat\n\t\t// (chat row uses items.length + 1).\n\t\tthis.select = new WrappingSelect(config.items, Math.min(config.items.length, MAX_VISIBLE_OPTIONS), config.theme, {\n\t\t\tnumberStartOffset: 0,\n\t\t\ttotalItemsForNumbering: config.items.length + 1,\n\t\t});\n\t}\n\n\tsetProps(props: OptionListViewProps): void {\n\t\tthis.select.setSelectedIndex(props.selectedIndex);\n\t\tthis.select.setFocused(props.focused);\n\t\tthis.select.setConfirmedIndex(props.confirmed?.index, props.confirmed?.labelOverride);\n\t\tthis.select.setInputBuffer(props.inputBuffer);\n\t\tthis.select.setInputCursor(props.inputCaret);\n\t}\n\n\thandleInput(_data: string): void {}\n\n\tinvalidate(): void {\n\t\tthis.select.invalidate();\n\t}\n\n\trender(width: number): string[] {\n\t\treturn this.select.render(width);\n\t}\n}\n"]}
@@ -17,6 +17,11 @@ export const NOTES_AFFORDANCE_OVERHEAD = 2;
17
17
  * first request, never re-constructed — count semantics frozen by tests.
18
18
  */
19
19
  export class MarkdownContentCache {
20
+ previewTexts;
21
+ markdownCache;
22
+ cachedWidth;
23
+ theme;
24
+ markdownTheme;
20
25
  constructor(question, theme, markdownTheme) {
21
26
  this.theme = theme;
22
27
  this.markdownTheme = markdownTheme;
@@ -1 +1 @@
1
- {"version":3,"file":"markdown-content-cache.js","sourceRoot":"","sources":["../../../../../../../src/core/tools/ask-user-question/view/components/preview/markdown-content-cache.ts"],"names":[],"mappings":"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","sourcesContent":["import { Markdown, type MarkdownTheme, visibleWidth } from \"@earendil-works/pi-tui\";\nimport type { Theme } from \"../../../../../../modes/interactive/theme/theme.js\";\nimport type { QuestionData } from \"../../../tool/types.js\";\nimport { stripFenceMarkers } from \"./preview-box-renderer.ts\";\n\n/** CC parity in side-by-side layout. */\nexport const MAX_PREVIEW_HEIGHT_SIDE_BY_SIDE = 20;\n/** Preserves narrow-terminal protection in stacked layout. */\nexport const MAX_PREVIEW_HEIGHT_STACKED = 15;\nexport const NO_PREVIEW_TEXT = \"No preview available\";\n/** 1 blank separator + 1 affordance text row reserved when `hasAnyPreview` (height stability of the affordance row's offset relative to the box). */\nexport const NOTES_AFFORDANCE_OVERHEAD = 2;\n\n/**\n * Per-question cache for rendered markdown previews. Width-keyed: switching the\n * inner width invalidates every cached `Markdown`'s render output (pi-tui's\n * `Markdown.render(width)` re-wraps when width changes).\n *\n * Replaces the inline `previewTexts`, `markdownCache`, `cachedWidth` triple from\n * the previous monolithic `preview-pane.ts`. One Markdown per option, lazy on\n * first request, never re-constructed — count semantics frozen by tests.\n */\nexport class MarkdownContentCache {\n\tprivate readonly previewTexts: Map<number, string>;\n\tprivate readonly markdownCache: Map<number, Markdown>;\n\tprivate cachedWidth: number | undefined;\n\tprivate readonly theme: Theme;\n\tprivate readonly markdownTheme: MarkdownTheme;\n\n\tconstructor(question: QuestionData, theme: Theme, markdownTheme: MarkdownTheme) {\n\t\tthis.theme = theme;\n\t\tthis.markdownTheme = markdownTheme;\n\t\tthis.previewTexts = new Map();\n\t\tfor (let i = 0; i < question.options.length; i++) {\n\t\t\tconst raw = question.options[i]?.preview;\n\t\t\tif (raw && raw.length > 0) this.previewTexts.set(i, raw);\n\t\t}\n\t\tthis.markdownCache = new Map();\n\t}\n\n\thasAnyPreview(): boolean {\n\t\treturn this.previewTexts.size > 0;\n\t}\n\n\thas(optionIndex: number): boolean {\n\t\treturn this.previewTexts.has(optionIndex);\n\t}\n\n\t/**\n\t * Compute the body lines for a given option at a given inner width. Width changes\n\t * invalidate the per-Markdown render cache.\n\t */\n\tbodyFor(optionIndex: number, innerWidth: number): string[] {\n\t\tif (this.cachedWidth !== innerWidth) {\n\t\t\tfor (const md of this.markdownCache.values()) md.invalidate();\n\t\t\tthis.cachedWidth = innerWidth;\n\t\t}\n\t\tconst text = this.previewTexts.get(optionIndex);\n\t\tif (!text) {\n\t\t\tconst placeholder = this.theme.fg(\"dim\", NO_PREVIEW_TEXT);\n\t\t\tconst pad = Math.max(0, innerWidth - visibleWidth(placeholder));\n\t\t\treturn [placeholder + \" \".repeat(pad)];\n\t\t}\n\t\tlet md = this.markdownCache.get(optionIndex);\n\t\tif (!md) {\n\t\t\tmd = new Markdown(text, 0, 0, this.markdownTheme);\n\t\t\tthis.markdownCache.set(optionIndex, md);\n\t\t}\n\t\treturn stripFenceMarkers(md.render(innerWidth));\n\t}\n\n\tinvalidate(): void {\n\t\tfor (const md of this.markdownCache.values()) md.invalidate();\n\t\tthis.cachedWidth = undefined;\n\t}\n}\n"]}
1
+ {"version":3,"file":"markdown-content-cache.js","sourceRoot":"","sources":["../../../../../../../src/core/tools/ask-user-question/view/components/preview/markdown-content-cache.ts"],"names":[],"mappings":"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","sourcesContent":["import { Markdown, type MarkdownTheme, visibleWidth } from \"@earendil-works/pi-tui\";\nimport type { Theme } from \"../../../../../../modes/interactive/theme/theme.js\";\nimport type { QuestionData } from \"../../../tool/types.js\";\nimport { stripFenceMarkers } from \"./preview-box-renderer.ts\";\n\n/** CC parity in side-by-side layout. */\nexport const MAX_PREVIEW_HEIGHT_SIDE_BY_SIDE = 20;\n/** Preserves narrow-terminal protection in stacked layout. */\nexport const MAX_PREVIEW_HEIGHT_STACKED = 15;\nexport const NO_PREVIEW_TEXT = \"No preview available\";\n/** 1 blank separator + 1 affordance text row reserved when `hasAnyPreview` (height stability of the affordance row's offset relative to the box). */\nexport const NOTES_AFFORDANCE_OVERHEAD = 2;\n\n/**\n * Per-question cache for rendered markdown previews. Width-keyed: switching the\n * inner width invalidates every cached `Markdown`'s render output (pi-tui's\n * `Markdown.render(width)` re-wraps when width changes).\n *\n * Replaces the inline `previewTexts`, `markdownCache`, `cachedWidth` triple from\n * the previous monolithic `preview-pane.ts`. One Markdown per option, lazy on\n * first request, never re-constructed — count semantics frozen by tests.\n */\nexport class MarkdownContentCache {\n\tprivate readonly previewTexts: Map<number, string>;\n\tprivate readonly markdownCache: Map<number, Markdown>;\n\tprivate cachedWidth: number | undefined;\n\tprivate readonly theme: Theme;\n\tprivate readonly markdownTheme: MarkdownTheme;\n\n\tconstructor(question: QuestionData, theme: Theme, markdownTheme: MarkdownTheme) {\n\t\tthis.theme = theme;\n\t\tthis.markdownTheme = markdownTheme;\n\t\tthis.previewTexts = new Map();\n\t\tfor (let i = 0; i < question.options.length; i++) {\n\t\t\tconst raw = question.options[i]?.preview;\n\t\t\tif (raw && raw.length > 0) this.previewTexts.set(i, raw);\n\t\t}\n\t\tthis.markdownCache = new Map();\n\t}\n\n\thasAnyPreview(): boolean {\n\t\treturn this.previewTexts.size > 0;\n\t}\n\n\thas(optionIndex: number): boolean {\n\t\treturn this.previewTexts.has(optionIndex);\n\t}\n\n\t/**\n\t * Compute the body lines for a given option at a given inner width. Width changes\n\t * invalidate the per-Markdown render cache.\n\t */\n\tbodyFor(optionIndex: number, innerWidth: number): string[] {\n\t\tif (this.cachedWidth !== innerWidth) {\n\t\t\tfor (const md of this.markdownCache.values()) md.invalidate();\n\t\t\tthis.cachedWidth = innerWidth;\n\t\t}\n\t\tconst text = this.previewTexts.get(optionIndex);\n\t\tif (!text) {\n\t\t\tconst placeholder = this.theme.fg(\"dim\", NO_PREVIEW_TEXT);\n\t\t\tconst pad = Math.max(0, innerWidth - visibleWidth(placeholder));\n\t\t\treturn [placeholder + \" \".repeat(pad)];\n\t\t}\n\t\tlet md = this.markdownCache.get(optionIndex);\n\t\tif (!md) {\n\t\t\tmd = new Markdown(text, 0, 0, this.markdownTheme);\n\t\t\tthis.markdownCache.set(optionIndex, md);\n\t\t}\n\t\treturn stripFenceMarkers(md.render(innerWidth));\n\t}\n\n\tinvalidate(): void {\n\t\tfor (const md of this.markdownCache.values()) md.invalidate();\n\t\tthis.cachedWidth = undefined;\n\t}\n}\n"]}
@@ -16,6 +16,8 @@ export const NOTES_AFFORDANCE_TEXT = "Notes: n add notes";
16
16
  * count is height-stable across affordance-state transitions.
17
17
  */
18
18
  export class PreviewBlockRenderer {
19
+ theme;
20
+ cache;
19
21
  constructor(config) {
20
22
  this.theme = config.theme;
21
23
  this.cache = new MarkdownContentCache(config.question, config.theme, config.markdownTheme);
@@ -1 +1 @@
1
- {"version":3,"file":"preview-block-renderer.js","sourceRoot":"","sources":["../../../../../../../src/core/tools/ask-user-question/view/components/preview/preview-block-renderer.ts"],"names":[],"mappings":"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","sourcesContent":["import type { MarkdownTheme } from \"@earendil-works/pi-tui\";\nimport type { Theme } from \"../../../../../../modes/interactive/theme/theme.js\";\nimport type { QuestionData } from \"../../../tool/types.js\";\nimport {\n\tMAX_PREVIEW_HEIGHT_SIDE_BY_SIDE,\n\tMAX_PREVIEW_HEIGHT_STACKED,\n\tMarkdownContentCache,\n\tNOTES_AFFORDANCE_OVERHEAD,\n} from \"./markdown-content-cache.ts\";\nimport {\n\tBORDER_HORIZONTAL_OVERHEAD,\n\tBORDER_INNER_PADDING_HORIZONTAL,\n\tBORDER_VERTICAL_OVERHEAD,\n\tcomputeBoxDimensions,\n\trenderBorderedBox,\n} from \"./preview-box-renderer.ts\";\nimport type { PreviewLayoutMode } from \"./preview-layout-decider.ts\";\n\n/**\n * Affordance text shown below the bordered preview when focused on a preview-bearing option.\n * Re-exported by `preview-pane.ts` for the existing test surface.\n */\nexport const NOTES_AFFORDANCE_TEXT = \"Notes: n add notes\";\n\nexport interface PreviewBlockRendererConfig {\n\tquestion: QuestionData;\n\ttheme: Theme;\n\tmarkdownTheme: MarkdownTheme;\n}\n\n/**\n * Renders the bordered markdown preview block for a single question (one block per render call,\n * for the option at `optionIndex`). Owns a per-question `MarkdownContentCache`.\n *\n * NOT a `Component` — pure render-and-measure helper consumed by `PreviewPane`. The layout mode\n * is threaded as an explicit param (never re-derived from column width post-split).\n *\n * The affordance row is always emitted (visually empty when gated) so the preview block's row\n * count is height-stable across affordance-state transitions.\n */\nexport class PreviewBlockRenderer {\n\tprivate readonly theme: Theme;\n\tprivate readonly cache: MarkdownContentCache;\n\n\tconstructor(config: PreviewBlockRendererConfig) {\n\t\tthis.theme = config.theme;\n\t\tthis.cache = new MarkdownContentCache(config.question, config.theme, config.markdownTheme);\n\t}\n\n\thasAnyPreview(): boolean {\n\t\treturn this.cache.hasAnyPreview();\n\t}\n\n\thas(optionIndex: number): boolean {\n\t\treturn this.cache.has(optionIndex);\n\t}\n\n\tinvalidate(): void {\n\t\tthis.cache.invalidate();\n\t}\n\n\t/**\n\t * Height contribution of the preview block: `BORDER_VERTICAL_OVERHEAD + contentRows +\n\t * NOTES_AFFORDANCE_OVERHEAD`. Always returns the same value as `renderBlock(...).length`\n\t * — the affordance overhead is constant, not gated by `focused`/`notesVisible`.\n\t */\n\tblockHeight(width: number, optionIndex: number, mode: PreviewLayoutMode): number {\n\t\tconst cap = mode === \"side-by-side\" ? MAX_PREVIEW_HEIGHT_SIDE_BY_SIDE : MAX_PREVIEW_HEIGHT_STACKED;\n\t\tconst contentBudget = Math.max(1, cap - BORDER_VERTICAL_OVERHEAD - NOTES_AFFORDANCE_OVERHEAD);\n\t\tconst innerWidth = Math.max(1, width - BORDER_HORIZONTAL_OVERHEAD - 2 * BORDER_INNER_PADDING_HORIZONTAL);\n\t\tconst rawRows = this.cache.bodyFor(optionIndex, innerWidth).length;\n\t\tconst contentRows = Math.min(rawRows, contentBudget);\n\t\treturn BORDER_VERTICAL_OVERHEAD + contentRows + NOTES_AFFORDANCE_OVERHEAD;\n\t}\n\n\t/**\n\t * Render the full preview block at `width`: bordered box + blank separator + affordance row.\n\t * `focused` and `notesVisible` together gate the affordance text (visible only when the\n\t * focused option carries a preview AND notes mode is inactive). The affordance row is ALWAYS\n\t * emitted (as an empty string when gated) so the row count is invariant.\n\t */\n\trenderBlock(\n\t\twidth: number,\n\t\toptionIndex: number,\n\t\tmode: PreviewLayoutMode,\n\t\tfocused: boolean,\n\t\tnotesVisible: boolean,\n\t): string[] {\n\t\tconst cap = mode === \"side-by-side\" ? MAX_PREVIEW_HEIGHT_SIDE_BY_SIDE : MAX_PREVIEW_HEIGHT_STACKED;\n\t\tconst contentBudget = Math.max(1, cap - BORDER_VERTICAL_OVERHEAD - NOTES_AFFORDANCE_OVERHEAD);\n\t\tconst maxInnerWidth = Math.max(1, width - BORDER_HORIZONTAL_OVERHEAD - 2 * BORDER_INNER_PADDING_HORIZONTAL);\n\n\t\tconst raw = this.cache.bodyFor(optionIndex, maxInnerWidth);\n\t\tconst truncated = raw.length > contentBudget;\n\t\tconst hidden = truncated ? raw.length - contentBudget : 0;\n\t\tconst contentLines = truncated ? raw.slice(0, contentBudget) : raw;\n\n\t\tconst { boxWidth } = computeBoxDimensions(contentLines, maxInnerWidth);\n\t\tconst colorFn = (s: string) => this.theme.fg(\"accent\", s);\n\t\tconst boxedLines = renderBorderedBox(contentLines, boxWidth, colorFn, hidden);\n\n\t\tconst showAffordance = focused && !notesVisible && this.cache.has(optionIndex);\n\t\tconst affordance = showAffordance ? this.theme.fg(\"muted\", NOTES_AFFORDANCE_TEXT) : \"\";\n\t\treturn [...boxedLines, \"\", affordance];\n\t}\n}\n"]}
1
+ {"version":3,"file":"preview-block-renderer.js","sourceRoot":"","sources":["../../../../../../../src/core/tools/ask-user-question/view/components/preview/preview-block-renderer.ts"],"names":[],"mappings":"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","sourcesContent":["import type { MarkdownTheme } from \"@earendil-works/pi-tui\";\nimport type { Theme } from \"../../../../../../modes/interactive/theme/theme.js\";\nimport type { QuestionData } from \"../../../tool/types.js\";\nimport {\n\tMAX_PREVIEW_HEIGHT_SIDE_BY_SIDE,\n\tMAX_PREVIEW_HEIGHT_STACKED,\n\tMarkdownContentCache,\n\tNOTES_AFFORDANCE_OVERHEAD,\n} from \"./markdown-content-cache.ts\";\nimport {\n\tBORDER_HORIZONTAL_OVERHEAD,\n\tBORDER_INNER_PADDING_HORIZONTAL,\n\tBORDER_VERTICAL_OVERHEAD,\n\tcomputeBoxDimensions,\n\trenderBorderedBox,\n} from \"./preview-box-renderer.ts\";\nimport type { PreviewLayoutMode } from \"./preview-layout-decider.ts\";\n\n/**\n * Affordance text shown below the bordered preview when focused on a preview-bearing option.\n * Re-exported by `preview-pane.ts` for the existing test surface.\n */\nexport const NOTES_AFFORDANCE_TEXT = \"Notes: n add notes\";\n\nexport interface PreviewBlockRendererConfig {\n\tquestion: QuestionData;\n\ttheme: Theme;\n\tmarkdownTheme: MarkdownTheme;\n}\n\n/**\n * Renders the bordered markdown preview block for a single question (one block per render call,\n * for the option at `optionIndex`). Owns a per-question `MarkdownContentCache`.\n *\n * NOT a `Component` — pure render-and-measure helper consumed by `PreviewPane`. The layout mode\n * is threaded as an explicit param (never re-derived from column width post-split).\n *\n * The affordance row is always emitted (visually empty when gated) so the preview block's row\n * count is height-stable across affordance-state transitions.\n */\nexport class PreviewBlockRenderer {\n\tprivate readonly theme: Theme;\n\tprivate readonly cache: MarkdownContentCache;\n\n\tconstructor(config: PreviewBlockRendererConfig) {\n\t\tthis.theme = config.theme;\n\t\tthis.cache = new MarkdownContentCache(config.question, config.theme, config.markdownTheme);\n\t}\n\n\thasAnyPreview(): boolean {\n\t\treturn this.cache.hasAnyPreview();\n\t}\n\n\thas(optionIndex: number): boolean {\n\t\treturn this.cache.has(optionIndex);\n\t}\n\n\tinvalidate(): void {\n\t\tthis.cache.invalidate();\n\t}\n\n\t/**\n\t * Height contribution of the preview block: `BORDER_VERTICAL_OVERHEAD + contentRows +\n\t * NOTES_AFFORDANCE_OVERHEAD`. Always returns the same value as `renderBlock(...).length`\n\t * — the affordance overhead is constant, not gated by `focused`/`notesVisible`.\n\t */\n\tblockHeight(width: number, optionIndex: number, mode: PreviewLayoutMode): number {\n\t\tconst cap = mode === \"side-by-side\" ? MAX_PREVIEW_HEIGHT_SIDE_BY_SIDE : MAX_PREVIEW_HEIGHT_STACKED;\n\t\tconst contentBudget = Math.max(1, cap - BORDER_VERTICAL_OVERHEAD - NOTES_AFFORDANCE_OVERHEAD);\n\t\tconst innerWidth = Math.max(1, width - BORDER_HORIZONTAL_OVERHEAD - 2 * BORDER_INNER_PADDING_HORIZONTAL);\n\t\tconst rawRows = this.cache.bodyFor(optionIndex, innerWidth).length;\n\t\tconst contentRows = Math.min(rawRows, contentBudget);\n\t\treturn BORDER_VERTICAL_OVERHEAD + contentRows + NOTES_AFFORDANCE_OVERHEAD;\n\t}\n\n\t/**\n\t * Render the full preview block at `width`: bordered box + blank separator + affordance row.\n\t * `focused` and `notesVisible` together gate the affordance text (visible only when the\n\t * focused option carries a preview AND notes mode is inactive). The affordance row is ALWAYS\n\t * emitted (as an empty string when gated) so the row count is invariant.\n\t */\n\trenderBlock(\n\t\twidth: number,\n\t\toptionIndex: number,\n\t\tmode: PreviewLayoutMode,\n\t\tfocused: boolean,\n\t\tnotesVisible: boolean,\n\t): string[] {\n\t\tconst cap = mode === \"side-by-side\" ? MAX_PREVIEW_HEIGHT_SIDE_BY_SIDE : MAX_PREVIEW_HEIGHT_STACKED;\n\t\tconst contentBudget = Math.max(1, cap - BORDER_VERTICAL_OVERHEAD - NOTES_AFFORDANCE_OVERHEAD);\n\t\tconst maxInnerWidth = Math.max(1, width - BORDER_HORIZONTAL_OVERHEAD - 2 * BORDER_INNER_PADDING_HORIZONTAL);\n\n\t\tconst raw = this.cache.bodyFor(optionIndex, maxInnerWidth);\n\t\tconst truncated = raw.length > contentBudget;\n\t\tconst hidden = truncated ? raw.length - contentBudget : 0;\n\t\tconst contentLines = truncated ? raw.slice(0, contentBudget) : raw;\n\n\t\tconst { boxWidth } = computeBoxDimensions(contentLines, maxInnerWidth);\n\t\tconst colorFn = (s: string) => this.theme.fg(\"accent\", s);\n\t\tconst boxedLines = renderBorderedBox(contentLines, boxWidth, colorFn, hidden);\n\n\t\tconst showAffordance = focused && !notesVisible && this.cache.has(optionIndex);\n\t\tconst affordance = showAffordance ? this.theme.fg(\"muted\", NOTES_AFFORDANCE_TEXT) : \"\";\n\t\treturn [...boxedLines, \"\", affordance];\n\t}\n}\n"]}
@@ -18,15 +18,20 @@ export { PREVIEW_COLUMN_GAP, PREVIEW_MIN_WIDTH, PREVIEW_PADDING_LEFT, STACKED_GA
18
18
  * re-derived).
19
19
  */
20
20
  export class PreviewPane {
21
+ question;
22
+ getTerminalWidth;
23
+ optionListView;
24
+ previewBlock;
25
+ props;
26
+ /**
27
+ * Cross-tab max left-width getter. Set exactly once by `buildQuestionnaire.injectGlobalLeftWidth`
28
+ * before any render. Initialized to a throwing sentinel so missing injection is a hard fail
29
+ * rather than a silent fallback to a magic constant — render is illegal until injected.
30
+ */
31
+ globalLeftWidth = () => {
32
+ throw new Error("PreviewPane.setGlobalLeftWidth must be called before render()");
33
+ };
21
34
  constructor(config) {
22
- /**
23
- * Cross-tab max left-width getter. Set exactly once by `buildQuestionnaire.injectGlobalLeftWidth`
24
- * before any render. Initialized to a throwing sentinel so missing injection is a hard fail
25
- * rather than a silent fallback to a magic constant — render is illegal until injected.
26
- */
27
- this.globalLeftWidth = () => {
28
- throw new Error("PreviewPane.setGlobalLeftWidth must be called before render()");
29
- };
30
35
  this.question = config.question;
31
36
  this.getTerminalWidth = config.getTerminalWidth;
32
37
  this.optionListView = config.optionListView;
@@ -1 +1 @@
1
- {"version":3,"file":"preview-pane.js","sourceRoot":"","sources":["../../../../../../../src/core/tools/ask-user-question/view/components/preview/preview-pane.ts"],"names":[],"mappings":"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","sourcesContent":["import { type Component, truncateToWidth, visibleWidth } from \"@earendil-works/pi-tui\";\nimport type { QuestionData } from \"../../../tool/types.js\";\nimport type { StatefulView } from \"../../stateful-view.ts\";\nimport type { OptionListView } from \"../option-list-view.ts\";\nimport type { PreviewBlockRenderer } from \"./preview-block-renderer.ts\";\nimport {\n\tbodyWidths,\n\tcolumnWidths,\n\tdecideLayout,\n\tPREVIEW_PADDING_LEFT,\n\ttype PreviewLayoutMode,\n\tSTACKED_GAP_ROWS,\n} from \"./preview-layout-decider.ts\";\n\n// ----- Re-exports for test imports — keep `./preview-pane.js` as the public surface -----\nexport {\n\tMAX_PREVIEW_HEIGHT_SIDE_BY_SIDE,\n\tMAX_PREVIEW_HEIGHT_STACKED,\n\tNO_PREVIEW_TEXT,\n\tNOTES_AFFORDANCE_OVERHEAD,\n} from \"./markdown-content-cache.ts\";\nexport { NOTES_AFFORDANCE_TEXT, PreviewBlockRenderer } from \"./preview-block-renderer.ts\";\nexport {\n\tBORDER_HORIZONTAL_OVERHEAD,\n\tBORDER_INNER_PADDING_HORIZONTAL,\n\tBORDER_VERTICAL_OVERHEAD,\n\tBOX_MIN_CONTENT_WIDTH,\n\trenderBorderedBox,\n\tstripFenceMarkers,\n} from \"./preview-box-renderer.ts\";\nexport {\n\tPREVIEW_COLUMN_GAP,\n\tPREVIEW_MIN_WIDTH,\n\tPREVIEW_PADDING_LEFT,\n\tSTACKED_GAP_ROWS,\n} from \"./preview-layout-decider.ts\";\n\n/**\n * Per-tick projection of PreviewPane state. Replaces the prior\n * `setNotesVisible(boolean)` sliver-setter and the sibling reads of\n * `optionListView.getSelectedIndex()` / `isFocused()`. The pane now reads\n * `selectedIndex` and `focused` from its own props — both derived from\n * canonical state via `selectPreviewPaneProps`. `OptionListView` and\n * `PreviewPane` see the same source of truth without the cross-component\n * live read.\n */\nexport interface PreviewPaneProps {\n\tnotesVisible: boolean;\n\tselectedIndex: number;\n\tfocused: boolean;\n}\n\nexport interface PreviewPaneConfig {\n\tquestion: QuestionData;\n\tgetTerminalWidth: () => number;\n\toptionListView: OptionListView;\n\tpreviewBlock: PreviewBlockRenderer;\n}\n\n/**\n * Thin layout composer. Receives `selectedIndex` / `focused` / `notesVisible`\n * via `setProps` per tick (computed by `selectPreviewPaneProps` from canonical\n * state). Delegates option-side rendering to `OptionListView` (which still\n * owns its render-time state — input buffer, confirmedIndex) and preview-side\n * rendering to `PreviewBlockRenderer` (which owns the markdown cache and\n * bordered-box composition).\n *\n * `naturalHeight` and `maxNaturalHeight` query both children's heights; `render`\n * combines them via `decideLayout` (mode threaded into both calls — never\n * re-derived).\n */\nexport class PreviewPane implements StatefulView<PreviewPaneProps>, Component {\n\tprivate readonly question: QuestionData;\n\tprivate readonly getTerminalWidth: () => number;\n\tprivate readonly optionListView: OptionListView;\n\tprivate readonly previewBlock: PreviewBlockRenderer;\n\tprivate props: PreviewPaneProps;\n\t/**\n\t * Cross-tab max left-width getter. Set exactly once by `buildQuestionnaire.injectGlobalLeftWidth`\n\t * before any render. Initialized to a throwing sentinel so missing injection is a hard fail\n\t * rather than a silent fallback to a magic constant — render is illegal until injected.\n\t */\n\tprivate globalLeftWidth: (paneWidth: number) => number = () => {\n\t\tthrow new Error(\"PreviewPane.setGlobalLeftWidth must be called before render()\");\n\t};\n\n\tconstructor(config: PreviewPaneConfig) {\n\t\tthis.question = config.question;\n\t\tthis.getTerminalWidth = config.getTerminalWidth;\n\t\tthis.optionListView = config.optionListView;\n\t\tthis.previewBlock = config.previewBlock;\n\t\tthis.props = { notesVisible: false, selectedIndex: 0, focused: false };\n\t}\n\n\tsetGlobalLeftWidth(getter: (paneWidth: number) => number): void {\n\t\tthis.globalLeftWidth = getter;\n\t}\n\n\tprivate getAdaptiveLeft(paneWidth: number): number {\n\t\treturn this.globalLeftWidth(paneWidth);\n\t}\n\n\tsetProps(props: PreviewPaneProps): void {\n\t\tthis.props = props;\n\t}\n\n\thandleInput(_data: string): void {}\n\n\tinvalidate(): void {\n\t\tthis.previewBlock.invalidate();\n\t\tthis.optionListView.invalidate();\n\t}\n\n\trender(width: number): string[] {\n\t\tif (this.question.multiSelect === true) return this.optionListView.render(width);\n\t\t// Spec: hide the preview pane entirely when no option carries a `preview`.\n\t\tif (!this.previewBlock.hasAnyPreview()) return this.optionListView.render(width);\n\n\t\tconst mode = decideLayout(this.getTerminalWidth(), width);\n\t\tif (mode === \"side-by-side\") return this.renderSideBySide(width, mode);\n\n\t\t// Stacked: options + blank gap + preview block.\n\t\treturn [\n\t\t\t...this.optionListView.render(width),\n\t\t\t...Array(STACKED_GAP_ROWS).fill(\"\"),\n\t\t\t...this.previewBlock.renderBlock(\n\t\t\t\twidth,\n\t\t\t\tthis.props.selectedIndex,\n\t\t\t\tmode,\n\t\t\t\tthis.props.focused,\n\t\t\t\tthis.props.notesVisible,\n\t\t\t),\n\t\t];\n\t}\n\n\tnaturalHeight(width: number): number {\n\t\tif (this.question.multiSelect === true) return this.optionListView.render(width).length;\n\t\tif (!this.previewBlock.hasAnyPreview()) return this.optionListView.render(width).length;\n\t\tconst mode = decideLayout(this.getTerminalWidth(), width);\n\t\tconst adaptiveLeft = this.getAdaptiveLeft(width);\n\t\tconst { optionsWidth, previewWidth } = bodyWidths(width, mode, adaptiveLeft);\n\t\tconst optionsHeight = this.optionListView.render(optionsWidth).length;\n\t\tconst previewBlockHeight = this.previewBlock.blockHeight(previewWidth, this.props.selectedIndex, mode);\n\t\tif (mode === \"side-by-side\") return Math.max(optionsHeight, previewBlockHeight);\n\t\treturn optionsHeight + STACKED_GAP_ROWS + previewBlockHeight;\n\t}\n\n\tmaxNaturalHeight(width: number): number {\n\t\tif (this.question.multiSelect === true) return this.optionListView.render(width).length;\n\t\tif (!this.previewBlock.hasAnyPreview()) return this.optionListView.render(width).length;\n\t\tconst mode = decideLayout(this.getTerminalWidth(), width);\n\t\tconst adaptiveLeft = this.getAdaptiveLeft(width);\n\t\tconst { optionsWidth, previewWidth } = bodyWidths(width, mode, adaptiveLeft);\n\t\tconst optionsHeight = this.optionListView.render(optionsWidth).length;\n\t\tlet maxPreviewBlock = 0;\n\t\tfor (let i = 0; i < this.question.options.length; i++) {\n\t\t\tconst h = this.previewBlock.blockHeight(previewWidth, i, mode);\n\t\t\tif (h > maxPreviewBlock) maxPreviewBlock = h;\n\t\t}\n\t\tif (mode === \"side-by-side\") return Math.max(optionsHeight, maxPreviewBlock);\n\t\treturn optionsHeight + STACKED_GAP_ROWS + maxPreviewBlock;\n\t}\n\n\tprivate renderSideBySide(width: number, mode: PreviewLayoutMode): string[] {\n\t\tconst adaptiveLeft = this.getAdaptiveLeft(width);\n\t\tconst { leftWidth, rightWidth, gap } = columnWidths(width, adaptiveLeft);\n\t\tconst leftLines = this.optionListView.render(leftWidth);\n\t\tconst rightLines = this.renderPaddedPreviewLines(rightWidth, mode);\n\t\tconst rows = Math.max(leftLines.length, rightLines.length);\n\t\tconst gapStr = \" \".repeat(gap);\n\t\tconst out: string[] = [];\n\t\tfor (let i = 0; i < rows; i++) {\n\t\t\tconst leftRaw = leftLines[i] ?? \"\";\n\t\t\tconst rightRaw = rightLines[i] ?? \"\";\n\t\t\tconst leftClamped = truncateToWidth(leftRaw, leftWidth, \"\");\n\t\t\tconst leftPad = \" \".repeat(Math.max(0, leftWidth - visibleWidth(leftClamped)));\n\t\t\tconst joined = `${leftClamped}${leftPad}${gapStr}${rightRaw}`;\n\t\t\tout.push(truncateToWidth(joined, width, \"\"));\n\t\t}\n\t\treturn out;\n\t}\n\n\tprivate renderPaddedPreviewLines(colWidth: number, mode: PreviewLayoutMode): string[] {\n\t\tconst inner = Math.max(1, colWidth - PREVIEW_PADDING_LEFT);\n\t\tconst contentLines = this.previewBlock.renderBlock(\n\t\t\tinner,\n\t\t\tthis.props.selectedIndex,\n\t\t\tmode,\n\t\t\tthis.props.focused,\n\t\t\tthis.props.notesVisible,\n\t\t);\n\t\tconst pad = \" \".repeat(PREVIEW_PADDING_LEFT);\n\t\treturn contentLines.map((l) => (l === \"\" ? \"\" : `${pad}${l}`));\n\t}\n}\n"]}
1
+ {"version":3,"file":"preview-pane.js","sourceRoot":"","sources":["../../../../../../../src/core/tools/ask-user-question/view/components/preview/preview-pane.ts"],"names":[],"mappings":"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","sourcesContent":["import { type Component, truncateToWidth, visibleWidth } from \"@earendil-works/pi-tui\";\nimport type { QuestionData } from \"../../../tool/types.js\";\nimport type { StatefulView } from \"../../stateful-view.ts\";\nimport type { OptionListView } from \"../option-list-view.ts\";\nimport type { PreviewBlockRenderer } from \"./preview-block-renderer.ts\";\nimport {\n\tbodyWidths,\n\tcolumnWidths,\n\tdecideLayout,\n\tPREVIEW_PADDING_LEFT,\n\ttype PreviewLayoutMode,\n\tSTACKED_GAP_ROWS,\n} from \"./preview-layout-decider.ts\";\n\n// ----- Re-exports for test imports — keep `./preview-pane.js` as the public surface -----\nexport {\n\tMAX_PREVIEW_HEIGHT_SIDE_BY_SIDE,\n\tMAX_PREVIEW_HEIGHT_STACKED,\n\tNO_PREVIEW_TEXT,\n\tNOTES_AFFORDANCE_OVERHEAD,\n} from \"./markdown-content-cache.ts\";\nexport { NOTES_AFFORDANCE_TEXT, PreviewBlockRenderer } from \"./preview-block-renderer.ts\";\nexport {\n\tBORDER_HORIZONTAL_OVERHEAD,\n\tBORDER_INNER_PADDING_HORIZONTAL,\n\tBORDER_VERTICAL_OVERHEAD,\n\tBOX_MIN_CONTENT_WIDTH,\n\trenderBorderedBox,\n\tstripFenceMarkers,\n} from \"./preview-box-renderer.ts\";\nexport {\n\tPREVIEW_COLUMN_GAP,\n\tPREVIEW_MIN_WIDTH,\n\tPREVIEW_PADDING_LEFT,\n\tSTACKED_GAP_ROWS,\n} from \"./preview-layout-decider.ts\";\n\n/**\n * Per-tick projection of PreviewPane state. Replaces the prior\n * `setNotesVisible(boolean)` sliver-setter and the sibling reads of\n * `optionListView.getSelectedIndex()` / `isFocused()`. The pane now reads\n * `selectedIndex` and `focused` from its own props — both derived from\n * canonical state via `selectPreviewPaneProps`. `OptionListView` and\n * `PreviewPane` see the same source of truth without the cross-component\n * live read.\n */\nexport interface PreviewPaneProps {\n\tnotesVisible: boolean;\n\tselectedIndex: number;\n\tfocused: boolean;\n}\n\nexport interface PreviewPaneConfig {\n\tquestion: QuestionData;\n\tgetTerminalWidth: () => number;\n\toptionListView: OptionListView;\n\tpreviewBlock: PreviewBlockRenderer;\n}\n\n/**\n * Thin layout composer. Receives `selectedIndex` / `focused` / `notesVisible`\n * via `setProps` per tick (computed by `selectPreviewPaneProps` from canonical\n * state). Delegates option-side rendering to `OptionListView` (which still\n * owns its render-time state — input buffer, confirmedIndex) and preview-side\n * rendering to `PreviewBlockRenderer` (which owns the markdown cache and\n * bordered-box composition).\n *\n * `naturalHeight` and `maxNaturalHeight` query both children's heights; `render`\n * combines them via `decideLayout` (mode threaded into both calls — never\n * re-derived).\n */\nexport class PreviewPane implements StatefulView<PreviewPaneProps>, Component {\n\tprivate readonly question: QuestionData;\n\tprivate readonly getTerminalWidth: () => number;\n\tprivate readonly optionListView: OptionListView;\n\tprivate readonly previewBlock: PreviewBlockRenderer;\n\tprivate props: PreviewPaneProps;\n\t/**\n\t * Cross-tab max left-width getter. Set exactly once by `buildQuestionnaire.injectGlobalLeftWidth`\n\t * before any render. Initialized to a throwing sentinel so missing injection is a hard fail\n\t * rather than a silent fallback to a magic constant — render is illegal until injected.\n\t */\n\tprivate globalLeftWidth: (paneWidth: number) => number = () => {\n\t\tthrow new Error(\"PreviewPane.setGlobalLeftWidth must be called before render()\");\n\t};\n\n\tconstructor(config: PreviewPaneConfig) {\n\t\tthis.question = config.question;\n\t\tthis.getTerminalWidth = config.getTerminalWidth;\n\t\tthis.optionListView = config.optionListView;\n\t\tthis.previewBlock = config.previewBlock;\n\t\tthis.props = { notesVisible: false, selectedIndex: 0, focused: false };\n\t}\n\n\tsetGlobalLeftWidth(getter: (paneWidth: number) => number): void {\n\t\tthis.globalLeftWidth = getter;\n\t}\n\n\tprivate getAdaptiveLeft(paneWidth: number): number {\n\t\treturn this.globalLeftWidth(paneWidth);\n\t}\n\n\tsetProps(props: PreviewPaneProps): void {\n\t\tthis.props = props;\n\t}\n\n\thandleInput(_data: string): void {}\n\n\tinvalidate(): void {\n\t\tthis.previewBlock.invalidate();\n\t\tthis.optionListView.invalidate();\n\t}\n\n\trender(width: number): string[] {\n\t\tif (this.question.multiSelect === true) return this.optionListView.render(width);\n\t\t// Spec: hide the preview pane entirely when no option carries a `preview`.\n\t\tif (!this.previewBlock.hasAnyPreview()) return this.optionListView.render(width);\n\n\t\tconst mode = decideLayout(this.getTerminalWidth(), width);\n\t\tif (mode === \"side-by-side\") return this.renderSideBySide(width, mode);\n\n\t\t// Stacked: options + blank gap + preview block.\n\t\treturn [\n\t\t\t...this.optionListView.render(width),\n\t\t\t...Array(STACKED_GAP_ROWS).fill(\"\"),\n\t\t\t...this.previewBlock.renderBlock(\n\t\t\t\twidth,\n\t\t\t\tthis.props.selectedIndex,\n\t\t\t\tmode,\n\t\t\t\tthis.props.focused,\n\t\t\t\tthis.props.notesVisible,\n\t\t\t),\n\t\t];\n\t}\n\n\tnaturalHeight(width: number): number {\n\t\tif (this.question.multiSelect === true) return this.optionListView.render(width).length;\n\t\tif (!this.previewBlock.hasAnyPreview()) return this.optionListView.render(width).length;\n\t\tconst mode = decideLayout(this.getTerminalWidth(), width);\n\t\tconst adaptiveLeft = this.getAdaptiveLeft(width);\n\t\tconst { optionsWidth, previewWidth } = bodyWidths(width, mode, adaptiveLeft);\n\t\tconst optionsHeight = this.optionListView.render(optionsWidth).length;\n\t\tconst previewBlockHeight = this.previewBlock.blockHeight(previewWidth, this.props.selectedIndex, mode);\n\t\tif (mode === \"side-by-side\") return Math.max(optionsHeight, previewBlockHeight);\n\t\treturn optionsHeight + STACKED_GAP_ROWS + previewBlockHeight;\n\t}\n\n\tmaxNaturalHeight(width: number): number {\n\t\tif (this.question.multiSelect === true) return this.optionListView.render(width).length;\n\t\tif (!this.previewBlock.hasAnyPreview()) return this.optionListView.render(width).length;\n\t\tconst mode = decideLayout(this.getTerminalWidth(), width);\n\t\tconst adaptiveLeft = this.getAdaptiveLeft(width);\n\t\tconst { optionsWidth, previewWidth } = bodyWidths(width, mode, adaptiveLeft);\n\t\tconst optionsHeight = this.optionListView.render(optionsWidth).length;\n\t\tlet maxPreviewBlock = 0;\n\t\tfor (let i = 0; i < this.question.options.length; i++) {\n\t\t\tconst h = this.previewBlock.blockHeight(previewWidth, i, mode);\n\t\t\tif (h > maxPreviewBlock) maxPreviewBlock = h;\n\t\t}\n\t\tif (mode === \"side-by-side\") return Math.max(optionsHeight, maxPreviewBlock);\n\t\treturn optionsHeight + STACKED_GAP_ROWS + maxPreviewBlock;\n\t}\n\n\tprivate renderSideBySide(width: number, mode: PreviewLayoutMode): string[] {\n\t\tconst adaptiveLeft = this.getAdaptiveLeft(width);\n\t\tconst { leftWidth, rightWidth, gap } = columnWidths(width, adaptiveLeft);\n\t\tconst leftLines = this.optionListView.render(leftWidth);\n\t\tconst rightLines = this.renderPaddedPreviewLines(rightWidth, mode);\n\t\tconst rows = Math.max(leftLines.length, rightLines.length);\n\t\tconst gapStr = \" \".repeat(gap);\n\t\tconst out: string[] = [];\n\t\tfor (let i = 0; i < rows; i++) {\n\t\t\tconst leftRaw = leftLines[i] ?? \"\";\n\t\t\tconst rightRaw = rightLines[i] ?? \"\";\n\t\t\tconst leftClamped = truncateToWidth(leftRaw, leftWidth, \"\");\n\t\t\tconst leftPad = \" \".repeat(Math.max(0, leftWidth - visibleWidth(leftClamped)));\n\t\t\tconst joined = `${leftClamped}${leftPad}${gapStr}${rightRaw}`;\n\t\t\tout.push(truncateToWidth(joined, width, \"\"));\n\t\t}\n\t\treturn out;\n\t}\n\n\tprivate renderPaddedPreviewLines(colWidth: number, mode: PreviewLayoutMode): string[] {\n\t\tconst inner = Math.max(1, colWidth - PREVIEW_PADDING_LEFT);\n\t\tconst contentLines = this.previewBlock.renderBlock(\n\t\t\tinner,\n\t\t\tthis.props.selectedIndex,\n\t\t\tmode,\n\t\t\tthis.props.focused,\n\t\t\tthis.props.notesVisible,\n\t\t);\n\t\tconst pad = \" \".repeat(PREVIEW_PADDING_LEFT);\n\t\treturn contentLines.map((l) => (l === \"\" ? \"\" : `${pad}${l}`));\n\t}\n}\n"]}
@@ -17,6 +17,7 @@ export const CANCEL_LABEL = "Cancel";
17
17
  * re-rendering.
18
18
  */
19
19
  export class SubmitPicker {
20
+ props;
20
21
  constructor(theme) {
21
22
  this.theme = theme;
22
23
  this.props = { rows: [{ active: false }, { active: false }] };
@@ -1 +1 @@
1
- {"version":3,"file":"submit-picker.js","sourceRoot":"","sources":["../../../../../../src/core/tools/ask-user-question/view/components/submit-picker.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AAEzD,OAAO,EAAE,yBAAyB,EAAE,MAAM,oCAAoC,CAAC;AAG/E,MAAM,cAAc,GAAG,IAAI,CAAC;AAC5B,MAAM,gBAAgB,GAAG,IAAI,CAAC;AAC9B,MAAM,gBAAgB,GAAG,IAAI,CAAC;AAE9B,MAAM,CAAC,MAAM,YAAY,GAAG,gBAAgB,CAAC;AAC7C,MAAM,CAAC,MAAM,YAAY,GAAG,QAAQ,CAAC;AAYrC;;;;;;;;;;GAUG;AACH,MAAM,OAAO,YAAY;IAKxB,YAAY,KAAY;QACvB,IAAI,CAAC,KAAK,GAAG,KAAK,CAAC;QACnB,IAAI,CAAC,KAAK,GAAG,EAAE,IAAI,EAAE,CAAC,EAAE,MAAM,EAAE,KAAK,EAAE,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC,EAAE,CAAC;IAC/D,CAAC;IAED,QAAQ,CAAC,KAAwB;QAChC,IAAI,CAAC,KAAK,GAAG,KAAK,CAAC;IACpB,CAAC;IAED,WAAW,CAAC,KAAa,IAAS,CAAC;IAEnC,UAAU,KAAU,CAAC;IAErB,aAAa,CAAC,MAAc;QAC3B,OAAO,CAAC,CAAC;IACV,CAAC;IAED,MAAM,CAAC,KAAa;QACnB,MAAM,KAAK,GAAa,EAAE,CAAC;QAC3B,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC;YAC5B,MAAM,IAAI,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,YAAY,CAAC;YACnD,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,MAAM,IAAI,KAAK,CAAC;YACnD,MAAM,OAAO,GAAG,MAAM,CAAC,CAAC,CAAC,cAAc,CAAC,CAAC,CAAC,gBAAgB,CAAC;YAC3D,MAAM,MAAM,GAAG,GAAG,CAAC,GAAG,CAAC,GAAG,gBAAgB,EAAE,CAAC;YAC7C,MAAM,KAAK,GAAG,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,QAAQ,EAAE,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;YACpG,MAAM,QAAQ,GAAG,eAAe,CAAC,GAAG,OAAO,GAAG,MAAM,GAAG,KAAK,EAAE,EAAE,KAAK,EAAE,EAAE,CAAC,CAAC;YAC3E,KAAK,CAAC,IAAI,CAAC,GAAG,QAAQ,GAAG,MAAM,CAAC,CAAC,CAAC,yBAAyB,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;QACrE,CAAC;QACD,OAAO,KAAK,CAAC;IACd,CAAC;CACD","sourcesContent":["import { truncateToWidth } from \"@earendil-works/pi-tui\";\nimport type { Theme } from \"../../../../../modes/interactive/theme/theme.js\";\nimport { OVERLAY_ACTIVE_ROW_MARKER } from \"../../../../extensions/ui-types.js\";\nimport type { StatefulView } from \"../stateful-view.ts\";\n\nconst ACTIVE_POINTER = \"❯ \";\nconst INACTIVE_POINTER = \" \";\nconst NUMBER_SEPARATOR = \". \";\n\nexport const SUBMIT_LABEL = \"Submit answers\";\nexport const CANCEL_LABEL = \"Cancel\";\n\n/**\n * Per-tick projection of SubmitPicker state. The picker is a fixed 2-row\n * structure (Submit / Cancel) — labels are static, only the active marker\n * varies per tick. `selectSubmitPickerProps` precomputes `active` per row.\n */\nexport interface SubmitPickerProps {\n\t/** Per-row active flag. Length always 2: row 0 = Submit, row 1 = Cancel. */\n\trows: ReadonlyArray<{ active: boolean }>;\n}\n\n/**\n * Static 2-row picker rendered on the Submit Tab. Row 0 = \"Submit answers\", Row 1 = \"Cancel\".\n *\n * - Active pointer (❯) follows `props.rows[i].active` per tick.\n * - Both rows render in normal style at all times — D1 (revised) allows partial submission,\n * so Submit is never dimmed or visually marked as unselectable. The warning header in\n * `buildSubmitContainer` is the sole signal of incompleteness.\n * - `naturalHeight(width)` is state-INDEPENDENT and returns a constant 2, so the\n * chrome-mirror layout in `buildSubmitContainer` can subtract a fixed 2 lines without\n * re-rendering.\n */\nexport class SubmitPicker implements StatefulView<SubmitPickerProps> {\n\tprivate props: SubmitPickerProps;\n\n\tprivate declare readonly theme: Theme;\n\n\tconstructor(theme: Theme) {\n\t\tthis.theme = theme;\n\t\tthis.props = { rows: [{ active: false }, { active: false }] };\n\t}\n\n\tsetProps(props: SubmitPickerProps): void {\n\t\tthis.props = props;\n\t}\n\n\thandleInput(_data: string): void {}\n\n\tinvalidate(): void {}\n\n\tnaturalHeight(_width: number): number {\n\t\treturn 2;\n\t}\n\n\trender(width: number): string[] {\n\t\tconst lines: string[] = [];\n\t\tfor (let i = 0; i < 2; i++) {\n\t\t\tconst text = i === 0 ? SUBMIT_LABEL : CANCEL_LABEL;\n\t\t\tconst active = this.props.rows[i]?.active ?? false;\n\t\t\tconst pointer = active ? ACTIVE_POINTER : INACTIVE_POINTER;\n\t\t\tconst number = `${i + 1}${NUMBER_SEPARATOR}`;\n\t\t\tconst label = active ? this.theme.fg(\"accent\", this.theme.bold(text)) : this.theme.fg(\"text\", text);\n\t\t\tconst rendered = truncateToWidth(`${pointer}${number}${label}`, width, \"\");\n\t\t\tlines.push(`${rendered}${active ? OVERLAY_ACTIVE_ROW_MARKER : \"\"}`);\n\t\t}\n\t\treturn lines;\n\t}\n}\n"]}
1
+ {"version":3,"file":"submit-picker.js","sourceRoot":"","sources":["../../../../../../src/core/tools/ask-user-question/view/components/submit-picker.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AAEzD,OAAO,EAAE,yBAAyB,EAAE,MAAM,oCAAoC,CAAC;AAG/E,MAAM,cAAc,GAAG,IAAI,CAAC;AAC5B,MAAM,gBAAgB,GAAG,IAAI,CAAC;AAC9B,MAAM,gBAAgB,GAAG,IAAI,CAAC;AAE9B,MAAM,CAAC,MAAM,YAAY,GAAG,gBAAgB,CAAC;AAC7C,MAAM,CAAC,MAAM,YAAY,GAAG,QAAQ,CAAC;AAYrC;;;;;;;;;;GAUG;AACH,MAAM,OAAO,YAAY;IAChB,KAAK,CAAoB;IAIjC,YAAY,KAAY;QACvB,IAAI,CAAC,KAAK,GAAG,KAAK,CAAC;QACnB,IAAI,CAAC,KAAK,GAAG,EAAE,IAAI,EAAE,CAAC,EAAE,MAAM,EAAE,KAAK,EAAE,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC,EAAE,CAAC;IAC/D,CAAC;IAED,QAAQ,CAAC,KAAwB;QAChC,IAAI,CAAC,KAAK,GAAG,KAAK,CAAC;IACpB,CAAC;IAED,WAAW,CAAC,KAAa,IAAS,CAAC;IAEnC,UAAU,KAAU,CAAC;IAErB,aAAa,CAAC,MAAc;QAC3B,OAAO,CAAC,CAAC;IACV,CAAC;IAED,MAAM,CAAC,KAAa;QACnB,MAAM,KAAK,GAAa,EAAE,CAAC;QAC3B,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC;YAC5B,MAAM,IAAI,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,YAAY,CAAC;YACnD,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,MAAM,IAAI,KAAK,CAAC;YACnD,MAAM,OAAO,GAAG,MAAM,CAAC,CAAC,CAAC,cAAc,CAAC,CAAC,CAAC,gBAAgB,CAAC;YAC3D,MAAM,MAAM,GAAG,GAAG,CAAC,GAAG,CAAC,GAAG,gBAAgB,EAAE,CAAC;YAC7C,MAAM,KAAK,GAAG,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,QAAQ,EAAE,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;YACpG,MAAM,QAAQ,GAAG,eAAe,CAAC,GAAG,OAAO,GAAG,MAAM,GAAG,KAAK,EAAE,EAAE,KAAK,EAAE,EAAE,CAAC,CAAC;YAC3E,KAAK,CAAC,IAAI,CAAC,GAAG,QAAQ,GAAG,MAAM,CAAC,CAAC,CAAC,yBAAyB,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;QACrE,CAAC;QACD,OAAO,KAAK,CAAC;IACd,CAAC;CACD","sourcesContent":["import { truncateToWidth } from \"@earendil-works/pi-tui\";\nimport type { Theme } from \"../../../../../modes/interactive/theme/theme.js\";\nimport { OVERLAY_ACTIVE_ROW_MARKER } from \"../../../../extensions/ui-types.js\";\nimport type { StatefulView } from \"../stateful-view.ts\";\n\nconst ACTIVE_POINTER = \"❯ \";\nconst INACTIVE_POINTER = \" \";\nconst NUMBER_SEPARATOR = \". \";\n\nexport const SUBMIT_LABEL = \"Submit answers\";\nexport const CANCEL_LABEL = \"Cancel\";\n\n/**\n * Per-tick projection of SubmitPicker state. The picker is a fixed 2-row\n * structure (Submit / Cancel) — labels are static, only the active marker\n * varies per tick. `selectSubmitPickerProps` precomputes `active` per row.\n */\nexport interface SubmitPickerProps {\n\t/** Per-row active flag. Length always 2: row 0 = Submit, row 1 = Cancel. */\n\trows: ReadonlyArray<{ active: boolean }>;\n}\n\n/**\n * Static 2-row picker rendered on the Submit Tab. Row 0 = \"Submit answers\", Row 1 = \"Cancel\".\n *\n * - Active pointer (❯) follows `props.rows[i].active` per tick.\n * - Both rows render in normal style at all times — D1 (revised) allows partial submission,\n * so Submit is never dimmed or visually marked as unselectable. The warning header in\n * `buildSubmitContainer` is the sole signal of incompleteness.\n * - `naturalHeight(width)` is state-INDEPENDENT and returns a constant 2, so the\n * chrome-mirror layout in `buildSubmitContainer` can subtract a fixed 2 lines without\n * re-rendering.\n */\nexport class SubmitPicker implements StatefulView<SubmitPickerProps> {\n\tprivate props: SubmitPickerProps;\n\n\tprivate declare readonly theme: Theme;\n\n\tconstructor(theme: Theme) {\n\t\tthis.theme = theme;\n\t\tthis.props = { rows: [{ active: false }, { active: false }] };\n\t}\n\n\tsetProps(props: SubmitPickerProps): void {\n\t\tthis.props = props;\n\t}\n\n\thandleInput(_data: string): void {}\n\n\tinvalidate(): void {}\n\n\tnaturalHeight(_width: number): number {\n\t\treturn 2;\n\t}\n\n\trender(width: number): string[] {\n\t\tconst lines: string[] = [];\n\t\tfor (let i = 0; i < 2; i++) {\n\t\t\tconst text = i === 0 ? SUBMIT_LABEL : CANCEL_LABEL;\n\t\t\tconst active = this.props.rows[i]?.active ?? false;\n\t\t\tconst pointer = active ? ACTIVE_POINTER : INACTIVE_POINTER;\n\t\t\tconst number = `${i + 1}${NUMBER_SEPARATOR}`;\n\t\t\tconst label = active ? this.theme.fg(\"accent\", this.theme.bold(text)) : this.theme.fg(\"text\", text);\n\t\t\tconst rendered = truncateToWidth(`${pointer}${number}${label}`, width, \"\");\n\t\t\tlines.push(`${rendered}${active ? OVERLAY_ACTIVE_ROW_MARKER : \"\"}`);\n\t\t}\n\t\treturn lines;\n\t}\n}\n"]}
@@ -1,5 +1,6 @@
1
1
  import { truncateToWidth } from "@earendil-works/pi-tui";
2
2
  export class TabBar {
3
+ props;
3
4
  constructor(theme) {
4
5
  this.theme = theme;
5
6
  this.props = { tabs: [], submit: { active: false, allAnswered: false } };
@@ -1 +1 @@
1
- {"version":3,"file":"tab-bar.js","sourceRoot":"","sources":["../../../../../../src/core/tools/ask-user-question/view/components/tab-bar.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AAkBzD,MAAM,OAAO,MAAM;IAKlB,YAAY,KAAY;QACvB,IAAI,CAAC,KAAK,GAAG,KAAK,CAAC;QACnB,IAAI,CAAC,KAAK,GAAG,EAAE,IAAI,EAAE,EAAE,EAAE,MAAM,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,WAAW,EAAE,KAAK,EAAE,EAAE,CAAC;IAC1E,CAAC;IAED,QAAQ,CAAC,KAAkB;QAC1B,IAAI,CAAC,KAAK,GAAG,KAAK,CAAC;IACpB,CAAC;IAED,WAAW,CAAC,KAAa,IAAS,CAAC;IAEnC,UAAU,KAAU,CAAC;IAErB,MAAM,CAAC,KAAa;QACnB,MAAM,MAAM,GAAa,CAAC,KAAK,CAAC,CAAC;QAEjC,KAAK,MAAM,GAAG,IAAI,IAAI,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC;YACnC,MAAM,GAAG,GAAG,GAAG,CAAC,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC;YACrC,MAAM,MAAM,GAAG,IAAI,GAAG,IAAI,GAAG,CAAC,KAAK,GAAG,CAAC;YACvC,MAAM,MAAM,GAAG,GAAG,CAAC,MAAM;gBACxB,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,YAAY,EAAE,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;gBAC5D,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;YAC7D,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;YACpB,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QAClB,CAAC;QAED,MAAM,UAAU,GAAG,YAAY,CAAC;QAChC,MAAM,YAAY,GAAG,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,MAAM;YAC5C,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,YAAY,EAAE,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC;YAChE,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,WAAW,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,KAAK,EAAE,UAAU,CAAC,CAAC;QAChF,MAAM,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC;QAC1B,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAElB,MAAM,OAAO,GAAG,eAAe,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC,EAAE,KAAK,EAAE,EAAE,CAAC,CAAC;QAC5D,OAAO,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC;IACtB,CAAC;CACD","sourcesContent":["import { truncateToWidth } from \"@earendil-works/pi-tui\";\nimport type { Theme } from \"../../../../../modes/interactive/theme/theme.js\";\nimport type { StatefulView } from \"../stateful-view.ts\";\n\n/**\n * Per-tick projection of TabBar state. The selector\n * (`selectTabBarProps`) hoists every render-time derivation\n * (`allAnswered`, `answered`, `isActive`, `submitActive`) into props so\n * `render()` is pure styling. Replaces the prior `setConfig(TabBarConfig)`\n * snowflake and the inline `+ 1` magic at `props-adapter.ts:127`.\n */\nexport interface TabBarProps {\n\t/** One per author-defined question, in order. */\n\ttabs: ReadonlyArray<{ label: string; answered: boolean; active: boolean }>;\n\t/** Submit-tab state. `allAnswered` drives the success/dim color picker. */\n\tsubmit: { active: boolean; allAnswered: boolean };\n}\n\nexport class TabBar implements StatefulView<TabBarProps> {\n\tprivate props: TabBarProps;\n\n\tprivate declare readonly theme: Theme;\n\n\tconstructor(theme: Theme) {\n\t\tthis.theme = theme;\n\t\tthis.props = { tabs: [], submit: { active: false, allAnswered: false } };\n\t}\n\n\tsetProps(props: TabBarProps): void {\n\t\tthis.props = props;\n\t}\n\n\thandleInput(_data: string): void {}\n\n\tinvalidate(): void {}\n\n\trender(width: number): string[] {\n\t\tconst pieces: string[] = [\" ← \"];\n\n\t\tfor (const tab of this.props.tabs) {\n\t\t\tconst box = tab.answered ? \"■\" : \"□\";\n\t\t\tconst rawSeg = ` ${box} ${tab.label} `;\n\t\t\tconst styled = tab.active\n\t\t\t\t? this.theme.bg(\"selectedBg\", this.theme.fg(\"text\", rawSeg))\n\t\t\t\t: this.theme.fg(tab.answered ? \"success\" : \"muted\", rawSeg);\n\t\t\tpieces.push(styled);\n\t\t\tpieces.push(\" \");\n\t\t}\n\n\t\tconst submitText = \" ✓ Submit \";\n\t\tconst submitStyled = this.props.submit.active\n\t\t\t? this.theme.bg(\"selectedBg\", this.theme.fg(\"text\", submitText))\n\t\t\t: this.theme.fg(this.props.submit.allAnswered ? \"success\" : \"dim\", submitText);\n\t\tpieces.push(submitStyled);\n\t\tpieces.push(\" →\");\n\n\t\tconst tabLine = truncateToWidth(pieces.join(\"\"), width, \"\");\n\t\treturn [tabLine, \"\"];\n\t}\n}\n"]}
1
+ {"version":3,"file":"tab-bar.js","sourceRoot":"","sources":["../../../../../../src/core/tools/ask-user-question/view/components/tab-bar.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AAkBzD,MAAM,OAAO,MAAM;IACV,KAAK,CAAc;IAI3B,YAAY,KAAY;QACvB,IAAI,CAAC,KAAK,GAAG,KAAK,CAAC;QACnB,IAAI,CAAC,KAAK,GAAG,EAAE,IAAI,EAAE,EAAE,EAAE,MAAM,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,WAAW,EAAE,KAAK,EAAE,EAAE,CAAC;IAC1E,CAAC;IAED,QAAQ,CAAC,KAAkB;QAC1B,IAAI,CAAC,KAAK,GAAG,KAAK,CAAC;IACpB,CAAC;IAED,WAAW,CAAC,KAAa,IAAS,CAAC;IAEnC,UAAU,KAAU,CAAC;IAErB,MAAM,CAAC,KAAa;QACnB,MAAM,MAAM,GAAa,CAAC,KAAK,CAAC,CAAC;QAEjC,KAAK,MAAM,GAAG,IAAI,IAAI,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC;YACnC,MAAM,GAAG,GAAG,GAAG,CAAC,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC;YACrC,MAAM,MAAM,GAAG,IAAI,GAAG,IAAI,GAAG,CAAC,KAAK,GAAG,CAAC;YACvC,MAAM,MAAM,GAAG,GAAG,CAAC,MAAM;gBACxB,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,YAAY,EAAE,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;gBAC5D,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;YAC7D,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;YACpB,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QAClB,CAAC;QAED,MAAM,UAAU,GAAG,YAAY,CAAC;QAChC,MAAM,YAAY,GAAG,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,MAAM;YAC5C,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,YAAY,EAAE,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC;YAChE,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,WAAW,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,KAAK,EAAE,UAAU,CAAC,CAAC;QAChF,MAAM,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC;QAC1B,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAElB,MAAM,OAAO,GAAG,eAAe,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC,EAAE,KAAK,EAAE,EAAE,CAAC,CAAC;QAC5D,OAAO,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC;IACtB,CAAC;CACD","sourcesContent":["import { truncateToWidth } from \"@earendil-works/pi-tui\";\nimport type { Theme } from \"../../../../../modes/interactive/theme/theme.js\";\nimport type { StatefulView } from \"../stateful-view.ts\";\n\n/**\n * Per-tick projection of TabBar state. The selector\n * (`selectTabBarProps`) hoists every render-time derivation\n * (`allAnswered`, `answered`, `isActive`, `submitActive`) into props so\n * `render()` is pure styling. Replaces the prior `setConfig(TabBarConfig)`\n * snowflake and the inline `+ 1` magic at `props-adapter.ts:127`.\n */\nexport interface TabBarProps {\n\t/** One per author-defined question, in order. */\n\ttabs: ReadonlyArray<{ label: string; answered: boolean; active: boolean }>;\n\t/** Submit-tab state. `allAnswered` drives the success/dim color picker. */\n\tsubmit: { active: boolean; allAnswered: boolean };\n}\n\nexport class TabBar implements StatefulView<TabBarProps> {\n\tprivate props: TabBarProps;\n\n\tprivate declare readonly theme: Theme;\n\n\tconstructor(theme: Theme) {\n\t\tthis.theme = theme;\n\t\tthis.props = { tabs: [], submit: { active: false, allAnswered: false } };\n\t}\n\n\tsetProps(props: TabBarProps): void {\n\t\tthis.props = props;\n\t}\n\n\thandleInput(_data: string): void {}\n\n\tinvalidate(): void {}\n\n\trender(width: number): string[] {\n\t\tconst pieces: string[] = [\" ← \"];\n\n\t\tfor (const tab of this.props.tabs) {\n\t\t\tconst box = tab.answered ? \"■\" : \"□\";\n\t\t\tconst rawSeg = ` ${box} ${tab.label} `;\n\t\t\tconst styled = tab.active\n\t\t\t\t? this.theme.bg(\"selectedBg\", this.theme.fg(\"text\", rawSeg))\n\t\t\t\t: this.theme.fg(tab.answered ? \"success\" : \"muted\", rawSeg);\n\t\t\tpieces.push(styled);\n\t\t\tpieces.push(\" \");\n\t\t}\n\n\t\tconst submitText = \" ✓ Submit \";\n\t\tconst submitStyled = this.props.submit.active\n\t\t\t? this.theme.bg(\"selectedBg\", this.theme.fg(\"text\", submitText))\n\t\t\t: this.theme.fg(this.props.submit.allAnswered ? \"success\" : \"dim\", submitText);\n\t\tpieces.push(submitStyled);\n\t\tpieces.push(\" →\");\n\n\t\tconst tabLine = truncateToWidth(pieces.join(\"\"), width, \"\");\n\t\treturn [tabLine, \"\"];\n\t}\n}\n"]}
@@ -2,31 +2,37 @@ import { visibleWidth, wrapTextWithAnsi } from "@earendil-works/pi-tui";
2
2
  import { OVERLAY_ACTIVE_ROW_MARKER } from "../../../../extensions/ui-types.js";
3
3
  import { ROW_INTENT_META } from "../../state/row-intent.js";
4
4
  export class WrappingSelect {
5
- static { this.ACTIVE_POINTER = "❯ "; }
6
- static { this.INACTIVE_POINTER = " "; }
7
- static { this.NUMBER_SEPARATOR = ". "; }
8
- static { this.CURSOR_INVERSE_START = "\x1b[7m"; }
9
- static { this.CURSOR_INVERSE_END = "\x1b[0m"; }
10
- static { this.CONFIRMED_MARK = " ✔"; }
11
- static { this.MIN_CONTENT_WIDTH = 1; }
5
+ static ACTIVE_POINTER = "❯ ";
6
+ static INACTIVE_POINTER = " ";
7
+ static NUMBER_SEPARATOR = ". ";
8
+ static CURSOR_INVERSE_START = "\x1b[7m";
9
+ static CURSOR_INVERSE_END = "\x1b[0m";
10
+ static CONFIRMED_MARK = " ✔";
11
+ static MIN_CONTENT_WIDTH = 1;
12
+ items;
13
+ maxVisible;
14
+ theme;
15
+ inlineInputEnabled;
16
+ numberStartOffset;
17
+ totalItemsForNumbering;
18
+ selectedIndex = 0;
19
+ focused = true;
20
+ inputBuffer = "";
21
+ inputCursor = undefined;
22
+ /**
23
+ * Index of the row that was previously confirmed for this list (e.g. the user's prior
24
+ * answer when re-entering a multi-question tab). Renders `<label> ✔` in the active-row
25
+ * styling but WITHOUT the `❯` pointer — pointer is reserved for the live cursor. When
26
+ * `selectedIndex === confirmedIndex && focused`, the active rendering wins (no double-mark).
27
+ */
28
+ confirmedIndex = undefined;
29
+ /**
30
+ * When set together with `confirmedIndex`, replaces the row's static label at render time.
31
+ * Used for the `kind: "other"` sentinel — its label is "Type something." but if the user's
32
+ * prior answer was custom text, we render that text instead (e.g. `4. Hello ✔`).
33
+ */
34
+ confirmedLabelOverride = undefined;
12
35
  constructor(items, maxVisible, theme, options = {}) {
13
- this.selectedIndex = 0;
14
- this.focused = true;
15
- this.inputBuffer = "";
16
- this.inputCursor = undefined;
17
- /**
18
- * Index of the row that was previously confirmed for this list (e.g. the user's prior
19
- * answer when re-entering a multi-question tab). Renders `<label> ✔` in the active-row
20
- * styling but WITHOUT the `❯` pointer — pointer is reserved for the live cursor. When
21
- * `selectedIndex === confirmedIndex && focused`, the active rendering wins (no double-mark).
22
- */
23
- this.confirmedIndex = undefined;
24
- /**
25
- * When set together with `confirmedIndex`, replaces the row's static label at render time.
26
- * Used for the `kind: "other"` sentinel — its label is "Type something." but if the user's
27
- * prior answer was custom text, we render that text instead (e.g. `4. Hello ✔`).
28
- */
29
- this.confirmedLabelOverride = undefined;
30
36
  this.items = items;
31
37
  this.maxVisible = Math.max(1, maxVisible);
32
38
  this.theme = theme;