@bastani/atomic 0.9.9-alpha.1 → 0.9.9-alpha.3

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 (576) hide show
  1. package/CHANGELOG.md +18 -0
  2. package/dist/builtin/cursor/CHANGELOG.md +12 -0
  3. package/dist/builtin/cursor/package.json +3 -3
  4. package/dist/builtin/intercom/CHANGELOG.md +14 -0
  5. package/dist/builtin/intercom/README.md +10 -3
  6. package/dist/builtin/intercom/broker/client.ts +2 -0
  7. package/dist/builtin/intercom/broker/send-handler.ts +22 -20
  8. package/dist/builtin/intercom/foreground-detach-handoff.ts +3 -1
  9. package/dist/builtin/intercom/inbound-idle-queue.ts +82 -0
  10. package/dist/builtin/intercom/index-heavy.ts +47 -37
  11. package/dist/builtin/intercom/intercom-tool.ts +6 -4
  12. package/dist/builtin/intercom/intercom-utils.ts +30 -0
  13. package/dist/builtin/intercom/lifecycle.ts +8 -5
  14. package/dist/builtin/intercom/package.json +3 -3
  15. package/dist/builtin/intercom/reply-tracker.ts +19 -19
  16. package/dist/builtin/intercom/session-target.ts +68 -0
  17. package/dist/builtin/intercom/skills/intercom/SKILL.md +7 -4
  18. package/dist/builtin/intercom/source-ownership.ts +34 -0
  19. package/dist/builtin/intercom/subagent-relay.ts +45 -20
  20. package/dist/builtin/intercom/terminal-ordering-barrier.ts +111 -0
  21. package/dist/builtin/intercom/types.ts +5 -0
  22. package/dist/builtin/mcp/CHANGELOG.md +13 -0
  23. package/dist/builtin/mcp/package.json +4 -4
  24. package/dist/builtin/subagents/CHANGELOG.md +13 -0
  25. package/dist/builtin/subagents/package.json +5 -5
  26. package/dist/builtin/subagents/src/runs/background/notify.ts +53 -9
  27. package/dist/builtin/subagents/src/shared/types-config.ts +1 -0
  28. package/dist/builtin/web-access/CHANGELOG.md +13 -0
  29. package/dist/builtin/web-access/package.json +3 -3
  30. package/dist/builtin/web-access/web-search-tool.ts +1 -2
  31. package/dist/builtin/workflows/CHANGELOG.md +13 -0
  32. package/dist/builtin/workflows/package.json +3 -3
  33. package/dist/builtin/workflows/src/extension/workflow-tool.ts +18 -2
  34. package/dist/builtin/workflows/src/shared/serializable.ts +1 -1
  35. package/dist/bun/cli.d.ts.map +1 -1
  36. package/dist/bun/internal-intercom-broker.d.ts.map +1 -1
  37. package/dist/bun/register-bedrock.d.ts.map +1 -1
  38. package/dist/bun/restore-sandbox-env.d.ts.map +1 -1
  39. package/dist/bun/split-loader.d.ts.map +1 -1
  40. package/dist/cli/args.d.ts.map +1 -1
  41. package/dist/cli/config-selector.d.ts.map +1 -1
  42. package/dist/cli/file-processor.d.ts.map +1 -1
  43. package/dist/cli/initial-message.d.ts.map +1 -1
  44. package/dist/cli/list-models.d.ts.map +1 -1
  45. package/dist/cli/project-trust.d.ts.map +1 -1
  46. package/dist/cli/session-picker.d.ts.map +1 -1
  47. package/dist/cli/startup-ui.d.ts.map +1 -1
  48. package/dist/cli.d.ts.map +1 -1
  49. package/dist/config-self-update.d.ts.map +1 -1
  50. package/dist/config.d.ts.map +1 -1
  51. package/dist/core/agent-session-accessors.d.ts.map +1 -1
  52. package/dist/core/agent-session-auto-compaction.d.ts.map +1 -1
  53. package/dist/core/agent-session-bash.d.ts.map +1 -1
  54. package/dist/core/agent-session-compaction.d.ts.map +1 -1
  55. package/dist/core/agent-session-events.d.ts.map +1 -1
  56. package/dist/core/agent-session-export.d.ts.map +1 -1
  57. package/dist/core/agent-session-extension-bindings.d.ts.map +1 -1
  58. package/dist/core/agent-session-extension-bindings.js +9 -0
  59. package/dist/core/agent-session-extension-bindings.js.map +1 -1
  60. package/dist/core/agent-session-message-queue.d.ts +4 -1
  61. package/dist/core/agent-session-message-queue.d.ts.map +1 -1
  62. package/dist/core/agent-session-message-queue.js +35 -0
  63. package/dist/core/agent-session-message-queue.js.map +1 -1
  64. package/dist/core/agent-session-methods.d.ts +2 -1
  65. package/dist/core/agent-session-methods.d.ts.map +1 -1
  66. package/dist/core/agent-session-methods.js.map +1 -1
  67. package/dist/core/agent-session-models.d.ts.map +1 -1
  68. package/dist/core/agent-session-prompt.d.ts.map +1 -1
  69. package/dist/core/agent-session-retry.d.ts.map +1 -1
  70. package/dist/core/agent-session-runtime.d.ts.map +1 -1
  71. package/dist/core/agent-session-services.d.ts.map +1 -1
  72. package/dist/core/agent-session-skill-block.d.ts.map +1 -1
  73. package/dist/core/agent-session-state.d.ts.map +1 -1
  74. package/dist/core/agent-session-tool-hooks.d.ts.map +1 -1
  75. package/dist/core/agent-session-tool-registry.d.ts.map +1 -1
  76. package/dist/core/agent-session-tree.d.ts.map +1 -1
  77. package/dist/core/agent-session-types.d.ts.map +1 -1
  78. package/dist/core/agent-session.d.ts.map +1 -1
  79. package/dist/core/anthropic-thinking-guard.d.ts.map +1 -1
  80. package/dist/core/async/format.d.ts.map +1 -1
  81. package/dist/core/async/job-manager.d.ts.map +1 -1
  82. package/dist/core/async/session-manager.d.ts.map +1 -1
  83. package/dist/core/async/types.d.ts.map +1 -1
  84. package/dist/core/atomic-guide-command.d.ts.map +1 -1
  85. package/dist/core/auth-guidance.d.ts.map +1 -1
  86. package/dist/core/auth-storage-backends.d.ts.map +1 -1
  87. package/dist/core/auth-storage.d.ts +4 -0
  88. package/dist/core/auth-storage.d.ts.map +1 -1
  89. package/dist/core/bash-executor.d.ts.map +1 -1
  90. package/dist/core/builtin-packages.d.ts.map +1 -1
  91. package/dist/core/codex-fast-mode.d.ts.map +1 -1
  92. package/dist/core/compaction/branch-summarization.d.ts.map +1 -1
  93. package/dist/core/compaction/compaction-boundary.d.ts.map +1 -1
  94. package/dist/core/compaction/compaction-parameters.d.ts.map +1 -1
  95. package/dist/core/compaction/compaction-runner.d.ts.map +1 -1
  96. package/dist/core/compaction/compaction-types.d.ts.map +1 -1
  97. package/dist/core/compaction/compaction.d.ts.map +1 -1
  98. package/dist/core/compaction/deleted-ranges.d.ts.map +1 -1
  99. package/dist/core/compaction/index.d.ts.map +1 -1
  100. package/dist/core/compaction/range-planner.d.ts.map +1 -1
  101. package/dist/core/compaction/transcript-serialization.d.ts.map +1 -1
  102. package/dist/core/compaction/utils.d.ts.map +1 -1
  103. package/dist/core/context-window.d.ts.map +1 -1
  104. package/dist/core/copilot-anthropic-sse-repair.d.ts.map +1 -1
  105. package/dist/core/copilot-errors.d.ts.map +1 -1
  106. package/dist/core/copilot-gemini-payload-sanitizer.d.ts.map +1 -1
  107. package/dist/core/copilot-gemini-reasoning.d.ts.map +1 -1
  108. package/dist/core/copilot-gemini-tool-arguments.d.ts.map +1 -1
  109. package/dist/core/copilot-hosts.d.ts.map +1 -1
  110. package/dist/core/copilot-model-catalog.d.ts.map +1 -1
  111. package/dist/core/copilot-model-static-fallbacks.d.ts.map +1 -1
  112. package/dist/core/copilot-model-synthesis.d.ts.map +1 -1
  113. package/dist/core/defaults.d.ts.map +1 -1
  114. package/dist/core/diagnostics.d.ts.map +1 -1
  115. package/dist/core/event-bus.d.ts.map +1 -1
  116. package/dist/core/exec.d.ts.map +1 -1
  117. package/dist/core/experimental.d.ts.map +1 -1
  118. package/dist/core/export-html/ansi-to-html.d.ts.map +1 -1
  119. package/dist/core/export-html/index.d.ts.map +1 -1
  120. package/dist/core/export-html/template-script.d.ts.map +1 -1
  121. package/dist/core/export-html/tool-renderer.d.ts.map +1 -1
  122. package/dist/core/extensions/agent-events.d.ts.map +1 -1
  123. package/dist/core/extensions/api-types.d.ts +3 -1
  124. package/dist/core/extensions/api-types.d.ts.map +1 -1
  125. package/dist/core/extensions/api-types.js.map +1 -1
  126. package/dist/core/extensions/command-types.d.ts.map +1 -1
  127. package/dist/core/extensions/context-types.d.ts.map +1 -1
  128. package/dist/core/extensions/event-results.d.ts.map +1 -1
  129. package/dist/core/extensions/event-types.d.ts.map +1 -1
  130. package/dist/core/extensions/index.d.ts +1 -1
  131. package/dist/core/extensions/index.d.ts.map +1 -1
  132. package/dist/core/extensions/index.js.map +1 -1
  133. package/dist/core/extensions/loader-api.d.ts.map +1 -1
  134. package/dist/core/extensions/loader-api.js +4 -0
  135. package/dist/core/extensions/loader-api.js.map +1 -1
  136. package/dist/core/extensions/loader-core.d.ts.map +1 -1
  137. package/dist/core/extensions/loader-discovery.d.ts.map +1 -1
  138. package/dist/core/extensions/loader-resources.d.ts.map +1 -1
  139. package/dist/core/extensions/loader-runtime.d.ts.map +1 -1
  140. package/dist/core/extensions/loader-runtime.js +1 -0
  141. package/dist/core/extensions/loader-runtime.js.map +1 -1
  142. package/dist/core/extensions/loader-virtual-modules.d.ts.map +1 -1
  143. package/dist/core/extensions/loader.d.ts.map +1 -1
  144. package/dist/core/extensions/message-types.d.ts +3 -0
  145. package/dist/core/extensions/message-types.d.ts.map +1 -1
  146. package/dist/core/extensions/message-types.js.map +1 -1
  147. package/dist/core/extensions/provider-types.d.ts.map +1 -1
  148. package/dist/core/extensions/reactive-widget.d.ts.map +1 -1
  149. package/dist/core/extensions/runner-context.d.ts.map +1 -1
  150. package/dist/core/extensions/runner-events.d.ts.map +1 -1
  151. package/dist/core/extensions/runner-handlers.d.ts.map +1 -1
  152. package/dist/core/extensions/runner-project-trust.d.ts.map +1 -1
  153. package/dist/core/extensions/runner-registries.d.ts.map +1 -1
  154. package/dist/core/extensions/runner-shortcuts.d.ts.map +1 -1
  155. package/dist/core/extensions/runner-ui.d.ts.map +1 -1
  156. package/dist/core/extensions/runner.d.ts.map +1 -1
  157. package/dist/core/extensions/runner.js +1 -0
  158. package/dist/core/extensions/runner.js.map +1 -1
  159. package/dist/core/extensions/runtime-types.d.ts +3 -1
  160. package/dist/core/extensions/runtime-types.d.ts.map +1 -1
  161. package/dist/core/extensions/runtime-types.js.map +1 -1
  162. package/dist/core/extensions/session-events.d.ts.map +1 -1
  163. package/dist/core/extensions/tool-events.d.ts.map +1 -1
  164. package/dist/core/extensions/tool-types.d.ts.map +1 -1
  165. package/dist/core/extensions/types.d.ts.map +1 -1
  166. package/dist/core/extensions/ui-types.d.ts.map +1 -1
  167. package/dist/core/extensions/wrapper.d.ts.map +1 -1
  168. package/dist/core/flattened-tool-arguments.d.ts.map +1 -1
  169. package/dist/core/footer-data-provider.d.ts.map +1 -1
  170. package/dist/core/http-dispatcher.d.ts.map +1 -1
  171. package/dist/core/index.d.ts.map +1 -1
  172. package/dist/core/keybindings.d.ts.map +1 -1
  173. package/dist/core/messages.d.ts.map +1 -1
  174. package/dist/core/model-registry-auth.d.ts.map +1 -1
  175. package/dist/core/model-registry-builtins.d.ts.map +1 -1
  176. package/dist/core/model-registry-builtins.js.map +1 -1
  177. package/dist/core/model-registry-custom-loader.d.ts.map +1 -1
  178. package/dist/core/model-registry-custom-loader.js.map +1 -1
  179. package/dist/core/model-registry-dynamic.d.ts.map +1 -1
  180. package/dist/core/model-registry-loader.d.ts.map +1 -1
  181. package/dist/core/model-registry-schemas.d.ts +65 -13
  182. package/dist/core/model-registry-schemas.d.ts.map +1 -1
  183. package/dist/core/model-registry-schemas.js +5 -1
  184. package/dist/core/model-registry-schemas.js.map +1 -1
  185. package/dist/core/model-registry-types.d.ts.map +1 -1
  186. package/dist/core/model-registry.d.ts.map +1 -1
  187. package/dist/core/model-resolver-cli.d.ts.map +1 -1
  188. package/dist/core/model-resolver-defaults.d.ts.map +1 -1
  189. package/dist/core/model-resolver-initial.d.ts.map +1 -1
  190. package/dist/core/model-resolver-patterns.d.ts.map +1 -1
  191. package/dist/core/model-resolver-scope.d.ts.map +1 -1
  192. package/dist/core/model-resolver-types.d.ts.map +1 -1
  193. package/dist/core/model-resolver.d.ts.map +1 -1
  194. package/dist/core/openai-responses-payload-sanitizer.d.ts.map +1 -1
  195. package/dist/core/output-guard.d.ts.map +1 -1
  196. package/dist/core/package-manager-auto-resources.d.ts.map +1 -1
  197. package/dist/core/package-manager-command.d.ts.map +1 -1
  198. package/dist/core/package-manager-constants.d.ts.map +1 -1
  199. package/dist/core/package-manager-env.d.ts.map +1 -1
  200. package/dist/core/package-manager-git.d.ts.map +1 -1
  201. package/dist/core/package-manager-manifest.d.ts.map +1 -1
  202. package/dist/core/package-manager-npm.d.ts.map +1 -1
  203. package/dist/core/package-manager-operations.d.ts.map +1 -1
  204. package/dist/core/package-manager-paths.d.ts.map +1 -1
  205. package/dist/core/package-manager-progress.d.ts.map +1 -1
  206. package/dist/core/package-manager-resolver.d.ts.map +1 -1
  207. package/dist/core/package-manager-resource-accumulator.d.ts.map +1 -1
  208. package/dist/core/package-manager-resource-collector.d.ts.map +1 -1
  209. package/dist/core/package-manager-resource-files.d.ts.map +1 -1
  210. package/dist/core/package-manager-resource-patterns.d.ts.map +1 -1
  211. package/dist/core/package-manager-settings.d.ts.map +1 -1
  212. package/dist/core/package-manager-source.d.ts.map +1 -1
  213. package/dist/core/package-manager-types.d.ts.map +1 -1
  214. package/dist/core/package-manager.d.ts.map +1 -1
  215. package/dist/core/project-trust.d.ts.map +1 -1
  216. package/dist/core/prompt-templates-async.d.ts.map +1 -1
  217. package/dist/core/prompt-templates.d.ts.map +1 -1
  218. package/dist/core/provider-attribution.d.ts.map +1 -1
  219. package/dist/core/provider-context-usage.d.ts.map +1 -1
  220. package/dist/core/provider-display-names.d.ts.map +1 -1
  221. package/dist/core/resolve-config-value.d.ts.map +1 -1
  222. package/dist/core/resource-loader-assets.d.ts.map +1 -1
  223. package/dist/core/resource-loader-context-files.d.ts.map +1 -1
  224. package/dist/core/resource-loader-core.d.ts.map +1 -1
  225. package/dist/core/resource-loader-discovery.d.ts.map +1 -1
  226. package/dist/core/resource-loader-extensions.d.ts.map +1 -1
  227. package/dist/core/resource-loader-helpers.d.ts.map +1 -1
  228. package/dist/core/resource-loader-internals.d.ts.map +1 -1
  229. package/dist/core/resource-loader-package-resources.d.ts.map +1 -1
  230. package/dist/core/resource-loader-paths.d.ts.map +1 -1
  231. package/dist/core/resource-loader-reload.d.ts.map +1 -1
  232. package/dist/core/resource-loader-source-info.d.ts.map +1 -1
  233. package/dist/core/resource-loader-types.d.ts.map +1 -1
  234. package/dist/core/resource-loader.d.ts.map +1 -1
  235. package/dist/core/sdk-exports.d.ts.map +1 -1
  236. package/dist/core/sdk-types.d.ts.map +1 -1
  237. package/dist/core/sdk.d.ts.map +1 -1
  238. package/dist/core/session-cwd.d.ts.map +1 -1
  239. package/dist/core/session-entry-normalization.d.ts.map +1 -1
  240. package/dist/core/session-manager-archive.d.ts.map +1 -1
  241. package/dist/core/session-manager-classification.d.ts.map +1 -1
  242. package/dist/core/session-manager-core.d.ts.map +1 -1
  243. package/dist/core/session-manager-entries.d.ts.map +1 -1
  244. package/dist/core/session-manager-history.d.ts.map +1 -1
  245. package/dist/core/session-manager-list.d.ts.map +1 -1
  246. package/dist/core/session-manager-migrations.d.ts.map +1 -1
  247. package/dist/core/session-manager-paths.d.ts.map +1 -1
  248. package/dist/core/session-manager-storage.d.ts.map +1 -1
  249. package/dist/core/session-manager-types.d.ts.map +1 -1
  250. package/dist/core/session-manager-validation.d.ts.map +1 -1
  251. package/dist/core/session-manager.d.ts.map +1 -1
  252. package/dist/core/settings-manager-basic-accessors.d.ts.map +1 -1
  253. package/dist/core/settings-manager-core.d.ts.map +1 -1
  254. package/dist/core/settings-manager-internals.d.ts.map +1 -1
  255. package/dist/core/settings-manager-resource-accessors.d.ts.map +1 -1
  256. package/dist/core/settings-manager-ui-accessors.d.ts.map +1 -1
  257. package/dist/core/settings-manager.d.ts.map +1 -1
  258. package/dist/core/settings-merge.d.ts.map +1 -1
  259. package/dist/core/settings-storage.d.ts.map +1 -1
  260. package/dist/core/settings-types.d.ts.map +1 -1
  261. package/dist/core/skills-async.d.ts.map +1 -1
  262. package/dist/core/skills.d.ts.map +1 -1
  263. package/dist/core/slash-commands.d.ts.map +1 -1
  264. package/dist/core/source-info.d.ts.map +1 -1
  265. package/dist/core/system-prompt.d.ts.map +1 -1
  266. package/dist/core/telemetry.d.ts.map +1 -1
  267. package/dist/core/thinking-blocks.d.ts.map +1 -1
  268. package/dist/core/timings.d.ts.map +1 -1
  269. package/dist/core/tools/artifact-protocol.d.ts.map +1 -1
  270. package/dist/core/tools/artifacts.d.ts.map +1 -1
  271. package/dist/core/tools/ask-user-question/ask-user-question.d.ts.map +1 -1
  272. package/dist/core/tools/ask-user-question/config.d.ts.map +1 -1
  273. package/dist/core/tools/ask-user-question/index.d.ts.map +1 -1
  274. package/dist/core/tools/ask-user-question/state/build-questionnaire.d.ts.map +1 -1
  275. package/dist/core/tools/ask-user-question/state/inline-input.d.ts.map +1 -1
  276. package/dist/core/tools/ask-user-question/state/key-router.d.ts.map +1 -1
  277. package/dist/core/tools/ask-user-question/state/questionnaire-session.d.ts.map +1 -1
  278. package/dist/core/tools/ask-user-question/state/row-intent.d.ts.map +1 -1
  279. package/dist/core/tools/ask-user-question/state/selectors/contract.d.ts.map +1 -1
  280. package/dist/core/tools/ask-user-question/state/selectors/derivations.d.ts.map +1 -1
  281. package/dist/core/tools/ask-user-question/state/selectors/focus.d.ts.map +1 -1
  282. package/dist/core/tools/ask-user-question/state/selectors/projections.d.ts.map +1 -1
  283. package/dist/core/tools/ask-user-question/state/state-reducer.d.ts.map +1 -1
  284. package/dist/core/tools/ask-user-question/state/state.d.ts.map +1 -1
  285. package/dist/core/tools/ask-user-question/tool/format-answer.d.ts.map +1 -1
  286. package/dist/core/tools/ask-user-question/tool/response-envelope.d.ts.map +1 -1
  287. package/dist/core/tools/ask-user-question/tool/types.d.ts.map +1 -1
  288. package/dist/core/tools/ask-user-question/tool/validate-questionnaire.d.ts.map +1 -1
  289. package/dist/core/tools/ask-user-question/view/body-residual-spacer.d.ts.map +1 -1
  290. package/dist/core/tools/ask-user-question/view/component-binding.d.ts.map +1 -1
  291. package/dist/core/tools/ask-user-question/view/components/chat-row-view.d.ts.map +1 -1
  292. package/dist/core/tools/ask-user-question/view/components/multi-select-view.d.ts.map +1 -1
  293. package/dist/core/tools/ask-user-question/view/components/option-list-view.d.ts.map +1 -1
  294. package/dist/core/tools/ask-user-question/view/components/preview/markdown-content-cache.d.ts.map +1 -1
  295. package/dist/core/tools/ask-user-question/view/components/preview/preview-block-renderer.d.ts.map +1 -1
  296. package/dist/core/tools/ask-user-question/view/components/preview/preview-box-renderer.d.ts.map +1 -1
  297. package/dist/core/tools/ask-user-question/view/components/preview/preview-layout-decider.d.ts.map +1 -1
  298. package/dist/core/tools/ask-user-question/view/components/preview/preview-pane.d.ts.map +1 -1
  299. package/dist/core/tools/ask-user-question/view/components/submit-picker.d.ts.map +1 -1
  300. package/dist/core/tools/ask-user-question/view/components/tab-bar.d.ts.map +1 -1
  301. package/dist/core/tools/ask-user-question/view/components/wrapping-select.d.ts.map +1 -1
  302. package/dist/core/tools/ask-user-question/view/dialog-builder.d.ts.map +1 -1
  303. package/dist/core/tools/ask-user-question/view/props-adapter.d.ts.map +1 -1
  304. package/dist/core/tools/ask-user-question/view/stateful-view.d.ts.map +1 -1
  305. package/dist/core/tools/ask-user-question/view/tab-components.d.ts.map +1 -1
  306. package/dist/core/tools/ask-user-question/view/tab-content-strategy.d.ts.map +1 -1
  307. package/dist/core/tools/bash-async-execution.d.ts.map +1 -1
  308. package/dist/core/tools/bash-async-jobs.d.ts.map +1 -1
  309. package/dist/core/tools/bash-async-output.d.ts.map +1 -1
  310. package/dist/core/tools/bash-interceptor.d.ts.map +1 -1
  311. package/dist/core/tools/bash-leading-cd.d.ts.map +1 -1
  312. package/dist/core/tools/bash-pty-native.d.ts.map +1 -1
  313. package/dist/core/tools/bash.d.ts.map +1 -1
  314. package/dist/core/tools/block-resolver.d.ts.map +1 -1
  315. package/dist/core/tools/conflict-registry.d.ts.map +1 -1
  316. package/dist/core/tools/directory-tree.d.ts.map +1 -1
  317. package/dist/core/tools/edit-diff-preserve.d.ts.map +1 -1
  318. package/dist/core/tools/edit-diff.d.ts.map +1 -1
  319. package/dist/core/tools/edit.d.ts.map +1 -1
  320. package/dist/core/tools/fetch-url.d.ts.map +1 -1
  321. package/dist/core/tools/file-mutation-queue.d.ts.map +1 -1
  322. package/dist/core/tools/find.d.ts.map +1 -1
  323. package/dist/core/tools/glob-path-utils.d.ts.map +1 -1
  324. package/dist/core/tools/grep.d.ts.map +1 -1
  325. package/dist/core/tools/hashline-engine/apply.d.ts.map +1 -1
  326. package/dist/core/tools/hashline-engine/block.d.ts.map +1 -1
  327. package/dist/core/tools/hashline-engine/diff-preview.d.ts.map +1 -1
  328. package/dist/core/tools/hashline-engine/format.d.ts.map +1 -1
  329. package/dist/core/tools/hashline-engine/fs.d.ts.map +1 -1
  330. package/dist/core/tools/hashline-engine/index.d.ts.map +1 -1
  331. package/dist/core/tools/hashline-engine/input.d.ts.map +1 -1
  332. package/dist/core/tools/hashline-engine/messages.d.ts.map +1 -1
  333. package/dist/core/tools/hashline-engine/mismatch.d.ts.map +1 -1
  334. package/dist/core/tools/hashline-engine/normalize.d.ts.map +1 -1
  335. package/dist/core/tools/hashline-engine/parser.d.ts.map +1 -1
  336. package/dist/core/tools/hashline-engine/patcher.d.ts.map +1 -1
  337. package/dist/core/tools/hashline-engine/prefixes.d.ts.map +1 -1
  338. package/dist/core/tools/hashline-engine/recovery.d.ts.map +1 -1
  339. package/dist/core/tools/hashline-engine/snapshots.d.ts.map +1 -1
  340. package/dist/core/tools/hashline-engine/stream.d.ts.map +1 -1
  341. package/dist/core/tools/hashline-engine/tokenizer.d.ts.map +1 -1
  342. package/dist/core/tools/hashline-engine/types.d.ts.map +1 -1
  343. package/dist/core/tools/hashline.d.ts.map +1 -1
  344. package/dist/core/tools/index.d.ts.map +1 -1
  345. package/dist/core/tools/ls.d.ts.map +1 -1
  346. package/dist/core/tools/notebook.d.ts.map +1 -1
  347. package/dist/core/tools/output-accumulator.d.ts.map +1 -1
  348. package/dist/core/tools/oversized-tool-result.d.ts.map +1 -1
  349. package/dist/core/tools/path-utils.d.ts.map +1 -1
  350. package/dist/core/tools/read-document-extract.d.ts.map +1 -1
  351. package/dist/core/tools/read-selectors.d.ts.map +1 -1
  352. package/dist/core/tools/read-url.d.ts.map +1 -1
  353. package/dist/core/tools/read.d.ts.map +1 -1
  354. package/dist/core/tools/render-utils.d.ts.map +1 -1
  355. package/dist/core/tools/resource-selectors.d.ts.map +1 -1
  356. package/dist/core/tools/search-details.d.ts.map +1 -1
  357. package/dist/core/tools/search-line-ranges.d.ts.map +1 -1
  358. package/dist/core/tools/search-native.d.ts.map +1 -1
  359. package/dist/core/tools/search.d.ts.map +1 -1
  360. package/dist/core/tools/structured-output.d.ts.map +1 -1
  361. package/dist/core/tools/todos-execute.d.ts.map +1 -1
  362. package/dist/core/tools/todos-locks.d.ts.map +1 -1
  363. package/dist/core/tools/todos-model.d.ts.map +1 -1
  364. package/dist/core/tools/todos-mutations.d.ts.map +1 -1
  365. package/dist/core/tools/todos-paths.d.ts.map +1 -1
  366. package/dist/core/tools/todos-render.d.ts.map +1 -1
  367. package/dist/core/tools/todos-storage.d.ts.map +1 -1
  368. package/dist/core/tools/todos-types.d.ts.map +1 -1
  369. package/dist/core/tools/todos-types.js +1 -1
  370. package/dist/core/tools/todos-types.js.map +1 -1
  371. package/dist/core/tools/todos.d.ts.map +1 -1
  372. package/dist/core/tools/tool-definition-wrapper.d.ts.map +1 -1
  373. package/dist/core/tools/tool-definition-wrapper.js +6 -2
  374. package/dist/core/tools/tool-definition-wrapper.js.map +1 -1
  375. package/dist/core/tools/tool-limits.d.ts.map +1 -1
  376. package/dist/core/tools/truncate.d.ts.map +1 -1
  377. package/dist/core/tools/url-ip-guards.d.ts.map +1 -1
  378. package/dist/core/tools/write.d.ts.map +1 -1
  379. package/dist/core/trust-manager.d.ts.map +1 -1
  380. package/dist/core/typebox-compat.d.ts +12 -0
  381. package/dist/core/typebox-compat.d.ts.map +1 -0
  382. package/dist/core/typebox-compat.js +10 -0
  383. package/dist/core/typebox-compat.js.map +1 -0
  384. package/dist/index-extensions.d.ts.map +1 -1
  385. package/dist/index.d.ts +1 -0
  386. package/dist/index.d.ts.map +1 -1
  387. package/dist/index.js +1 -0
  388. package/dist/index.js.map +1 -1
  389. package/dist/main-app-mode.d.ts.map +1 -1
  390. package/dist/main-deferred-startup.d.ts.map +1 -1
  391. package/dist/main-early-input.d.ts.map +1 -1
  392. package/dist/main-session-options.d.ts.map +1 -1
  393. package/dist/main-session.d.ts.map +1 -1
  394. package/dist/main-stdio.d.ts.map +1 -1
  395. package/dist/main.d.ts.map +1 -1
  396. package/dist/migrations-config-values.d.ts.map +1 -1
  397. package/dist/migrations.d.ts.map +1 -1
  398. package/dist/modes/index.d.ts.map +1 -1
  399. package/dist/modes/interactive/chat-input-actions.d.ts.map +1 -1
  400. package/dist/modes/interactive/components/armin.d.ts.map +1 -1
  401. package/dist/modes/interactive/components/assistant-message.d.ts.map +1 -1
  402. package/dist/modes/interactive/components/atomic-banner.d.ts.map +1 -1
  403. package/dist/modes/interactive/components/bash-execution.d.ts.map +1 -1
  404. package/dist/modes/interactive/components/bordered-loader.d.ts.map +1 -1
  405. package/dist/modes/interactive/components/branch-summary-message.d.ts.map +1 -1
  406. package/dist/modes/interactive/components/chat-message-renderer.d.ts.map +1 -1
  407. package/dist/modes/interactive/components/chat-session-host-actions.d.ts.map +1 -1
  408. package/dist/modes/interactive/components/chat-session-host-editor.d.ts.map +1 -1
  409. package/dist/modes/interactive/components/chat-session-host-events.d.ts.map +1 -1
  410. package/dist/modes/interactive/components/chat-session-host-rendering.d.ts.map +1 -1
  411. package/dist/modes/interactive/components/chat-session-host-runtime.d.ts.map +1 -1
  412. package/dist/modes/interactive/components/chat-session-host-state.d.ts.map +1 -1
  413. package/dist/modes/interactive/components/chat-session-host-terminal-cleanup.d.ts.map +1 -1
  414. package/dist/modes/interactive/components/chat-session-host-types.d.ts.map +1 -1
  415. package/dist/modes/interactive/components/chat-session-host-utils.d.ts.map +1 -1
  416. package/dist/modes/interactive/components/chat-session-host.d.ts.map +1 -1
  417. package/dist/modes/interactive/components/chat-transcript.d.ts.map +1 -1
  418. package/dist/modes/interactive/components/compaction-boundary-message.d.ts.map +1 -1
  419. package/dist/modes/interactive/components/config-selector-list.d.ts.map +1 -1
  420. package/dist/modes/interactive/components/config-selector.d.ts.map +1 -1
  421. package/dist/modes/interactive/components/context-window-selector.d.ts.map +1 -1
  422. package/dist/modes/interactive/components/countdown-timer.d.ts.map +1 -1
  423. package/dist/modes/interactive/components/custom-editor.d.ts.map +1 -1
  424. package/dist/modes/interactive/components/custom-message.d.ts.map +1 -1
  425. package/dist/modes/interactive/components/daxnuts.d.ts.map +1 -1
  426. package/dist/modes/interactive/components/diff.d.ts.map +1 -1
  427. package/dist/modes/interactive/components/dynamic-border.d.ts.map +1 -1
  428. package/dist/modes/interactive/components/earendil-announcement.d.ts.map +1 -1
  429. package/dist/modes/interactive/components/extension-editor.d.ts.map +1 -1
  430. package/dist/modes/interactive/components/extension-input.d.ts.map +1 -1
  431. package/dist/modes/interactive/components/extension-selector.d.ts.map +1 -1
  432. package/dist/modes/interactive/components/fast-mode-selector.d.ts.map +1 -1
  433. package/dist/modes/interactive/components/footer.d.ts.map +1 -1
  434. package/dist/modes/interactive/components/index.d.ts.map +1 -1
  435. package/dist/modes/interactive/components/keybinding-hints.d.ts.map +1 -1
  436. package/dist/modes/interactive/components/login-dialog.d.ts.map +1 -1
  437. package/dist/modes/interactive/components/model-selector.d.ts.map +1 -1
  438. package/dist/modes/interactive/components/oauth-selector.d.ts.map +1 -1
  439. package/dist/modes/interactive/components/scoped-models-selector.d.ts.map +1 -1
  440. package/dist/modes/interactive/components/session-selector-delete.d.ts.map +1 -1
  441. package/dist/modes/interactive/components/session-selector-header.d.ts.map +1 -1
  442. package/dist/modes/interactive/components/session-selector-list.d.ts.map +1 -1
  443. package/dist/modes/interactive/components/session-selector-search.d.ts.map +1 -1
  444. package/dist/modes/interactive/components/session-selector-tree.d.ts.map +1 -1
  445. package/dist/modes/interactive/components/session-selector-types.d.ts.map +1 -1
  446. package/dist/modes/interactive/components/session-selector-utils.d.ts.map +1 -1
  447. package/dist/modes/interactive/components/session-selector.d.ts.map +1 -1
  448. package/dist/modes/interactive/components/settings-selector-handlers.d.ts.map +1 -1
  449. package/dist/modes/interactive/components/settings-selector-items.d.ts.map +1 -1
  450. package/dist/modes/interactive/components/settings-selector-options.d.ts.map +1 -1
  451. package/dist/modes/interactive/components/settings-selector-submenus.d.ts.map +1 -1
  452. package/dist/modes/interactive/components/settings-selector-types.d.ts.map +1 -1
  453. package/dist/modes/interactive/components/settings-selector.d.ts.map +1 -1
  454. package/dist/modes/interactive/components/show-images-selector.d.ts.map +1 -1
  455. package/dist/modes/interactive/components/skill-invocation-message.d.ts.map +1 -1
  456. package/dist/modes/interactive/components/theme-selector.d.ts.map +1 -1
  457. package/dist/modes/interactive/components/thinking-selector.d.ts.map +1 -1
  458. package/dist/modes/interactive/components/tool-execution.d.ts.map +1 -1
  459. package/dist/modes/interactive/components/tree-selector-component.d.ts.map +1 -1
  460. package/dist/modes/interactive/components/tree-selector-content.d.ts.map +1 -1
  461. package/dist/modes/interactive/components/tree-selector-help.d.ts.map +1 -1
  462. package/dist/modes/interactive/components/tree-selector-label-input.d.ts.map +1 -1
  463. package/dist/modes/interactive/components/tree-selector-list.d.ts.map +1 -1
  464. package/dist/modes/interactive/components/tree-selector-model.d.ts.map +1 -1
  465. package/dist/modes/interactive/components/tree-selector-types.d.ts.map +1 -1
  466. package/dist/modes/interactive/components/tree-selector-viewport.d.ts.map +1 -1
  467. package/dist/modes/interactive/components/tree-selector.d.ts.map +1 -1
  468. package/dist/modes/interactive/components/trust-selector.d.ts.map +1 -1
  469. package/dist/modes/interactive/components/user-message-selector.d.ts.map +1 -1
  470. package/dist/modes/interactive/components/user-message.d.ts.map +1 -1
  471. package/dist/modes/interactive/components/visual-truncate.d.ts.map +1 -1
  472. package/dist/modes/interactive/components/working-status.d.ts.map +1 -1
  473. package/dist/modes/interactive/interactive-agent-events.d.ts.map +1 -1
  474. package/dist/modes/interactive/interactive-auth-login.d.ts.map +1 -1
  475. package/dist/modes/interactive/interactive-auth-routing.d.ts.map +1 -1
  476. package/dist/modes/interactive/interactive-autocomplete.d.ts.map +1 -1
  477. package/dist/modes/interactive/interactive-bash-compact.d.ts.map +1 -1
  478. package/dist/modes/interactive/interactive-child-ordering.d.ts.map +1 -1
  479. package/dist/modes/interactive/interactive-deferred-startup.d.ts.map +1 -1
  480. package/dist/modes/interactive/interactive-editor-actions.d.ts.map +1 -1
  481. package/dist/modes/interactive/interactive-extension-context.d.ts.map +1 -1
  482. package/dist/modes/interactive/interactive-extension-custom-ui.d.ts.map +1 -1
  483. package/dist/modes/interactive/interactive-extension-dialogs.d.ts.map +1 -1
  484. package/dist/modes/interactive/interactive-extension-runtime.d.ts.map +1 -1
  485. package/dist/modes/interactive/interactive-extension-widgets.d.ts.map +1 -1
  486. package/dist/modes/interactive/interactive-hotkeys-debug.d.ts.map +1 -1
  487. package/dist/modes/interactive/interactive-input-handling.d.ts.map +1 -1
  488. package/dist/modes/interactive/interactive-mode-base.d.ts.map +1 -1
  489. package/dist/modes/interactive/interactive-mode-deps.d.ts.map +1 -1
  490. package/dist/modes/interactive/interactive-mode-helpers.d.ts.map +1 -1
  491. package/dist/modes/interactive/interactive-mode-surface.d.ts.map +1 -1
  492. package/dist/modes/interactive/interactive-mode-types.d.ts.map +1 -1
  493. package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  494. package/dist/modes/interactive/interactive-model-routing.d.ts.map +1 -1
  495. package/dist/modes/interactive/interactive-onboarding.d.ts.map +1 -1
  496. package/dist/modes/interactive/interactive-process-lifecycle.d.ts.map +1 -1
  497. package/dist/modes/interactive/interactive-queueing.d.ts.map +1 -1
  498. package/dist/modes/interactive/interactive-render-chat.d.ts.map +1 -1
  499. package/dist/modes/interactive/interactive-resource-disclosure.d.ts.map +1 -1
  500. package/dist/modes/interactive/interactive-resource-paths.d.ts.map +1 -1
  501. package/dist/modes/interactive/interactive-resource-rendering.d.ts.map +1 -1
  502. package/dist/modes/interactive/interactive-selectors.d.ts.map +1 -1
  503. package/dist/modes/interactive/interactive-session-routing.d.ts.map +1 -1
  504. package/dist/modes/interactive/interactive-session-runtime.d.ts.map +1 -1
  505. package/dist/modes/interactive/interactive-slash-commands.d.ts.map +1 -1
  506. package/dist/modes/interactive/interactive-startup.d.ts.map +1 -1
  507. package/dist/modes/interactive/model-search.d.ts.map +1 -1
  508. package/dist/modes/interactive/theme/color-utils.d.ts.map +1 -1
  509. package/dist/modes/interactive/theme/export-colors.d.ts.map +1 -1
  510. package/dist/modes/interactive/theme/global-theme.d.ts.map +1 -1
  511. package/dist/modes/interactive/theme/terminal-detection.d.ts.map +1 -1
  512. package/dist/modes/interactive/theme/theme-class.d.ts.map +1 -1
  513. package/dist/modes/interactive/theme/theme-controller.d.ts.map +1 -1
  514. package/dist/modes/interactive/theme/theme-loading.d.ts.map +1 -1
  515. package/dist/modes/interactive/theme/theme-parse.d.ts.map +1 -1
  516. package/dist/modes/interactive/theme/theme-schema.d.ts.map +1 -1
  517. package/dist/modes/interactive/theme/theme.d.ts.map +1 -1
  518. package/dist/modes/interactive/theme/tui-theme.d.ts.map +1 -1
  519. package/dist/modes/interactive/whimsical-messages.d.ts.map +1 -1
  520. package/dist/modes/print-mode.d.ts.map +1 -1
  521. package/dist/modes/rpc/jsonl.d.ts.map +1 -1
  522. package/dist/modes/rpc/rpc-client.d.ts.map +1 -1
  523. package/dist/modes/rpc/rpc-command-handler.d.ts.map +1 -1
  524. package/dist/modes/rpc/rpc-extension-ui.d.ts.map +1 -1
  525. package/dist/modes/rpc/rpc-input.d.ts.map +1 -1
  526. package/dist/modes/rpc/rpc-mode.d.ts.map +1 -1
  527. package/dist/modes/rpc/rpc-responses.d.ts.map +1 -1
  528. package/dist/modes/rpc/rpc-session-binding.d.ts.map +1 -1
  529. package/dist/modes/rpc/rpc-types.d.ts.map +1 -1
  530. package/dist/package-manager-cli-parser.d.ts.map +1 -1
  531. package/dist/package-manager-cli.d.ts.map +1 -1
  532. package/dist/rpc-entry.d.ts.map +1 -1
  533. package/dist/utils/ansi.d.ts.map +1 -1
  534. package/dist/utils/changelog.d.ts.map +1 -1
  535. package/dist/utils/child-process.d.ts.map +1 -1
  536. package/dist/utils/clipboard-image.d.ts.map +1 -1
  537. package/dist/utils/clipboard-native.d.ts.map +1 -1
  538. package/dist/utils/clipboard.d.ts.map +1 -1
  539. package/dist/utils/deprecation.d.ts.map +1 -1
  540. package/dist/utils/event-loop.d.ts.map +1 -1
  541. package/dist/utils/exif-orientation.d.ts.map +1 -1
  542. package/dist/utils/frontmatter.d.ts.map +1 -1
  543. package/dist/utils/fs-watch.d.ts.map +1 -1
  544. package/dist/utils/git-env.d.ts.map +1 -1
  545. package/dist/utils/git.d.ts.map +1 -1
  546. package/dist/utils/html.d.ts.map +1 -1
  547. package/dist/utils/image-convert.d.ts.map +1 -1
  548. package/dist/utils/image-process.d.ts.map +1 -1
  549. package/dist/utils/image-resize-core.d.ts.map +1 -1
  550. package/dist/utils/image-resize-worker.d.ts.map +1 -1
  551. package/dist/utils/image-resize.d.ts.map +1 -1
  552. package/dist/utils/json.d.ts.map +1 -1
  553. package/dist/utils/markit.d.ts.map +1 -1
  554. package/dist/utils/mime.d.ts.map +1 -1
  555. package/dist/utils/module-require.d.ts.map +1 -1
  556. package/dist/utils/open-browser.d.ts.map +1 -1
  557. package/dist/utils/paths.d.ts.map +1 -1
  558. package/dist/utils/photon.d.ts.map +1 -1
  559. package/dist/utils/pi-user-agent.d.ts.map +1 -1
  560. package/dist/utils/shell.d.ts.map +1 -1
  561. package/dist/utils/sleep.d.ts.map +1 -1
  562. package/dist/utils/split-launcher.d.ts.map +1 -1
  563. package/dist/utils/syntax-highlight.d.ts.map +1 -1
  564. package/dist/utils/tools-manager.d.ts.map +1 -1
  565. package/dist/utils/version-check.d.ts.map +1 -1
  566. package/dist/utils/windows-self-update.d.ts.map +1 -1
  567. package/docs/custom-provider.md +7 -1
  568. package/docs/extensions.md +19 -4
  569. package/docs/subagents.md +2 -0
  570. package/docs/workflows.md +2 -2
  571. package/examples/extensions/README.md +3 -1
  572. package/examples/extensions/subagent/schemas.ts +1 -1
  573. package/examples/extensions/tic-tac-toe.ts +1 -2
  574. package/examples/extensions/todo.ts +1 -2
  575. package/npm-shrinkwrap.json +214 -98
  576. package/package.json +12 -12
@@ -1 +1 @@
1
- {"version":3,"file":"projections.d.ts","sourceRoot":"","sources":["../../../../../../src/core/tools/ask-user-question/state/selectors/projections.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,wCAAwC,CAAC;AAC/E,OAAO,EAAsB,KAAK,oBAAoB,EAAE,MAAM,4CAA4C,CAAC;AAC3G,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,2CAA2C,CAAC;AACrF,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,+CAA+C,CAAC;AACtF,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,wCAAwC,CAAC;AAChF,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,kCAAkC,CAAC;AACpE,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,8BAA8B,CAAC;AAEhE,OAAO,KAAK,EAAE,cAAc,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAGpE,eAAO,MAAM,sBAAsB,EAAE,cAAc,CAAC,oBAAoB,CAevE,CAAC;AAEF,eAAO,MAAM,qBAAqB,EAAE,cAAc,CAAC,mBAAmB,CAWrE,CAAC;AAEF,eAAO,MAAM,uBAAuB,EAAE,cAAc,CAAC,iBAAiB,CAQrE,CAAC;AAEF,eAAO,MAAM,sBAAsB,EAAE,cAAc,CAAC,gBAAgB,CAIlE,CAAC;AAEH,eAAO,MAAM,iBAAiB,EAAE,cAAc,CAAC,WAAW,CAazD,CAAC;AAEF,eAAO,MAAM,kBAAkB,EAAE,cAAc,CAAC,gBAAgB,CAQ/D,CAAC;AAEF,eAAO,MAAM,iBAAiB,EAAE,cAAc,CAAC,WAAW,CAGxD,CAAC","sourcesContent":["import type { ChatRowViewProps } from \"../../view/components/chat-row-view.ts\";\nimport { MULTI_SUBMIT_LABEL, type MultiSelectViewProps } from \"../../view/components/multi-select-view.ts\";\nimport type { OptionListViewProps } from \"../../view/components/option-list-view.ts\";\nimport type { PreviewPaneProps } from \"../../view/components/preview/preview-pane.ts\";\nimport type { SubmitPickerProps } from \"../../view/components/submit-picker.ts\";\nimport type { TabBarProps } from \"../../view/components/tab-bar.ts\";\nimport type { DialogProps } from \"../../view/dialog-builder.ts\";\nimport { ROW_INTENT_META } from \"../row-intent.ts\";\nimport type { GlobalSelector, PerTabSelector } from \"./contract.ts\";\nimport { chatNumberingFor, selectActiveTabItems, selectConfirmedIndicator } from \"./derivations.ts\";\n\nexport const selectMultiSelectProps: PerTabSelector<MultiSelectViewProps> = (state, ctx) => {\n\tconst question = ctx.questions[ctx.i];\n\tif (!question) return { rows: [], nextActive: false, nextLabel: ROW_INTENT_META.next.label };\n\tconst focused = ctx.activeView === \"options\";\n\tconst rows: { checked: boolean; active: boolean }[] = [];\n\tfor (let i = 0; i < question.options.length; i++) {\n\t\trows.push({\n\t\t\tchecked: state.multiSelectChecked.has(i),\n\t\t\tactive: focused && i === state.optionIndex,\n\t\t});\n\t}\n\tconst nextActive = focused && state.optionIndex === question.options.length;\n\tconst isLastQuestion = ctx.i === ctx.questions.length - 1;\n\tconst nextLabel = isLastQuestion ? MULTI_SUBMIT_LABEL : ROW_INTENT_META.next.label;\n\treturn { rows, nextActive, nextLabel };\n};\n\nexport const selectOptionListProps: PerTabSelector<OptionListViewProps> = (state, ctx) => {\n\tconst items = ctx.itemsByTab[ctx.i] ?? [];\n\tconst focused = ctx.activeView === \"options\";\n\tconst confirmed = selectConfirmedIndicator(ctx.questions, state.currentTab, state.answers, items);\n\treturn {\n\t\tselectedIndex: state.optionIndex,\n\t\tfocused,\n\t\tinputBuffer: ctx.inputBuffer,\n\t\tinputCaret: ctx.inputCaret,\n\t\t...(confirmed ? { confirmed } : {}),\n\t};\n};\n\nexport const selectSubmitPickerProps: GlobalSelector<SubmitPickerProps> = (state, ctx) => {\n\tconst focused = ctx.activeView === \"submit\";\n\treturn {\n\t\trows: [\n\t\t\t{ active: focused && state.submitChoiceIndex === 0 },\n\t\t\t{ active: focused && state.submitChoiceIndex === 1 },\n\t\t],\n\t};\n};\n\nexport const selectPreviewPaneProps: PerTabSelector<PreviewPaneProps> = (state, ctx) => ({\n\tnotesVisible: state.notesVisible,\n\tselectedIndex: state.optionIndex,\n\tfocused: ctx.activeView === \"options\",\n});\n\nexport const selectTabBarProps: GlobalSelector<TabBarProps> = (state, ctx) => {\n\tconst tabs = ctx.questions.map((q, i) => ({\n\t\tlabel: q.header && q.header.length > 0 ? q.header : `Q${i + 1}`,\n\t\tanswered: state.answers.has(i),\n\t\tactive: i === state.currentTab,\n\t}));\n\treturn {\n\t\ttabs,\n\t\tsubmit: {\n\t\t\tactive: state.currentTab === ctx.questions.length,\n\t\t\tallAnswered: state.answers.size === ctx.questions.length && ctx.questions.length > 0,\n\t\t},\n\t};\n};\n\nexport const selectChatRowProps: GlobalSelector<ChatRowViewProps> = (state, ctx) => {\n\tconst activeItems = selectActiveTabItems(ctx.itemsByTab, state.currentTab, ctx.totalQuestions);\n\treturn {\n\t\tfocused: ctx.activeView === \"chat\",\n\t\tnumbering: chatNumberingFor(activeItems),\n\t\tinputBuffer: ctx.chatInputBuffer,\n\t\tinputCaret: ctx.chatInputCaret,\n\t};\n};\n\nexport const selectDialogProps: GlobalSelector<DialogProps> = (state, ctx) => ({\n\tstate,\n\tactivePreviewPane: ctx.activePreviewPane,\n});\n"]}
1
+ {"version":3,"file":"projections.d.ts","sourceRoot":"","sources":["../../../../../../src/core/tools/ask-user-question/state/selectors/projections.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,wCAAwC,CAAC;AAC/E,OAAO,EAAsB,KAAK,oBAAoB,EAAE,MAAM,4CAA4C,CAAC;AAC3G,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,2CAA2C,CAAC;AACrF,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,+CAA+C,CAAC;AACtF,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,wCAAwC,CAAC;AAChF,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,kCAAkC,CAAC;AACpE,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,8BAA8B,CAAC;AAEhE,OAAO,KAAK,EAAE,cAAc,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAGpE,eAAO,MAAM,sBAAsB,EAAE,cAAc,CAAC,oBAAoB,CAevE,CAAC;AAEF,eAAO,MAAM,qBAAqB,EAAE,cAAc,CAAC,mBAAmB,CAWrE,CAAC;AAEF,eAAO,MAAM,uBAAuB,EAAE,cAAc,CAAC,iBAAiB,CAQrE,CAAC;AAEF,eAAO,MAAM,sBAAsB,EAAE,cAAc,CAAC,gBAAgB,CAIlE,CAAC;AAEH,eAAO,MAAM,iBAAiB,EAAE,cAAc,CAAC,WAAW,CAazD,CAAC;AAEF,eAAO,MAAM,kBAAkB,EAAE,cAAc,CAAC,gBAAgB,CAQ/D,CAAC;AAEF,eAAO,MAAM,iBAAiB,EAAE,cAAc,CAAC,WAAW,CAGxD,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"state-reducer.d.ts","sourceRoot":"","sources":["../../../../../src/core/tools/ask-user-question/state/state-reducer.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAkB,YAAY,EAAE,mBAAmB,EAAE,MAAM,kBAAkB,CAAC;AAC1F,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,uCAAuC,CAAC;AAChF,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,iBAAiB,CAAC;AAI3D,OAAO,KAAK,EAAoB,kBAAkB,EAAE,MAAM,YAAY,CAAC;AAEvE,uGAAuG;AACvG,MAAM,WAAW,YAAY;IAC5B,SAAS,EAAE,SAAS,YAAY,EAAE,CAAC;IACnC,UAAU,EAAE,aAAa,CAAC,SAAS,kBAAkB,EAAE,CAAC,CAAC;CACzD;AAED;;;;;GAKG;AACH,MAAM,MAAM,MAAM,GACf;IAAE,IAAI,EAAE,kBAAkB,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,GAC1D;IAAE,IAAI,EAAE,iBAAiB,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,GAC1C;IAAE,IAAI,EAAE,mBAAmB,CAAC;IAAC,OAAO,EAAE,OAAO,CAAA;CAAE,GAC/C;IAAE,IAAI,EAAE,yBAAyB,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,GACjD;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,mBAAmB,CAAA;CAAE,CAAC;AAEjD,MAAM,WAAW,WAAW;IAC3B,KAAK,EAAE,kBAAkB,CAAC;IAC1B,OAAO,EAAE,SAAS,MAAM,EAAE,CAAC;CAC3B;AA4RD;;;;GAIG;AACH,wBAAgB,MAAM,CAAC,KAAK,EAAE,kBAAkB,EAAE,MAAM,EAAE,mBAAmB,EAAE,GAAG,EAAE,YAAY,GAAG,WAAW,CAG7G","sourcesContent":["import type { QuestionAnswer, QuestionData, QuestionnaireResult } from \"../tool/types.ts\";\nimport type { WrappingSelectItem } from \"../view/components/wrapping-select.ts\";\nimport type { QuestionnaireAction } from \"./key-router.ts\";\nimport { readInlineCaret, resolveInlineDraftValue, withInlineDraft } from \"./inline-input.ts\";\nimport { ROW_INTENT_META } from \"./row-intent.ts\";\nimport { computeFocusedOptionHasPreview } from \"./selectors/derivations.ts\";\nimport type { InlineInputOwner, QuestionnaireState } from \"./state.ts\";\n\n/** Session-lifetime constants. No live-component reads — peripheral values live on canonical state. */\nexport interface ApplyContext {\n\tquestions: readonly QuestionData[];\n\titemsByTab: ReadonlyArray<readonly WrappingSelectItem[]>;\n}\n\n/**\n * Declarative side-effects emitted by `reduce`. The runtime executes them after\n * committing the new state, then asks the props-adapter to re-project. Closed set —\n * adding an effect requires updating both the union AND the runtime's `runEffect` switch\n * (compiler-enforced exhaustive). No string-keyed escape hatch.\n */\nexport type Effect =\n\t| { kind: \"set_input_buffer\"; value: string; caret: number }\n\t| { kind: \"set_notes_value\"; value: string }\n\t| { kind: \"set_notes_focused\"; focused: boolean }\n\t| { kind: \"forward_notes_keystroke\"; data: string }\n\t| { kind: \"done\"; result: QuestionnaireResult };\n\nexport interface ApplyResult {\n\tstate: QuestionnaireState;\n\teffects: readonly Effect[];\n}\n\nfunction orderedAnswers(state: QuestionnaireState, questions: readonly QuestionData[]): QuestionAnswer[] {\n\tconst out: QuestionAnswer[] = [];\n\tfor (let i = 0; i < questions.length; i++) {\n\t\tconst a = state.answers.get(i);\n\t\tif (a) out.push(a);\n\t}\n\treturn out;\n}\n\nfunction withFocusedOptionHasPreview(\n\tstate: QuestionnaireState,\n\tquestions: readonly QuestionData[],\n): QuestionnaireState {\n\tconst focusedOptionHasPreview = computeFocusedOptionHasPreview(questions, state.currentTab, state.optionIndex);\n\tif (state.focusedOptionHasPreview === focusedOptionHasPreview) return state;\n\treturn { ...state, focusedOptionHasPreview };\n}\n\nfunction syncMultiSelectFromAnswers(\n\tanswers: ReadonlyMap<number, QuestionAnswer>,\n\tquestions: readonly QuestionData[],\n\ttab: number,\n): ReadonlySet<number> {\n\tconst q = questions[tab];\n\tif (!q?.multiSelect) return new Set();\n\tconst saved = answers.get(tab);\n\tconst labels = saved?.selected ?? [];\n\tconst indices = new Set<number>();\n\tfor (let i = 0; i < q.options.length; i++) {\n\t\tif (labels.includes(q.options[i]!.label)) indices.add(i);\n\t}\n\treturn indices;\n}\n\nfunction persistMultiSelectAnswer(state: QuestionnaireState, ctx: ApplyContext): ReadonlyMap<number, QuestionAnswer> {\n\tconst q = ctx.questions[state.currentTab];\n\tif (!q?.multiSelect) return state.answers;\n\tconst selected: string[] = [];\n\tfor (let i = 0; i < q.options.length; i++) {\n\t\tif (state.multiSelectChecked.has(i)) selected.push(q.options[i]!.label);\n\t}\n\tconst out = new Map(state.answers);\n\tif (selected.length === 0) {\n\t\tout.delete(state.currentTab);\n\t\treturn out;\n\t}\n\tconst pendingNotes = state.notesByTab.get(state.currentTab);\n\tout.set(state.currentTab, {\n\t\tquestionIndex: state.currentTab,\n\t\tquestion: q.question,\n\t\tkind: \"multi\",\n\t\tanswer: null,\n\t\tselected,\n\t\t...(pendingNotes && pendingNotes.length > 0 ? { notes: pendingNotes } : {}),\n\t});\n\treturn out;\n}\n\nfunction clampCaret(value: string, caret: number | undefined): number {\n\tif (caret === undefined || !Number.isFinite(caret)) return value.length;\n\treturn Math.max(0, Math.min(caret, value.length));\n}\n\nfunction inlineOwnerForItem(item: WrappingSelectItem | undefined): InlineInputOwner | null {\n\tif (!item || !ROW_INTENT_META[item.kind].activatesInputMode) return null;\n\tswitch (item.kind) {\n\t\tcase \"other\":\n\t\tcase \"chat\":\n\t\t\treturn item.kind;\n\t\tcase \"option\":\n\t\tcase \"next\":\n\t\t\treturn null;\n\t}\n}\n\nfunction hydrateInlineInputResult(state: QuestionnaireState, owner: InlineInputOwner): ApplyResult {\n\tconst value = resolveInlineDraftValue(state, owner);\n\tconst caret = clampCaret(value, readInlineCaret(state, owner));\n\treturn {\n\t\tstate: withInlineDraft({ ...state, inputMode: true, inlineInputOwner: owner }, owner, value, caret),\n\t\teffects: [{ kind: \"set_input_buffer\", value, caret }],\n\t};\n}\n\nfunction switchTabResult(state: QuestionnaireState, nextTab: number, ctx: ApplyContext): ApplyResult {\n\tconst notesValue = state.notesByTab.get(nextTab) ?? state.answers.get(nextTab)?.notes ?? \"\";\n\tconst transitioned: QuestionnaireState = {\n\t\t...state,\n\t\tcurrentTab: nextTab,\n\t\toptionIndex: 0,\n\t\tinputMode: false,\n\t\tinlineInputOwner: null,\n\t\tnotesVisible: false,\n\t\tchatFocused: false,\n\t\tsubmitChoiceIndex: 0,\n\t\tmultiSelectChecked: syncMultiSelectFromAnswers(state.answers, ctx.questions, nextTab),\n\t\tnotesDraft: notesValue,\n\t};\n\tconst finalState = withFocusedOptionHasPreview(transitioned, ctx.questions);\n\treturn {\n\t\tstate: finalState,\n\t\teffects: [\n\t\t\t{ kind: \"set_notes_focused\", focused: false },\n\t\t\t{ kind: \"set_notes_value\", value: notesValue },\n\t\t],\n\t};\n}\n\nfunction doneFor(state: QuestionnaireState, ctx: ApplyContext, cancelled: boolean): ApplyResult {\n\tconst result: QuestionnaireResult = { answers: orderedAnswers(state, ctx.questions), cancelled };\n\treturn { state, effects: [{ kind: \"done\", result }] };\n}\n\n/**\n * Per-kind handler signature: action payload narrows to the matching union member\n * via `Extract`, so handlers consume fully-typed actions without `as` casts.\n */\ntype Handler<K extends QuestionnaireAction[\"kind\"]> = (\n\tstate: QuestionnaireState,\n\taction: Extract<QuestionnaireAction, { kind: K }>,\n\tctx: ApplyContext,\n) => ApplyResult;\n\nconst navHandler: Handler<\"nav\"> = (state, action, ctx) => {\n\tconst items = ctx.itemsByTab[state.currentTab] ?? [];\n\tconst item = items[action.nextIndex];\n\tconst owner = inlineOwnerForItem(item);\n\tconst next = withFocusedOptionHasPreview(\n\t\t{\n\t\t\t...state,\n\t\t\tchatFocused: false,\n\t\t\toptionIndex: action.nextIndex,\n\t\t\tinputMode: owner !== null,\n\t\t\tinlineInputOwner: owner,\n\t\t},\n\t\tctx.questions,\n\t);\n\treturn owner ? hydrateInlineInputResult(next, owner) : { state: next, effects: [] };\n};\n\nconst tabSwitchHandler: Handler<\"tab_switch\"> = (state, action, ctx) => switchTabResult(state, action.nextTab, ctx);\n\nconst confirmHandler: Handler<\"confirm\"> = (state, action, ctx) => {\n\tlet answer = action.answer;\n\tif (answer.kind === \"option\" && answer.answer) {\n\t\tconst q = ctx.questions[answer.questionIndex];\n\t\tconst matched = q?.options.find((o) => o.label === answer.answer);\n\t\tif (matched?.preview && matched.preview.length > 0) {\n\t\t\tanswer = { ...answer, preview: matched.preview };\n\t\t}\n\t}\n\tconst pendingNotes = state.notesByTab.get(answer.questionIndex);\n\tif (pendingNotes && pendingNotes.length > 0) {\n\t\tanswer = { ...answer, notes: pendingNotes };\n\t}\n\tconst answers = new Map(state.answers);\n\tanswers.set(answer.questionIndex, answer);\n\tconst next: QuestionnaireState = { ...state, answers };\n\tif (answer.kind === \"chat\") return doneFor(next, ctx, false);\n\tif (action.autoAdvanceTab !== undefined) return switchTabResult(next, action.autoAdvanceTab, ctx);\n\treturn doneFor(next, ctx, false);\n};\n\nconst toggleHandler: Handler<\"toggle\"> = (state, action, ctx) => {\n\tconst checked = new Set(state.multiSelectChecked);\n\tif (checked.has(action.index)) checked.delete(action.index);\n\telse checked.add(action.index);\n\tconst intermediate: QuestionnaireState = { ...state, multiSelectChecked: checked };\n\tconst answers = persistMultiSelectAnswer(intermediate, ctx);\n\treturn { state: { ...intermediate, answers }, effects: [] };\n};\n\nconst multiConfirmHandler: Handler<\"multi_confirm\"> = (state, action, ctx) => {\n\tconst q = ctx.questions[state.currentTab];\n\tif (!q) return { state, effects: [] };\n\tconst pendingNotes = state.notesByTab.get(state.currentTab);\n\tconst answers = new Map(state.answers);\n\tanswers.set(state.currentTab, {\n\t\tquestionIndex: state.currentTab,\n\t\tquestion: q.question,\n\t\tkind: \"multi\",\n\t\tanswer: null,\n\t\tselected: action.selected,\n\t\t...(pendingNotes && pendingNotes.length > 0 ? { notes: pendingNotes } : {}),\n\t});\n\tconst synced: QuestionnaireState = {\n\t\t...state,\n\t\tanswers,\n\t\tmultiSelectChecked: syncMultiSelectFromAnswers(answers, ctx.questions, state.currentTab),\n\t};\n\tif (action.autoAdvanceTab !== undefined) return switchTabResult(synced, action.autoAdvanceTab, ctx);\n\treturn doneFor(synced, ctx, false);\n};\n\nconst notesEnterHandler: Handler<\"notes_enter\"> = (state, _action, _ctx) => {\n\tconst value = state.answers.get(state.currentTab)?.notes ?? \"\";\n\treturn {\n\t\tstate: { ...state, notesVisible: true, notesDraft: value },\n\t\teffects: [\n\t\t\t{ kind: \"set_notes_value\", value },\n\t\t\t{ kind: \"set_notes_focused\", focused: true },\n\t\t],\n\t};\n};\n\nconst notesExitHandler: Handler<\"notes_exit\"> = (state, _action, _ctx) => {\n\tconst trimmed = state.notesDraft.trim();\n\tconst notes = new Map(state.notesByTab);\n\tconst answers = new Map(state.answers);\n\tif (trimmed.length === 0) {\n\t\tnotes.delete(state.currentTab);\n\t\tconst prev = answers.get(state.currentTab);\n\t\tif (prev?.notes) {\n\t\t\tconst stripped = { ...prev };\n\t\t\tdelete (stripped as { notes?: string }).notes;\n\t\t\tanswers.set(state.currentTab, stripped);\n\t\t}\n\t} else {\n\t\tnotes.set(state.currentTab, trimmed);\n\t\tconst prev = answers.get(state.currentTab);\n\t\tif (prev) answers.set(state.currentTab, { ...prev, notes: trimmed });\n\t}\n\treturn {\n\t\tstate: { ...state, notesByTab: notes, answers, notesVisible: false },\n\t\teffects: [{ kind: \"set_notes_focused\", focused: false }],\n\t};\n};\n\nconst focusOptionsHandler: Handler<\"focus_options\"> = (state, action, ctx) => {\n\tconst items = ctx.itemsByTab[state.currentTab] ?? [];\n\tconst focused = items[action.optionIndex];\n\tconst owner = inlineOwnerForItem(focused);\n\tconst next = withFocusedOptionHasPreview(\n\t\t{\n\t\t\t...state,\n\t\t\tchatFocused: false,\n\t\t\toptionIndex: action.optionIndex,\n\t\t\tinputMode: owner !== null,\n\t\t\tinlineInputOwner: owner,\n\t\t},\n\t\tctx.questions,\n\t);\n\treturn owner ? hydrateInlineInputResult(next, owner) : { state: next, effects: [] };\n};\n\nconst cancelHandler: Handler<\"cancel\"> = (s, _a, c) => doneFor(s, c, true);\nconst submitHandler: Handler<\"submit\"> = (s, _a, c) => doneFor(s, c, false);\nconst submitNavHandler: Handler<\"submit_nav\"> = (s, a, _c) => ({\n\tstate: { ...s, submitChoiceIndex: a.nextIndex },\n\teffects: [],\n});\nconst focusChatHandler: Handler<\"focus_chat\"> = (s, _a, _c) =>\n\thydrateInlineInputResult({ ...s, chatFocused: true, inputMode: true, inlineInputOwner: \"chat\" }, \"chat\");\nconst notesForwardHandler: Handler<\"notes_forward\"> = (s, a, _c) => ({\n\tstate: s,\n\teffects: [{ kind: \"forward_notes_keystroke\", data: a.data }],\n});\nconst ignoreHandler: Handler<\"ignore\"> = (s, _a, _c) => ({ state: s, effects: [] });\n\n/**\n * Compile-time-exhaustive dispatch table. `{ [K in Kind]: Handler<K> }` requires\n * an entry per union member — adding a new `QuestionnaireAction` variant fails to\n * compile here until a handler is registered, mirroring the `Record<RowKind, …>`\n * pattern used by `ROW_INTENT_META`.\n */\nconst HANDLERS: { [K in QuestionnaireAction[\"kind\"]]: Handler<K> } = {\n\tnav: navHandler,\n\ttab_switch: tabSwitchHandler,\n\tconfirm: confirmHandler,\n\ttoggle: toggleHandler,\n\tmulti_confirm: multiConfirmHandler,\n\tcancel: cancelHandler,\n\tnotes_enter: notesEnterHandler,\n\tnotes_exit: notesExitHandler,\n\tnotes_forward: notesForwardHandler,\n\tsubmit: submitHandler,\n\tsubmit_nav: submitNavHandler,\n\tfocus_chat: focusChatHandler,\n\tfocus_options: focusOptionsHandler,\n\tignore: ignoreHandler,\n};\n\n/**\n * Pure reducer: (state, action, ctx) → (state, Effect[]). Mirrors `rpiv-todo`'s `applyTaskMutation`.\n * Delegates to `HANDLERS` — per-kind handlers above are pure, named, and individually testable.\n * `ignore` is also handled outside the reducer by `handleIgnoreInline` in the runtime fast path.\n */\nexport function reduce(state: QuestionnaireState, action: QuestionnaireAction, ctx: ApplyContext): ApplyResult {\n\tconst handler = HANDLERS[action.kind] as Handler<typeof action.kind>;\n\treturn handler(state, action as never, ctx);\n}\n"]}
1
+ {"version":3,"file":"state-reducer.d.ts","sourceRoot":"","sources":["../../../../../src/core/tools/ask-user-question/state/state-reducer.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAkB,YAAY,EAAE,mBAAmB,EAAE,MAAM,kBAAkB,CAAC;AAC1F,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,uCAAuC,CAAC;AAChF,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,iBAAiB,CAAC;AAI3D,OAAO,KAAK,EAAoB,kBAAkB,EAAE,MAAM,YAAY,CAAC;AAEvE,uGAAuG;AACvG,MAAM,WAAW,YAAY;IAC5B,SAAS,EAAE,SAAS,YAAY,EAAE,CAAC;IACnC,UAAU,EAAE,aAAa,CAAC,SAAS,kBAAkB,EAAE,CAAC,CAAC;CACzD;AAED;;;;;GAKG;AACH,MAAM,MAAM,MAAM,GACf;IAAE,IAAI,EAAE,kBAAkB,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,GAC1D;IAAE,IAAI,EAAE,iBAAiB,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,GAC1C;IAAE,IAAI,EAAE,mBAAmB,CAAC;IAAC,OAAO,EAAE,OAAO,CAAA;CAAE,GAC/C;IAAE,IAAI,EAAE,yBAAyB,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,GACjD;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,mBAAmB,CAAA;CAAE,CAAC;AAEjD,MAAM,WAAW,WAAW;IAC3B,KAAK,EAAE,kBAAkB,CAAC;IAC1B,OAAO,EAAE,SAAS,MAAM,EAAE,CAAC;CAC3B;AA4RD;;;;GAIG;AACH,wBAAgB,MAAM,CAAC,KAAK,EAAE,kBAAkB,EAAE,MAAM,EAAE,mBAAmB,EAAE,GAAG,EAAE,YAAY,GAAG,WAAW,CAG7G"}
@@ -1 +1 @@
1
- {"version":3,"file":"state.d.ts","sourceRoot":"","sources":["../../../../../src/core/tools/ask-user-question/state/state.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AACrE,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,uCAAuC,CAAC;AAChF,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,iBAAiB,CAAC;AAE/C,MAAM,MAAM,gBAAgB,GAAG,OAAO,CAAC,OAAO,EAAE,OAAO,GAAG,MAAM,CAAC,CAAC;AAElE;;;GAGG;AACH,MAAM,WAAW,kBAAkB;IAClC,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,OAAO,CAAC;IACnB,YAAY,EAAE,OAAO,CAAC;IACtB,WAAW,EAAE,OAAO,CAAC;IACrB,2FAA2F;IAC3F,gBAAgB,EAAE,gBAAgB,GAAG,IAAI,CAAC;IAC1C,OAAO,EAAE,WAAW,CAAC,MAAM,EAAE,cAAc,CAAC,CAAC;IAC7C,kBAAkB,EAAE,WAAW,CAAC,MAAM,CAAC,CAAC;IACxC;;;;OAIG;IACH,UAAU,EAAE,WAAW,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACxC,gIAAgI;IAChI,uBAAuB,EAAE,OAAO,CAAC;IACjC,0FAA0F;IAC1F,iBAAiB,EAAE,MAAM,CAAC;IAC1B,uGAAuG;IACvG,UAAU,EAAE,MAAM,CAAC;IACnB,4GAA4G;IAC5G,gBAAgB,EAAE,WAAW,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC9C,uEAAuE;IACvE,gBAAgB,EAAE,WAAW,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC9C,uEAAuE;IACvE,cAAc,EAAE,WAAW,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC5C,qEAAqE;IACrE,cAAc,EAAE,WAAW,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAC5C;AAED;;;GAGG;AACH,MAAM,WAAW,oBAAoB;IACpC,WAAW,EAAE;QAAE,OAAO,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAA;KAAE,CAAC;IAC9D,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,SAAS,YAAY,EAAE,CAAC;IACnC,OAAO,EAAE,OAAO,CAAC;IACjB,WAAW,EAAE,kBAAkB,GAAG,SAAS,CAAC;IAC5C,KAAK,EAAE,SAAS,kBAAkB,EAAE,CAAC;CACrC","sourcesContent":["import type { QuestionAnswer, QuestionData } from \"../tool/types.ts\";\nimport type { WrappingSelectItem } from \"../view/components/wrapping-select.ts\";\nimport type { RowKind } from \"./row-intent.ts\";\n\nexport type InlineInputOwner = Extract<RowKind, \"other\" | \"chat\">;\n\n/**\n * Canonical state for the questionnaire dialog. Single source of truth — both the\n * dispatcher (`routeKey`) and the view layer read this same shape.\n */\nexport interface QuestionnaireState {\n\tcurrentTab: number;\n\toptionIndex: number;\n\tinputMode: boolean;\n\tnotesVisible: boolean;\n\tchatFocused: boolean;\n\t/** Active owner of the shared inline input, or null when no inline row is being edited. */\n\tinlineInputOwner: InlineInputOwner | null;\n\tanswers: ReadonlyMap<number, QuestionAnswer>;\n\tmultiSelectChecked: ReadonlySet<number>;\n\t/**\n\t * Pre-answer notes side-band, keyed by tab index. Decoupled from `answers` so adding\n\t * notes does NOT mark a question answered (the Submit-tab missing-check would falsely\n\t * pass otherwise). Merged into the answer at confirm time.\n\t */\n\tnotesByTab: ReadonlyMap<number, string>;\n\t/** True iff the focused option carries a non-empty `preview` string. Gates `notes_enter` and the \"n to add notes\" hint chip. */\n\tfocusedOptionHasPreview: boolean;\n\t/** Focused row in the Submit-tab picker (0 = Submit, 1 = Cancel). Reset on tab switch. */\n\tsubmitChoiceIndex: number;\n\t/** Canonical mirror of the in-flight notes editor; runtime mirrors after `forward_notes_keystroke`. */\n\tnotesDraft: string;\n\t/** In-flight custom \"Type something.\" drafts keyed by question tab. Preserved when focus leaves the row. */\n\tcustomDraftByTab: ReadonlyMap<number, string>;\n\t/** Caret offsets for in-flight custom drafts keyed by question tab. */\n\tcustomCaretByTab: ReadonlyMap<number, number>;\n\t/** In-flight \"Chat about this\" inline drafts keyed by question tab. */\n\tchatDraftByTab: ReadonlyMap<number, string>;\n\t/** Caret offsets for in-flight chat drafts keyed by question tab. */\n\tchatCaretByTab: ReadonlyMap<number, number>;\n}\n\n/**\n * Per-tick context the dispatcher needs alongside canonical state. Held separately\n * because `keybindings` / `inputBuffer` must never reach view setProps consumers.\n */\nexport interface QuestionnaireRuntime {\n\tkeybindings: { matches(data: string, name: string): boolean };\n\tinputBuffer: string;\n\tquestions: readonly QuestionData[];\n\tisMulti: boolean;\n\tcurrentItem: WrappingSelectItem | undefined;\n\titems: readonly WrappingSelectItem[];\n}\n"]}
1
+ {"version":3,"file":"state.d.ts","sourceRoot":"","sources":["../../../../../src/core/tools/ask-user-question/state/state.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AACrE,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,uCAAuC,CAAC;AAChF,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,iBAAiB,CAAC;AAE/C,MAAM,MAAM,gBAAgB,GAAG,OAAO,CAAC,OAAO,EAAE,OAAO,GAAG,MAAM,CAAC,CAAC;AAElE;;;GAGG;AACH,MAAM,WAAW,kBAAkB;IAClC,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,OAAO,CAAC;IACnB,YAAY,EAAE,OAAO,CAAC;IACtB,WAAW,EAAE,OAAO,CAAC;IACrB,2FAA2F;IAC3F,gBAAgB,EAAE,gBAAgB,GAAG,IAAI,CAAC;IAC1C,OAAO,EAAE,WAAW,CAAC,MAAM,EAAE,cAAc,CAAC,CAAC;IAC7C,kBAAkB,EAAE,WAAW,CAAC,MAAM,CAAC,CAAC;IACxC;;;;OAIG;IACH,UAAU,EAAE,WAAW,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACxC,gIAAgI;IAChI,uBAAuB,EAAE,OAAO,CAAC;IACjC,0FAA0F;IAC1F,iBAAiB,EAAE,MAAM,CAAC;IAC1B,uGAAuG;IACvG,UAAU,EAAE,MAAM,CAAC;IACnB,4GAA4G;IAC5G,gBAAgB,EAAE,WAAW,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC9C,uEAAuE;IACvE,gBAAgB,EAAE,WAAW,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC9C,uEAAuE;IACvE,cAAc,EAAE,WAAW,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC5C,qEAAqE;IACrE,cAAc,EAAE,WAAW,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAC5C;AAED;;;GAGG;AACH,MAAM,WAAW,oBAAoB;IACpC,WAAW,EAAE;QAAE,OAAO,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAA;KAAE,CAAC;IAC9D,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,SAAS,YAAY,EAAE,CAAC;IACnC,OAAO,EAAE,OAAO,CAAC;IACjB,WAAW,EAAE,kBAAkB,GAAG,SAAS,CAAC;IAC5C,KAAK,EAAE,SAAS,kBAAkB,EAAE,CAAC;CACrC"}
@@ -1 +1 @@
1
- {"version":3,"file":"format-answer.d.ts","sourceRoot":"","sources":["../../../../../src/core/tools/ask-user-question/tool/format-answer.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAEjD;;;;GAIG;AACH,eAAO,MAAM,yBAAyB,oGAC4D,CAAC;AAEnG;;;;GAIG;AACH,eAAO,MAAM,oBAAoB,kCAAkC,CAAC;AAEpE;;;;GAIG;AACH,eAAO,MAAM,oBAAoB,eAAe,CAAC;AAEjD,MAAM,MAAM,mBAAmB,GAAG,SAAS,GAAG,UAAU,CAAC;AAEzD;;;;GAIG;AACH,wBAAgB,gBAAgB,CAAC,CAAC,EAAE,cAAc,GAAG,MAAM,GAAG,SAAS,CAKtE;AAED;;;;;;;GAOG;AACH,wBAAgB,kBAAkB,CAAC,CAAC,EAAE,cAAc,EAAE,OAAO,EAAE,mBAAmB,GAAG,MAAM,CAc1F","sourcesContent":["import { ROW_INTENT_META } from \"../state/row-intent.ts\";\nimport type { QuestionAnswer } from \"./types.ts\";\n\n/**\n * Continuation message used in the LLM-facing envelope. Two-sentence form —\n * the model needs the \"Stop…wait…\" directive to know what to do next after the\n * user picks chat instead of answering.\n */\nexport const CHAT_CONTINUATION_MESSAGE =\n\t\"User wants to chat about this. Stop the current task flow and wait for the user's next message.\";\n\n/**\n * One-sentence summary form shown in the on-screen Submit-tab review pane. The dialog\n * already shows the question; the imperative continuation directive belongs in the\n * envelope, not in the user-facing summary box.\n */\nexport const CHAT_SUMMARY_MESSAGE = \"User wants to chat about this\";\n\n/**\n * Placeholder for empty / null answer text. Used uniformly across both variants — the\n * earlier `(no answer)` fallback in the dialog summary was accidental drift; tests pin\n * `(no input)` only.\n */\nexport const NO_INPUT_PLACEHOLDER = \"(no input)\";\n\nexport type FormatAnswerVariant = \"summary\" | \"envelope\";\n\n/**\n * Return the user-provided chat message, or undefined for the legacy signal-only\n * chat answer. Whitespace-only text and the reserved sentinel label both keep\n * the legacy stop/wait semantics.\n */\nexport function chatAnswerIntent(a: QuestionAnswer): string | undefined {\n\tif (a.kind !== \"chat\" || typeof a.answer !== \"string\") return undefined;\n\tconst trimmed = a.answer.trim();\n\tif (trimmed.length === 0 || trimmed === ROW_INTENT_META.chat.label) return undefined;\n\treturn a.answer;\n}\n\n/**\n * Format a `QuestionAnswer` to its scalar string form. Variant controls only the\n * `kind: \"chat\"` branch — the envelope's stop/wait directive is needed by the LLM,\n * the dialog summary's one-sentence reminder is not. All other branches return identical\n * strings; the `kind: \"custom\"` empty-string handling and the option fallback both unify\n * on `NO_INPUT_PLACEHOLDER`. Switch is exhaustive — non-`void` return enforces every\n * variant is handled.\n */\nexport function formatAnswerScalar(a: QuestionAnswer, variant: FormatAnswerVariant): string {\n\tswitch (a.kind) {\n\t\tcase \"chat\": {\n\t\t\tconst typedChat = chatAnswerIntent(a);\n\t\t\tif (typedChat !== undefined) return typedChat;\n\t\t\treturn variant === \"envelope\" ? CHAT_CONTINUATION_MESSAGE : CHAT_SUMMARY_MESSAGE;\n\t\t}\n\t\tcase \"multi\":\n\t\t\treturn a.selected && a.selected.length > 0 ? a.selected.join(\", \") : NO_INPUT_PLACEHOLDER;\n\t\tcase \"custom\":\n\t\t\treturn a.answer && a.answer.length > 0 ? a.answer : NO_INPUT_PLACEHOLDER;\n\t\tcase \"option\":\n\t\t\treturn a.answer ?? NO_INPUT_PLACEHOLDER;\n\t}\n}\n"]}
1
+ {"version":3,"file":"format-answer.d.ts","sourceRoot":"","sources":["../../../../../src/core/tools/ask-user-question/tool/format-answer.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAEjD;;;;GAIG;AACH,eAAO,MAAM,yBAAyB,oGAC4D,CAAC;AAEnG;;;;GAIG;AACH,eAAO,MAAM,oBAAoB,kCAAkC,CAAC;AAEpE;;;;GAIG;AACH,eAAO,MAAM,oBAAoB,eAAe,CAAC;AAEjD,MAAM,MAAM,mBAAmB,GAAG,SAAS,GAAG,UAAU,CAAC;AAEzD;;;;GAIG;AACH,wBAAgB,gBAAgB,CAAC,CAAC,EAAE,cAAc,GAAG,MAAM,GAAG,SAAS,CAKtE;AAED;;;;;;;GAOG;AACH,wBAAgB,kBAAkB,CAAC,CAAC,EAAE,cAAc,EAAE,OAAO,EAAE,mBAAmB,GAAG,MAAM,CAc1F"}
@@ -1 +1 @@
1
- {"version":3,"file":"response-envelope.d.ts","sourceRoot":"","sources":["../../../../../src/core/tools/ask-user-question/tool/response-envelope.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,cAAc,EAAE,mBAAmB,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAEtF,eAAO,MAAM,eAAe,sCAAsC,CAAC;AACnE,eAAO,MAAM,eAAe,sCAAsC,CAAC;AACnE,eAAO,MAAM,eAAe,0DAA0D,CAAC;AAMvF;;;GAGG;AACH,wBAAgB,aAAa,CAAC,MAAM,EAAE,mBAAmB,GAAG,OAAO,CAElE;AAED;;;;;;;;;GASG;AACH,wBAAgB,0BAA0B,CAAC,MAAM,EAAE,mBAAmB,GAAG,IAAI,GAAG,SAAS,EAAE,MAAM,EAAE,cAAc;;cA8C7F,MAAM;;;;;EAfzB;AAED;;;GAGG;AACH,wBAAgB,kBAAkB,CAAC,CAAC,EAAE,cAAc,GAAG,MAAM,CAK5D;AAED,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,mBAAmB,EAAE,OAAO,CAAC,EAAE;IAAE,SAAS,CAAC,EAAE,OAAO,CAAA;CAAE;;cAEzF,MAAM;;;;;EAIzB","sourcesContent":["import { chatAnswerIntent, formatAnswerScalar } from \"./format-answer.ts\";\nimport type { QuestionAnswer, QuestionnaireResult, QuestionParams } from \"./types.ts\";\n\nexport const DECLINE_MESSAGE = \"User declined to answer questions\";\nexport const ENVELOPE_PREFIX = \"User has answered your questions:\";\nexport const ENVELOPE_SUFFIX = \"You can now continue with the user's answers in mind.\";\nconst CHAT_TERMINATION_DIRECTIVE =\n\t\"User wants to chat about this before choosing. Stop the current task flow and wait for the user's next message.\";\nconst TYPED_CHAT_DIRECTIVE =\n\t\"User wants to chat about this before choosing and provided inline text. Stop the structured-choice flow and respond to the user's message.\";\n\n/**\n * True when any answer in the result carries `kind: \"chat\"`.\n * Used by `buildQuestionnaireResponse` to switch to the terminate path.\n */\nexport function hasChatAnswer(result: QuestionnaireResult): boolean {\n\treturn result.answers.some((a) => a.kind === \"chat\");\n}\n\n/**\n * Map a `QuestionnaireResult` (or null/cancelled) to the LLM-facing tool envelope.\n * Pure of `(result, params)`; cancelled and non-chat \"no segments\" both fall to\n * `DECLINE_MESSAGE` so the model sees a single canonical \"didn't answer\" signal\n * regardless of why.\n *\n * Chat rule: signal-only chat keeps the legacy `terminate: true` stop/wait\n * wording. Typed chat omits `terminate` and the generic continuation suffix so\n * the model can respond to the inline user message in this same turn.\n */\nexport function buildQuestionnaireResponse(result: QuestionnaireResult | null | undefined, params: QuestionParams) {\n\tif (!result || result.cancelled) {\n\t\treturn buildToolResult(DECLINE_MESSAGE, {\n\t\t\tanswers: result?.answers ?? [],\n\t\t\tcancelled: true,\n\t\t});\n\t}\n\tconst containsChatAnswer = hasChatAnswer(result);\n\tconst segments: string[] = [];\n\tfor (let i = 0; i < params.questions.length; i++) {\n\t\tconst a = result.answers.find((x) => x.questionIndex === i);\n\t\tif (a) segments.push(buildAnswerSegment(a));\n\t}\n\tif (containsChatAnswer) {\n\t\tconst answerSegments = segments.length > 0 ? ` ${segments.join(\" \")}` : \"\";\n\t\t// Mixed-dialog precedence: signal-only chat WINS. In a multi-question dialog where one\n\t\t// question carried typed chat and another is signal-only, the whole envelope takes the\n\t\t// `terminate: true` stop/wait path — a bare \"chat about this\" is an explicit request to\n\t\t// pause the structured flow, so it dominates. The typed message is not lost: it still\n\t\t// rides along in `answerSegments` for context. This precedence is a deliberate product\n\t\t// decision; mixing typed + signal-only chat across questions is a rare edge.\n\t\tconst hasSignalOnlyChat = result.answers.some((a) => a.kind === \"chat\" && chatAnswerIntent(a) === undefined);\n\t\tif (!hasSignalOnlyChat) {\n\t\t\treturn buildToolResult(`${TYPED_CHAT_DIRECTIVE}${answerSegments}`, result);\n\t\t}\n\t\treturn buildToolResult(`${CHAT_TERMINATION_DIRECTIVE}${answerSegments}`, result, { terminate: true });\n\t}\n\tif (segments.length === 0) {\n\t\treturn buildToolResult(DECLINE_MESSAGE, { answers: result.answers, cancelled: true });\n\t}\n\treturn buildToolResult(`${ENVELOPE_PREFIX} ${segments.join(\" \")} ${ENVELOPE_SUFFIX}`, result);\n}\n\n/**\n * Format a single answer segment for the envelope. Pure of `a`. The `\"Q\"=\"A\"` shape and\n * the optional `selected preview:` / `user notes:` suffixes are pinned by envelope tests.\n */\nexport function buildAnswerSegment(a: QuestionAnswer): string {\n\tconst parts: string[] = [`\"${a.question}\"=\"${formatAnswerScalar(a, \"envelope\")}\"`];\n\tif (a.preview && a.preview.length > 0) parts.push(`selected preview: ${a.preview}`);\n\tif (a.notes && a.notes.length > 0) parts.push(`user notes: ${a.notes}`);\n\treturn `${parts.join(\". \")}.`;\n}\n\nexport function buildToolResult(text: string, details: QuestionnaireResult, options?: { terminate?: boolean }) {\n\treturn {\n\t\tcontent: [{ type: \"text\" as const, text }],\n\t\tdetails,\n\t\t...(options?.terminate === true ? { terminate: true } : {}),\n\t};\n}\n"]}
1
+ {"version":3,"file":"response-envelope.d.ts","sourceRoot":"","sources":["../../../../../src/core/tools/ask-user-question/tool/response-envelope.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,cAAc,EAAE,mBAAmB,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAEtF,eAAO,MAAM,eAAe,sCAAsC,CAAC;AACnE,eAAO,MAAM,eAAe,sCAAsC,CAAC;AACnE,eAAO,MAAM,eAAe,0DAA0D,CAAC;AAMvF;;;GAGG;AACH,wBAAgB,aAAa,CAAC,MAAM,EAAE,mBAAmB,GAAG,OAAO,CAElE;AAED;;;;;;;;;GASG;AACH,wBAAgB,0BAA0B,CAAC,MAAM,EAAE,mBAAmB,GAAG,IAAI,GAAG,SAAS,EAAE,MAAM,EAAE,cAAc;;cA8C7F,MAAM;;;;;EAfzB;AAED;;;GAGG;AACH,wBAAgB,kBAAkB,CAAC,CAAC,EAAE,cAAc,GAAG,MAAM,CAK5D;AAED,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,mBAAmB,EAAE,OAAO,CAAC,EAAE;IAAE,SAAS,CAAC,EAAE,OAAO,CAAA;CAAE;;cAEzF,MAAM;;;;;EAIzB"}
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../../../src/core/tools/ask-user-question/tool/types.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,MAAM,EAAE,IAAI,EAAE,MAAM,SAAS,CAAC;AAG5C,eAAO,MAAM,aAAa,IAAI,CAAC;AAC/B,eAAO,MAAM,WAAW,IAAI,CAAC;AAC7B,eAAO,MAAM,WAAW,IAAI,CAAC;AAC7B,eAAO,MAAM,iBAAiB,KAAK,CAAC;AACpC,eAAO,MAAM,gBAAgB,KAAK,CAAC;AAEnC;;;;;;GAMG;AACH,eAAO,MAAM,eAAe;;;;CAAiB,CAAC;AAE9C,MAAM,MAAM,YAAY,GAAG,MAAM,OAAO,eAAe,CAAC;AACxD,MAAM,MAAM,aAAa,GAAG,CAAC,OAAO,eAAe,CAAC,CAAC,YAAY,CAAC,CAAC;AAEnE;;;;;;;;;;;;;;GAcG;AACH,eAAO,MAAM,eAAe,YAC3B,OAAO,yBAIE,CAAC;AACX,MAAM,MAAM,aAAa,GAAG,CAAC,OAAO,eAAe,CAAC,CAAC,MAAM,CAAC,CAAC;AAE7D,eAAO,MAAM,YAAY;;;;EAevB,CAAC;AAEH,eAAO,MAAM,cAAc;;;;;;;;;EAsBzB,CAAC;AAEH,eAAO,MAAM,eAAe;;;;;;;;;GAI1B,CAAC;AAEH,eAAO,MAAM,oBAAoB;;;;;;;;;;;EAE/B,CAAC;AAEH,MAAM,MAAM,UAAU,GAAG,MAAM,CAAC,OAAO,YAAY,CAAC,CAAC;AACrD,MAAM,MAAM,YAAY,GAAG,MAAM,CAAC,OAAO,cAAc,CAAC,CAAC;AACzD,MAAM,MAAM,cAAc,GAAG,MAAM,CAAC,OAAO,oBAAoB,CAAC,CAAC;AAEjE;;;;;;;;;;;;GAYG;AACH,MAAM,WAAW,cAAc;IAC9B,aAAa,EAAE,MAAM,CAAC;IACtB,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,QAAQ,GAAG,QAAQ,GAAG,MAAM,GAAG,OAAO,CAAC;IAC7C,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;IACpB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf;;;;;;OAMG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,MAAM,kBAAkB,GAC3B,OAAO,GACP,cAAc,GACd,eAAe,GACf,oBAAoB,GACpB,oBAAoB,GACpB,wBAAwB,GACxB,gBAAgB,CAAC;AAEpB,MAAM,WAAW,mBAAmB;IACnC,OAAO,EAAE,cAAc,EAAE,CAAC;IAC1B,SAAS,EAAE,OAAO,CAAC;IACnB,KAAK,CAAC,EAAE,kBAAkB,CAAC;CAC3B;AAED,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,mBAAmB,CAIlF","sourcesContent":["import { type Static, Type } from \"typebox\";\nimport { LABELS_BY_KIND, ROW_INTENT_META } from \"../state/row-intent.ts\";\n\nexport const MAX_QUESTIONS = 4;\nexport const MIN_OPTIONS = 2;\nexport const MAX_OPTIONS = 4;\nexport const MAX_HEADER_LENGTH = 16;\nexport const MAX_LABEL_LENGTH = 60;\n\n/**\n * User-facing labels for the three runtime sentinel rows, keyed by their\n * `WrappingSelectItem.kind` discriminator. Sourced from\n * `ROW_INTENT_META` via `LABELS_BY_KIND` (`row-intent.ts`) — single source of\n * truth. Adding a new sentinel requires extending the `WrappingSelectItem`\n * union AND adding an entry to `ROW_INTENT_META`; this map then auto-extends.\n */\nexport const SENTINEL_LABELS = LABELS_BY_KIND;\n\nexport type SentinelKind = keyof typeof SENTINEL_LABELS;\nexport type SentinelLabel = (typeof SENTINEL_LABELS)[SentinelKind];\n\n/**\n * Labels reserved for Pi-internal sentinels — authoring an option with any\n * of these labels triggers the `reserved_label` runtime guard. Three of the\n * four come from `ROW_INTENT_META` (the runtime kinds); `\"Other\"` is\n * reserved for CC parity only (the model is conditioned to reach for\n * \"Other\" in CC; we reject it so the runtime sentinel is the single source\n * of truth) and has no runtime kind.\n *\n * Reserved unconditionally — multiSelect questions also reject these labels\n * even though the runtime sentinel is suppressed there.\n *\n * Order is pinned by `types.test.ts:292` — keep the explicit\n * `[\"Other\", other, chat, next]` literal so consumers using\n * `RESERVED_LABELS[i]` indexing or `Set` membership see no behavior change.\n */\nexport const RESERVED_LABELS = [\n\t\"Other\",\n\tROW_INTENT_META.other.label,\n\tROW_INTENT_META.chat.label,\n\tROW_INTENT_META.next.label,\n] as const;\nexport type ReservedLabel = (typeof RESERVED_LABELS)[number];\n\nexport const OptionSchema = Type.Object({\n\tlabel: Type.String({\n\t\tmaxLength: MAX_LABEL_LENGTH,\n\t\tdescription: `MAX ${MAX_LABEL_LENGTH} CHARACTERS — hard limit, requests over the limit are rejected. The display text for this option that the user will see and select. Should be concise (1-5 words) and clearly describe the choice.`,\n\t}),\n\tdescription: Type.String({\n\t\tdescription:\n\t\t\t\"Explanation of what this option means or what will happen if chosen. Useful for providing context about trade-offs or implications.\",\n\t}),\n\tpreview: Type.Optional(\n\t\tType.String({\n\t\t\tdescription:\n\t\t\t\t\"Optional preview content rendered when this option is focused. Use for mockups, code snippets, or visual comparisons that help users compare options. See the tool description for the expected content format.\",\n\t\t}),\n\t),\n});\n\nexport const QuestionSchema = Type.Object({\n\tquestion: Type.String({\n\t\tdescription:\n\t\t\t'The complete question to ask the user. Should be clear, specific, and end with a question mark. Example: \"Which library should we use for date formatting?\" If multiSelect is true, phrase it accordingly, e.g. \"Which features do you want to enable?\"',\n\t}),\n\theader: Type.String({\n\t\tmaxLength: MAX_HEADER_LENGTH,\n\t\tdescription: `MAX ${MAX_HEADER_LENGTH} CHARACTERS — hard limit, requests over the limit are rejected. Very short chip/tag shown next to the question. Examples: \"Auth method\", \"Library\", \"Approach\".`,\n\t}),\n\toptions: Type.Array(OptionSchema, {\n\t\tminItems: MIN_OPTIONS,\n\t\tmaxItems: MAX_OPTIONS,\n\t\tdescription:\n\t\t\t\"The available choices for this question. Must have 2-4 options. Each option should be a distinct, mutually exclusive choice (unless multiSelect is enabled). The 'Type something.' row is appended automatically — do NOT author it.\",\n\t}),\n\tmultiSelect: Type.Optional(\n\t\tType.Boolean({\n\t\t\tdefault: false,\n\t\t\tdescription:\n\t\t\t\t\"Set to true to allow the user to select multiple options instead of just one. Use when choices are not mutually exclusive.\",\n\t\t}),\n\t),\n});\n\nexport const QuestionsSchema = Type.Array(QuestionSchema, {\n\tminItems: 1,\n\tmaxItems: MAX_QUESTIONS,\n\tdescription: \"Questions to ask the user (1-4 questions)\",\n});\n\nexport const QuestionParamsSchema = Type.Object({\n\tquestions: QuestionsSchema,\n});\n\nexport type OptionData = Static<typeof OptionSchema>;\nexport type QuestionData = Static<typeof QuestionSchema>;\nexport type QuestionParams = Static<typeof QuestionParamsSchema>;\n\n/**\n * Answer-intent discriminated union. `kind` is the single discriminator —\n * pre-1.0.3 boolean flags have been removed (see `banned-flags.test.ts`).\n * Mirrors the row-side `WrappingSelectItem.kind` vocabulary where possible;\n * `multi` is the multi-select variant (no row-side analog).\n *\n * Variant semantics:\n * - `option`: user picked one of the author-defined options. `answer` is the option's label.\n * - `custom`: user typed free-text via the \"Type something.\" row. `answer` is the typed text or null.\n * - `chat`: user picked the chat sentinel. `answer` is either typed inline chat text or the legacy\n * literal \"Chat about this\" when submitted empty / whitespace-only.\n * - `multi`: user committed multi-select choices. `selected` carries chosen labels; `answer` is null.\n */\nexport interface QuestionAnswer {\n\tquestionIndex: number;\n\tquestion: string;\n\tkind: \"option\" | \"custom\" | \"chat\" | \"multi\";\n\tanswer: string | null;\n\tselected?: string[];\n\tnotes?: string;\n\t/**\n\t * Markdown text from the matched option's `preview` field, populated only\n\t * when the user lands on a single-select option carrying a `preview`.\n\t * Used by `buildQuestionnaireResponse` to echo `selected preview: <preview>`\n\t * into the LLM-facing envelope. Undefined for multi-select, custom-text\n\t * (`kind: \"custom\"`), and chat (`kind: \"chat\"`) answers.\n\t */\n\tpreview?: string;\n}\n\nexport type QuestionnaireError =\n\t| \"no_ui\"\n\t| \"no_questions\"\n\t| \"empty_options\"\n\t| \"too_many_questions\"\n\t| \"duplicate_question\"\n\t| \"duplicate_option_label\"\n\t| \"reserved_label\";\n\nexport interface QuestionnaireResult {\n\tanswers: QuestionAnswer[];\n\tcancelled: boolean;\n\terror?: QuestionnaireError;\n}\n\nexport function isQuestionnaireResult(value: unknown): value is QuestionnaireResult {\n\tif (!value || typeof value !== \"object\") return false;\n\tconst v = value as Record<string, unknown>;\n\treturn Array.isArray(v.answers) && typeof v.cancelled === \"boolean\";\n}\n"]}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../../../src/core/tools/ask-user-question/tool/types.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,MAAM,EAAE,IAAI,EAAE,MAAM,SAAS,CAAC;AAG5C,eAAO,MAAM,aAAa,IAAI,CAAC;AAC/B,eAAO,MAAM,WAAW,IAAI,CAAC;AAC7B,eAAO,MAAM,WAAW,IAAI,CAAC;AAC7B,eAAO,MAAM,iBAAiB,KAAK,CAAC;AACpC,eAAO,MAAM,gBAAgB,KAAK,CAAC;AAEnC;;;;;;GAMG;AACH,eAAO,MAAM,eAAe;;;;CAAiB,CAAC;AAE9C,MAAM,MAAM,YAAY,GAAG,MAAM,OAAO,eAAe,CAAC;AACxD,MAAM,MAAM,aAAa,GAAG,CAAC,OAAO,eAAe,CAAC,CAAC,YAAY,CAAC,CAAC;AAEnE;;;;;;;;;;;;;;GAcG;AACH,eAAO,MAAM,eAAe,YAC3B,OAAO,yBAIE,CAAC;AACX,MAAM,MAAM,aAAa,GAAG,CAAC,OAAO,eAAe,CAAC,CAAC,MAAM,CAAC,CAAC;AAE7D,eAAO,MAAM,YAAY;;;;EAevB,CAAC;AAEH,eAAO,MAAM,cAAc;;;;;;;;;EAsBzB,CAAC;AAEH,eAAO,MAAM,eAAe;;;;;;;;;GAI1B,CAAC;AAEH,eAAO,MAAM,oBAAoB;;;;;;;;;;;EAE/B,CAAC;AAEH,MAAM,MAAM,UAAU,GAAG,MAAM,CAAC,OAAO,YAAY,CAAC,CAAC;AACrD,MAAM,MAAM,YAAY,GAAG,MAAM,CAAC,OAAO,cAAc,CAAC,CAAC;AACzD,MAAM,MAAM,cAAc,GAAG,MAAM,CAAC,OAAO,oBAAoB,CAAC,CAAC;AAEjE;;;;;;;;;;;;GAYG;AACH,MAAM,WAAW,cAAc;IAC9B,aAAa,EAAE,MAAM,CAAC;IACtB,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,QAAQ,GAAG,QAAQ,GAAG,MAAM,GAAG,OAAO,CAAC;IAC7C,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;IACpB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf;;;;;;OAMG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,MAAM,kBAAkB,GAC3B,OAAO,GACP,cAAc,GACd,eAAe,GACf,oBAAoB,GACpB,oBAAoB,GACpB,wBAAwB,GACxB,gBAAgB,CAAC;AAEpB,MAAM,WAAW,mBAAmB;IACnC,OAAO,EAAE,cAAc,EAAE,CAAC;IAC1B,SAAS,EAAE,OAAO,CAAC;IACnB,KAAK,CAAC,EAAE,kBAAkB,CAAC;CAC3B;AAED,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,mBAAmB,CAIlF"}
@@ -1 +1 @@
1
- {"version":3,"file":"validate-questionnaire.d.ts","sourceRoot":"","sources":["../../../../../src/core/tools/ask-user-question/tool/validate-questionnaire.ts"],"names":[],"mappings":"AAAA,OAAO,EAA8B,KAAK,kBAAkB,EAAE,KAAK,cAAc,EAAmB,MAAM,YAAY,CAAC;AAEvH,eAAO,MAAM,kBAAkB,6CAA6C,CAAC;AAC7E,eAAO,MAAM,wBAAwB,0DAAyE,CAAC;AAC/G,eAAO,MAAM,wBAAwB,6DAA6D,CAAC;AACnG,eAAO,MAAM,qBAAqB,qDAAkE,CAAC;AACrG,eAAO,MAAM,oBAAoB,QAAoE,CAAC;AACtG,eAAO,MAAM,4BAA4B,0DAA0D,CAAC;AAIpG,MAAM,MAAM,gBAAgB,GAAG;IAAE,EAAE,EAAE,IAAI,CAAA;CAAE,GAAG;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,KAAK,EAAE,kBAAkB,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAAC;AAExG;;;;GAIG;AACH,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,cAAc,GAAG,gBAAgB,CAqC7E","sourcesContent":["import { MAX_QUESTIONS, MIN_OPTIONS, type QuestionnaireError, type QuestionParams, RESERVED_LABELS } from \"./types.ts\";\n\nexport const ERROR_NO_QUESTIONS = \"Error: At least one question is required\";\nexport const ERROR_TOO_MANY_QUESTIONS = `Error: At most ${MAX_QUESTIONS} questions are allowed per invocation`;\nexport const ERROR_DUPLICATE_QUESTION = \"Error: Question text must be unique within an invocation\";\nexport const ERROR_TOO_FEW_OPTIONS = `Error: Each question requires at least ${MIN_OPTIONS} options`;\nexport const ERROR_RESERVED_LABEL = `Error: Option label is reserved (${RESERVED_LABELS.join(\", \")})`;\nexport const ERROR_DUPLICATE_OPTION_LABEL = \"Error: Option labels must be unique within a question\";\n\nconst RESERVED_LABEL_SET: ReadonlySet<string> = new Set(RESERVED_LABELS);\n\nexport type ValidationResult = { ok: true } | { ok: false; error: QuestionnaireError; message: string };\n\n/**\n * Pure runtime validator for `QuestionParams`. Covers every guard except\n * `no_ui` (which depends on `ctx.hasUI` and stays inline at the call site).\n * `reserved_label` MUST short-circuit before `duplicate_option_label`.\n */\nexport function validateQuestionnaire(typed: QuestionParams): ValidationResult {\n\tif (typed.questions.length === 0) {\n\t\treturn { ok: false, error: \"no_questions\", message: ERROR_NO_QUESTIONS };\n\t}\n\tif (typed.questions.length > MAX_QUESTIONS) {\n\t\treturn { ok: false, error: \"too_many_questions\", message: ERROR_TOO_MANY_QUESTIONS };\n\t}\n\n\tconst seenQuestions = new Set<string>();\n\tfor (const q of typed.questions) {\n\t\tif (seenQuestions.has(q.question)) {\n\t\t\treturn { ok: false, error: \"duplicate_question\", message: ERROR_DUPLICATE_QUESTION };\n\t\t}\n\t\tseenQuestions.add(q.question);\n\t}\n\n\tfor (const q of typed.questions) {\n\t\tif (q.options.length < MIN_OPTIONS) {\n\t\t\treturn { ok: false, error: \"empty_options\", message: ERROR_TOO_FEW_OPTIONS };\n\t\t}\n\t\tconst seenLabels = new Set<string>();\n\t\tfor (const o of q.options) {\n\t\t\tif (RESERVED_LABEL_SET.has(o.label)) {\n\t\t\t\treturn { ok: false, error: \"reserved_label\", message: ERROR_RESERVED_LABEL };\n\t\t\t}\n\t\t\tif (seenLabels.has(o.label)) {\n\t\t\t\treturn {\n\t\t\t\t\tok: false,\n\t\t\t\t\terror: \"duplicate_option_label\",\n\t\t\t\t\tmessage: ERROR_DUPLICATE_OPTION_LABEL,\n\t\t\t\t};\n\t\t\t}\n\t\t\tseenLabels.add(o.label);\n\t\t}\n\t}\n\n\treturn { ok: true };\n}\n"]}
1
+ {"version":3,"file":"validate-questionnaire.d.ts","sourceRoot":"","sources":["../../../../../src/core/tools/ask-user-question/tool/validate-questionnaire.ts"],"names":[],"mappings":"AAAA,OAAO,EAA8B,KAAK,kBAAkB,EAAE,KAAK,cAAc,EAAmB,MAAM,YAAY,CAAC;AAEvH,eAAO,MAAM,kBAAkB,6CAA6C,CAAC;AAC7E,eAAO,MAAM,wBAAwB,0DAAyE,CAAC;AAC/G,eAAO,MAAM,wBAAwB,6DAA6D,CAAC;AACnG,eAAO,MAAM,qBAAqB,qDAAkE,CAAC;AACrG,eAAO,MAAM,oBAAoB,QAAoE,CAAC;AACtG,eAAO,MAAM,4BAA4B,0DAA0D,CAAC;AAIpG,MAAM,MAAM,gBAAgB,GAAG;IAAE,EAAE,EAAE,IAAI,CAAA;CAAE,GAAG;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,KAAK,EAAE,kBAAkB,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAAC;AAExG;;;;GAIG;AACH,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,cAAc,GAAG,gBAAgB,CAqC7E"}
@@ -1 +1 @@
1
- {"version":3,"file":"body-residual-spacer.d.ts","sourceRoot":"","sources":["../../../../../src/core/tools/ask-user-question/view/body-residual-spacer.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,wBAAwB,CAAC;AAExD;;;;;;;GAOG;AACH,qBAAa,kBAAmB,YAAW,SAAS;IACnD,iBAAyB,MAAM,CAA4B;IAC3D,iBAAyB,UAAU,CAA4B;IAE/D,YACC,MAAM,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,MAAM,EACjC,UAAU,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,MAAM,EAIrC;IAED,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAAG;IAEnC,UAAU,IAAI,IAAI,CAAG;IAErB,MAAM,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE,CAG9B;CACD","sourcesContent":["import type { Component } from \"@earendil-works/pi-tui\";\n\n/**\n * Emits `max(0, getMax(width) - getCurrent(width))` empty rows. Used in\n * `dialog-builder.ts` to absorb the dialog-height residual OUTSIDE the bordered\n * body region: the body renders at its natural height (no internal `\"\"`\n * padding), and this spacer makes up the difference between the global max\n * across tabs and the current tab's body height so the overall dialog footprint\n * stays constant on tab switches.\n */\nexport class BodyResidualSpacer implements Component {\n\tdeclare private readonly getMax: (width: number) => number;\n\tdeclare private readonly getCurrent: (width: number) => number;\n\n\tconstructor(\n\t\tgetMax: (width: number) => number,\n\t\tgetCurrent: (width: number) => number,\n\t) {\n\t\tthis.getMax = getMax;\n\t\tthis.getCurrent = getCurrent;\n\t}\n\n\thandleInput(_data: string): void {}\n\n\tinvalidate(): void {}\n\n\trender(width: number): string[] {\n\t\tconst diff = Math.max(0, this.getMax(width) - this.getCurrent(width));\n\t\treturn Array<string>(diff).fill(\"\");\n\t}\n}\n"]}
1
+ {"version":3,"file":"body-residual-spacer.d.ts","sourceRoot":"","sources":["../../../../../src/core/tools/ask-user-question/view/body-residual-spacer.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,wBAAwB,CAAC;AAExD;;;;;;;GAOG;AACH,qBAAa,kBAAmB,YAAW,SAAS;IACnD,iBAAyB,MAAM,CAA4B;IAC3D,iBAAyB,UAAU,CAA4B;IAE/D,YACC,MAAM,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,MAAM,EACjC,UAAU,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,MAAM,EAIrC;IAED,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAAG;IAEnC,UAAU,IAAI,IAAI,CAAG;IAErB,MAAM,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE,CAG9B;CACD"}
@@ -1 +1 @@
1
- {"version":3,"file":"component-binding.d.ts","sourceRoot":"","sources":["../../../../../src/core/tools/ask-user-question/view/component-binding.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACX,cAAc,EACd,cAAc,EACd,oBAAoB,EACpB,cAAc,EACd,MAAM,gCAAgC,CAAC;AACxC,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAC;AAC5D,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AACvD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAEzD,MAAM,WAAW,gBAAgB,CAAC,CAAC;IAClC,QAAQ,CAAC,SAAS,EAAE,YAAY,CAAC,CAAC,CAAC,CAAC;IACpC,QAAQ,CAAC,MAAM,EAAE,cAAc,CAAC,CAAC,CAAC,CAAC;CACnC;AAED,MAAM,WAAW,aAAa,CAAC,CAAC;IAC/B,QAAQ,CAAC,OAAO,EAAE,CAAC,GAAG,EAAE,aAAa,KAAK,YAAY,CAAC,CAAC,CAAC,GAAG,SAAS,CAAC;IACtE,QAAQ,CAAC,MAAM,EAAE,cAAc,CAAC,CAAC,CAAC,CAAC;IACnC,QAAQ,CAAC,SAAS,CAAC,EAAE,cAAc,CAAC,OAAO,CAAC,CAAC;CAC7C;AAED,MAAM,WAAW,kBAAkB;IAClC,KAAK,CAAC,KAAK,EAAE,kBAAkB,EAAE,GAAG,EAAE,cAAc,GAAG,IAAI,CAAC;IAC5D,UAAU,IAAI,IAAI,CAAC;CACnB;AAED,MAAM,WAAW,kBAAkB;IAClC,KAAK,CAAC,KAAK,EAAE,kBAAkB,EAAE,GAAG,EAAE,oBAAoB,GAAG,IAAI,CAAC;CAClE;AAED,wBAAgB,aAAa,CAAC,CAAC,EAAE,IAAI,EAAE,gBAAgB,CAAC,CAAC,CAAC,GAAG,kBAAkB,CAK9E;AAED,wBAAgB,aAAa,CAAC,CAAC,EAAE,IAAI,EAAE,aAAa,CAAC,CAAC,CAAC,GAAG,kBAAkB,CAO3E","sourcesContent":["import type {\n\tBindingContext,\n\tGlobalSelector,\n\tPerTabBindingContext,\n\tPerTabSelector,\n} from \"../state/selectors/contract.ts\";\nimport type { QuestionnaireState } from \"../state/state.ts\";\nimport type { StatefulView } from \"./stateful-view.ts\";\nimport type { TabComponents } from \"./tab-components.ts\";\n\nexport interface ComponentBinding<P> {\n\treadonly component: StatefulView<P>;\n\treadonly select: GlobalSelector<P>;\n}\n\nexport interface PerTabBinding<P> {\n\treadonly resolve: (tab: TabComponents) => StatefulView<P> | undefined;\n\treadonly select: PerTabSelector<P>;\n\treadonly predicate?: PerTabSelector<boolean>;\n}\n\nexport interface BoundGlobalBinding {\n\tapply(state: QuestionnaireState, ctx: BindingContext): void;\n\tinvalidate(): void;\n}\n\nexport interface BoundPerTabBinding {\n\tapply(state: QuestionnaireState, ctx: PerTabBindingContext): void;\n}\n\nexport function globalBinding<P>(spec: ComponentBinding<P>): BoundGlobalBinding {\n\treturn {\n\t\tapply: (state, ctx) => spec.component.setProps(spec.select(state, ctx)),\n\t\tinvalidate: () => spec.component.invalidate(),\n\t};\n}\n\nexport function perTabBinding<P>(spec: PerTabBinding<P>): BoundPerTabBinding {\n\treturn {\n\t\tapply: (state, ctx) => {\n\t\t\tif (spec.predicate && !spec.predicate(state, ctx)) return;\n\t\t\tspec.resolve(ctx.tab)?.setProps(spec.select(state, ctx));\n\t\t},\n\t};\n}\n"]}
1
+ {"version":3,"file":"component-binding.d.ts","sourceRoot":"","sources":["../../../../../src/core/tools/ask-user-question/view/component-binding.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACX,cAAc,EACd,cAAc,EACd,oBAAoB,EACpB,cAAc,EACd,MAAM,gCAAgC,CAAC;AACxC,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAC;AAC5D,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AACvD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAEzD,MAAM,WAAW,gBAAgB,CAAC,CAAC;IAClC,QAAQ,CAAC,SAAS,EAAE,YAAY,CAAC,CAAC,CAAC,CAAC;IACpC,QAAQ,CAAC,MAAM,EAAE,cAAc,CAAC,CAAC,CAAC,CAAC;CACnC;AAED,MAAM,WAAW,aAAa,CAAC,CAAC;IAC/B,QAAQ,CAAC,OAAO,EAAE,CAAC,GAAG,EAAE,aAAa,KAAK,YAAY,CAAC,CAAC,CAAC,GAAG,SAAS,CAAC;IACtE,QAAQ,CAAC,MAAM,EAAE,cAAc,CAAC,CAAC,CAAC,CAAC;IACnC,QAAQ,CAAC,SAAS,CAAC,EAAE,cAAc,CAAC,OAAO,CAAC,CAAC;CAC7C;AAED,MAAM,WAAW,kBAAkB;IAClC,KAAK,CAAC,KAAK,EAAE,kBAAkB,EAAE,GAAG,EAAE,cAAc,GAAG,IAAI,CAAC;IAC5D,UAAU,IAAI,IAAI,CAAC;CACnB;AAED,MAAM,WAAW,kBAAkB;IAClC,KAAK,CAAC,KAAK,EAAE,kBAAkB,EAAE,GAAG,EAAE,oBAAoB,GAAG,IAAI,CAAC;CAClE;AAED,wBAAgB,aAAa,CAAC,CAAC,EAAE,IAAI,EAAE,gBAAgB,CAAC,CAAC,CAAC,GAAG,kBAAkB,CAK9E;AAED,wBAAgB,aAAa,CAAC,CAAC,EAAE,IAAI,EAAE,aAAa,CAAC,CAAC,CAAC,GAAG,kBAAkB,CAO3E"}
@@ -1 +1 @@
1
- {"version":3,"file":"chat-row-view.d.ts","sourceRoot":"","sources":["../../../../../../src/core/tools/ask-user-question/view/components/chat-row-view.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,wBAAwB,CAAC;AACxD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AACxD,OAAO,EAAkB,KAAK,kBAAkB,EAAE,KAAK,mBAAmB,EAAE,MAAM,sBAAsB,CAAC;AAEzG;;;;;;GAMG;AACH,MAAM,WAAW,gBAAgB;IAChC,OAAO,EAAE,OAAO,CAAC;IACjB,SAAS,EAAE;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC;IAC7C,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,iBAAiB;IACjC,oFAAoF;IACpF,IAAI,EAAE,kBAAkB,CAAC;IACzB,KAAK,EAAE,mBAAmB,CAAC;CAC3B;AAED;;;;;;;;;GASG;AACH,qBAAa,WAAY,YAAW,YAAY,CAAC,gBAAgB,CAAC,EAAE,SAAS;IAC5E,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAiB;IAExC,YAAY,MAAM,EAAE,iBAAiB,EAKpC;IAED,QAAQ,CAAC,KAAK,EAAE,gBAAgB,GAAG,IAAI,CAKtC;IAED,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAAG;IAEnC,UAAU,IAAI,IAAI,CAEjB;IAED,MAAM,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE,CAE9B;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}\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});\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.d.ts","sourceRoot":"","sources":["../../../../../../src/core/tools/ask-user-question/view/components/chat-row-view.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,wBAAwB,CAAC;AACxD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AACxD,OAAO,EAAkB,KAAK,kBAAkB,EAAE,KAAK,mBAAmB,EAAE,MAAM,sBAAsB,CAAC;AAEzG;;;;;;GAMG;AACH,MAAM,WAAW,gBAAgB;IAChC,OAAO,EAAE,OAAO,CAAC;IACjB,SAAS,EAAE;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC;IAC7C,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,iBAAiB;IACjC,oFAAoF;IACpF,IAAI,EAAE,kBAAkB,CAAC;IACzB,KAAK,EAAE,mBAAmB,CAAC;CAC3B;AAED;;;;;;;;;GASG;AACH,qBAAa,WAAY,YAAW,YAAY,CAAC,gBAAgB,CAAC,EAAE,SAAS;IAC5E,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAiB;IAExC,YAAY,MAAM,EAAE,iBAAiB,EAKpC;IAED,QAAQ,CAAC,KAAK,EAAE,gBAAgB,GAAG,IAAI,CAKtC;IAED,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAAG;IAEnC,UAAU,IAAI,IAAI,CAEjB;IAED,MAAM,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE,CAE9B;CACD"}
@@ -1 +1 @@
1
- {"version":3,"file":"multi-select-view.d.ts","sourceRoot":"","sources":["../../../../../../src/core/tools/ask-user-question/view/components/multi-select-view.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,iDAAiD,CAAC;AAG7E,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AACxD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AAYxD,eAAO,MAAM,kBAAkB,WAAW,CAAC;AAE3C,MAAM,WAAW,oBAAoB;IACpC,IAAI,EAAE,aAAa,CAAC;QAAE,OAAO,EAAE,OAAO,CAAC;QAAC,MAAM,EAAE,OAAO,CAAA;KAAE,CAAC,CAAC;IAC3D,UAAU,EAAE,OAAO,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC;CAClB;AAED;;;;;;;;;GASG;AACH,qBAAa,eAAgB,YAAW,YAAY,CAAC,oBAAoB,CAAC;IACzE,OAAO,CAAC,KAAK,CAAuB;IAEpC,iBAAyB,KAAK,CAAQ;IACtC,iBAAyB,QAAQ,CAAe;IAEhD,YACC,KAAK,EAAE,KAAK,EACZ,QAAQ,EAAE,YAAY,EAKtB;IAED,QAAQ,CAAC,KAAK,EAAE,oBAAoB,GAAG,IAAI,CAE1C;IAED,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAAG;IAEnC,UAAU,IAAI,IAAI,CAAG;IAErB,MAAM,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE,CAgC9B;IAED,aAAa,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAWnC;IAED,OAAO,CAAC,kBAAkB;CAW1B","sourcesContent":["import type { Theme } from \"../../../../../modes/interactive/theme/theme.ts\";\nimport { truncateToWidth, visibleWidth, wrapTextWithAnsi } from \"@earendil-works/pi-tui\";\nimport { ROW_INTENT_META } from \"../../state/row-intent.ts\";\nimport type { QuestionData } from \"../../tool/types.ts\";\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\tdeclare private readonly theme: Theme;\n\tdeclare private readonly question: QuestionData;\n\n\tconstructor(\n\t\ttheme: Theme,\n\t\tquestion: QuestionData,\n\t) {\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\tlines.push(truncateToWidth(line, width, \"\"));\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\tlines.push(truncateToWidth(`${nextPointer}${nextLabel}`, width, \"\"));\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.d.ts","sourceRoot":"","sources":["../../../../../../src/core/tools/ask-user-question/view/components/multi-select-view.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,iDAAiD,CAAC;AAG7E,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AACxD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AAYxD,eAAO,MAAM,kBAAkB,WAAW,CAAC;AAE3C,MAAM,WAAW,oBAAoB;IACpC,IAAI,EAAE,aAAa,CAAC;QAAE,OAAO,EAAE,OAAO,CAAC;QAAC,MAAM,EAAE,OAAO,CAAA;KAAE,CAAC,CAAC;IAC3D,UAAU,EAAE,OAAO,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC;CAClB;AAED;;;;;;;;;GASG;AACH,qBAAa,eAAgB,YAAW,YAAY,CAAC,oBAAoB,CAAC;IACzE,OAAO,CAAC,KAAK,CAAuB;IAEpC,iBAAyB,KAAK,CAAQ;IACtC,iBAAyB,QAAQ,CAAe;IAEhD,YACC,KAAK,EAAE,KAAK,EACZ,QAAQ,EAAE,YAAY,EAKtB;IAED,QAAQ,CAAC,KAAK,EAAE,oBAAoB,GAAG,IAAI,CAE1C;IAED,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAAG;IAEnC,UAAU,IAAI,IAAI,CAAG;IAErB,MAAM,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE,CAgC9B;IAED,aAAa,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAWnC;IAED,OAAO,CAAC,kBAAkB;CAW1B"}
@@ -1 +1 @@
1
- {"version":3,"file":"option-list-view.d.ts","sourceRoot":"","sources":["../../../../../../src/core/tools/ask-user-question/view/components/option-list-view.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AACxD,OAAO,EAAkB,KAAK,kBAAkB,EAAE,KAAK,mBAAmB,EAAE,MAAM,sBAAsB,CAAC;AAEzG;;;GAGG;AACH,eAAO,MAAM,mBAAmB,KAAK,CAAC;AAEtC,MAAM,WAAW,oBAAoB;IACpC,KAAK,EAAE,SAAS,kBAAkB,EAAE,CAAC;IACrC,KAAK,EAAE,mBAAmB,CAAC;CAC3B;AAED;;;;;;GAMG;AACH,MAAM,WAAW,mBAAmB;IACnC,aAAa,EAAE,MAAM,CAAC;IACtB,OAAO,EAAE,OAAO,CAAC;IACjB,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,MAAM,CAAC;IACnB,oFAAoF;IACpF,SAAS,CAAC,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,aAAa,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;CACtD;AAED;;;;;GAKG;AACH,qBAAa,cAAe,YAAW,YAAY,CAAC,mBAAmB,CAAC;IACvE,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAiB;IAExC,YAAY,MAAM,EAAE,oBAAoB,EAQvC;IAED,QAAQ,CAAC,KAAK,EAAE,mBAAmB,GAAG,IAAI,CAMzC;IAED,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAAG;IAEnC,UAAU,IAAI,IAAI,CAEjB;IAED,MAAM,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE,CAE9B;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.d.ts","sourceRoot":"","sources":["../../../../../../src/core/tools/ask-user-question/view/components/option-list-view.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AACxD,OAAO,EAAkB,KAAK,kBAAkB,EAAE,KAAK,mBAAmB,EAAE,MAAM,sBAAsB,CAAC;AAEzG;;;GAGG;AACH,eAAO,MAAM,mBAAmB,KAAK,CAAC;AAEtC,MAAM,WAAW,oBAAoB;IACpC,KAAK,EAAE,SAAS,kBAAkB,EAAE,CAAC;IACrC,KAAK,EAAE,mBAAmB,CAAC;CAC3B;AAED;;;;;;GAMG;AACH,MAAM,WAAW,mBAAmB;IACnC,aAAa,EAAE,MAAM,CAAC;IACtB,OAAO,EAAE,OAAO,CAAC;IACjB,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,MAAM,CAAC;IACnB,oFAAoF;IACpF,SAAS,CAAC,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,aAAa,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;CACtD;AAED;;;;;GAKG;AACH,qBAAa,cAAe,YAAW,YAAY,CAAC,mBAAmB,CAAC;IACvE,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAiB;IAExC,YAAY,MAAM,EAAE,oBAAoB,EAQvC;IAED,QAAQ,CAAC,KAAK,EAAE,mBAAmB,GAAG,IAAI,CAMzC;IAED,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAAG;IAEnC,UAAU,IAAI,IAAI,CAEjB;IAED,MAAM,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE,CAE9B;CACD"}
@@ -1 +1 @@
1
- {"version":3,"file":"markdown-content-cache.d.ts","sourceRoot":"","sources":["../../../../../../../src/core/tools/ask-user-question/view/components/preview/markdown-content-cache.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,oDAAoD,CAAC;AAChF,OAAO,EAAY,KAAK,aAAa,EAAgB,MAAM,wBAAwB,CAAC;AACpF,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,wBAAwB,CAAC;AAG3D,wCAAwC;AACxC,eAAO,MAAM,+BAA+B,KAAK,CAAC;AAClD,8DAA8D;AAC9D,eAAO,MAAM,0BAA0B,KAAK,CAAC;AAC7C,eAAO,MAAM,eAAe,yBAAyB,CAAC;AACtD,qJAAqJ;AACrJ,eAAO,MAAM,yBAAyB,IAAI,CAAC;AAE3C;;;;;;;;GAQG;AACH,qBAAa,oBAAoB;IAChC,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAsB;IACnD,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAwB;IACtD,OAAO,CAAC,WAAW,CAAqB;IACxC,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAQ;IAC9B,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAgB;IAE9C,YAAY,QAAQ,EAAE,YAAY,EAAE,KAAK,EAAE,KAAK,EAAE,aAAa,EAAE,aAAa,EAS7E;IAED,aAAa,IAAI,OAAO,CAEvB;IAED,GAAG,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAEhC;IAED;;;OAGG;IACH,OAAO,CAAC,WAAW,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,MAAM,EAAE,CAiBzD;IAED,UAAU,IAAI,IAAI,CAGjB;CACD","sourcesContent":["import type { Theme } from \"../../../../../../modes/interactive/theme/theme.ts\";\nimport { Markdown, type MarkdownTheme, visibleWidth } from \"@earendil-works/pi-tui\";\nimport type { QuestionData } from \"../../../tool/types.ts\";\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.d.ts","sourceRoot":"","sources":["../../../../../../../src/core/tools/ask-user-question/view/components/preview/markdown-content-cache.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,oDAAoD,CAAC;AAChF,OAAO,EAAY,KAAK,aAAa,EAAgB,MAAM,wBAAwB,CAAC;AACpF,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,wBAAwB,CAAC;AAG3D,wCAAwC;AACxC,eAAO,MAAM,+BAA+B,KAAK,CAAC;AAClD,8DAA8D;AAC9D,eAAO,MAAM,0BAA0B,KAAK,CAAC;AAC7C,eAAO,MAAM,eAAe,yBAAyB,CAAC;AACtD,qJAAqJ;AACrJ,eAAO,MAAM,yBAAyB,IAAI,CAAC;AAE3C;;;;;;;;GAQG;AACH,qBAAa,oBAAoB;IAChC,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAsB;IACnD,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAwB;IACtD,OAAO,CAAC,WAAW,CAAqB;IACxC,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAQ;IAC9B,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAgB;IAE9C,YAAY,QAAQ,EAAE,YAAY,EAAE,KAAK,EAAE,KAAK,EAAE,aAAa,EAAE,aAAa,EAS7E;IAED,aAAa,IAAI,OAAO,CAEvB;IAED,GAAG,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAEhC;IAED;;;OAGG;IACH,OAAO,CAAC,WAAW,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,MAAM,EAAE,CAiBzD;IAED,UAAU,IAAI,IAAI,CAGjB;CACD"}
@@ -1 +1 @@
1
- {"version":3,"file":"preview-block-renderer.d.ts","sourceRoot":"","sources":["../../../../../../../src/core/tools/ask-user-question/view/components/preview/preview-block-renderer.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,oDAAoD,CAAC;AAChF,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,wBAAwB,CAAC;AAC5D,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,wBAAwB,CAAC;AAc3D,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,6BAA6B,CAAC;AAErE;;;GAGG;AACH,eAAO,MAAM,qBAAqB,uBAAuB,CAAC;AAE1D,MAAM,WAAW,0BAA0B;IAC1C,QAAQ,EAAE,YAAY,CAAC;IACvB,KAAK,EAAE,KAAK,CAAC;IACb,aAAa,EAAE,aAAa,CAAC;CAC7B;AAED;;;;;;;;;GASG;AACH,qBAAa,oBAAoB;IAChC,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAQ;IAC9B,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAuB;IAE7C,YAAY,MAAM,EAAE,0BAA0B,EAG7C;IAED,aAAa,IAAI,OAAO,CAEvB;IAED,GAAG,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAEhC;IAED,UAAU,IAAI,IAAI,CAEjB;IAED;;;;OAIG;IACH,WAAW,CAAC,KAAK,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,EAAE,IAAI,EAAE,iBAAiB,GAAG,MAAM,CAO/E;IAED;;;;;OAKG;IACH,WAAW,CACV,KAAK,EAAE,MAAM,EACb,WAAW,EAAE,MAAM,EACnB,IAAI,EAAE,iBAAiB,EACvB,OAAO,EAAE,OAAO,EAChB,YAAY,EAAE,OAAO,GACnB,MAAM,EAAE,CAmBV;CACD","sourcesContent":["import type { Theme } from \"../../../../../../modes/interactive/theme/theme.ts\";\nimport type { MarkdownTheme } from \"@earendil-works/pi-tui\";\nimport type { QuestionData } from \"../../../tool/types.ts\";\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\n\t\t\t? this.theme.fg(\"muted\", NOTES_AFFORDANCE_TEXT)\n\t\t\t: \"\";\n\t\treturn [...boxedLines, \"\", affordance];\n\t}\n}\n"]}
1
+ {"version":3,"file":"preview-block-renderer.d.ts","sourceRoot":"","sources":["../../../../../../../src/core/tools/ask-user-question/view/components/preview/preview-block-renderer.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,oDAAoD,CAAC;AAChF,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,wBAAwB,CAAC;AAC5D,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,wBAAwB,CAAC;AAc3D,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,6BAA6B,CAAC;AAErE;;;GAGG;AACH,eAAO,MAAM,qBAAqB,uBAAuB,CAAC;AAE1D,MAAM,WAAW,0BAA0B;IAC1C,QAAQ,EAAE,YAAY,CAAC;IACvB,KAAK,EAAE,KAAK,CAAC;IACb,aAAa,EAAE,aAAa,CAAC;CAC7B;AAED;;;;;;;;;GASG;AACH,qBAAa,oBAAoB;IAChC,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAQ;IAC9B,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAuB;IAE7C,YAAY,MAAM,EAAE,0BAA0B,EAG7C;IAED,aAAa,IAAI,OAAO,CAEvB;IAED,GAAG,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAEhC;IAED,UAAU,IAAI,IAAI,CAEjB;IAED;;;;OAIG;IACH,WAAW,CAAC,KAAK,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,EAAE,IAAI,EAAE,iBAAiB,GAAG,MAAM,CAO/E;IAED;;;;;OAKG;IACH,WAAW,CACV,KAAK,EAAE,MAAM,EACb,WAAW,EAAE,MAAM,EACnB,IAAI,EAAE,iBAAiB,EACvB,OAAO,EAAE,OAAO,EAChB,YAAY,EAAE,OAAO,GACnB,MAAM,EAAE,CAmBV;CACD"}
@@ -1 +1 @@
1
- {"version":3,"file":"preview-box-renderer.d.ts","sourceRoot":"","sources":["../../../../../../../src/core/tools/ask-user-question/view/components/preview/preview-box-renderer.ts"],"names":[],"mappings":"AAMA,gEAAgE;AAChE,eAAO,MAAM,wBAAwB,IAAI,CAAC;AAC1C,qFAAqF;AACrF,eAAO,MAAM,0BAA0B,IAAI,CAAC;AAC5C,iFAAiF;AACjF,eAAO,MAAM,+BAA+B,IAAI,CAAC;AACjD,2FAA2F;AAC3F,eAAO,MAAM,qBAAqB,KAAK,CAAC;AAExC;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,SAAS,MAAM,EAAE,GAAG,MAAM,EAAE,CAKpE;AAED;;;;;;GAMG;AACH,wBAAgB,iBAAiB,CAChC,KAAK,EAAE,SAAS,MAAM,EAAE,EACxB,KAAK,EAAE,MAAM,EACb,OAAO,EAAE,CAAC,CAAC,EAAE,MAAM,KAAK,MAAM,EAC9B,MAAM,SAAI,GACR,MAAM,EAAE,CAoBV;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,oBAAoB,CACnC,YAAY,EAAE,SAAS,MAAM,EAAE,EAC/B,aAAa,EAAE,MAAM,GACnB;IAAE,UAAU,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,CAS1C","sourcesContent":["import { truncateToWidth, visibleWidth } from \"@earendil-works/pi-tui\";\n\nconst ANSI_SGR_RE = /\\x1b\\[[0-9;]*m/g;\nconst ANSI_OSC8_RE = /\\x1b\\]8;[^\\x07\\x1b]*(?:\\x07|\\x1b\\\\)/g;\nconst FENCE_MARKER_RE = /^`{3}/;\n\n/** Top + bottom border rows consumed by `renderBorderedBox`. */\nexport const BORDER_VERTICAL_OVERHEAD = 2;\n/** Left + right vertical bar columns (`│ ... │`) consumed by `renderBorderedBox`. */\nexport const BORDER_HORIZONTAL_OVERHEAD = 2;\n/** Inner horizontal padding (1 col) between each border bar and content area. */\nexport const BORDER_INNER_PADDING_HORIZONTAL = 1;\n/** Floor for the preview box's inner content width — CC parity (`PreviewBox.minWidth`). */\nexport const BOX_MIN_CONTENT_WIDTH = 40;\n\n/**\n * Drops fenced-code-block marker lines (` ``` ` opener/closer) from rendered markdown.\n * pi-tui's Markdown emits literal opening ` ```lang ` and closing ` ``` ` lines around\n * code blocks; this strip leaves only the highlighted code body. Inline code\n * (`codespan`) is unaffected — pi-tui already renders it without backticks.\n */\nexport function stripFenceMarkers(lines: readonly string[]): string[] {\n\treturn lines.filter((line) => {\n\t\tconst clean = line.replace(ANSI_SGR_RE, \"\").replace(ANSI_OSC8_RE, \"\");\n\t\treturn !FENCE_MARKER_RE.test(clean);\n\t});\n}\n\n/**\n * Wraps `lines` in a 4-sided ASCII border with 1 col of inner horizontal padding.\n * Layout per content row: `│` + ` ` + content padded to `contentInner` + ` ` + `│`,\n * where `contentInner = width - BORDER_HORIZONTAL_OVERHEAD - 2 * BORDER_INNER_PADDING_HORIZONTAL`.\n * Top/bottom dash runs span corner-to-corner (`width - BORDER_HORIZONTAL_OVERHEAD`). When\n * `hidden > 0`, the bottom-row dash run is replaced with ` ✂ ── N lines hidden ── ` (corners stay).\n */\nexport function renderBorderedBox(\n\tlines: readonly string[],\n\twidth: number,\n\tcolorFn: (s: string) => string,\n\thidden = 0,\n): string[] {\n\tconst dashSpan = Math.max(1, width - BORDER_HORIZONTAL_OVERHEAD);\n\tconst contentInner = Math.max(1, dashSpan - 2 * BORDER_INNER_PADDING_HORIZONTAL);\n\tconst pad = \" \".repeat(BORDER_INNER_PADDING_HORIZONTAL);\n\tconst top = colorFn(`┌${\"─\".repeat(dashSpan)}┐`);\n\tconst out: string[] = [top];\n\tfor (const line of lines) {\n\t\tconst padded = truncateToWidth(line, contentInner, \"\", true);\n\t\tout.push(`${colorFn(\"│\")}${pad}${padded}${pad}${colorFn(\"│\")}`);\n\t}\n\tif (hidden > 0) {\n\t\tconst indicator = ` ✂ ── ${hidden} lines hidden ── `;\n\t\tconst space = dashSpan - indicator.length;\n\t\tconst leftFill = \"─\".repeat(Math.max(0, Math.floor(space / 2)));\n\t\tconst rightFill = \"─\".repeat(Math.max(0, dashSpan - leftFill.length - indicator.length));\n\t\tout.push(colorFn(`└${leftFill}${indicator}${rightFill}┘`));\n\t} else {\n\t\tout.push(colorFn(`└${\"─\".repeat(dashSpan)}┘`));\n\t}\n\treturn out;\n}\n\n/**\n * Compute box dimensions from content lines. Pure of args.\n *\n * CC parity:\n * contentWidth = max(minWidth, widestRenderedLine)\n * boxWidth = min(contentWidth + 4, effectiveMaxWidth)\n *\n * Trailing whitespace is stripped before measuring because pi-tui's\n * `Markdown.render(width)` pads every line to `width`, which would otherwise force\n * the box to fill the whole column allocation.\n */\nexport function computeBoxDimensions(\n\tcontentLines: readonly string[],\n\tmaxInnerWidth: number,\n): { innerWidth: number; boxWidth: number } {\n\tlet widest = Math.min(BOX_MIN_CONTENT_WIDTH, maxInnerWidth);\n\tfor (const line of contentLines) {\n\t\tconst w = visibleWidth(line.replace(/\\s+$/, \"\"));\n\t\tif (w > widest) widest = w;\n\t}\n\tconst innerWidth = Math.min(widest, maxInnerWidth);\n\tconst boxWidth = innerWidth + BORDER_HORIZONTAL_OVERHEAD + 2 * BORDER_INNER_PADDING_HORIZONTAL;\n\treturn { innerWidth, boxWidth };\n}\n"]}
1
+ {"version":3,"file":"preview-box-renderer.d.ts","sourceRoot":"","sources":["../../../../../../../src/core/tools/ask-user-question/view/components/preview/preview-box-renderer.ts"],"names":[],"mappings":"AAMA,gEAAgE;AAChE,eAAO,MAAM,wBAAwB,IAAI,CAAC;AAC1C,qFAAqF;AACrF,eAAO,MAAM,0BAA0B,IAAI,CAAC;AAC5C,iFAAiF;AACjF,eAAO,MAAM,+BAA+B,IAAI,CAAC;AACjD,2FAA2F;AAC3F,eAAO,MAAM,qBAAqB,KAAK,CAAC;AAExC;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,SAAS,MAAM,EAAE,GAAG,MAAM,EAAE,CAKpE;AAED;;;;;;GAMG;AACH,wBAAgB,iBAAiB,CAChC,KAAK,EAAE,SAAS,MAAM,EAAE,EACxB,KAAK,EAAE,MAAM,EACb,OAAO,EAAE,CAAC,CAAC,EAAE,MAAM,KAAK,MAAM,EAC9B,MAAM,SAAI,GACR,MAAM,EAAE,CAoBV;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,oBAAoB,CACnC,YAAY,EAAE,SAAS,MAAM,EAAE,EAC/B,aAAa,EAAE,MAAM,GACnB;IAAE,UAAU,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,CAS1C"}
@@ -1 +1 @@
1
- {"version":3,"file":"preview-layout-decider.d.ts","sourceRoot":"","sources":["../../../../../../../src/core/tools/ask-user-question/view/components/preview/preview-layout-decider.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,wBAAwB,CAAC;AAC3D,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,uBAAuB,CAAC;AAGhE,qEAAqE;AACrE,eAAO,MAAM,iBAAiB,MAAM,CAAC;AACrC,4EAA4E;AAC5E,eAAO,MAAM,kBAAkB,IAAI,CAAC;AACpC,qEAAqE;AACrE,eAAO,MAAM,oBAAoB,IAAI,CAAC;AACtC,gFAAgF;AAChF,eAAO,MAAM,gBAAgB,IAAI,CAAC;AAElC,oFAAoF;AACpF,eAAO,MAAM,QAAQ,KAAK,CAAC;AAC3B,4EAA4E;AAC5E,eAAO,MAAM,cAAc,MAAM,CAAC;AAClC,6FAA6F;AAC7F,eAAO,MAAM,iBAAiB,KAAK,CAAC;AACpC;+EAC+E;AAC/E,eAAO,MAAM,kBAAkB,IAAI,CAAC;AAEpC,MAAM,MAAM,iBAAiB,GAAG,cAAc,GAAG,SAAS,CAAC;AAE3D;;;;;;;;GAQG;AACH,wBAAgB,YAAY,CAAC,aAAa,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,iBAAiB,CAExF;AAED;;;;;;;;GAQG;AACH,wBAAgB,iBAAiB,CAChC,KAAK,EAAE,SAAS,kBAAkB,EAAE,EACpC,iBAAiB,EAAE,MAAM,EACzB,SAAS,EAAE,MAAM,GACf,MAAM,CAYR;AAED;;;;;;;GAOG;AACH,wBAAgB,oBAAoB,CACnC,IAAI,EAAE,aAAa,CAAC;IAAE,WAAW,CAAC,EAAE,OAAO,CAAA;CAAE,CAAC,EAC9C,UAAU,EAAE,aAAa,CAAC,SAAS,kBAAkB,EAAE,CAAC,EACxD,SAAS,EAAE,MAAM,GACf,MAAM,CASR;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,kBAAkB,CAAC,QAAQ,EAAE,YAAY,GAAG,MAAM,CAWjE;AAED;;;;;;;GAOG;AACH,wBAAgB,qBAAqB,CAAC,SAAS,EAAE,SAAS,YAAY,EAAE,EAAE,SAAS,EAAE,MAAM,GAAG,MAAM,CASnG;AAED;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,6BAA6B,CAC5C,IAAI,EAAE,aAAa,CAAC;IAAE,WAAW,CAAC,EAAE,OAAO,CAAA;CAAE,CAAC,EAC9C,UAAU,EAAE,aAAa,CAAC,SAAS,kBAAkB,EAAE,CAAC,EACxD,SAAS,EAAE,SAAS,YAAY,EAAE,EAClC,SAAS,EAAE,MAAM,GACf,MAAM,CAUR;AAED;;;;;GAKG;AACH,wBAAgB,YAAY,CAC3B,SAAS,EAAE,MAAM,EACjB,YAAY,EAAE,MAAM,GAClB;IAAE,SAAS,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE,CAKxD;AAED;;;;GAIG;AACH,wBAAgB,UAAU,CACzB,SAAS,EAAE,MAAM,EACjB,IAAI,EAAE,iBAAiB,EACvB,YAAY,EAAE,MAAM,GAClB;IAAE,YAAY,EAAE,MAAM,CAAC;IAAC,YAAY,EAAE,MAAM,CAAA;CAAE,CAIhD","sourcesContent":["import { visibleWidth } from \"@earendil-works/pi-tui\";\nimport type { QuestionData } from \"../../../tool/types.ts\";\nimport type { WrappingSelectItem } from \"../wrapping-select.ts\";\nimport { BORDER_HORIZONTAL_OVERHEAD, BORDER_INNER_PADDING_HORIZONTAL } from \"./preview-box-renderer.ts\";\n\n/** Min terminal/pane width for the side-by-side layout to engage. */\nexport const PREVIEW_MIN_WIDTH = 100;\n/** Visual gap between options column and preview column in side-by-side. */\nexport const PREVIEW_COLUMN_GAP = 2;\n/** 1 col padding inside the preview column (between gap and `│`). */\nexport const PREVIEW_PADDING_LEFT = 1;\n/** Empty rows between options and preview blocks in stacked (narrow) layout. */\nexport const STACKED_GAP_ROWS = 1;\n\n/** Floor for the adaptive left column width — prevents collapse on short labels. */\nexport const MIN_LEFT = 30;\n/** Ceiling ratio: left column never exceeds this fraction of pane width. */\nexport const MAX_LEFT_RATIO = 0.5;\n/** Floor for the preview column width — prevents right-side collapse on narrow terminals. */\nexport const MIN_PREVIEW_WIDTH = 45;\n/** visibleWidth(\" ✔\") = 2 (space + ✔ codepoint). Reserved on the longest-label measurement\n * so a confirmed row never gets truncated when MIN_LEFT clamps the column. */\nexport const CONFIRMED_OVERHEAD = 2;\n\nexport type PreviewLayoutMode = \"side-by-side\" | \"stacked\";\n\n/**\n * Decide layout mode from terminal + pane widths. Pure of inputs.\n *\n * The terminal-width gate is the AND check from the previous `preview-pane.ts` —\n * lifted here so the decision is computed ONCE per render and threaded explicitly\n * through `previewBlockHeight`. Removes the bug class where `previewBlockHeight`\n * re-derived `sideBySide` from a column width (already < pane width post-split),\n * capping height too short.\n */\nexport function decideLayout(terminalWidth: number, paneWidth: number): PreviewLayoutMode {\n\treturn terminalWidth >= PREVIEW_MIN_WIDTH && paneWidth >= PREVIEW_MIN_WIDTH ? \"side-by-side\" : \"stacked\";\n}\n\n/**\n * Compute the adaptive left column width from option labels.\n * Pure function — deterministic for a given (items, totalForNumbering, paneWidth).\n *\n * Pipeline:\n * 1. Measure: longest visible label width + prefix overhead + confirmed-mark overhead\n * 2. Clamp: floor MIN_LEFT, ceiling paneWidth * MAX_LEFT_RATIO\n * 3. Safety net: never exceed available = paneWidth - GAP - MIN_PREVIEW_WIDTH\n */\nexport function adaptiveLeftWidth(\n\titems: readonly WrappingSelectItem[],\n\ttotalForNumbering: number,\n\tpaneWidth: number,\n): number {\n\tconst prefixW = String(Math.max(1, totalForNumbering)).length + 4; // digits + \"❯ \" + \". \"\n\tconst confirmedOverhead = CONFIRMED_OVERHEAD; // visibleWidth(\" ✔\") = 2 (space + ✔ codepoint)\n\tlet maxLabel = 0;\n\tfor (const item of items) {\n\t\tconst w = visibleWidth(item.label);\n\t\tif (w > maxLabel) maxLabel = w;\n\t}\n\tconst desired = maxLabel + prefixW + confirmedOverhead;\n\tconst ratioCapped = Math.min(desired, Math.floor(paneWidth * MAX_LEFT_RATIO));\n\tconst available = paneWidth - PREVIEW_COLUMN_GAP - MIN_PREVIEW_WIDTH;\n\treturn Math.max(MIN_LEFT, Math.min(ratioCapped, Math.max(1, available)));\n}\n\n/**\n * Cross-tab maximum left-column width. Aggregates `adaptiveLeftWidth` over every tab\n * and returns the widest result, so the options column stays stable on tab switch.\n *\n * Pure function — `tabs.length` MUST equal `itemsByTab.length`. Multi-select tabs\n * use `items.length` for numbering; single-select tabs add 1 for the chat row slot.\n * Floor is `MIN_LEFT` so an all-empty input still produces a usable column.\n */\nexport function crossTabMaxLeftWidth(\n\ttabs: ReadonlyArray<{ multiSelect?: boolean }>,\n\titemsByTab: ReadonlyArray<readonly WrappingSelectItem[]>,\n\tpaneWidth: number,\n): number {\n\tlet max = MIN_LEFT;\n\tfor (let i = 0; i < tabs.length; i++) {\n\t\tconst items = itemsByTab[i] ?? [];\n\t\tconst totalForNumbering = tabs[i]?.multiSelect ? items.length : items.length + 1;\n\t\tconst tabWidth = adaptiveLeftWidth(items, totalForNumbering, paneWidth);\n\t\tif (tabWidth > max) max = tabWidth;\n\t}\n\treturn max;\n}\n\n/**\n * Source-line probe: measures the widest source line across all options' previews.\n * Returns 0 when no option carries a preview.\n *\n * V1 heuristic — works well for tree/table/heading/list content where source-line\n * width ≈ rendered width. Paragraphs overstate (source width \"wants\" the full pane);\n * the worst case is \"donation does nothing useful\" — fallback to the label-driven path.\n * Upgrade path: replace with a render-width probe that invokes Markdown at a probe width.\n *\n * Pure function — O(total chars) per question; no Markdown invocation, no cache interaction.\n */\nexport function previewSourceWidth(question: QuestionData): number {\n\tlet max = 0;\n\tfor (const option of question.options) {\n\t\tconst text = option.preview;\n\t\tif (!text) continue;\n\t\tfor (const line of text.split(\"\\n\")) {\n\t\t\tconst w = visibleWidth(line);\n\t\t\tif (w > max) max = w;\n\t\t}\n\t}\n\treturn max;\n}\n\n/**\n * Cross-tab/cross-option preview budget. Iterates all questions, computes each question's\n * preview appetite via `previewSourceWidth`, adds box + padding overhead (5 cols), and\n * returns the widest result. Floor is `MIN_PREVIEW_WIDTH` so a previewless question still\n * reserves a usable column. Ceiling ensures the left column retains its `MIN_LEFT` floor.\n *\n * Pure function — same cross-tab max pattern as `crossTabMaxLeftWidth`.\n */\nexport function crossTabPreviewBudget(questions: readonly QuestionData[], paneWidth: number): number {\n\tlet max = MIN_PREVIEW_WIDTH;\n\tfor (const question of questions) {\n\t\tconst rawWidth = previewSourceWidth(question);\n\t\tconst capped = Math.min(rawWidth, paneWidth - PREVIEW_COLUMN_GAP - MIN_LEFT);\n\t\tconst budget = capped + BORDER_HORIZONTAL_OVERHEAD + 2 * BORDER_INNER_PADDING_HORIZONTAL + PREVIEW_PADDING_LEFT;\n\t\tif (budget > max) max = budget;\n\t}\n\treturn max;\n}\n\n/**\n * Cross-tab left-column width with slack donation. Combines the label-driven width\n * (from `crossTabMaxLeftWidth`) with the slack donated by narrow previews.\n *\n * Pipeline:\n * 1. `labelDriven` = `crossTabMaxLeftWidth(tabs, itemsByTab, paneWidth)`\n * 2. `previewBudget` = `crossTabPreviewBudget(questions, paneWidth)`\n * 3. `slackDonation` = `paneWidth − GAP − previewBudget`\n * 4. Return `min(max(labelDriven, slackDonation), ceiling)` where `ceiling = paneWidth − GAP − MIN_PREVIEW_WIDTH`\n *\n * Invariants:\n * - Floor: result ≥ MIN_LEFT (labelDriven ≥ MIN_LEFT)\n * - Preview floor: right column ≥ MIN_PREVIEW_WIDTH (ceiling enforces this)\n * - Cross-tab stability: both reductions are tab-independent\n * - Determinism: pure of (questions, itemsByTab, paneWidth)\n *\n * `crossTabMaxLeftWidth` is NOT replaced — it continues to exist as the primitive.\n * `MAX_LEFT_RATIO` caps the label-driven path inside `adaptiveLeftWidth`; donation\n * operates above the cap when preview slack is available.\n */\nexport function crossTabLeftWidthWithDonation(\n\ttabs: ReadonlyArray<{ multiSelect?: boolean }>,\n\titemsByTab: ReadonlyArray<readonly WrappingSelectItem[]>,\n\tquestions: readonly QuestionData[],\n\tpaneWidth: number,\n): number {\n\tconst labelDriven = crossTabMaxLeftWidth(tabs, itemsByTab, paneWidth);\n\t// Compact-content guard: labels at MIN_LEFT fit below the floor, so widening\n\t// the column would only inject dead space between the option list and the\n\t// preview box. Skip donation and preserve the compact intent.\n\tif (labelDriven <= MIN_LEFT) return labelDriven;\n\tconst previewBudget = crossTabPreviewBudget(questions, paneWidth);\n\tconst slackDonation = paneWidth - PREVIEW_COLUMN_GAP - previewBudget;\n\tconst ceiling = paneWidth - PREVIEW_COLUMN_GAP - MIN_PREVIEW_WIDTH;\n\treturn Math.min(Math.max(labelDriven, slackDonation), Math.max(1, ceiling));\n}\n\n/**\n * Width allocation for side-by-side mode.\n * `adaptiveLeft` is the pre-computed left column width (from `adaptiveLeftWidth`,\n * cross-tab aggregated). The Math.max(1, ...) calls keep both columns >= 1 col on\n * extreme inputs.\n */\nexport function columnWidths(\n\tpaneWidth: number,\n\tadaptiveLeft: number,\n): { leftWidth: number; rightWidth: number; gap: number } {\n\tconst gap = PREVIEW_COLUMN_GAP;\n\tconst leftWidth = Math.min(adaptiveLeft, Math.max(1, paneWidth - gap - 1));\n\tconst rightWidth = Math.max(1, paneWidth - leftWidth - gap);\n\treturn { leftWidth, rightWidth, gap };\n}\n\n/**\n * Returns the widths actually passed to `options.render` and `previewLines` inside\n * `render()`. Stacked uses the full pane width for both; side-by-side splits via\n * `columnWidths`, with the preview column offset by `PREVIEW_PADDING_LEFT`.\n */\nexport function bodyWidths(\n\tpaneWidth: number,\n\tmode: PreviewLayoutMode,\n\tadaptiveLeft: number,\n): { optionsWidth: number; previewWidth: number } {\n\tif (mode === \"stacked\") return { optionsWidth: paneWidth, previewWidth: paneWidth };\n\tconst { leftWidth, rightWidth } = columnWidths(paneWidth, adaptiveLeft);\n\treturn { optionsWidth: leftWidth, previewWidth: Math.max(1, rightWidth - PREVIEW_PADDING_LEFT) };\n}\n"]}
1
+ {"version":3,"file":"preview-layout-decider.d.ts","sourceRoot":"","sources":["../../../../../../../src/core/tools/ask-user-question/view/components/preview/preview-layout-decider.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,wBAAwB,CAAC;AAC3D,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,uBAAuB,CAAC;AAGhE,qEAAqE;AACrE,eAAO,MAAM,iBAAiB,MAAM,CAAC;AACrC,4EAA4E;AAC5E,eAAO,MAAM,kBAAkB,IAAI,CAAC;AACpC,qEAAqE;AACrE,eAAO,MAAM,oBAAoB,IAAI,CAAC;AACtC,gFAAgF;AAChF,eAAO,MAAM,gBAAgB,IAAI,CAAC;AAElC,oFAAoF;AACpF,eAAO,MAAM,QAAQ,KAAK,CAAC;AAC3B,4EAA4E;AAC5E,eAAO,MAAM,cAAc,MAAM,CAAC;AAClC,6FAA6F;AAC7F,eAAO,MAAM,iBAAiB,KAAK,CAAC;AACpC;+EAC+E;AAC/E,eAAO,MAAM,kBAAkB,IAAI,CAAC;AAEpC,MAAM,MAAM,iBAAiB,GAAG,cAAc,GAAG,SAAS,CAAC;AAE3D;;;;;;;;GAQG;AACH,wBAAgB,YAAY,CAAC,aAAa,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,iBAAiB,CAExF;AAED;;;;;;;;GAQG;AACH,wBAAgB,iBAAiB,CAChC,KAAK,EAAE,SAAS,kBAAkB,EAAE,EACpC,iBAAiB,EAAE,MAAM,EACzB,SAAS,EAAE,MAAM,GACf,MAAM,CAYR;AAED;;;;;;;GAOG;AACH,wBAAgB,oBAAoB,CACnC,IAAI,EAAE,aAAa,CAAC;IAAE,WAAW,CAAC,EAAE,OAAO,CAAA;CAAE,CAAC,EAC9C,UAAU,EAAE,aAAa,CAAC,SAAS,kBAAkB,EAAE,CAAC,EACxD,SAAS,EAAE,MAAM,GACf,MAAM,CASR;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,kBAAkB,CAAC,QAAQ,EAAE,YAAY,GAAG,MAAM,CAWjE;AAED;;;;;;;GAOG;AACH,wBAAgB,qBAAqB,CAAC,SAAS,EAAE,SAAS,YAAY,EAAE,EAAE,SAAS,EAAE,MAAM,GAAG,MAAM,CASnG;AAED;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,6BAA6B,CAC5C,IAAI,EAAE,aAAa,CAAC;IAAE,WAAW,CAAC,EAAE,OAAO,CAAA;CAAE,CAAC,EAC9C,UAAU,EAAE,aAAa,CAAC,SAAS,kBAAkB,EAAE,CAAC,EACxD,SAAS,EAAE,SAAS,YAAY,EAAE,EAClC,SAAS,EAAE,MAAM,GACf,MAAM,CAUR;AAED;;;;;GAKG;AACH,wBAAgB,YAAY,CAC3B,SAAS,EAAE,MAAM,EACjB,YAAY,EAAE,MAAM,GAClB;IAAE,SAAS,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE,CAKxD;AAED;;;;GAIG;AACH,wBAAgB,UAAU,CACzB,SAAS,EAAE,MAAM,EACjB,IAAI,EAAE,iBAAiB,EACvB,YAAY,EAAE,MAAM,GAClB;IAAE,YAAY,EAAE,MAAM,CAAC;IAAC,YAAY,EAAE,MAAM,CAAA;CAAE,CAIhD"}
@@ -1 +1 @@
1
- {"version":3,"file":"preview-pane.d.ts","sourceRoot":"","sources":["../../../../../../../src/core/tools/ask-user-question/view/components/preview/preview-pane.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,SAAS,EAAiC,MAAM,wBAAwB,CAAC;AACvF,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,wBAAwB,CAAC;AAC3D,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,wBAAwB,CAAC;AAC3D,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAC;AAC7D,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,6BAA6B,CAAC;AAWxE,OAAO,EACN,+BAA+B,EAC/B,0BAA0B,EAC1B,eAAe,EACf,yBAAyB,GACzB,MAAM,6BAA6B,CAAC;AACrC,OAAO,EAAE,qBAAqB,EAAE,oBAAoB,EAAE,MAAM,6BAA6B,CAAC;AAC1F,OAAO,EACN,0BAA0B,EAC1B,+BAA+B,EAC/B,wBAAwB,EACxB,qBAAqB,EACrB,iBAAiB,EACjB,iBAAiB,GACjB,MAAM,2BAA2B,CAAC;AACnC,OAAO,EACN,kBAAkB,EAClB,iBAAiB,EACjB,oBAAoB,EACpB,gBAAgB,GAChB,MAAM,6BAA6B,CAAC;AAErC;;;;;;;;GAQG;AACH,MAAM,WAAW,gBAAgB;IAChC,YAAY,EAAE,OAAO,CAAC;IACtB,aAAa,EAAE,MAAM,CAAC;IACtB,OAAO,EAAE,OAAO,CAAC;CACjB;AAED,MAAM,WAAW,iBAAiB;IACjC,QAAQ,EAAE,YAAY,CAAC;IACvB,gBAAgB,EAAE,MAAM,MAAM,CAAC;IAC/B,cAAc,EAAE,cAAc,CAAC;IAC/B,YAAY,EAAE,oBAAoB,CAAC;CACnC;AAED;;;;;;;;;;;GAWG;AACH,qBAAa,WAAY,YAAW,YAAY,CAAC,gBAAgB,CAAC,EAAE,SAAS;IAC5E,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAe;IACxC,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAAe;IAChD,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAiB;IAChD,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAuB;IACpD,OAAO,CAAC,KAAK,CAAmB;IAChC;;;;OAIG;IACH,OAAO,CAAC,eAAe,CAErB;IAEF,YAAY,MAAM,EAAE,iBAAiB,EAMpC;IAED,kBAAkB,CAAC,MAAM,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,MAAM,GAAG,IAAI,CAE9D;IAED,OAAO,CAAC,eAAe;IAIvB,QAAQ,CAAC,KAAK,EAAE,gBAAgB,GAAG,IAAI,CAEtC;IAED,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAAG;IAEnC,UAAU,IAAI,IAAI,CAGjB;IAED,MAAM,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE,CAoB9B;IAED,aAAa,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAUnC;IAED,gBAAgB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CActC;IAED,OAAO,CAAC,gBAAgB;IAmBxB,OAAO,CAAC,wBAAwB;CAYhC","sourcesContent":["import { type Component, truncateToWidth, visibleWidth } from \"@earendil-works/pi-tui\";\nimport type { QuestionData } from \"../../../tool/types.ts\";\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.d.ts","sourceRoot":"","sources":["../../../../../../../src/core/tools/ask-user-question/view/components/preview/preview-pane.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,SAAS,EAAiC,MAAM,wBAAwB,CAAC;AACvF,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,wBAAwB,CAAC;AAC3D,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,wBAAwB,CAAC;AAC3D,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAC;AAC7D,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,6BAA6B,CAAC;AAWxE,OAAO,EACN,+BAA+B,EAC/B,0BAA0B,EAC1B,eAAe,EACf,yBAAyB,GACzB,MAAM,6BAA6B,CAAC;AACrC,OAAO,EAAE,qBAAqB,EAAE,oBAAoB,EAAE,MAAM,6BAA6B,CAAC;AAC1F,OAAO,EACN,0BAA0B,EAC1B,+BAA+B,EAC/B,wBAAwB,EACxB,qBAAqB,EACrB,iBAAiB,EACjB,iBAAiB,GACjB,MAAM,2BAA2B,CAAC;AACnC,OAAO,EACN,kBAAkB,EAClB,iBAAiB,EACjB,oBAAoB,EACpB,gBAAgB,GAChB,MAAM,6BAA6B,CAAC;AAErC;;;;;;;;GAQG;AACH,MAAM,WAAW,gBAAgB;IAChC,YAAY,EAAE,OAAO,CAAC;IACtB,aAAa,EAAE,MAAM,CAAC;IACtB,OAAO,EAAE,OAAO,CAAC;CACjB;AAED,MAAM,WAAW,iBAAiB;IACjC,QAAQ,EAAE,YAAY,CAAC;IACvB,gBAAgB,EAAE,MAAM,MAAM,CAAC;IAC/B,cAAc,EAAE,cAAc,CAAC;IAC/B,YAAY,EAAE,oBAAoB,CAAC;CACnC;AAED;;;;;;;;;;;GAWG;AACH,qBAAa,WAAY,YAAW,YAAY,CAAC,gBAAgB,CAAC,EAAE,SAAS;IAC5E,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAe;IACxC,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAAe;IAChD,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAiB;IAChD,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAuB;IACpD,OAAO,CAAC,KAAK,CAAmB;IAChC;;;;OAIG;IACH,OAAO,CAAC,eAAe,CAErB;IAEF,YAAY,MAAM,EAAE,iBAAiB,EAMpC;IAED,kBAAkB,CAAC,MAAM,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,MAAM,GAAG,IAAI,CAE9D;IAED,OAAO,CAAC,eAAe;IAIvB,QAAQ,CAAC,KAAK,EAAE,gBAAgB,GAAG,IAAI,CAEtC;IAED,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAAG;IAEnC,UAAU,IAAI,IAAI,CAGjB;IAED,MAAM,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE,CAoB9B;IAED,aAAa,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAUnC;IAED,gBAAgB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CActC;IAED,OAAO,CAAC,gBAAgB;IAmBxB,OAAO,CAAC,wBAAwB;CAYhC"}
@@ -1 +1 @@
1
- {"version":3,"file":"submit-picker.d.ts","sourceRoot":"","sources":["../../../../../../src/core/tools/ask-user-question/view/components/submit-picker.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,iDAAiD,CAAC;AAE7E,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AAMxD,eAAO,MAAM,YAAY,mBAAmB,CAAC;AAC7C,eAAO,MAAM,YAAY,WAAW,CAAC;AAErC;;;;GAIG;AACH,MAAM,WAAW,iBAAiB;IACjC,4EAA4E;IAC5E,IAAI,EAAE,aAAa,CAAC;QAAE,MAAM,EAAE,OAAO,CAAA;KAAE,CAAC,CAAC;CACzC;AAED;;;;;;;;;;GAUG;AACH,qBAAa,YAAa,YAAW,YAAY,CAAC,iBAAiB,CAAC;IACnE,OAAO,CAAC,KAAK,CAAoB;IAEjC,iBAAyB,KAAK,CAAQ;IAEtC,YAAY,KAAK,EAAE,KAAK,EAGvB;IAED,QAAQ,CAAC,KAAK,EAAE,iBAAiB,GAAG,IAAI,CAEvC;IAED,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAAG;IAEnC,UAAU,IAAI,IAAI,CAAG;IAErB,aAAa,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAEpC;IAED,MAAM,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE,CAW9B;CACD","sourcesContent":["import type { Theme } from \"../../../../../modes/interactive/theme/theme.ts\";\nimport { truncateToWidth } from \"@earendil-works/pi-tui\";\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\tdeclare private 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\tlines.push(truncateToWidth(`${pointer}${number}${label}`, width, \"\"));\n\t\t}\n\t\treturn lines;\n\t}\n}\n"]}
1
+ {"version":3,"file":"submit-picker.d.ts","sourceRoot":"","sources":["../../../../../../src/core/tools/ask-user-question/view/components/submit-picker.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,iDAAiD,CAAC;AAE7E,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AAMxD,eAAO,MAAM,YAAY,mBAAmB,CAAC;AAC7C,eAAO,MAAM,YAAY,WAAW,CAAC;AAErC;;;;GAIG;AACH,MAAM,WAAW,iBAAiB;IACjC,4EAA4E;IAC5E,IAAI,EAAE,aAAa,CAAC;QAAE,MAAM,EAAE,OAAO,CAAA;KAAE,CAAC,CAAC;CACzC;AAED;;;;;;;;;;GAUG;AACH,qBAAa,YAAa,YAAW,YAAY,CAAC,iBAAiB,CAAC;IACnE,OAAO,CAAC,KAAK,CAAoB;IAEjC,iBAAyB,KAAK,CAAQ;IAEtC,YAAY,KAAK,EAAE,KAAK,EAGvB;IAED,QAAQ,CAAC,KAAK,EAAE,iBAAiB,GAAG,IAAI,CAEvC;IAED,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAAG;IAEnC,UAAU,IAAI,IAAI,CAAG;IAErB,aAAa,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAEpC;IAED,MAAM,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE,CAW9B;CACD"}
@@ -1 +1 @@
1
- {"version":3,"file":"tab-bar.d.ts","sourceRoot":"","sources":["../../../../../../src/core/tools/ask-user-question/view/components/tab-bar.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,iDAAiD,CAAC;AAE7E,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AAExD;;;;;;GAMG;AACH,MAAM,WAAW,WAAW;IAC3B,iDAAiD;IACjD,IAAI,EAAE,aAAa,CAAC;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,OAAO,CAAC;QAAC,MAAM,EAAE,OAAO,CAAA;KAAE,CAAC,CAAC;IAC3E,2EAA2E;IAC3E,MAAM,EAAE;QAAE,MAAM,EAAE,OAAO,CAAC;QAAC,WAAW,EAAE,OAAO,CAAA;KAAE,CAAC;CAClD;AAED,qBAAa,MAAO,YAAW,YAAY,CAAC,WAAW,CAAC;IACvD,OAAO,CAAC,KAAK,CAAc;IAE3B,iBAAyB,KAAK,CAAQ;IAEtC,YAAY,KAAK,EAAE,KAAK,EAGvB;IAED,QAAQ,CAAC,KAAK,EAAE,WAAW,GAAG,IAAI,CAEjC;IAED,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAAG;IAEnC,UAAU,IAAI,IAAI,CAAG;IAErB,MAAM,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE,CAsB9B;CACD","sourcesContent":["import type { Theme } from \"../../../../../modes/interactive/theme/theme.ts\";\nimport { truncateToWidth } from \"@earendil-works/pi-tui\";\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\tdeclare private 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.d.ts","sourceRoot":"","sources":["../../../../../../src/core/tools/ask-user-question/view/components/tab-bar.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,iDAAiD,CAAC;AAE7E,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AAExD;;;;;;GAMG;AACH,MAAM,WAAW,WAAW;IAC3B,iDAAiD;IACjD,IAAI,EAAE,aAAa,CAAC;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,OAAO,CAAC;QAAC,MAAM,EAAE,OAAO,CAAA;KAAE,CAAC,CAAC;IAC3E,2EAA2E;IAC3E,MAAM,EAAE;QAAE,MAAM,EAAE,OAAO,CAAC;QAAC,WAAW,EAAE,OAAO,CAAA;KAAE,CAAC;CAClD;AAED,qBAAa,MAAO,YAAW,YAAY,CAAC,WAAW,CAAC;IACvD,OAAO,CAAC,KAAK,CAAc;IAE3B,iBAAyB,KAAK,CAAQ;IAEtC,YAAY,KAAK,EAAE,KAAK,EAGvB;IAED,QAAQ,CAAC,KAAK,EAAE,WAAW,GAAG,IAAI,CAEjC;IAED,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAAG;IAEnC,UAAU,IAAI,IAAI,CAAG;IAErB,MAAM,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE,CAsB9B;CACD"}
@@ -1 +1 @@
1
- {"version":3,"file":"wrapping-select.d.ts","sourceRoot":"","sources":["../../../../../../src/core/tools/ask-user-question/view/components/wrapping-select.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,wBAAwB,CAAC;AAIxD;;;;;;;;;;;;;;;GAeG;AACH,MAAM,MAAM,kBAAkB,GAC3B;IAAE,IAAI,EAAE,QAAQ,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,WAAW,CAAC,EAAE,MAAM,CAAA;CAAE,GACvD;IAAE,IAAI,EAAE,OAAO,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,WAAW,CAAC,EAAE,MAAM,CAAA;CAAE,GACtD;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,WAAW,CAAC,EAAE,MAAM,CAAA;CAAE,GACrD;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,WAAW,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC;AAEzD,MAAM,WAAW,mBAAmB;IACnC,YAAY,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,CAAC;IACvC,WAAW,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,CAAC;IACtC,UAAU,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,CAAC;CACrC;AAED;;;;;;;GAOG;AACH,MAAM,WAAW,qBAAqB;IACrC,+EAA+E;IAC/E,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,gGAAgG;IAChG,sBAAsB,CAAC,EAAE,MAAM,CAAC;CAChC;AAED,qBAAa,cAAe,YAAW,SAAS;IAC/C,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,cAAc,CAAQ;IAC9C,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,gBAAgB,CAAQ;IAChD,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,gBAAgB,CAAQ;IAChD,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,oBAAoB,CAAa;IACzD,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,kBAAkB,CAAa;IACvD,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,cAAc,CAAQ;IAC9C,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,iBAAiB,CAAK;IAE9C,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAgC;IACtD,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAS;IACpC,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAsB;IAC5C,OAAO,CAAC,iBAAiB,CAAS;IAClC,OAAO,CAAC,sBAAsB,CAAS;IAEvC,OAAO,CAAC,aAAa,CAAK;IAC1B,OAAO,CAAC,OAAO,CAAQ;IACvB,OAAO,CAAC,WAAW,CAAM;IACzB,OAAO,CAAC,WAAW,CAAiC;IACpD;;;;;OAKG;IACH,OAAO,CAAC,cAAc,CAAiC;IACvD;;;;OAIG;IACH,OAAO,CAAC,sBAAsB,CAAiC;IAE/D,YACC,KAAK,EAAE,SAAS,kBAAkB,EAAE,EACpC,UAAU,EAAE,MAAM,EAClB,KAAK,EAAE,mBAAmB,EAC1B,OAAO,GAAE,qBAA0B,EAOnC;IAED;;;;OAIG;IACH,YAAY,CAAC,iBAAiB,EAAE,MAAM,EAAE,sBAAsB,EAAE,MAAM,GAAG,IAAI,CAG5E;IAED,gBAAgB,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAEpC;IAED,UAAU,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAEjC;IAED;;;;;OAKG;IACH,iBAAiB,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,EAAE,aAAa,CAAC,EAAE,MAAM,GAAG,IAAI,CAQzE;IAED,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAGjC;IAED,cAAc,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAEnC;IAED,oEAAoE;IACpE,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAAG;IAEnC,UAAU,IAAI,IAAI,CAAG;IAErB,MAAM,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE,CAkB9B;IAED,OAAO,CAAC,oBAAoB;IAO5B,OAAO,CAAC,qBAAqB;IAI7B,OAAO,CAAC,UAAU;IAiClB,OAAO,CAAC,cAAc;IAOtB,OAAO,CAAC,yBAAyB;IAIjC;;;;;;;;OAQG;IACH,OAAO,CAAC,oBAAoB;IAS5B,OAAO,CAAC,mBAAmB;IAY3B,OAAO,CAAC,gBAAgB;IAKxB,OAAO,CAAC,gBAAgB;IAexB,OAAO,CAAC,sBAAsB;CAS9B","sourcesContent":["import type { Component } from \"@earendil-works/pi-tui\";\nimport { visibleWidth, wrapTextWithAnsi } from \"@earendil-works/pi-tui\";\nimport { ROW_INTENT_META } from \"../../state/row-intent.ts\";\n\n/**\n * Row-intent discriminated union. `kind` is the single discriminator —\n * pre-1.0.3 boolean flags have been removed (see `banned-flags.test.ts`).\n * Modeled after `QuestionnaireAction` (`key-router.ts:13-32`) and `Effect`\n * (`state-reducer.ts:26-32`) — pure literal-tagged variants, no shared base,\n * exhaustive-`switch` enforcement via non-`void` returns.\n *\n * Variant semantics:\n * - `option`: a regular author-defined option row.\n * - `other`: the inline free-text input row appended to single-select questions\n * (label is \"Type something.\"). Renders as inline `Input` when active.\n * - `chat`: the abandon-questionnaire escape-hatch row (label is \"Chat about this\").\n * Renders as inline `Input` when active.\n * - `next`: the explicit commit-and-advance row appended to multi-select questions\n * (label is \"Next\"). Renders without a number / checkbox.\n */\nexport type WrappingSelectItem =\n\t| { kind: \"option\"; label: string; description?: string }\n\t| { kind: \"other\"; label: string; description?: string }\n\t| { kind: \"chat\"; label: string; description?: string }\n\t| { kind: \"next\"; label: string; description?: string };\n\nexport interface WrappingSelectTheme {\n\tselectedText: (text: string) => string;\n\tdescription: (text: string) => string;\n\tscrollInfo: (text: string) => string;\n}\n\n/**\n * Numbering controls.\n *\n * Use `numberStartOffset` + `totalItemsForNumbering` when a list is logically a slice of a\n * larger numbered sequence — e.g. the chat row lives in its own WrappingSelect but should\n * render as `(N+1).` where N is the previous list's item count, with the column padded as\n * if both lists were one continuous numbered sequence.\n */\nexport interface WrappingSelectOptions {\n\t/** Start numbering at this offset + 1 (default 0 → rows labeled 1, 2, 3 …). */\n\tnumberStartOffset?: number;\n\t/** Override the total used to pad the number column (useful when items span multiple lists). */\n\ttotalItemsForNumbering?: number;\n}\n\nexport class WrappingSelect implements Component {\n\tprivate static readonly ACTIVE_POINTER = \"❯ \";\n\tprivate static readonly INACTIVE_POINTER = \" \";\n\tprivate static readonly NUMBER_SEPARATOR = \". \";\n\tprivate static readonly CURSOR_INVERSE_START = \"\\x1b[7m\";\n\tprivate static readonly CURSOR_INVERSE_END = \"\\x1b[0m\";\n\tprivate static readonly CONFIRMED_MARK = \" ✔\";\n\tprivate static readonly MIN_CONTENT_WIDTH = 1;\n\n\tprivate readonly items: readonly WrappingSelectItem[];\n\tprivate readonly maxVisible: number;\n\tprivate readonly theme: WrappingSelectTheme;\n\tprivate numberStartOffset: number;\n\tprivate totalItemsForNumbering: number;\n\n\tprivate selectedIndex = 0;\n\tprivate focused = true;\n\tprivate inputBuffer = \"\";\n\tprivate inputCursor: number | undefined = undefined;\n\t/**\n\t * Index of the row that was previously confirmed for this list (e.g. the user's prior\n\t * answer when re-entering a multi-question tab). Renders `<label> ✔` in the active-row\n\t * styling but WITHOUT the `❯` pointer — pointer is reserved for the live cursor. When\n\t * `selectedIndex === confirmedIndex && focused`, the active rendering wins (no double-mark).\n\t */\n\tprivate confirmedIndex: number | undefined = undefined;\n\t/**\n\t * When set together with `confirmedIndex`, replaces the row's static label at render time.\n\t * Used for the `kind: \"other\"` sentinel — its label is \"Type something.\" but if the user's\n\t * prior answer was custom text, we render that text instead (e.g. `4. Hello ✔`).\n\t */\n\tprivate confirmedLabelOverride: string | undefined = undefined;\n\n\tconstructor(\n\t\titems: readonly WrappingSelectItem[],\n\t\tmaxVisible: number,\n\t\ttheme: WrappingSelectTheme,\n\t\toptions: WrappingSelectOptions = {},\n\t) {\n\t\tthis.items = items;\n\t\tthis.maxVisible = Math.max(1, maxVisible);\n\t\tthis.theme = theme;\n\t\tthis.numberStartOffset = options.numberStartOffset ?? 0;\n\t\tthis.totalItemsForNumbering = options.totalItemsForNumbering ?? items.length;\n\t}\n\n\t/**\n\t * Update the numbering offset + total padding width without rebuilding the component.\n\t * Used by the host to keep the chat-row WrappingSelect's number aligned with the active tab's\n\t * options list when the user switches tabs (each tab can have a different items count).\n\t */\n\tsetNumbering(numberStartOffset: number, totalItemsForNumbering: number): void {\n\t\tthis.numberStartOffset = numberStartOffset;\n\t\tthis.totalItemsForNumbering = Math.max(1, totalItemsForNumbering);\n\t}\n\n\tsetSelectedIndex(index: number): void {\n\t\tthis.selectedIndex = Math.max(0, Math.min(index, this.items.length - 1));\n\t}\n\n\tsetFocused(focused: boolean): void {\n\t\tthis.focused = focused;\n\t}\n\n\t/**\n\t * Mark a previously-confirmed row. Pass `undefined` to clear. `labelOverride` replaces\n\t * the row's static `item.label` at render time — used for the `kind: \"other\"` sentinel so\n\t * the row reads `Hello ✔` instead of `Type something. ✔` when the prior answer was custom\n\t * text.\n\t */\n\tsetConfirmedIndex(index: number | undefined, labelOverride?: string): void {\n\t\tif (index === undefined) {\n\t\t\tthis.confirmedIndex = undefined;\n\t\t\tthis.confirmedLabelOverride = undefined;\n\t\t\treturn;\n\t\t}\n\t\tthis.confirmedIndex = Math.max(0, Math.min(index, this.items.length - 1));\n\t\tthis.confirmedLabelOverride = labelOverride;\n\t}\n\n\tsetInputBuffer(text: string): void {\n\t\tthis.inputBuffer = text;\n\t\tif (this.inputCursor !== undefined) this.inputCursor = this.clampInputCursor(this.inputCursor);\n\t}\n\n\tsetInputCursor(cursor: number): void {\n\t\tthis.inputCursor = this.clampInputCursor(cursor);\n\t}\n\n\t/** Intentionally empty — input is routed at the container level. */\n\thandleInput(_data: string): void {}\n\n\tinvalidate(): void {}\n\n\trender(width: number): string[] {\n\t\tif (this.items.length === 0) return [];\n\n\t\tconst { startIndex, endIndex } = this.computeVisibleWindow();\n\t\tconst numberWidth = String(Math.max(1, this.totalItemsForNumbering)).length;\n\t\tconst lines: string[] = [];\n\n\t\tfor (let i = startIndex; i < endIndex; i++) {\n\t\t\tconst item = this.items[i];\n\t\t\tif (!item) continue;\n\t\t\tconst isActive = i === this.selectedIndex && this.focused;\n\t\t\tlines.push(...this.renderItem(item, i, isActive, width, numberWidth));\n\t\t}\n\n\t\tif (this.hasItemsOutsideWindow(startIndex, endIndex)) {\n\t\t\tlines.push(this.theme.scrollInfo(` (${this.selectedIndex + 1}/${this.items.length})`));\n\t\t}\n\t\treturn lines;\n\t}\n\n\tprivate computeVisibleWindow(): { startIndex: number; endIndex: number } {\n\t\tconst half = Math.floor(this.maxVisible / 2);\n\t\tconst startIndex = Math.max(0, Math.min(this.selectedIndex - half, this.items.length - this.maxVisible));\n\t\tconst endIndex = Math.min(startIndex + this.maxVisible, this.items.length);\n\t\treturn { startIndex, endIndex };\n\t}\n\n\tprivate hasItemsOutsideWindow(startIndex: number, endIndex: number): boolean {\n\t\treturn startIndex > 0 || endIndex < this.items.length;\n\t}\n\n\tprivate renderItem(\n\t\titem: WrappingSelectItem,\n\t\tindex: number,\n\t\tisActive: boolean,\n\t\twidth: number,\n\t\tnumberWidth: number,\n\t): string[] {\n\t\tconst rowPrefix = this.buildRowPrefix(index, isActive, numberWidth);\n\t\tconst continuationPrefix = \" \".repeat(visibleWidth(rowPrefix));\n\t\tconst contentWidth = Math.max(WrappingSelect.MIN_CONTENT_WIDTH, width - visibleWidth(rowPrefix));\n\n\t\tif (this.shouldRenderAsInlineInput(item, isActive)) {\n\t\t\treturn this.renderInlineInputRow(rowPrefix, continuationPrefix, contentWidth);\n\t\t}\n\n\t\t// Confirmed row gets a trailing ` ✔` and accent+bold styling; pointer is independent\n\t\t// (still ❯ when active). When `index === confirmedIndex` AND `isActive`, both `❯` and\n\t\t// `✔` appear on the same row — load-bearing for the case where the prior answer was\n\t\t// row 0 (cursor resets to 0 on tab-back, so the confirmed row IS the active row).\n\t\t// Optional `confirmedLabelOverride` replaces the static label (used for `kind: \"other\"`\n\t\t// + `kind: \"custom\"` answer); the inline-input branch above still wins for `kind: \"other\" + isActive`.\n\t\tconst isConfirmed = index === this.confirmedIndex;\n\t\tconst label = isConfirmed\n\t\t\t? `${this.confirmedLabelOverride ?? item.label}${WrappingSelect.CONFIRMED_MARK}`\n\t\t\t: item.label;\n\t\tconst applySelectedStyle = isActive || isConfirmed;\n\n\t\treturn [\n\t\t\t...this.renderLabelBlock(label, rowPrefix, continuationPrefix, contentWidth, applySelectedStyle),\n\t\t\t...this.renderDescriptionBlock(item.description, continuationPrefix, contentWidth),\n\t\t];\n\t}\n\n\tprivate buildRowPrefix(index: number, isActive: boolean, numberWidth: number): string {\n\t\tconst pointer = isActive ? WrappingSelect.ACTIVE_POINTER : WrappingSelect.INACTIVE_POINTER;\n\t\tconst displayNumber = this.numberStartOffset + index + 1;\n\t\tconst paddedNumber = String(displayNumber).padStart(numberWidth, \" \");\n\t\treturn `${pointer}${paddedNumber}${WrappingSelect.NUMBER_SEPARATOR}`;\n\t}\n\n\tprivate shouldRenderAsInlineInput(item: WrappingSelectItem, isActive: boolean): boolean {\n\t\treturn isActive && ROW_INTENT_META[item.kind].activatesInputMode;\n\t}\n\n\t/**\n\t * Render the inline input row across one or more lines, wrapping at `contentWidth`\n\t * so long input doesn't run off the right edge or trip the parent renderer's\n\t * width invariant. Mirrors `renderLabelBlock`'s contract: first line carries\n\t * `rowPrefix`, continuation lines carry `continuationPrefix` (spaces), and every\n\t * emitted line passes through `theme.selectedText`. The cursor mirrors the\n\t * main chat editor: inverse-video over the character at the caret, or an\n\t * inverse-video space at end-of-line.\n\t */\n\tprivate renderInlineInputRow(rowPrefix: string, continuationPrefix: string, contentWidth: number): string[] {\n\t\tconst raw = this.inputTextWithCursor();\n\t\tconst wrapped = wrapTextWithAnsi(raw, contentWidth);\n\t\treturn wrapped.map((segment, index) => {\n\t\t\tconst prefix = index === 0 ? rowPrefix : continuationPrefix;\n\t\t\treturn this.theme.selectedText(`${prefix}${segment}`);\n\t\t});\n\t}\n\n\tprivate inputTextWithCursor(): string {\n\t\tconst cursor = this.clampInputCursor(this.inputCursor ?? this.inputBuffer.length);\n\t\tconst before = this.inputBuffer.slice(0, cursor);\n\t\tconst after = this.inputBuffer.slice(cursor);\n\t\tconst [at = \"\"] = Array.from(after);\n\t\tif (at === \"\") {\n\t\t\treturn `${before}${WrappingSelect.CURSOR_INVERSE_START} ${WrappingSelect.CURSOR_INVERSE_END}`;\n\t\t}\n\t\tconst rest = after.slice(at.length);\n\t\treturn `${before}${WrappingSelect.CURSOR_INVERSE_START}${at}${WrappingSelect.CURSOR_INVERSE_END}${rest}`;\n\t}\n\n\tprivate clampInputCursor(cursor: number): number {\n\t\tif (!Number.isFinite(cursor)) return this.inputBuffer.length;\n\t\treturn Math.max(0, Math.min(cursor, this.inputBuffer.length));\n\t}\n\n\tprivate renderLabelBlock(\n\t\tlabel: string,\n\t\trowPrefix: string,\n\t\tcontinuationPrefix: string,\n\t\tcontentWidth: number,\n\t\tapplySelectedStyle: boolean,\n\t): string[] {\n\t\tconst wrapped = wrapTextWithAnsi(label, contentWidth);\n\t\treturn wrapped.map((segment, index) => {\n\t\t\tconst prefix = index === 0 ? rowPrefix : continuationPrefix;\n\t\t\tconst line = `${prefix}${segment}`;\n\t\t\treturn applySelectedStyle ? this.theme.selectedText(line) : line;\n\t\t});\n\t}\n\n\tprivate renderDescriptionBlock(\n\t\tdescription: string | undefined,\n\t\tcontinuationPrefix: string,\n\t\tcontentWidth: number,\n\t): string[] {\n\t\tif (!description) return [];\n\t\tconst wrapped = wrapTextWithAnsi(description, contentWidth);\n\t\treturn wrapped.map((segment) => `${continuationPrefix}${this.theme.description(segment)}`);\n\t}\n}\n"]}
1
+ {"version":3,"file":"wrapping-select.d.ts","sourceRoot":"","sources":["../../../../../../src/core/tools/ask-user-question/view/components/wrapping-select.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,wBAAwB,CAAC;AAIxD;;;;;;;;;;;;;;;GAeG;AACH,MAAM,MAAM,kBAAkB,GAC3B;IAAE,IAAI,EAAE,QAAQ,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,WAAW,CAAC,EAAE,MAAM,CAAA;CAAE,GACvD;IAAE,IAAI,EAAE,OAAO,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,WAAW,CAAC,EAAE,MAAM,CAAA;CAAE,GACtD;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,WAAW,CAAC,EAAE,MAAM,CAAA;CAAE,GACrD;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,WAAW,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC;AAEzD,MAAM,WAAW,mBAAmB;IACnC,YAAY,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,CAAC;IACvC,WAAW,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,CAAC;IACtC,UAAU,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,CAAC;CACrC;AAED;;;;;;;GAOG;AACH,MAAM,WAAW,qBAAqB;IACrC,+EAA+E;IAC/E,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,gGAAgG;IAChG,sBAAsB,CAAC,EAAE,MAAM,CAAC;CAChC;AAED,qBAAa,cAAe,YAAW,SAAS;IAC/C,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,cAAc,CAAQ;IAC9C,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,gBAAgB,CAAQ;IAChD,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,gBAAgB,CAAQ;IAChD,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,oBAAoB,CAAa;IACzD,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,kBAAkB,CAAa;IACvD,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,cAAc,CAAQ;IAC9C,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,iBAAiB,CAAK;IAE9C,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAgC;IACtD,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAS;IACpC,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAsB;IAC5C,OAAO,CAAC,iBAAiB,CAAS;IAClC,OAAO,CAAC,sBAAsB,CAAS;IAEvC,OAAO,CAAC,aAAa,CAAK;IAC1B,OAAO,CAAC,OAAO,CAAQ;IACvB,OAAO,CAAC,WAAW,CAAM;IACzB,OAAO,CAAC,WAAW,CAAiC;IACpD;;;;;OAKG;IACH,OAAO,CAAC,cAAc,CAAiC;IACvD;;;;OAIG;IACH,OAAO,CAAC,sBAAsB,CAAiC;IAE/D,YACC,KAAK,EAAE,SAAS,kBAAkB,EAAE,EACpC,UAAU,EAAE,MAAM,EAClB,KAAK,EAAE,mBAAmB,EAC1B,OAAO,GAAE,qBAA0B,EAOnC;IAED;;;;OAIG;IACH,YAAY,CAAC,iBAAiB,EAAE,MAAM,EAAE,sBAAsB,EAAE,MAAM,GAAG,IAAI,CAG5E;IAED,gBAAgB,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAEpC;IAED,UAAU,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAEjC;IAED;;;;;OAKG;IACH,iBAAiB,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,EAAE,aAAa,CAAC,EAAE,MAAM,GAAG,IAAI,CAQzE;IAED,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAGjC;IAED,cAAc,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAEnC;IAED,oEAAoE;IACpE,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAAG;IAEnC,UAAU,IAAI,IAAI,CAAG;IAErB,MAAM,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE,CAkB9B;IAED,OAAO,CAAC,oBAAoB;IAO5B,OAAO,CAAC,qBAAqB;IAI7B,OAAO,CAAC,UAAU;IAiClB,OAAO,CAAC,cAAc;IAOtB,OAAO,CAAC,yBAAyB;IAIjC;;;;;;;;OAQG;IACH,OAAO,CAAC,oBAAoB;IAS5B,OAAO,CAAC,mBAAmB;IAY3B,OAAO,CAAC,gBAAgB;IAKxB,OAAO,CAAC,gBAAgB;IAexB,OAAO,CAAC,sBAAsB;CAS9B"}
@@ -1 +1 @@
1
- {"version":3,"file":"dialog-builder.d.ts","sourceRoot":"","sources":["../../../../../src/core/tools/ask-user-question/view/dialog-builder.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,8CAA8C,CAAC;AAC1E,OAAO,EAAE,KAAK,SAAS,EAAa,KAAK,KAAK,EAAU,MAAM,wBAAwB,CAAC;AACvF,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAC;AAC5D,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAErD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,+BAA+B,CAAC;AACjE,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,sCAAsC,CAAC;AAC7E,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,yBAAyB,CAAC;AACtD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AACvD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAGzD,eAAO,MAAM,eAAe,iBAAiB,CAAC;AAC9C,eAAO,MAAM,aAAa,2BAAiB,CAAC;AAC5C,eAAO,MAAM,gBAAgB,iBAAiB,CAAC;AAC/C,eAAO,MAAM,eAAe,gBAAgB,CAAC;AAC7C,eAAO,MAAM,aAAa,yBAAyB,CAAC;AACpD,eAAO,MAAM,gBAAgB,eAAe,CAAC;AAC7C,eAAO,MAAM,WAAW,QAAiE,CAAC;AAC1F,eAAO,MAAM,UAAU,QAAgF,CAAC;AACxG,eAAO,MAAM,uBAAuB,yBAA2B,CAAC;AAChE,eAAO,MAAM,iBAAiB,wBAA0B,CAAC;AACzD,eAAO,MAAM,cAAc,wBAAwB,CAAC;AACpD,eAAO,MAAM,YAAY,kCAAkC,CAAC;AAC5D,eAAO,MAAM,yBAAyB,yDAAoD,CAAC;AAE3F,MAAM,MAAM,WAAW,GAAG,kBAAkB,CAAC;AAE7C,+FAA+F;AAC/F,MAAM,WAAW,WAAW;IAC3B,KAAK,EAAE,WAAW,CAAC;IACnB,iBAAiB,EAAE,YAAY,CAAC,gBAAgB,CAAC,CAAC;CAClD;AAED,4EAA4E;AAC5E,MAAM,WAAW,YAAY;IAC5B,KAAK,EAAE,KAAK,CAAC;IACb,SAAS,EAAE,SAAS,YAAY,EAAE,CAAC;IACnC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC;IAC3B,UAAU,EAAE,KAAK,CAAC;IAClB,OAAO,EAAE,WAAW,CAAC;IACrB,OAAO,EAAE,OAAO,CAAC;IACjB,WAAW,EAAE,aAAa,CAAC,aAAa,CAAC,CAAC;IAC1C,sHAAsH;IACtH,YAAY,CAAC,EAAE,SAAS,CAAC;IACzB,sGAAsG;IACtG,aAAa,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,MAAM,CAAC;IACzC,yJAAyJ;IACzJ,oBAAoB,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,MAAM,CAAC;CAChD;AAED;;;;;;;GAOG;AACH,qBAAa,UAAW,YAAW,YAAY,CAAC,WAAW,CAAC;IAC3D,OAAO,CAAC,SAAS,CAAc;IAC/B,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAe;IACtC,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAAqB;IACtD,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAiC;IAChE,OAAO,CAAC,QAAQ,CAAC,iBAAiB,CAAS;IAE3C,YAAY,MAAM,EAAE,YAAY,EAAE,YAAY,EAAE,WAAW,EAqB1D;IAED,QAAQ,CAAC,KAAK,EAAE,WAAW,GAAG,IAAI,CAEjC;IAED,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAAG;IAKnC,UAAU,IAAI,IAAI,CAAG;IAErB,MAAM,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE,CAI9B;IAED,OAAO,CAAC,0BAA0B;CA2BlC","sourcesContent":["import { DynamicBorder } from \"../../../../modes/interactive/components/index.ts\";\nimport type { Theme } from \"../../../../modes/interactive/theme/theme.ts\";\nimport { type Component, Container, type Input, Spacer } from \"@earendil-works/pi-tui\";\nimport type { QuestionnaireState } from \"../state/state.ts\";\nimport type { QuestionData } from \"../tool/types.ts\";\nimport { BodyResidualSpacer } from \"./body-residual-spacer.ts\";\nimport type { ChatRowView } from \"./components/chat-row-view.ts\";\nimport type { PreviewPaneProps } from \"./components/preview/preview-pane.ts\";\nimport type { TabBar } from \"./components/tab-bar.ts\";\nimport type { StatefulView } from \"./stateful-view.ts\";\nimport type { TabComponents } from \"./tab-components.ts\";\nimport { QuestionTabStrategy, SubmitTabStrategy, type TabContentStrategy } from \"./tab-content-strategy.ts\";\n\nexport const HINT_PART_ENTER = \"enter select\";\nexport const HINT_PART_NAV = \"↑/↓ navigate\";\nexport const HINT_PART_TOGGLE = \"space toggle\";\nexport const HINT_PART_NOTES = \"n add notes\";\nexport const HINT_PART_TAB = \"tab switch questions\";\nexport const HINT_PART_CANCEL = \"esc cancel\";\nexport const HINT_SINGLE = [HINT_PART_ENTER, HINT_PART_NAV, HINT_PART_CANCEL].join(\" · \");\nexport const HINT_MULTI = [HINT_PART_ENTER, HINT_PART_NAV, HINT_PART_TAB, HINT_PART_CANCEL].join(\" · \");\nexport const HINT_MULTISELECT_SUFFIX = ` · ${HINT_PART_TOGGLE}`;\nexport const HINT_NOTES_SUFFIX = ` · ${HINT_PART_NOTES}`;\nexport const REVIEW_HEADING = \"Review your answers\";\nexport const READY_PROMPT = \"Ready to submit your answers?\";\nexport const INCOMPLETE_WARNING_PREFIX = \"⚠ Answer remaining questions before submitting:\";\n\nexport type DialogState = QuestionnaireState;\n\n/** Per-tick projection of dialog state. Written by the adapter; read by the strategy thunk. */\nexport interface DialogProps {\n\tstate: DialogState;\n\tactivePreviewPane: StatefulView<PreviewPaneProps>;\n}\n\n/** Construction-time config for `DialogView`. Frozen after construction. */\nexport interface DialogConfig {\n\ttheme: Theme;\n\tquestions: readonly QuestionData[];\n\ttabBar: TabBar | undefined;\n\tnotesInput: Input;\n\tchatRow: ChatRowView;\n\tisMulti: boolean;\n\ttabsByIndex: ReadonlyArray<TabComponents>;\n\t/** Optional so single-question mode and non-submit tests can omit it; SubmitTabStrategy falls back to Spacer rows. */\n\tsubmitPicker?: Component;\n\t/** Worst-case body height across all tabs/options. Determines the stable overall dialog footprint. */\n\tgetBodyHeight: (width: number) => number;\n\t/** Body height of the CURRENTLY active tab/option. The chrome subtracts this from `getBodyHeight` to absorb the residual OUTSIDE the bordered region. */\n\tgetCurrentBodyHeight: (width: number) => number;\n}\n\n/**\n * The 7th renderable, promoted from a structural literal to a named class so\n * all view-layer components share one explicit `implements StatefulView<P>`\n * contract. `setProps(DialogProps)` writes the live cell read by the\n * strategy thunk during `render()`. `liveProps.activePreviewPane` is a\n * resolved pane reference threaded by the adapter per tick — the dialog\n * itself does not derive it.\n */\nexport class DialogView implements StatefulView<DialogProps> {\n\tprivate liveProps: DialogProps;\n\tprivate readonly config: DialogConfig;\n\tprivate readonly questionStrategy: TabContentStrategy;\n\tprivate readonly submitStrategy: TabContentStrategy | undefined;\n\tprivate readonly maxFooterRowCount: number;\n\n\tconstructor(config: DialogConfig, initialProps: DialogProps) {\n\t\tthis.config = config;\n\t\tthis.liveProps = initialProps;\n\t\tthis.questionStrategy = new QuestionTabStrategy({\n\t\t\ttheme: config.theme,\n\t\t\tquestions: config.questions,\n\t\t\tgetPreviewPane: () => this.liveProps.activePreviewPane,\n\t\t\ttabsByIndex: config.tabsByIndex,\n\t\t\tnotesInput: config.notesInput,\n\t\t\tchatRow: config.chatRow,\n\t\t\tisMulti: config.isMulti,\n\t\t\tgetCurrentBodyHeight: config.getCurrentBodyHeight,\n\t\t});\n\t\tthis.submitStrategy = config.isMulti\n\t\t\t? new SubmitTabStrategy({\n\t\t\t\t\ttheme: config.theme,\n\t\t\t\t\tquestions: config.questions,\n\t\t\t\t\tsubmitPicker: config.submitPicker,\n\t\t\t\t})\n\t\t\t: undefined;\n\t\tthis.maxFooterRowCount = Math.max(this.questionStrategy.footerRowCount, this.submitStrategy?.footerRowCount ?? 0);\n\t}\n\n\tsetProps(props: DialogProps): void {\n\t\tthis.liveProps = props;\n\t}\n\n\thandleInput(_data: string): void {}\n\n\t// Invalidation is driven by `QuestionnairePropsAdapter.invalidate()`, which\n\t// owns the full set of renderables (binding registries + extras like\n\t// `notesInput`). DialogView has no cached layout of its own.\n\tinvalidate(): void {}\n\n\trender(width: number): string[] {\n\t\tconst onSubmit = this.config.isMulti && this.liveProps.state.currentTab === this.config.questions.length;\n\t\tconst strategy = onSubmit && this.submitStrategy ? this.submitStrategy : this.questionStrategy;\n\t\treturn this.buildContainerFromStrategy(strategy).render(width);\n\t}\n\n\tprivate buildContainerFromStrategy(strategy: TabContentStrategy): Container {\n\t\tconst { theme, isMulti, tabBar } = this.config;\n\t\tconst state = this.liveProps.state;\n\t\tconst container = new Container();\n\t\tconst border = () => new DynamicBorder((s) => theme.fg(\"accent\", s));\n\n\t\tcontainer.addChild(border());\n\t\tif (isMulti && tabBar) container.addChild(tabBar);\n\t\tcontainer.addChild(new Spacer(1));\n\n\t\tfor (const c of strategy.headingRows(state)) container.addChild(c);\n\t\tcontainer.addChild(strategy.bodyComponent(state));\n\t\tcontainer.addChild(new Spacer(1));\n\t\tfor (const c of strategy.midRows(state)) container.addChild(c);\n\n\t\tcontainer.addChild(border());\n\t\tfor (const c of strategy.footerRows(state)) container.addChild(c);\n\n\t\t// Residual spacer equalizes total height across strategies; rendered AFTER the bottom border.\n\t\tcontainer.addChild(\n\t\t\tnew BodyResidualSpacer(\n\t\t\t\t(w) => this.config.getBodyHeight(w) + this.maxFooterRowCount,\n\t\t\t\t(w) => strategy.bodyHeight(w, state) + strategy.footerRowCount,\n\t\t\t),\n\t\t);\n\t\treturn container;\n\t}\n}\n"]}
1
+ {"version":3,"file":"dialog-builder.d.ts","sourceRoot":"","sources":["../../../../../src/core/tools/ask-user-question/view/dialog-builder.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,8CAA8C,CAAC;AAC1E,OAAO,EAAE,KAAK,SAAS,EAAa,KAAK,KAAK,EAAU,MAAM,wBAAwB,CAAC;AACvF,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAC;AAC5D,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAErD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,+BAA+B,CAAC;AACjE,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,sCAAsC,CAAC;AAC7E,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,yBAAyB,CAAC;AACtD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AACvD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAGzD,eAAO,MAAM,eAAe,iBAAiB,CAAC;AAC9C,eAAO,MAAM,aAAa,2BAAiB,CAAC;AAC5C,eAAO,MAAM,gBAAgB,iBAAiB,CAAC;AAC/C,eAAO,MAAM,eAAe,gBAAgB,CAAC;AAC7C,eAAO,MAAM,aAAa,yBAAyB,CAAC;AACpD,eAAO,MAAM,gBAAgB,eAAe,CAAC;AAC7C,eAAO,MAAM,WAAW,QAAiE,CAAC;AAC1F,eAAO,MAAM,UAAU,QAAgF,CAAC;AACxG,eAAO,MAAM,uBAAuB,yBAA2B,CAAC;AAChE,eAAO,MAAM,iBAAiB,wBAA0B,CAAC;AACzD,eAAO,MAAM,cAAc,wBAAwB,CAAC;AACpD,eAAO,MAAM,YAAY,kCAAkC,CAAC;AAC5D,eAAO,MAAM,yBAAyB,yDAAoD,CAAC;AAE3F,MAAM,MAAM,WAAW,GAAG,kBAAkB,CAAC;AAE7C,+FAA+F;AAC/F,MAAM,WAAW,WAAW;IAC3B,KAAK,EAAE,WAAW,CAAC;IACnB,iBAAiB,EAAE,YAAY,CAAC,gBAAgB,CAAC,CAAC;CAClD;AAED,4EAA4E;AAC5E,MAAM,WAAW,YAAY;IAC5B,KAAK,EAAE,KAAK,CAAC;IACb,SAAS,EAAE,SAAS,YAAY,EAAE,CAAC;IACnC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC;IAC3B,UAAU,EAAE,KAAK,CAAC;IAClB,OAAO,EAAE,WAAW,CAAC;IACrB,OAAO,EAAE,OAAO,CAAC;IACjB,WAAW,EAAE,aAAa,CAAC,aAAa,CAAC,CAAC;IAC1C,sHAAsH;IACtH,YAAY,CAAC,EAAE,SAAS,CAAC;IACzB,sGAAsG;IACtG,aAAa,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,MAAM,CAAC;IACzC,yJAAyJ;IACzJ,oBAAoB,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,MAAM,CAAC;CAChD;AAED;;;;;;;GAOG;AACH,qBAAa,UAAW,YAAW,YAAY,CAAC,WAAW,CAAC;IAC3D,OAAO,CAAC,SAAS,CAAc;IAC/B,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAe;IACtC,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAAqB;IACtD,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAiC;IAChE,OAAO,CAAC,QAAQ,CAAC,iBAAiB,CAAS;IAE3C,YAAY,MAAM,EAAE,YAAY,EAAE,YAAY,EAAE,WAAW,EAqB1D;IAED,QAAQ,CAAC,KAAK,EAAE,WAAW,GAAG,IAAI,CAEjC;IAED,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAAG;IAKnC,UAAU,IAAI,IAAI,CAAG;IAErB,MAAM,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE,CAI9B;IAED,OAAO,CAAC,0BAA0B;CA2BlC"}
@@ -1 +1 @@
1
- {"version":3,"file":"props-adapter.d.ts","sourceRoot":"","sources":["../../../../../src/core/tools/ask-user-question/view/props-adapter.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,wBAAwB,CAAC;AAIpD,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAC;AAC5D,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AACrD,OAAO,KAAK,EAAE,kBAAkB,EAAE,kBAAkB,EAAE,MAAM,wBAAwB,CAAC;AACrF,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,iCAAiC,CAAC;AAC1E,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAEzD,kGAAkG;AAClG,UAAU,aAAa;IACtB,UAAU,IAAI,IAAI,CAAC;CACnB;AAED,MAAM,WAAW,+BAA+B;IAC/C,GAAG,EAAE;QAAE,aAAa,IAAI,IAAI,CAAA;KAAE,CAAC;IAC/B,SAAS,EAAE,SAAS,YAAY,EAAE,CAAC;IACnC,UAAU,EAAE,aAAa,CAAC,SAAS,kBAAkB,EAAE,CAAC,CAAC;IACzD,WAAW,EAAE,aAAa,CAAC,aAAa,CAAC,CAAC;IAC1C,WAAW,EAAE,KAAK,CAAC;IACnB,cAAc,EAAE,aAAa,CAAC,kBAAkB,CAAC,CAAC;IAClD,cAAc,EAAE,aAAa,CAAC,kBAAkB,CAAC,CAAC;IAClD;;;;OAIG;IACH,mBAAmB,CAAC,EAAE,aAAa,CAAC,aAAa,CAAC,CAAC;CACnD;AAED;;;;;;;;GAQG;AACH,qBAAa,yBAAyB;IACrC,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAyC;IAC7D,OAAO,CAAC,QAAQ,CAAC,SAAS,CAA0B;IACpD,OAAO,CAAC,QAAQ,CAAC,UAAU,CAA+C;IAC1E,OAAO,CAAC,QAAQ,CAAC,WAAW,CAA+B;IAC3D,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAQ;IACpC,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAoC;IACnE,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAoC;IACnE,OAAO,CAAC,QAAQ,CAAC,mBAAmB,CAA+B;IAEnE,YAAY,MAAM,EAAE,+BAA+B,EASlD;IAED,KAAK,CAAC,KAAK,EAAE,kBAAkB,GAAG,IAAI,CAwCrC;IAED;;;;;;OAMG;IACH,UAAU,IAAI,IAAI,CAQjB;CACD","sourcesContent":["import type { Input } from \"@earendil-works/pi-tui\";\nimport type { BindingContext, PerTabBindingContext } from \"../state/selectors/contract.ts\";\nimport { selectActivePreviewPaneIndex } from \"../state/selectors/derivations.ts\";\nimport { selectActiveView } from \"../state/selectors/focus.ts\";\nimport type { QuestionnaireState } from \"../state/state.ts\";\nimport type { QuestionData } from \"../tool/types.ts\";\nimport type { BoundGlobalBinding, BoundPerTabBinding } from \"./component-binding.ts\";\nimport type { WrappingSelectItem } from \"./components/wrapping-select.ts\";\nimport type { TabComponents } from \"./tab-components.ts\";\n\n/** Cache-invalidation contract used by the adapter. `pi-tui` `Component` already satisfies it. */\ninterface Invalidatable {\n\tinvalidate(): void;\n}\n\nexport interface QuestionnairePropsAdapterConfig {\n\ttui: { requestRender(): void };\n\tquestions: readonly QuestionData[];\n\titemsByTab: ReadonlyArray<readonly WrappingSelectItem[]>;\n\ttabsByIndex: ReadonlyArray<TabComponents>;\n\tinlineInput: Input;\n\tglobalBindings: ReadonlyArray<BoundGlobalBinding>;\n\tperTabBindings: ReadonlyArray<BoundPerTabBinding>;\n\t/**\n\t * Renderables not reached by the binding registries (e.g. the notes\n\t * `Input`, which is typed into directly and has no props). Walked by\n\t * `invalidate()` after the binding-driven components.\n\t */\n\textraInvalidatables?: ReadonlyArray<Invalidatable>;\n}\n\n/**\n * View fan-out: drives every component setter from the canonical state via\n * two binding registries. `globalBindings` covers the cross-tab components\n * (chatRow, dialog, submitPicker?, tabBar?); `perTabBindings` covers the\n * per-tab kinds (optionList, preview, multiSelect?). The hand-coded fan-out\n * collapses to one global loop + one nested per-tab loop. The shared inline\n * input is projected through owner-specific draft slots so the option list and\n * chat footer never display each other's in-flight text.\n */\nexport class QuestionnairePropsAdapter {\n\tprivate readonly tui: QuestionnairePropsAdapterConfig[\"tui\"];\n\tprivate readonly questions: readonly QuestionData[];\n\tprivate readonly itemsByTab: ReadonlyArray<readonly WrappingSelectItem[]>;\n\tprivate readonly tabsByIndex: ReadonlyArray<TabComponents>;\n\tprivate readonly inlineInput: Input;\n\tprivate readonly globalBindings: ReadonlyArray<BoundGlobalBinding>;\n\tprivate readonly perTabBindings: ReadonlyArray<BoundPerTabBinding>;\n\tprivate readonly extraInvalidatables: ReadonlyArray<Invalidatable>;\n\n\tconstructor(config: QuestionnairePropsAdapterConfig) {\n\t\tthis.tui = config.tui;\n\t\tthis.questions = config.questions;\n\t\tthis.itemsByTab = config.itemsByTab;\n\t\tthis.tabsByIndex = config.tabsByIndex;\n\t\tthis.inlineInput = config.inlineInput;\n\t\tthis.globalBindings = config.globalBindings;\n\t\tthis.perTabBindings = config.perTabBindings;\n\t\tthis.extraInvalidatables = config.extraInvalidatables ?? [];\n\t}\n\n\tapply(state: QuestionnaireState): void {\n\t\tconst totalQuestions = this.questions.length;\n\t\tconst activeView = selectActiveView(state, totalQuestions);\n\t\tconst paneIndex = selectActivePreviewPaneIndex(state.currentTab, totalQuestions);\n\t\tconst activePreviewPane = this.tabsByIndex[paneIndex]?.preview ?? this.tabsByIndex[0]!.preview;\n\n\t\tconst liveInlineValue = this.inlineInput.getValue();\n\t\tconst inputBuffer =\n\t\t\tstate.customDraftByTab.get(state.currentTab) ??\n\t\t\t(state.inlineInputOwner === \"other\" ? liveInlineValue : \"\");\n\t\tconst inputCaret = state.customCaretByTab.get(state.currentTab) ?? inputBuffer.length;\n\t\tconst chatInputBuffer =\n\t\t\tstate.chatDraftByTab.get(state.currentTab) ??\n\t\t\t(state.inlineInputOwner === \"chat\" ? liveInlineValue : \"\");\n\t\tconst chatInputCaret = state.chatCaretByTab.get(state.currentTab) ?? chatInputBuffer.length;\n\t\tconst ctx: BindingContext = {\n\t\t\tquestions: this.questions,\n\t\t\titemsByTab: this.itemsByTab,\n\t\t\ttotalQuestions,\n\t\t\tactiveView,\n\t\t\tinputBuffer,\n\t\t\tinputCaret,\n\t\t\tchatInputBuffer,\n\t\t\tchatInputCaret,\n\t\t\tactivePreviewPane,\n\t\t};\n\n\t\tfor (const binding of this.globalBindings) {\n\t\t\tbinding.apply(state, ctx);\n\t\t}\n\n\t\tfor (let i = 0; i < this.tabsByIndex.length; i++) {\n\t\t\tconst tab = this.tabsByIndex[i]!;\n\t\t\tconst tabCtx: PerTabBindingContext = { ...ctx, tab, i };\n\t\t\tfor (const binding of this.perTabBindings) {\n\t\t\t\tbinding.apply(state, tabCtx);\n\t\t\t}\n\t\t}\n\n\t\tthis.tui.requestRender();\n\t}\n\n\t/**\n\t * Invalidates every owned renderable. Called by the session in place of\n\t * the old `dialog.invalidate()` forwarding chain — DialogView no longer\n\t * reaches into siblings (chatRow, tabBar, notesInput, activePreviewPane).\n\t * Iterates the same registries used by `apply()` plus\n\t * `extraInvalidatables` for components outside the binding system.\n\t */\n\tinvalidate(): void {\n\t\tfor (const b of this.globalBindings) b.invalidate();\n\t\tfor (const tab of this.tabsByIndex) {\n\t\t\ttab.optionList.invalidate();\n\t\t\ttab.preview.invalidate();\n\t\t\ttab.multiSelect?.invalidate();\n\t\t}\n\t\tfor (const x of this.extraInvalidatables) x.invalidate();\n\t}\n}\n"]}
1
+ {"version":3,"file":"props-adapter.d.ts","sourceRoot":"","sources":["../../../../../src/core/tools/ask-user-question/view/props-adapter.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,wBAAwB,CAAC;AAIpD,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAC;AAC5D,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AACrD,OAAO,KAAK,EAAE,kBAAkB,EAAE,kBAAkB,EAAE,MAAM,wBAAwB,CAAC;AACrF,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,iCAAiC,CAAC;AAC1E,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAEzD,kGAAkG;AAClG,UAAU,aAAa;IACtB,UAAU,IAAI,IAAI,CAAC;CACnB;AAED,MAAM,WAAW,+BAA+B;IAC/C,GAAG,EAAE;QAAE,aAAa,IAAI,IAAI,CAAA;KAAE,CAAC;IAC/B,SAAS,EAAE,SAAS,YAAY,EAAE,CAAC;IACnC,UAAU,EAAE,aAAa,CAAC,SAAS,kBAAkB,EAAE,CAAC,CAAC;IACzD,WAAW,EAAE,aAAa,CAAC,aAAa,CAAC,CAAC;IAC1C,WAAW,EAAE,KAAK,CAAC;IACnB,cAAc,EAAE,aAAa,CAAC,kBAAkB,CAAC,CAAC;IAClD,cAAc,EAAE,aAAa,CAAC,kBAAkB,CAAC,CAAC;IAClD;;;;OAIG;IACH,mBAAmB,CAAC,EAAE,aAAa,CAAC,aAAa,CAAC,CAAC;CACnD;AAED;;;;;;;;GAQG;AACH,qBAAa,yBAAyB;IACrC,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAyC;IAC7D,OAAO,CAAC,QAAQ,CAAC,SAAS,CAA0B;IACpD,OAAO,CAAC,QAAQ,CAAC,UAAU,CAA+C;IAC1E,OAAO,CAAC,QAAQ,CAAC,WAAW,CAA+B;IAC3D,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAQ;IACpC,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAoC;IACnE,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAoC;IACnE,OAAO,CAAC,QAAQ,CAAC,mBAAmB,CAA+B;IAEnE,YAAY,MAAM,EAAE,+BAA+B,EASlD;IAED,KAAK,CAAC,KAAK,EAAE,kBAAkB,GAAG,IAAI,CAwCrC;IAED;;;;;;OAMG;IACH,UAAU,IAAI,IAAI,CAQjB;CACD"}
@@ -1 +1 @@
1
- {"version":3,"file":"stateful-view.d.ts","sourceRoot":"","sources":["../../../../../src/core/tools/ask-user-question/view/stateful-view.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,wBAAwB,CAAC;AAExD;;;;;GAKG;AACH,MAAM,WAAW,YAAY,CAAC,CAAC,CAAE,SAAQ,SAAS;IACjD,QAAQ,CAAC,KAAK,EAAE,CAAC,GAAG,IAAI,CAAC;CACzB;AAED;;;;;;;;;;;GAWG;AACH,MAAM,MAAM,UAAU,GAAG,OAAO,GAAG,MAAM,GAAG,SAAS,GAAG,QAAQ,CAAC","sourcesContent":["import type { Component } from \"@earendil-works/pi-tui\";\n\n/**\n * Generic prop-driven component contract. Every renderable owns its own `P` shape;\n * the adapter computes `P` from canonical state via per-component selectors and\n * pushes it via `setProps`. `focused: boolean` is a field on `P` only where the\n * component needs it.\n */\nexport interface StatefulView<P> extends Component {\n\tsetProps(props: P): void;\n}\n\n/**\n * Discriminated focus union — encodes the four-cell focus invariant\n * (`notesVisible`, submit-tab, `chatFocused`, options) that was previously\n * structural-only. Dispatcher cascade (`key-router.ts:151-178`) and reducer's\n * defensive clears (`state-reducer.ts:104-126`) enforce mutual exclusion;\n * this type makes it explicit so per-component `focused: boolean` flags\n * derive from one equality check against this discriminant rather than\n * four parallel boolean reads.\n *\n * Priority order: notes > submit > chat > options. Matches the dispatcher\n * cascade exactly so the union is observably equivalent to today's reads.\n */\nexport type ActiveView = \"notes\" | \"chat\" | \"options\" | \"submit\";\n"]}
1
+ {"version":3,"file":"stateful-view.d.ts","sourceRoot":"","sources":["../../../../../src/core/tools/ask-user-question/view/stateful-view.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,wBAAwB,CAAC;AAExD;;;;;GAKG;AACH,MAAM,WAAW,YAAY,CAAC,CAAC,CAAE,SAAQ,SAAS;IACjD,QAAQ,CAAC,KAAK,EAAE,CAAC,GAAG,IAAI,CAAC;CACzB;AAED;;;;;;;;;;;GAWG;AACH,MAAM,MAAM,UAAU,GAAG,OAAO,GAAG,MAAM,GAAG,SAAS,GAAG,QAAQ,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"tab-components.d.ts","sourceRoot":"","sources":["../../../../../src/core/tools/ask-user-question/view/tab-components.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,mCAAmC,CAAC;AAC9E,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,kCAAkC,CAAC;AAC5E,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,sCAAsC,CAAC;AACxE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAEvD,MAAM,WAAW,cAAc;IAC9B,OAAO,EAAE,MAAM,CAAC;IAChB,GAAG,EAAE,MAAM,CAAC;CACZ;AAED,MAAM,WAAW,aAAa;IAC7B,UAAU,EAAE,YAAY,CAAC,mBAAmB,CAAC,CAAC;IAC9C,OAAO,EAAE,WAAW,CAAC;IACrB,WAAW,CAAC,EAAE,YAAY,CAAC,oBAAoB,CAAC,CAAC;IACjD,WAAW,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,cAAc,CAAC;CAC/C","sourcesContent":["import type { MultiSelectViewProps } from \"./components/multi-select-view.ts\";\nimport type { OptionListViewProps } from \"./components/option-list-view.ts\";\nimport type { PreviewPane } from \"./components/preview/preview-pane.ts\";\nimport type { StatefulView } from \"./stateful-view.ts\";\n\nexport interface TabBodyHeights {\n\tcurrent: number;\n\tmax: number;\n}\n\nexport interface TabComponents {\n\toptionList: StatefulView<OptionListViewProps>;\n\tpreview: PreviewPane;\n\tmultiSelect?: StatefulView<MultiSelectViewProps>;\n\tbodyHeights: (width: number) => TabBodyHeights;\n}\n"]}
1
+ {"version":3,"file":"tab-components.d.ts","sourceRoot":"","sources":["../../../../../src/core/tools/ask-user-question/view/tab-components.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,mCAAmC,CAAC;AAC9E,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,kCAAkC,CAAC;AAC5E,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,sCAAsC,CAAC;AACxE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAEvD,MAAM,WAAW,cAAc;IAC9B,OAAO,EAAE,MAAM,CAAC;IAChB,GAAG,EAAE,MAAM,CAAC;CACZ;AAED,MAAM,WAAW,aAAa;IAC7B,UAAU,EAAE,YAAY,CAAC,mBAAmB,CAAC,CAAC;IAC9C,OAAO,EAAE,WAAW,CAAC;IACrB,WAAW,CAAC,EAAE,YAAY,CAAC,oBAAoB,CAAC,CAAC;IACjD,WAAW,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,cAAc,CAAC;CAC/C"}
@@ -1 +1 @@
1
- {"version":3,"file":"tab-content-strategy.d.ts","sourceRoot":"","sources":["../../../../../src/core/tools/ask-user-question/view/tab-content-strategy.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,8CAA8C,CAAC;AAC1E,OAAO,EAAE,KAAK,SAAS,EAAa,KAAK,KAAK,EAAgB,MAAM,wBAAwB,CAAC;AAE7F,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AACrD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,+BAA+B,CAAC;AACjE,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,sCAAsC,CAAC;AAC7E,OAAO,EACN,KAAK,WAAW,EAUhB,MAAM,qBAAqB,CAAC;AAC7B,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AACvD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAIzD;;;;GAIG;AACH,MAAM,WAAW,kBAAkB;IAClC,wGAAwG;IACxG,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAC;IAEhC,iFAAiF;IACjF,WAAW,CAAC,KAAK,EAAE,WAAW,GAAG,SAAS,EAAE,CAAC;IAE7C,8CAA8C;IAC9C,aAAa,CAAC,KAAK,EAAE,WAAW,GAAG,SAAS,CAAC;IAE7C,wEAAwE;IACxE,UAAU,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,WAAW,GAAG,MAAM,CAAC;IAEtD,0EAA0E;IAC1E,OAAO,CAAC,KAAK,EAAE,WAAW,GAAG,SAAS,EAAE,CAAC;IAEzC,2FAA2F;IAC3F,UAAU,CAAC,KAAK,EAAE,WAAW,GAAG,SAAS,EAAE,CAAC;CAC5C;AAED,MAAM,WAAW,yBAAyB;IACzC,KAAK,EAAE,KAAK,CAAC;IACb,SAAS,EAAE,SAAS,YAAY,EAAE,CAAC;IACnC,cAAc,EAAE,MAAM,YAAY,CAAC,gBAAgB,CAAC,CAAC;IACrD,WAAW,EAAE,aAAa,CAAC,aAAa,CAAC,CAAC;IAC1C,UAAU,EAAE,KAAK,CAAC;IAClB,OAAO,EAAE,WAAW,CAAC;IACrB,OAAO,EAAE,OAAO,CAAC;IACjB,oBAAoB,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,MAAM,CAAC;CAChD;AAED,qBAAa,mBAAoB,YAAW,kBAAkB;IAC7D,4EAA4E;IAC5E,QAAQ,CAAC,cAAc,KAAK;IAE5B,iBAAyB,MAAM,CAA4B;IAE3D,YAAY,MAAM,EAAE,yBAAyB,EAE5C;IAED,WAAW,CAAC,KAAK,EAAE,WAAW,GAAG,SAAS,EAAE,CAa3C;IAED,aAAa,CAAC,KAAK,EAAE,WAAW,GAAG,SAAS,CAK3C;IAED,UAAU,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,WAAW,GAAG,MAAM,CAErD;IAED,OAAO,CAAC,KAAK,EAAE,WAAW,GAAG,SAAS,EAAE,CAOvC;IAED,UAAU,CAAC,KAAK,EAAE,WAAW,GAAG,SAAS,EAAE,CAQ1C;CACD;AAED,MAAM,WAAW,uBAAuB;IACvC,KAAK,EAAE,KAAK,CAAC;IACb,SAAS,EAAE,SAAS,YAAY,EAAE,CAAC;IACnC,YAAY,EAAE,SAAS,GAAG,SAAS,CAAC;CACpC;AAED,qBAAa,iBAAkB,YAAW,kBAAkB;IAC3D,uIAAuI;IACvI,QAAQ,CAAC,cAAc,KAAK;IAE5B,iBAAyB,MAAM,CAA0B;IAEzD,YAAY,MAAM,EAAE,uBAAuB,EAE1C;IAED,WAAW,CAAC,MAAM,EAAE,WAAW,GAAG,SAAS,EAAE,CAK5C;IAED,aAAa,CAAC,KAAK,EAAE,WAAW,GAAG,SAAS,CAiB3C;IAED,UAAU,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,WAAW,GAAG,MAAM,CAEpD;IAED,OAAO,CAAC,MAAM,EAAE,WAAW,GAAG,SAAS,EAAE,CAExC;IAED,UAAU,CAAC,KAAK,EAAE,WAAW,GAAG,SAAS,EAAE,CAwB1C;CACD;AAED;;;;GAIG;AACH,wBAAgB,aAAa,CAAC,QAAQ,EAAE,YAAY,GAAG,SAAS,EAAE,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,WAAW,GAAG,MAAM,CAS9G","sourcesContent":["import type { Theme } from \"../../../../modes/interactive/theme/theme.ts\";\nimport { type Component, Container, type Input, Spacer, Text } from \"@earendil-works/pi-tui\";\nimport { formatAnswerScalar } from \"../tool/format-answer.ts\";\nimport type { QuestionData } from \"../tool/types.ts\";\nimport type { ChatRowView } from \"./components/chat-row-view.ts\";\nimport type { PreviewPaneProps } from \"./components/preview/preview-pane.ts\";\nimport {\n\ttype DialogState,\n\tHINT_PART_CANCEL,\n\tHINT_PART_ENTER,\n\tHINT_PART_NAV,\n\tHINT_PART_NOTES,\n\tHINT_PART_TAB,\n\tHINT_PART_TOGGLE,\n\tINCOMPLETE_WARNING_PREFIX,\n\tREADY_PROMPT,\n\tREVIEW_HEADING,\n} from \"./dialog-builder.ts\";\nimport type { StatefulView } from \"./stateful-view.ts\";\nimport type { TabComponents } from \"./tab-components.ts\";\n\nconst NOTES_HEADER = \"Notes:\";\n\n/**\n * Per-tab content provider. Pure functional — closes over construction-time\n * config; per-tick state threads through method args. The chrome wrapper\n * enforces height equality across tabs via `bodyHeight + footerRowCount`.\n */\nexport interface TabContentStrategy {\n\t/** Total RENDERED footer rows — MUST equal what `footerRows()` actually emits. Drives residual math. */\n\treadonly footerRowCount: number;\n\n\t/** Variable rows above the body, after top chrome (border + tabBar + Spacer). */\n\theadingRows(state: DialogState): Component[];\n\n\t/** Body Component placed at the body slot. */\n\tbodyComponent(state: DialogState): Component;\n\n\t/** Natural rendered height of `bodyComponent(state)` at given width. */\n\tbodyHeight(width: number, state: DialogState): number;\n\n\t/** Optional rows between body's trailing Spacer and the bottom border. */\n\tmidRows(state: DialogState): Component[];\n\n\t/** Footer rows below the bottom border. Rendered row count MUST equal `footerRowCount`. */\n\tfooterRows(state: DialogState): Component[];\n}\n\nexport interface QuestionTabStrategyConfig {\n\ttheme: Theme;\n\tquestions: readonly QuestionData[];\n\tgetPreviewPane: () => StatefulView<PreviewPaneProps>;\n\ttabsByIndex: ReadonlyArray<TabComponents>;\n\tnotesInput: Input;\n\tchatRow: ChatRowView;\n\tisMulti: boolean;\n\tgetCurrentBodyHeight: (width: number) => number;\n}\n\nexport class QuestionTabStrategy implements TabContentStrategy {\n\t/** Spacer(1) + chatRow(1) + Spacer(1) + Text(hint, 1) = 4 rendered rows. */\n\treadonly footerRowCount = 4;\n\n\tdeclare private readonly config: QuestionTabStrategyConfig;\n\n\tconstructor(config: QuestionTabStrategyConfig) {\n\t\tthis.config = config;\n\t}\n\n\theadingRows(state: DialogState): Component[] {\n\t\tconst out: Component[] = [];\n\t\tconst question = this.config.questions[state.currentTab];\n\t\t// In multi-question mode the tab bar already shows the header; suppress the inline badge.\n\t\tif (!this.config.isMulti && question?.header && question.header.length > 0) {\n\t\t\tout.push(new Text(this.config.theme.bg(\"selectedBg\", ` ${question.header} `), 1, 0));\n\t\t\tout.push(new Spacer(1));\n\t\t}\n\t\tif (question) {\n\t\t\tout.push(new Text(this.config.theme.bold(question.question), 1, 0));\n\t\t\tout.push(new Spacer(1));\n\t\t}\n\t\treturn out;\n\t}\n\n\tbodyComponent(state: DialogState): Component {\n\t\tconst question = this.config.questions[state.currentTab];\n\t\tconst mso = this.config.tabsByIndex[state.currentTab]?.multiSelect;\n\t\tif (question?.multiSelect === true && mso) return mso;\n\t\treturn this.config.getPreviewPane();\n\t}\n\n\tbodyHeight(width: number, _state: DialogState): number {\n\t\treturn this.config.getCurrentBodyHeight(width);\n\t}\n\n\tmidRows(state: DialogState): Component[] {\n\t\tif (!state.notesVisible) return [];\n\t\treturn [\n\t\t\tnew Text(this.config.theme.fg(\"muted\", NOTES_HEADER), 1, 0),\n\t\t\tthis.config.notesInput,\n\t\t\tnew Spacer(1),\n\t\t];\n\t}\n\n\tfooterRows(state: DialogState): Component[] {\n\t\tconst question = this.config.questions[state.currentTab];\n\t\treturn [\n\t\t\tnew Spacer(1),\n\t\t\tthis.config.chatRow,\n\t\t\tnew Spacer(1),\n\t\t\tnew Text(this.config.theme.fg(\"dim\", buildHintText(question, this.config.isMulti, state)), 1, 0),\n\t\t];\n\t}\n}\n\nexport interface SubmitTabStrategyConfig {\n\ttheme: Theme;\n\tquestions: readonly QuestionData[];\n\tsubmitPicker: Component | undefined;\n}\n\nexport class SubmitTabStrategy implements TabContentStrategy {\n\t/** Spacer(1) + Text(prompt, 1) + Spacer(1) + submitPicker(2) = 5 rendered rows. Fallback path lands at 5 via 2 trailing Spacer(1)s. */\n\treadonly footerRowCount = 5;\n\n\tdeclare private readonly config: SubmitTabStrategyConfig;\n\n\tconstructor(config: SubmitTabStrategyConfig) {\n\t\tthis.config = config;\n\t}\n\n\theadingRows(_state: DialogState): Component[] {\n\t\treturn [\n\t\t\tnew Text(this.config.theme.bold(this.config.theme.fg(\"accent\", REVIEW_HEADING)), 1, 0),\n\t\t\tnew Spacer(1),\n\t\t];\n\t}\n\n\tbodyComponent(state: DialogState): Component {\n\t\tconst c = new Container();\n\t\tfor (let i = 0; i < this.config.questions.length; i++) {\n\t\t\tconst q = this.config.questions[i];\n\t\t\tconst a = state.answers.get(i);\n\t\t\tif (!a) continue;\n\t\t\tconst label = q.header && q.header.length > 0 ? q.header : `Q${i + 1}`;\n\t\t\tconst answerText = formatAnswerScalar(a, \"summary\");\n\t\t\tc.addChild(new Text(this.config.theme.fg(\"muted\", ` ● ${label}`), 1, 0));\n\t\t\tc.addChild(\n\t\t\t\tnew Text(` ${this.config.theme.fg(\"muted\", \"→\")} ${this.config.theme.fg(\"text\", answerText)}`, 1, 0),\n\t\t\t);\n\t\t\tif (a.notes && a.notes.length > 0) {\n\t\t\t\tc.addChild(new Text(this.config.theme.fg(\"dim\", ` notes: ${a.notes}`), 1, 0));\n\t\t\t}\n\t\t}\n\t\treturn c;\n\t}\n\n\tbodyHeight(width: number, state: DialogState): number {\n\t\treturn this.bodyComponent(state).render(width).length;\n\t}\n\n\tmidRows(_state: DialogState): Component[] {\n\t\treturn [];\n\t}\n\n\tfooterRows(state: DialogState): Component[] {\n\t\tconst missing: string[] = [];\n\t\tfor (let i = 0; i < this.config.questions.length; i++) {\n\t\t\tconst q = this.config.questions[i];\n\t\t\tif (!state.answers.has(i)) {\n\t\t\t\tmissing.push(q.header && q.header.length > 0 ? q.header : `Q${i + 1}`);\n\t\t\t}\n\t\t}\n\t\tconst promptText =\n\t\t\tmissing.length === 0\n\t\t\t\t? this.config.theme.fg(\"muted\", READY_PROMPT)\n\t\t\t\t: this.config.theme.fg(\n\t\t\t\t\t\t\"warning\",\n\t\t\t\t\t\t`${INCOMPLETE_WARNING_PREFIX} ${missing.join(\", \")}`,\n\t\t\t\t\t);\n\t\tconst out: Component[] = [new Spacer(1), new Text(promptText, 1, 0), new Spacer(1)];\n\t\tif (this.config.submitPicker) {\n\t\t\tout.push(this.config.submitPicker);\n\t\t} else {\n\t\t\t// Padding when the picker isn't wired — keeps rendered row count at footerRowCount=5.\n\t\t\tout.push(new Spacer(1));\n\t\t\tout.push(new Spacer(1));\n\t\t}\n\t\treturn out;\n\t}\n}\n\n/**\n * Build the controls hint line. Order is fixed so `HINT_SINGLE` / `HINT_MULTI`\n * remain contiguous substrings of the result:\n * Enter · ↑/↓ [· Space toggle] [· n notes] [· Tab switch] · Esc\n */\nexport function buildHintText(question: QuestionData | undefined, isMulti: boolean, state: DialogState): string {\n\tconst parts: string[] = [HINT_PART_ENTER, HINT_PART_NAV];\n\tif (question?.multiSelect === true) parts.push(HINT_PART_TOGGLE);\n\tif (question && question.multiSelect !== true && state.focusedOptionHasPreview && !state.notesVisible) {\n\t\tparts.push(HINT_PART_NOTES);\n\t}\n\tif (isMulti) parts.push(HINT_PART_TAB);\n\tparts.push(HINT_PART_CANCEL);\n\treturn parts.join(\" · \");\n}\n"]}
1
+ {"version":3,"file":"tab-content-strategy.d.ts","sourceRoot":"","sources":["../../../../../src/core/tools/ask-user-question/view/tab-content-strategy.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,8CAA8C,CAAC;AAC1E,OAAO,EAAE,KAAK,SAAS,EAAa,KAAK,KAAK,EAAgB,MAAM,wBAAwB,CAAC;AAE7F,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AACrD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,+BAA+B,CAAC;AACjE,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,sCAAsC,CAAC;AAC7E,OAAO,EACN,KAAK,WAAW,EAUhB,MAAM,qBAAqB,CAAC;AAC7B,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AACvD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAIzD;;;;GAIG;AACH,MAAM,WAAW,kBAAkB;IAClC,wGAAwG;IACxG,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAC;IAEhC,iFAAiF;IACjF,WAAW,CAAC,KAAK,EAAE,WAAW,GAAG,SAAS,EAAE,CAAC;IAE7C,8CAA8C;IAC9C,aAAa,CAAC,KAAK,EAAE,WAAW,GAAG,SAAS,CAAC;IAE7C,wEAAwE;IACxE,UAAU,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,WAAW,GAAG,MAAM,CAAC;IAEtD,0EAA0E;IAC1E,OAAO,CAAC,KAAK,EAAE,WAAW,GAAG,SAAS,EAAE,CAAC;IAEzC,2FAA2F;IAC3F,UAAU,CAAC,KAAK,EAAE,WAAW,GAAG,SAAS,EAAE,CAAC;CAC5C;AAED,MAAM,WAAW,yBAAyB;IACzC,KAAK,EAAE,KAAK,CAAC;IACb,SAAS,EAAE,SAAS,YAAY,EAAE,CAAC;IACnC,cAAc,EAAE,MAAM,YAAY,CAAC,gBAAgB,CAAC,CAAC;IACrD,WAAW,EAAE,aAAa,CAAC,aAAa,CAAC,CAAC;IAC1C,UAAU,EAAE,KAAK,CAAC;IAClB,OAAO,EAAE,WAAW,CAAC;IACrB,OAAO,EAAE,OAAO,CAAC;IACjB,oBAAoB,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,MAAM,CAAC;CAChD;AAED,qBAAa,mBAAoB,YAAW,kBAAkB;IAC7D,4EAA4E;IAC5E,QAAQ,CAAC,cAAc,KAAK;IAE5B,iBAAyB,MAAM,CAA4B;IAE3D,YAAY,MAAM,EAAE,yBAAyB,EAE5C;IAED,WAAW,CAAC,KAAK,EAAE,WAAW,GAAG,SAAS,EAAE,CAa3C;IAED,aAAa,CAAC,KAAK,EAAE,WAAW,GAAG,SAAS,CAK3C;IAED,UAAU,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,WAAW,GAAG,MAAM,CAErD;IAED,OAAO,CAAC,KAAK,EAAE,WAAW,GAAG,SAAS,EAAE,CAOvC;IAED,UAAU,CAAC,KAAK,EAAE,WAAW,GAAG,SAAS,EAAE,CAQ1C;CACD;AAED,MAAM,WAAW,uBAAuB;IACvC,KAAK,EAAE,KAAK,CAAC;IACb,SAAS,EAAE,SAAS,YAAY,EAAE,CAAC;IACnC,YAAY,EAAE,SAAS,GAAG,SAAS,CAAC;CACpC;AAED,qBAAa,iBAAkB,YAAW,kBAAkB;IAC3D,uIAAuI;IACvI,QAAQ,CAAC,cAAc,KAAK;IAE5B,iBAAyB,MAAM,CAA0B;IAEzD,YAAY,MAAM,EAAE,uBAAuB,EAE1C;IAED,WAAW,CAAC,MAAM,EAAE,WAAW,GAAG,SAAS,EAAE,CAK5C;IAED,aAAa,CAAC,KAAK,EAAE,WAAW,GAAG,SAAS,CAiB3C;IAED,UAAU,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,WAAW,GAAG,MAAM,CAEpD;IAED,OAAO,CAAC,MAAM,EAAE,WAAW,GAAG,SAAS,EAAE,CAExC;IAED,UAAU,CAAC,KAAK,EAAE,WAAW,GAAG,SAAS,EAAE,CAwB1C;CACD;AAED;;;;GAIG;AACH,wBAAgB,aAAa,CAAC,QAAQ,EAAE,YAAY,GAAG,SAAS,EAAE,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,WAAW,GAAG,MAAM,CAS9G"}
@@ -1 +1 @@
1
- {"version":3,"file":"bash-async-execution.d.ts","sourceRoot":"","sources":["../../../src/core/tools/bash-async-execution.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,yBAAyB,CAAC;AAC/D,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,mBAAmB,CAAC;AAIjE,OAAO,KAAK,EAAE,cAAc,EAAE,eAAe,EAAE,MAAM,WAAW,CAAC;AAEjE,UAAU,4BAA4B;IACrC,OAAO,EAAE,MAAM,CAAC;IAChB,GAAG,EAAE,MAAM,CAAC;IACZ,GAAG,EAAE,MAAM,CAAC,UAAU,CAAC;IACvB,GAAG,CAAC,EAAE,OAAO,CAAC;IACd,cAAc,EAAE,MAAM,CAAC;IACvB,uBAAuB,CAAC,EAAE,MAAM,CAAC;IACjC,MAAM,CAAC,EAAE,WAAW,CAAC;IACrB,UAAU,EAAE,cAAc,CAAC;IAC3B,OAAO,CAAC,EAAE,eAAe,CAAC;IAC1B,eAAe,CAAC,EAAE,uBAAuB,CAAC;IAC1C,SAAS,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,wBAAsB,qBAAqB,CAAC,OAAO,EAAE,4BAA4B,GAAG,OAAO,CAAC;IAC3F,OAAO,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAC/C,OAAO,EAAE,eAAe,CAAC;CACzB,CAAC,CA2DD","sourcesContent":["import type { AsyncJobManager } from \"../async/job-manager.js\";\nimport type { AsyncJobDeliveryHandler } from \"../async/types.js\";\nimport { invalidateNativeSearchCache } from \"./search-native.js\";\nimport { createAsyncOutputAppender } from \"./bash-async-output.js\";\nimport { createManagedBashJob, discardManagedBashJob, formatAsyncJobError } from \"./bash-async-jobs.js\";\nimport type { BashOperations, BashToolDetails } from \"./bash.js\";\n\ninterface StartAsyncBashCommandOptions {\n\tcommand: string;\n\tcwd: string;\n\tenv: NodeJS.ProcessEnv;\n\tpty?: boolean;\n\ttimeoutSeconds: number;\n\trequestedTimeoutSeconds?: number;\n\tsignal?: AbortSignal;\n\toperations: BashOperations;\n\tmanager?: AsyncJobManager;\n\tdeliveryHandler?: AsyncJobDeliveryHandler;\n\tsessionId?: symbol;\n}\n\nexport async function startAsyncBashCommand(options: StartAsyncBashCommandOptions): Promise<{\n\tcontent: Array<{ type: \"text\"; text: string }>;\n\tdetails: BashToolDetails;\n}> {\n\tif (options.manager?.atCapacity) throw new Error(\"Background job limit reached. Wait for running jobs to finish or cancel one.\");\n\tconst job = createManagedBashJob(options.command, options.cwd, options.timeoutSeconds, options.requestedTimeoutSeconds);\n\ttry {\n\t\toptions.manager?.registerBashJob(job, options.deliveryHandler, options.sessionId);\n\t} catch (registerError) {\n\t\t// The job was inserted into the managed map but never started; drop it so\n\t\t// it cannot linger as a permanently-\"running\" zombie entry (see\n\t\t// discardManagedBashJob). Registration failures (disposed manager/session,\n\t\t// capacity race) still surface to the caller as tool errors.\n\t\tdiscardManagedBashJob(job.jobId);\n\t\tthrow registerError;\n\t}\n\tconst appendAsyncOutput = createAsyncOutputAppender(job, { persistAfterBytes: 12_000 });\n\tconst onParentAbort = () => {\n\t\toptions.manager?.acknowledgeDeliveries([job.jobId]);\n\t\tjob.abortController?.abort();\n\t};\n\tif (options.signal?.aborted) onParentAbort();\n\telse options.signal?.addEventListener(\"abort\", onParentAbort, { once: true });\n\tvoid (async () => {\n\t\tlet error: Error | string | undefined;\n\t\tlet exitCode: number | null | undefined;\n\t\ttry {\n\t\t\texitCode = (await options.operations.exec(options.command, options.cwd, {\n\t\t\t\tonData: appendAsyncOutput.append,\n\t\t\t\ttimeout: options.timeoutSeconds,\n\t\t\t\tenv: options.env,\n\t\t\t\tpty: options.pty,\n\t\t\t\tsignal: job.abortController?.signal,\n\t\t\t})).exitCode;\n\t\t} catch (execError) {\n\t\t\terror = execError instanceof Error ? execError : String(execError);\n\t\t}\n\t\ttry {\n\t\t\tawait appendAsyncOutput.close();\n\t\t} catch (closeError) {\n\t\t\terror ??= closeError instanceof Error ? closeError : String(closeError);\n\t\t}\n\t\tif (error !== undefined) {\n\t\t\tjob.status = \"failed\";\n\t\t\tjob.error = job.abortController?.signal.aborted ? \"aborted\" : formatAsyncJobError(error);\n\t\t} else {\n\t\t\tjob.exitCode = exitCode;\n\t\t\tjob.status = exitCode && exitCode !== 0 ? \"failed\" : \"completed\";\n\t\t}\n\t\tjob.endedAt = Date.now();\n\t\tinvalidateNativeSearchCache();\n\t\toptions.manager?.completeBashJob(job);\n\t\toptions.signal?.removeEventListener(\"abort\", onParentAbort);\n\t})();\n\treturn {\n\t\tcontent: [{ type: \"text\", text: `Started async bash command ${job.jobId}: ${options.command}\\nPoll with bash({ command: \"__atomic_bash_job ${job.jobId}\" }); cancel with bash({ command: \"__atomic_bash_job_cancel ${job.jobId}\" })` }],\n\t\tdetails: {\n\t\t\tasync: { jobId: job.jobId, type: \"bash\", state: \"running\", command: options.command, status: \"running\" },\n\t\t\ttimeoutSeconds: options.timeoutSeconds,\n\t\t\t...(options.requestedTimeoutSeconds !== undefined ? { requestedTimeoutSeconds: options.requestedTimeoutSeconds } : {}),\n\t\t},\n\t};\n}\n"]}
1
+ {"version":3,"file":"bash-async-execution.d.ts","sourceRoot":"","sources":["../../../src/core/tools/bash-async-execution.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,yBAAyB,CAAC;AAC/D,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,mBAAmB,CAAC;AAIjE,OAAO,KAAK,EAAE,cAAc,EAAE,eAAe,EAAE,MAAM,WAAW,CAAC;AAEjE,UAAU,4BAA4B;IACrC,OAAO,EAAE,MAAM,CAAC;IAChB,GAAG,EAAE,MAAM,CAAC;IACZ,GAAG,EAAE,MAAM,CAAC,UAAU,CAAC;IACvB,GAAG,CAAC,EAAE,OAAO,CAAC;IACd,cAAc,EAAE,MAAM,CAAC;IACvB,uBAAuB,CAAC,EAAE,MAAM,CAAC;IACjC,MAAM,CAAC,EAAE,WAAW,CAAC;IACrB,UAAU,EAAE,cAAc,CAAC;IAC3B,OAAO,CAAC,EAAE,eAAe,CAAC;IAC1B,eAAe,CAAC,EAAE,uBAAuB,CAAC;IAC1C,SAAS,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,wBAAsB,qBAAqB,CAAC,OAAO,EAAE,4BAA4B,GAAG,OAAO,CAAC;IAC3F,OAAO,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAC/C,OAAO,EAAE,eAAe,CAAC;CACzB,CAAC,CA2DD"}
@@ -1 +1 @@
1
- {"version":3,"file":"bash-async-jobs.d.ts","sourceRoot":"","sources":["../../../src/core/tools/bash-async-jobs.ts"],"names":[],"mappings":"AAEA,MAAM,WAAW,cAAc;IAC9B,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,GAAG,EAAE,MAAM,CAAC;IACZ,MAAM,EAAE,SAAS,GAAG,WAAW,GAAG,QAAQ,CAAC;IAC3C,MAAM,EAAE,MAAM,CAAC;IACf,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,uBAAuB,CAAC,EAAE,MAAM,CAAC;IACjC,eAAe,CAAC,EAAE,eAAe,CAAC;CAClC;AAED,eAAO,MAAM,qBAAqB,MAAM,CAAC;AACzC,eAAO,MAAM,oBAAoB,QAAiB,CAAC;AAEnD,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,CAI1D;AAgBD,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,cAAc,CAAC,EAAE,MAAM,EAAE,uBAAuB,CAAC,EAAE,MAAM,GAAG,cAAc,CAO5I;AAED;;;;;GAKG;AACH,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAKzD;AAED,wBAAgB,qBAAqB,IAAI,MAAM,EAAE,CAEhD;AAED,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,MAAM,GAAG,cAAc,GAAG,SAAS,CAM3E;AAED,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,MAAM,GAAG,cAAc,GAAG,SAAS,CAK7E","sourcesContent":["import { rmSync } from \"node:fs\";\n\nexport interface ManagedBashJob {\n\tjobId: string;\n\tcommand: string;\n\tcwd: string;\n\tstatus: \"running\" | \"completed\" | \"failed\";\n\toutput: string;\n\tfullOutputPath?: string;\n\texitCode?: number | null;\n\terror?: string;\n\tstartedAt: number;\n\tendedAt?: number;\n\ttimeoutSeconds?: number;\n\trequestedTimeoutSeconds?: number;\n\tabortController?: AbortController;\n}\n\nexport const MAX_MANAGED_BASH_JOBS = 100;\nexport const COMPLETED_JOB_TTL_MS = 30 * 60 * 1000;\nconst managedBashJobs = new Map<string, ManagedBashJob>();\nexport function formatAsyncJobError(error: unknown): string {\n\tconst message = error instanceof Error ? error.message : String(error);\n\tif (message.startsWith(\"timeout:\")) return `Command timed out after ${message.slice(\"timeout:\".length)} seconds`;\n\treturn message;\n}\nfunction deleteJobOutput(job: ManagedBashJob): void {\n\tif (!job.fullOutputPath) return;\n\ttry { rmSync(job.fullOutputPath, { force: true }); } catch { /* best-effort temp cleanup */ }\n}\n\nfunction cleanupManagedBashJobs(now = Date.now()): void {\n\tfor (const [jobId, job] of managedBashJobs) if (job.status !== \"running\" && job.endedAt !== undefined && now - job.endedAt > COMPLETED_JOB_TTL_MS) { deleteJobOutput(job); managedBashJobs.delete(jobId); }\n\twhile (managedBashJobs.size > MAX_MANAGED_BASH_JOBS) {\n\t\tconst oldest = [...managedBashJobs.values()].sort((a, b) => (a.endedAt ?? a.startedAt) - (b.endedAt ?? b.startedAt))[0];\n\t\tif (!oldest) break;\n\t\tif (oldest.status === \"running\") oldest.abortController?.abort(); else deleteJobOutput(oldest);\n\t\tmanagedBashJobs.delete(oldest.jobId);\n\t}\n}\n\nexport function createManagedBashJob(command: string, cwd: string, timeoutSeconds?: number, requestedTimeoutSeconds?: number): ManagedBashJob {\n\tcleanupManagedBashJobs();\n\tconst jobId = `bash-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 8)}`;\n\tconst job: ManagedBashJob = { jobId, command, cwd, status: \"running\", output: \"\", startedAt: Date.now(), timeoutSeconds, requestedTimeoutSeconds, abortController: new AbortController() };\n\tmanagedBashJobs.set(jobId, job);\n\tcleanupManagedBashJobs();\n\treturn job;\n}\n\n/**\n * Remove a managed job that never actually started executing (for example when\n * async-manager registration fails after the map insert). Without this, the\n * entry would stay \"running\" forever: TTL cleanup only evicts settled jobs, so\n * the zombie would linger until the max-jobs overflow forcibly removed it.\n */\nexport function discardManagedBashJob(jobId: string): void {\n\tconst job = managedBashJobs.get(jobId);\n\tif (!job) return;\n\tdeleteJobOutput(job);\n\tmanagedBashJobs.delete(jobId);\n}\n\nexport function listManagedBashJobIds(): string[] {\n\treturn [...managedBashJobs.keys()];\n}\n\nexport function getManagedBashJob(jobId: string): ManagedBashJob | undefined {\n\tcleanupManagedBashJobs();\n\tconst job = managedBashJobs.get(jobId);\n\tif (!job) return undefined;\n\tconst { abortController: _abortController, ...snapshot } = job;\n\treturn { ...snapshot };\n}\n\nexport function abortManagedBashJob(jobId: string): ManagedBashJob | undefined {\n\tconst job = managedBashJobs.get(jobId);\n\tif (!job) return undefined;\n\tjob.abortController?.abort();\n\treturn job;\n}\n"]}
1
+ {"version":3,"file":"bash-async-jobs.d.ts","sourceRoot":"","sources":["../../../src/core/tools/bash-async-jobs.ts"],"names":[],"mappings":"AAEA,MAAM,WAAW,cAAc;IAC9B,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,GAAG,EAAE,MAAM,CAAC;IACZ,MAAM,EAAE,SAAS,GAAG,WAAW,GAAG,QAAQ,CAAC;IAC3C,MAAM,EAAE,MAAM,CAAC;IACf,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,uBAAuB,CAAC,EAAE,MAAM,CAAC;IACjC,eAAe,CAAC,EAAE,eAAe,CAAC;CAClC;AAED,eAAO,MAAM,qBAAqB,MAAM,CAAC;AACzC,eAAO,MAAM,oBAAoB,QAAiB,CAAC;AAEnD,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,CAI1D;AAgBD,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,cAAc,CAAC,EAAE,MAAM,EAAE,uBAAuB,CAAC,EAAE,MAAM,GAAG,cAAc,CAO5I;AAED;;;;;GAKG;AACH,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAKzD;AAED,wBAAgB,qBAAqB,IAAI,MAAM,EAAE,CAEhD;AAED,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,MAAM,GAAG,cAAc,GAAG,SAAS,CAM3E;AAED,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,MAAM,GAAG,cAAc,GAAG,SAAS,CAK7E"}
@@ -1 +1 @@
1
- {"version":3,"file":"bash-async-output.d.ts","sourceRoot":"","sources":["../../../src/core/tools/bash-async-output.ts"],"names":[],"mappings":"AASA,MAAM,WAAW,qBAAqB;IACrC,MAAM,EAAE,MAAM,CAAC;IACf,cAAc,CAAC,EAAE,MAAM,CAAC;CACxB;AAED,MAAM,WAAW,uBAAuB;IACvC,MAAM,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CACvB;AAeD,wBAAgB,yBAAyB,CAAC,GAAG,EAAE,qBAAqB,EAAE,OAAO,CAAC,EAAE;IAAE,iBAAiB,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,uBAAuB,CAqDvI","sourcesContent":["import { randomBytes } from \"node:crypto\";\nimport { createWriteStream, type WriteStream } from \"node:fs\";\nimport { tmpdir } from \"node:os\";\nimport { join } from \"node:path\";\nimport { APP_NAME } from \"../../config.ts\";\nimport { stripAnsi } from \"../../utils/ansi.ts\";\nimport { sanitizeBinaryOutput } from \"../../utils/shell.ts\";\nimport { DEFAULT_MAX_BYTES, formatSize } from \"./truncate.ts\";\n\nexport interface BashAsyncOutputTarget {\n\toutput: string;\n\tfullOutputPath?: string;\n}\n\nexport interface BashAsyncOutputAppender {\n\tappend(chunk: Buffer): void;\n\tclose(): Promise<void>;\n}\n\nfunction outputPath(): string {\n\treturn join(tmpdir(), `${APP_NAME}-bash-async-${randomBytes(8).toString(\"hex\")}.log`);\n}\n\nfunction byteLength(text: string): number { return Buffer.byteLength(text, \"utf8\"); }\nfunction sanitizeDecodedOutput(text: string): string { return sanitizeBinaryOutput(stripAnsi(text)).replace(/\\r/g, \"\"); }\nfunction utf8Prefix(text: string, maxBytes: number): string {\n\tif (byteLength(text) <= maxBytes) return text;\n\tlet end = text.length;\n\twhile (end > 0 && byteLength(text.slice(0, end)) > maxBytes) end--;\n\treturn text.slice(0, end);\n}\n\nexport function createAsyncOutputAppender(job: BashAsyncOutputTarget, options?: { persistAfterBytes?: number }): BashAsyncOutputAppender {\n\tconst persistAfterBytes = options?.persistAfterBytes ?? DEFAULT_MAX_BYTES;\n\tlet outputBytes = 0;\n\tlet truncated = false;\n\tlet fullOutputStream: WriteStream | undefined;\n\tlet bufferedChunks: Buffer[] = [];\n\tconst decoder = new TextDecoder();\n\n\tconst ensureFullOutputStream = (): WriteStream => {\n\t\tif (fullOutputStream) return fullOutputStream;\n\t\tjob.fullOutputPath = outputPath();\n\t\tfullOutputStream = createWriteStream(job.fullOutputPath);\n\t\tfor (const chunk of bufferedChunks) fullOutputStream.write(chunk);\n\t\tbufferedChunks = [];\n\t\treturn fullOutputStream;\n\t};\n\tconst appendDecodedText = (decoded: string): void => {\n\t\tif (truncated || decoded.length === 0) return;\n\t\tconst text = sanitizeDecodedOutput(decoded);\n\t\tif (text.length === 0) return;\n\t\tconst bytes = byteLength(text);\n\t\tif (outputBytes + bytes > persistAfterBytes) ensureFullOutputStream();\n\t\tif (outputBytes + bytes > DEFAULT_MAX_BYTES) {\n\t\t\tensureFullOutputStream();\n\t\t\tconst remaining = Math.max(0, DEFAULT_MAX_BYTES - outputBytes);\n\t\t\tif (remaining > 0) job.output += utf8Prefix(text, remaining);\n\t\t\tjob.output += `\\n[Output truncated at ${formatSize(DEFAULT_MAX_BYTES)} for async job polling. Full output: ${job.fullOutputPath}]`;\n\t\t\toutputBytes += bytes;\n\t\t\ttruncated = true;\n\t\t\treturn;\n\t\t}\n\t\toutputBytes += bytes;\n\t\tjob.output += text;\n\t};\n\n\treturn {\n\t\tappend(chunk) {\n\t\t\tif (fullOutputStream) fullOutputStream.write(chunk);\n\t\t\telse bufferedChunks.push(chunk);\n\t\t\tappendDecodedText(decoder.decode(chunk, { stream: true }));\n\t\t},\n\t\tasync close() {\n\t\t\tappendDecodedText(decoder.decode());\n\t\t\tif (!fullOutputStream) return;\n\t\t\tconst stream = fullOutputStream;\n\t\t\tfullOutputStream = undefined;\n\t\t\tawait new Promise<void>((resolve, reject) => {\n\t\t\t\tstream.once(\"error\", reject);\n\t\t\t\tstream.once(\"finish\", resolve);\n\t\t\t\tstream.end();\n\t\t\t});\n\t\t},\n\t};\n}\n"]}
1
+ {"version":3,"file":"bash-async-output.d.ts","sourceRoot":"","sources":["../../../src/core/tools/bash-async-output.ts"],"names":[],"mappings":"AASA,MAAM,WAAW,qBAAqB;IACrC,MAAM,EAAE,MAAM,CAAC;IACf,cAAc,CAAC,EAAE,MAAM,CAAC;CACxB;AAED,MAAM,WAAW,uBAAuB;IACvC,MAAM,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CACvB;AAeD,wBAAgB,yBAAyB,CAAC,GAAG,EAAE,qBAAqB,EAAE,OAAO,CAAC,EAAE;IAAE,iBAAiB,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,uBAAuB,CAqDvI"}