@n-dx/web 0.1.0

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 (671) hide show
  1. package/LICENSE +96 -0
  2. package/README.md +5 -0
  3. package/build.js +243 -0
  4. package/dist/cli/index.d.ts +8 -0
  5. package/dist/cli/index.js +50 -0
  6. package/dist/cli/index.js.map +1 -0
  7. package/dist/landing/index.html +325 -0
  8. package/dist/landing/landing.d.ts +38 -0
  9. package/dist/landing/landing.js +299 -0
  10. package/dist/landing/landing.js.map +1 -0
  11. package/dist/public.d.ts +55 -0
  12. package/dist/public.js +51 -0
  13. package/dist/public.js.map +1 -0
  14. package/dist/schema/features.d.ts +28 -0
  15. package/dist/schema/features.js +9 -0
  16. package/dist/schema/features.js.map +1 -0
  17. package/dist/schema/v1.d.ts +317 -0
  18. package/dist/schema/v1.js +3 -0
  19. package/dist/schema/v1.js.map +1 -0
  20. package/dist/server/aggregation-cache.d.ts +105 -0
  21. package/dist/server/aggregation-cache.js +163 -0
  22. package/dist/server/aggregation-cache.js.map +1 -0
  23. package/dist/server/concurrent-execution-metrics.d.ts +103 -0
  24. package/dist/server/concurrent-execution-metrics.js +253 -0
  25. package/dist/server/concurrent-execution-metrics.js.map +1 -0
  26. package/dist/server/domain-gateway.d.ts +18 -0
  27. package/dist/server/domain-gateway.js +19 -0
  28. package/dist/server/domain-gateway.js.map +1 -0
  29. package/dist/server/incremental-task-usage.d.ts +92 -0
  30. package/dist/server/incremental-task-usage.js +251 -0
  31. package/dist/server/incremental-task-usage.js.map +1 -0
  32. package/dist/server/index.d.ts +39 -0
  33. package/dist/server/index.js +36 -0
  34. package/dist/server/index.js.map +1 -0
  35. package/dist/server/port.d.ts +96 -0
  36. package/dist/server/port.js +134 -0
  37. package/dist/server/port.js.map +1 -0
  38. package/dist/server/pr-markdown-refresh-diagnostics.d.ts +59 -0
  39. package/dist/server/pr-markdown-refresh-diagnostics.js +429 -0
  40. package/dist/server/pr-markdown-refresh-diagnostics.js.map +1 -0
  41. package/dist/server/prd-io.d.ts +40 -0
  42. package/dist/server/prd-io.js +66 -0
  43. package/dist/server/prd-io.js.map +1 -0
  44. package/dist/server/process-memory-tracker.d.ts +79 -0
  45. package/dist/server/process-memory-tracker.js +194 -0
  46. package/dist/server/process-memory-tracker.js.map +1 -0
  47. package/dist/server/register-scheduler.d.ts +53 -0
  48. package/dist/server/register-scheduler.js +36 -0
  49. package/dist/server/register-scheduler.js.map +1 -0
  50. package/dist/server/rex-gateway.d.ts +39 -0
  51. package/dist/server/rex-gateway.js +47 -0
  52. package/dist/server/rex-gateway.js.map +1 -0
  53. package/dist/server/routes-adaptive.d.ts +21 -0
  54. package/dist/server/routes-adaptive.js +659 -0
  55. package/dist/server/routes-adaptive.js.map +1 -0
  56. package/dist/server/routes-config.d.ts +47 -0
  57. package/dist/server/routes-config.js +222 -0
  58. package/dist/server/routes-config.js.map +1 -0
  59. package/dist/server/routes-data.d.ts +14 -0
  60. package/dist/server/routes-data.js +129 -0
  61. package/dist/server/routes-data.js.map +1 -0
  62. package/dist/server/routes-features.d.ts +14 -0
  63. package/dist/server/routes-features.js +245 -0
  64. package/dist/server/routes-features.js.map +1 -0
  65. package/dist/server/routes-hench.d.ts +116 -0
  66. package/dist/server/routes-hench.js +2016 -0
  67. package/dist/server/routes-hench.js.map +1 -0
  68. package/dist/server/routes-integrations.d.ts +23 -0
  69. package/dist/server/routes-integrations.js +277 -0
  70. package/dist/server/routes-integrations.js.map +1 -0
  71. package/dist/server/routes-mcp.d.ts +31 -0
  72. package/dist/server/routes-mcp.js +175 -0
  73. package/dist/server/routes-mcp.js.map +1 -0
  74. package/dist/server/routes-notion.d.ts +23 -0
  75. package/dist/server/routes-notion.js +723 -0
  76. package/dist/server/routes-notion.js.map +1 -0
  77. package/dist/server/routes-project.d.ts +47 -0
  78. package/dist/server/routes-project.js +128 -0
  79. package/dist/server/routes-project.js.map +1 -0
  80. package/dist/server/routes-rex/analysis.d.ts +11 -0
  81. package/dist/server/routes-rex/analysis.js +583 -0
  82. package/dist/server/routes-rex/analysis.js.map +1 -0
  83. package/dist/server/routes-rex/execution.d.ts +37 -0
  84. package/dist/server/routes-rex/execution.js +355 -0
  85. package/dist/server/routes-rex/execution.js.map +1 -0
  86. package/dist/server/routes-rex/health.d.ts +8 -0
  87. package/dist/server/routes-rex/health.js +136 -0
  88. package/dist/server/routes-rex/health.js.map +1 -0
  89. package/dist/server/routes-rex/index.d.ts +35 -0
  90. package/dist/server/routes-rex/index.js +72 -0
  91. package/dist/server/routes-rex/index.js.map +1 -0
  92. package/dist/server/routes-rex/items.d.ts +8 -0
  93. package/dist/server/routes-rex/items.js +359 -0
  94. package/dist/server/routes-rex/items.js.map +1 -0
  95. package/dist/server/routes-rex/prune.d.ts +11 -0
  96. package/dist/server/routes-rex/prune.js +379 -0
  97. package/dist/server/routes-rex/prune.js.map +1 -0
  98. package/dist/server/routes-rex/reads.d.ts +9 -0
  99. package/dist/server/routes-rex/reads.js +119 -0
  100. package/dist/server/routes-rex/reads.js.map +1 -0
  101. package/dist/server/routes-rex/requirements.d.ts +10 -0
  102. package/dist/server/routes-rex/requirements.js +408 -0
  103. package/dist/server/routes-rex/requirements.js.map +1 -0
  104. package/dist/server/routes-rex/shared.d.ts +37 -0
  105. package/dist/server/routes-rex/shared.js +73 -0
  106. package/dist/server/routes-rex/shared.js.map +1 -0
  107. package/dist/server/routes-search.d.ts +26 -0
  108. package/dist/server/routes-search.js +82 -0
  109. package/dist/server/routes-search.js.map +1 -0
  110. package/dist/server/routes-sourcevision.d.ts +18 -0
  111. package/dist/server/routes-sourcevision.js +444 -0
  112. package/dist/server/routes-sourcevision.js.map +1 -0
  113. package/dist/server/routes-static.d.ts +20 -0
  114. package/dist/server/routes-static.js +168 -0
  115. package/dist/server/routes-static.js.map +1 -0
  116. package/dist/server/routes-status.d.ts +58 -0
  117. package/dist/server/routes-status.js +191 -0
  118. package/dist/server/routes-status.js.map +1 -0
  119. package/dist/server/routes-token-usage.d.ts +63 -0
  120. package/dist/server/routes-token-usage.js +720 -0
  121. package/dist/server/routes-token-usage.js.map +1 -0
  122. package/dist/server/routes-validation.d.ts +10 -0
  123. package/dist/server/routes-validation.js +365 -0
  124. package/dist/server/routes-validation.js.map +1 -0
  125. package/dist/server/routes-workflow.d.ts +15 -0
  126. package/dist/server/routes-workflow.js +498 -0
  127. package/dist/server/routes-workflow.js.map +1 -0
  128. package/dist/server/search-index.d.ts +111 -0
  129. package/dist/server/search-index.js +348 -0
  130. package/dist/server/search-index.js.map +1 -0
  131. package/dist/server/shared-types.d.ts +65 -0
  132. package/dist/server/shared-types.js +31 -0
  133. package/dist/server/shared-types.js.map +1 -0
  134. package/dist/server/start.d.ts +68 -0
  135. package/dist/server/start.js +568 -0
  136. package/dist/server/start.js.map +1 -0
  137. package/dist/server/task-usage.d.ts +17 -0
  138. package/dist/server/task-usage.js +20 -0
  139. package/dist/server/task-usage.js.map +1 -0
  140. package/dist/server/types.d.ts +27 -0
  141. package/dist/server/types.js +26 -0
  142. package/dist/server/types.js.map +1 -0
  143. package/dist/server/usage-cleanup-scheduler.d.ts +107 -0
  144. package/dist/server/usage-cleanup-scheduler.js +232 -0
  145. package/dist/server/usage-cleanup-scheduler.js.map +1 -0
  146. package/dist/server/websocket.d.ts +131 -0
  147. package/dist/server/websocket.js +512 -0
  148. package/dist/server/websocket.js.map +1 -0
  149. package/dist/shared/data-files.d.ts +17 -0
  150. package/dist/shared/data-files.js +18 -0
  151. package/dist/shared/data-files.js.map +1 -0
  152. package/dist/shared/index.d.ts +9 -0
  153. package/dist/shared/index.js +9 -0
  154. package/dist/shared/index.js.map +1 -0
  155. package/dist/shared/view-id.d.ts +8 -0
  156. package/dist/shared/view-id.js +9 -0
  157. package/dist/shared/view-id.js.map +1 -0
  158. package/dist/viewer/Hench-F.png +0 -0
  159. package/dist/viewer/Rex-F.png +0 -0
  160. package/dist/viewer/SourceVision-F.png +0 -0
  161. package/dist/viewer/SourceVision.png +0 -0
  162. package/dist/viewer/api.d.ts +19 -0
  163. package/dist/viewer/api.js +17 -0
  164. package/dist/viewer/api.js.map +1 -0
  165. package/dist/viewer/bootstrap.d.ts +13 -0
  166. package/dist/viewer/bootstrap.js +40 -0
  167. package/dist/viewer/bootstrap.js.map +1 -0
  168. package/dist/viewer/components/active-tasks-panel.d.ts +36 -0
  169. package/dist/viewer/components/active-tasks-panel.js +183 -0
  170. package/dist/viewer/components/active-tasks-panel.js.map +1 -0
  171. package/dist/viewer/components/breadcrumb.d.ts +21 -0
  172. package/dist/viewer/components/breadcrumb.js +117 -0
  173. package/dist/viewer/components/breadcrumb.js.map +1 -0
  174. package/dist/viewer/components/concurrency-panel.d.ts +18 -0
  175. package/dist/viewer/components/concurrency-panel.js +175 -0
  176. package/dist/viewer/components/concurrency-panel.js.map +1 -0
  177. package/dist/viewer/components/config-footer.d.ts +11 -0
  178. package/dist/viewer/components/config-footer.js +132 -0
  179. package/dist/viewer/components/config-footer.js.map +1 -0
  180. package/dist/viewer/components/constants.d.ts +10 -0
  181. package/dist/viewer/components/constants.js +11 -0
  182. package/dist/viewer/components/constants.js.map +1 -0
  183. package/dist/viewer/components/copy-link-button.d.ts +30 -0
  184. package/dist/viewer/components/copy-link-button.js +74 -0
  185. package/dist/viewer/components/copy-link-button.js.map +1 -0
  186. package/dist/viewer/components/crash-recovery-banner.d.ts +28 -0
  187. package/dist/viewer/components/crash-recovery-banner.js +47 -0
  188. package/dist/viewer/components/crash-recovery-banner.js.map +1 -0
  189. package/dist/viewer/components/data-display/collapsible-section.d.ts +14 -0
  190. package/dist/viewer/components/data-display/collapsible-section.js +66 -0
  191. package/dist/viewer/components/data-display/collapsible-section.js.map +1 -0
  192. package/dist/viewer/components/data-display/findings-list.d.ts +13 -0
  193. package/dist/viewer/components/data-display/findings-list.js +136 -0
  194. package/dist/viewer/components/data-display/findings-list.js.map +1 -0
  195. package/dist/viewer/components/data-display/health-gauge.d.ts +37 -0
  196. package/dist/viewer/components/data-display/health-gauge.js +62 -0
  197. package/dist/viewer/components/data-display/health-gauge.js.map +1 -0
  198. package/dist/viewer/components/data-display/mini-charts.d.ts +36 -0
  199. package/dist/viewer/components/data-display/mini-charts.js +124 -0
  200. package/dist/viewer/components/data-display/mini-charts.js.map +1 -0
  201. package/dist/viewer/components/data-display/tree-view.d.ts +18 -0
  202. package/dist/viewer/components/data-display/tree-view.js +107 -0
  203. package/dist/viewer/components/data-display/tree-view.js.map +1 -0
  204. package/dist/viewer/components/data-display/zone-map.d.ts +28 -0
  205. package/dist/viewer/components/data-display/zone-map.js +197 -0
  206. package/dist/viewer/components/data-display/zone-map.js.map +1 -0
  207. package/dist/viewer/components/degradation-banner.d.ts +28 -0
  208. package/dist/viewer/components/degradation-banner.js +47 -0
  209. package/dist/viewer/components/degradation-banner.js.map +1 -0
  210. package/dist/viewer/components/detail-panel.d.ts +12 -0
  211. package/dist/viewer/components/detail-panel.js +166 -0
  212. package/dist/viewer/components/detail-panel.js.map +1 -0
  213. package/dist/viewer/components/elapsed-time.d.ts +38 -0
  214. package/dist/viewer/components/elapsed-time.js +35 -0
  215. package/dist/viewer/components/elapsed-time.js.map +1 -0
  216. package/dist/viewer/components/faq.d.ts +12 -0
  217. package/dist/viewer/components/faq.js +237 -0
  218. package/dist/viewer/components/faq.js.map +1 -0
  219. package/dist/viewer/components/favicon.d.ts +30 -0
  220. package/dist/viewer/components/favicon.js +90 -0
  221. package/dist/viewer/components/favicon.js.map +1 -0
  222. package/dist/viewer/components/guide.d.ts +5 -0
  223. package/dist/viewer/components/guide.js +116 -0
  224. package/dist/viewer/components/guide.js.map +1 -0
  225. package/dist/viewer/components/index.d.ts +37 -0
  226. package/dist/viewer/components/index.js +46 -0
  227. package/dist/viewer/components/index.js.map +1 -0
  228. package/dist/viewer/components/logos.d.ts +42 -0
  229. package/dist/viewer/components/logos.js +46 -0
  230. package/dist/viewer/components/logos.js.map +1 -0
  231. package/dist/viewer/components/memory-panel.d.ts +19 -0
  232. package/dist/viewer/components/memory-panel.js +181 -0
  233. package/dist/viewer/components/memory-panel.js.map +1 -0
  234. package/dist/viewer/components/memory-warning.d.ts +23 -0
  235. package/dist/viewer/components/memory-warning.js +42 -0
  236. package/dist/viewer/components/memory-warning.js.map +1 -0
  237. package/dist/viewer/components/notion-schema-wizard.d.ts +16 -0
  238. package/dist/viewer/components/notion-schema-wizard.js +263 -0
  239. package/dist/viewer/components/notion-schema-wizard.js.map +1 -0
  240. package/dist/viewer/components/polling-suspension-indicator.d.ts +24 -0
  241. package/dist/viewer/components/polling-suspension-indicator.js +31 -0
  242. package/dist/viewer/components/polling-suspension-indicator.js.map +1 -0
  243. package/dist/viewer/components/prd-tree/add-item-form.d.ts +31 -0
  244. package/dist/viewer/components/prd-tree/add-item-form.js +231 -0
  245. package/dist/viewer/components/prd-tree/add-item-form.js.map +1 -0
  246. package/dist/viewer/components/prd-tree/analyze-panel.d.ts +13 -0
  247. package/dist/viewer/components/prd-tree/analyze-panel.js +245 -0
  248. package/dist/viewer/components/prd-tree/analyze-panel.js.map +1 -0
  249. package/dist/viewer/components/prd-tree/batch-import-panel.d.ts +34 -0
  250. package/dist/viewer/components/prd-tree/batch-import-panel.js +415 -0
  251. package/dist/viewer/components/prd-tree/batch-import-panel.js.map +1 -0
  252. package/dist/viewer/components/prd-tree/bulk-actions.d.ts +19 -0
  253. package/dist/viewer/components/prd-tree/bulk-actions.js +90 -0
  254. package/dist/viewer/components/prd-tree/bulk-actions.js.map +1 -0
  255. package/dist/viewer/components/prd-tree/compute.d.ts +32 -0
  256. package/dist/viewer/components/prd-tree/compute.js +129 -0
  257. package/dist/viewer/components/prd-tree/compute.js.map +1 -0
  258. package/dist/viewer/components/prd-tree/delete-confirmation.d.ts +23 -0
  259. package/dist/viewer/components/prd-tree/delete-confirmation.js +83 -0
  260. package/dist/viewer/components/prd-tree/delete-confirmation.js.map +1 -0
  261. package/dist/viewer/components/prd-tree/execution-panel.d.ts +14 -0
  262. package/dist/viewer/components/prd-tree/execution-panel.js +291 -0
  263. package/dist/viewer/components/prd-tree/execution-panel.js.map +1 -0
  264. package/dist/viewer/components/prd-tree/facet-filter.d.ts +40 -0
  265. package/dist/viewer/components/prd-tree/facet-filter.js +188 -0
  266. package/dist/viewer/components/prd-tree/facet-filter.js.map +1 -0
  267. package/dist/viewer/components/prd-tree/index.d.ts +24 -0
  268. package/dist/viewer/components/prd-tree/index.js +16 -0
  269. package/dist/viewer/components/prd-tree/index.js.map +1 -0
  270. package/dist/viewer/components/prd-tree/inline-add-form.d.ts +32 -0
  271. package/dist/viewer/components/prd-tree/inline-add-form.js +175 -0
  272. package/dist/viewer/components/prd-tree/inline-add-form.js.map +1 -0
  273. package/dist/viewer/components/prd-tree/inline-status-picker.d.ts +31 -0
  274. package/dist/viewer/components/prd-tree/inline-status-picker.js +110 -0
  275. package/dist/viewer/components/prd-tree/inline-status-picker.js.map +1 -0
  276. package/dist/viewer/components/prd-tree/lazy-children.d.ts +40 -0
  277. package/dist/viewer/components/prd-tree/lazy-children.js +73 -0
  278. package/dist/viewer/components/prd-tree/lazy-children.js.map +1 -0
  279. package/dist/viewer/components/prd-tree/levels.d.ts +31 -0
  280. package/dist/viewer/components/prd-tree/levels.js +72 -0
  281. package/dist/viewer/components/prd-tree/levels.js.map +1 -0
  282. package/dist/viewer/components/prd-tree/listener-lifecycle.d.ts +105 -0
  283. package/dist/viewer/components/prd-tree/listener-lifecycle.js +173 -0
  284. package/dist/viewer/components/prd-tree/listener-lifecycle.js.map +1 -0
  285. package/dist/viewer/components/prd-tree/merge-preview.d.ts +25 -0
  286. package/dist/viewer/components/prd-tree/merge-preview.js +181 -0
  287. package/dist/viewer/components/prd-tree/merge-preview.js.map +1 -0
  288. package/dist/viewer/components/prd-tree/prd-tree.d.ts +91 -0
  289. package/dist/viewer/components/prd-tree/prd-tree.js +565 -0
  290. package/dist/viewer/components/prd-tree/prd-tree.js.map +1 -0
  291. package/dist/viewer/components/prd-tree/proposal-editor.d.ts +44 -0
  292. package/dist/viewer/components/prd-tree/proposal-editor.js +438 -0
  293. package/dist/viewer/components/prd-tree/proposal-editor.js.map +1 -0
  294. package/dist/viewer/components/prd-tree/prune-confirmation.d.ts +25 -0
  295. package/dist/viewer/components/prd-tree/prune-confirmation.js +336 -0
  296. package/dist/viewer/components/prd-tree/prune-confirmation.js.map +1 -0
  297. package/dist/viewer/components/prd-tree/prune-diff-tree.d.ts +39 -0
  298. package/dist/viewer/components/prd-tree/prune-diff-tree.js +319 -0
  299. package/dist/viewer/components/prd-tree/prune-diff-tree.js.map +1 -0
  300. package/dist/viewer/components/prd-tree/reorganize-panel.d.ts +16 -0
  301. package/dist/viewer/components/prd-tree/reorganize-panel.js +213 -0
  302. package/dist/viewer/components/prd-tree/reorganize-panel.js.map +1 -0
  303. package/dist/viewer/components/prd-tree/smart-add-input.d.ts +19 -0
  304. package/dist/viewer/components/prd-tree/smart-add-input.js +383 -0
  305. package/dist/viewer/components/prd-tree/smart-add-input.js.map +1 -0
  306. package/dist/viewer/components/prd-tree/status-filter.d.ts +40 -0
  307. package/dist/viewer/components/prd-tree/status-filter.js +131 -0
  308. package/dist/viewer/components/prd-tree/status-filter.js.map +1 -0
  309. package/dist/viewer/components/prd-tree/task-detail.d.ts +41 -0
  310. package/dist/viewer/components/prd-tree/task-detail.js +1205 -0
  311. package/dist/viewer/components/prd-tree/task-detail.js.map +1 -0
  312. package/dist/viewer/components/prd-tree/task-utilization.d.ts +7 -0
  313. package/dist/viewer/components/prd-tree/task-utilization.js +25 -0
  314. package/dist/viewer/components/prd-tree/task-utilization.js.map +1 -0
  315. package/dist/viewer/components/prd-tree/tree-differ.d.ts +59 -0
  316. package/dist/viewer/components/prd-tree/tree-differ.js +200 -0
  317. package/dist/viewer/components/prd-tree/tree-differ.js.map +1 -0
  318. package/dist/viewer/components/prd-tree/tree-event-delegate.d.ts +70 -0
  319. package/dist/viewer/components/prd-tree/tree-event-delegate.js +176 -0
  320. package/dist/viewer/components/prd-tree/tree-event-delegate.js.map +1 -0
  321. package/dist/viewer/components/prd-tree/tree-search.d.ts +65 -0
  322. package/dist/viewer/components/prd-tree/tree-search.js +178 -0
  323. package/dist/viewer/components/prd-tree/tree-search.js.map +1 -0
  324. package/dist/viewer/components/prd-tree/tree-utils.d.ts +38 -0
  325. package/dist/viewer/components/prd-tree/tree-utils.js +107 -0
  326. package/dist/viewer/components/prd-tree/tree-utils.js.map +1 -0
  327. package/dist/viewer/components/prd-tree/types.d.ts +93 -0
  328. package/dist/viewer/components/prd-tree/types.js +16 -0
  329. package/dist/viewer/components/prd-tree/types.js.map +1 -0
  330. package/dist/viewer/components/prd-tree/virtual-scroll.d.ts +119 -0
  331. package/dist/viewer/components/prd-tree/virtual-scroll.js +169 -0
  332. package/dist/viewer/components/prd-tree/virtual-scroll.js.map +1 -0
  333. package/dist/viewer/components/progressive-loader.d.ts +114 -0
  334. package/dist/viewer/components/progressive-loader.js +225 -0
  335. package/dist/viewer/components/progressive-loader.js.map +1 -0
  336. package/dist/viewer/components/refresh-queue-status.d.ts +20 -0
  337. package/dist/viewer/components/refresh-queue-status.js +65 -0
  338. package/dist/viewer/components/refresh-queue-status.js.map +1 -0
  339. package/dist/viewer/components/rex-task-link.d.ts +50 -0
  340. package/dist/viewer/components/rex-task-link.js +218 -0
  341. package/dist/viewer/components/rex-task-link.js.map +1 -0
  342. package/dist/viewer/components/search-filter.d.ts +20 -0
  343. package/dist/viewer/components/search-filter.js +28 -0
  344. package/dist/viewer/components/search-filter.js.map +1 -0
  345. package/dist/viewer/components/search-overlay.d.ts +31 -0
  346. package/dist/viewer/components/search-overlay.js +472 -0
  347. package/dist/viewer/components/search-overlay.js.map +1 -0
  348. package/dist/viewer/components/sidebar.d.ts +18 -0
  349. package/dist/viewer/components/sidebar.js +357 -0
  350. package/dist/viewer/components/sidebar.js.map +1 -0
  351. package/dist/viewer/components/status-indicators.d.ts +63 -0
  352. package/dist/viewer/components/status-indicators.js +136 -0
  353. package/dist/viewer/components/status-indicators.js.map +1 -0
  354. package/dist/viewer/components/theme-toggle.d.ts +8 -0
  355. package/dist/viewer/components/theme-toggle.js +28 -0
  356. package/dist/viewer/components/theme-toggle.js.map +1 -0
  357. package/dist/viewer/components/throttle-controls.d.ts +18 -0
  358. package/dist/viewer/components/throttle-controls.js +304 -0
  359. package/dist/viewer/components/throttle-controls.js.map +1 -0
  360. package/dist/viewer/components/ws-health-panel.d.ts +18 -0
  361. package/dist/viewer/components/ws-health-panel.js +250 -0
  362. package/dist/viewer/components/ws-health-panel.js.map +1 -0
  363. package/dist/viewer/components/zone-slideout.d.ts +17 -0
  364. package/dist/viewer/components/zone-slideout.js +162 -0
  365. package/dist/viewer/components/zone-slideout.js.map +1 -0
  366. package/dist/viewer/crash/crash-detector.d.ts +69 -0
  367. package/dist/viewer/crash/crash-detector.js +239 -0
  368. package/dist/viewer/crash/crash-detector.js.map +1 -0
  369. package/dist/viewer/crash/index.d.ts +7 -0
  370. package/dist/viewer/crash/index.js +8 -0
  371. package/dist/viewer/crash/index.js.map +1 -0
  372. package/dist/viewer/deployed-mode.d.ts +37 -0
  373. package/dist/viewer/deployed-mode.js +94 -0
  374. package/dist/viewer/deployed-mode.js.map +1 -0
  375. package/dist/viewer/external.d.ts +17 -0
  376. package/dist/viewer/external.js +17 -0
  377. package/dist/viewer/external.js.map +1 -0
  378. package/dist/viewer/graph/index.d.ts +9 -0
  379. package/dist/viewer/graph/index.js +12 -0
  380. package/dist/viewer/graph/index.js.map +1 -0
  381. package/dist/viewer/graph/physics.d.ts +96 -0
  382. package/dist/viewer/graph/physics.js +366 -0
  383. package/dist/viewer/graph/physics.js.map +1 -0
  384. package/dist/viewer/graph/renderer.d.ts +184 -0
  385. package/dist/viewer/graph/renderer.js +1438 -0
  386. package/dist/viewer/graph/renderer.js.map +1 -0
  387. package/dist/viewer/hooks/index.d.ts +27 -0
  388. package/dist/viewer/hooks/index.js +28 -0
  389. package/dist/viewer/hooks/index.js.map +1 -0
  390. package/dist/viewer/hooks/use-app-data.d.ts +31 -0
  391. package/dist/viewer/hooks/use-app-data.js +152 -0
  392. package/dist/viewer/hooks/use-app-data.js.map +1 -0
  393. package/dist/viewer/hooks/use-crash-recovery.d.ts +50 -0
  394. package/dist/viewer/hooks/use-crash-recovery.js +76 -0
  395. package/dist/viewer/hooks/use-crash-recovery.js.map +1 -0
  396. package/dist/viewer/hooks/use-delete-actions.d.ts +48 -0
  397. package/dist/viewer/hooks/use-delete-actions.js +103 -0
  398. package/dist/viewer/hooks/use-delete-actions.js.map +1 -0
  399. package/dist/viewer/hooks/use-dom-performance-monitor.d.ts +68 -0
  400. package/dist/viewer/hooks/use-dom-performance-monitor.js +71 -0
  401. package/dist/viewer/hooks/use-dom-performance-monitor.js.map +1 -0
  402. package/dist/viewer/hooks/use-facet-state.d.ts +32 -0
  403. package/dist/viewer/hooks/use-facet-state.js +119 -0
  404. package/dist/viewer/hooks/use-facet-state.js.map +1 -0
  405. package/dist/viewer/hooks/use-feature-toggle.d.ts +18 -0
  406. package/dist/viewer/hooks/use-feature-toggle.js +57 -0
  407. package/dist/viewer/hooks/use-feature-toggle.js.map +1 -0
  408. package/dist/viewer/hooks/use-file-edges.d.ts +23 -0
  409. package/dist/viewer/hooks/use-file-edges.js +221 -0
  410. package/dist/viewer/hooks/use-file-edges.js.map +1 -0
  411. package/dist/viewer/hooks/use-graceful-degradation.d.ts +30 -0
  412. package/dist/viewer/hooks/use-graceful-degradation.js +45 -0
  413. package/dist/viewer/hooks/use-graceful-degradation.js.map +1 -0
  414. package/dist/viewer/hooks/use-item-selection.d.ts +49 -0
  415. package/dist/viewer/hooks/use-item-selection.js +117 -0
  416. package/dist/viewer/hooks/use-item-selection.js.map +1 -0
  417. package/dist/viewer/hooks/use-memory-monitor.d.ts +39 -0
  418. package/dist/viewer/hooks/use-memory-monitor.js +73 -0
  419. package/dist/viewer/hooks/use-memory-monitor.js.map +1 -0
  420. package/dist/viewer/hooks/use-pan-zoom.d.ts +33 -0
  421. package/dist/viewer/hooks/use-pan-zoom.js +110 -0
  422. package/dist/viewer/hooks/use-pan-zoom.js.map +1 -0
  423. package/dist/viewer/hooks/use-persistent-filter.d.ts +24 -0
  424. package/dist/viewer/hooks/use-persistent-filter.js +37 -0
  425. package/dist/viewer/hooks/use-persistent-filter.js.map +1 -0
  426. package/dist/viewer/hooks/use-polling-suspension.d.ts +32 -0
  427. package/dist/viewer/hooks/use-polling-suspension.js +41 -0
  428. package/dist/viewer/hooks/use-polling-suspension.js.map +1 -0
  429. package/dist/viewer/hooks/use-polling.d.ts +39 -0
  430. package/dist/viewer/hooks/use-polling.js +55 -0
  431. package/dist/viewer/hooks/use-polling.js.map +1 -0
  432. package/dist/viewer/hooks/use-prd-actions.d.ts +126 -0
  433. package/dist/viewer/hooks/use-prd-actions.js +250 -0
  434. package/dist/viewer/hooks/use-prd-actions.js.map +1 -0
  435. package/dist/viewer/hooks/use-prd-data.d.ts +45 -0
  436. package/dist/viewer/hooks/use-prd-data.js +159 -0
  437. package/dist/viewer/hooks/use-prd-data.js.map +1 -0
  438. package/dist/viewer/hooks/use-prd-deep-link.d.ts +45 -0
  439. package/dist/viewer/hooks/use-prd-deep-link.js +60 -0
  440. package/dist/viewer/hooks/use-prd-deep-link.js.map +1 -0
  441. package/dist/viewer/hooks/use-prd-websocket.d.ts +47 -0
  442. package/dist/viewer/hooks/use-prd-websocket.js +139 -0
  443. package/dist/viewer/hooks/use-prd-websocket.js.map +1 -0
  444. package/dist/viewer/hooks/use-project-metadata.d.ts +25 -0
  445. package/dist/viewer/hooks/use-project-metadata.js +55 -0
  446. package/dist/viewer/hooks/use-project-metadata.js.map +1 -0
  447. package/dist/viewer/hooks/use-project-status.d.ts +60 -0
  448. package/dist/viewer/hooks/use-project-status.js +133 -0
  449. package/dist/viewer/hooks/use-project-status.js.map +1 -0
  450. package/dist/viewer/hooks/use-refresh-throttle.d.ts +45 -0
  451. package/dist/viewer/hooks/use-refresh-throttle.js +52 -0
  452. package/dist/viewer/hooks/use-refresh-throttle.js.map +1 -0
  453. package/dist/viewer/hooks/use-route-state.d.ts +18 -0
  454. package/dist/viewer/hooks/use-route-state.js +115 -0
  455. package/dist/viewer/hooks/use-route-state.js.map +1 -0
  456. package/dist/viewer/hooks/use-subzone-edges.d.ts +21 -0
  457. package/dist/viewer/hooks/use-subzone-edges.js +147 -0
  458. package/dist/viewer/hooks/use-subzone-edges.js.map +1 -0
  459. package/dist/viewer/hooks/use-tab-visibility.d.ts +31 -0
  460. package/dist/viewer/hooks/use-tab-visibility.js +43 -0
  461. package/dist/viewer/hooks/use-tab-visibility.js.map +1 -0
  462. package/dist/viewer/hooks/use-tick.d.ts +43 -0
  463. package/dist/viewer/hooks/use-tick.js +76 -0
  464. package/dist/viewer/hooks/use-tick.js.map +1 -0
  465. package/dist/viewer/hooks/use-toast.d.ts +24 -0
  466. package/dist/viewer/hooks/use-toast.js +26 -0
  467. package/dist/viewer/hooks/use-toast.js.map +1 -0
  468. package/dist/viewer/hooks/use-zone-drag.d.ts +30 -0
  469. package/dist/viewer/hooks/use-zone-drag.js +60 -0
  470. package/dist/viewer/hooks/use-zone-drag.js.map +1 -0
  471. package/dist/viewer/index.html +36 -0
  472. package/dist/viewer/loader.d.ts +33 -0
  473. package/dist/viewer/loader.js +195 -0
  474. package/dist/viewer/loader.js.map +1 -0
  475. package/dist/viewer/main.d.ts +1 -0
  476. package/dist/viewer/main.js +121 -0
  477. package/dist/viewer/main.js.map +1 -0
  478. package/dist/viewer/messaging/call-rate-limiter.d.ts +50 -0
  479. package/dist/viewer/messaging/call-rate-limiter.js +103 -0
  480. package/dist/viewer/messaging/call-rate-limiter.js.map +1 -0
  481. package/dist/viewer/messaging/fetch-pipeline.d.ts +58 -0
  482. package/dist/viewer/messaging/fetch-pipeline.js +58 -0
  483. package/dist/viewer/messaging/fetch-pipeline.js.map +1 -0
  484. package/dist/viewer/messaging/index.d.ts +43 -0
  485. package/dist/viewer/messaging/index.js +46 -0
  486. package/dist/viewer/messaging/index.js.map +1 -0
  487. package/dist/viewer/messaging/message-coalescer.d.ts +96 -0
  488. package/dist/viewer/messaging/message-coalescer.js +121 -0
  489. package/dist/viewer/messaging/message-coalescer.js.map +1 -0
  490. package/dist/viewer/messaging/message-throttle.d.ts +95 -0
  491. package/dist/viewer/messaging/message-throttle.js +147 -0
  492. package/dist/viewer/messaging/message-throttle.js.map +1 -0
  493. package/dist/viewer/messaging/request-dedup.d.ts +43 -0
  494. package/dist/viewer/messaging/request-dedup.js +55 -0
  495. package/dist/viewer/messaging/request-dedup.js.map +1 -0
  496. package/dist/viewer/messaging/ws-pipeline.d.ts +85 -0
  497. package/dist/viewer/messaging/ws-pipeline.js +68 -0
  498. package/dist/viewer/messaging/ws-pipeline.js.map +1 -0
  499. package/dist/viewer/n-dx.png +0 -0
  500. package/dist/viewer/performance/dom-performance-monitor.d.ts +157 -0
  501. package/dist/viewer/performance/dom-performance-monitor.js +341 -0
  502. package/dist/viewer/performance/dom-performance-monitor.js.map +1 -0
  503. package/dist/viewer/performance/dom-update-gate.d.ts +122 -0
  504. package/dist/viewer/performance/dom-update-gate.js +229 -0
  505. package/dist/viewer/performance/dom-update-gate.js.map +1 -0
  506. package/dist/viewer/performance/graceful-degradation.d.ts +73 -0
  507. package/dist/viewer/performance/graceful-degradation.js +152 -0
  508. package/dist/viewer/performance/graceful-degradation.js.map +1 -0
  509. package/dist/viewer/performance/index.d.ts +14 -0
  510. package/dist/viewer/performance/index.js +20 -0
  511. package/dist/viewer/performance/index.js.map +1 -0
  512. package/dist/viewer/performance/memory-monitor.d.ts +78 -0
  513. package/dist/viewer/performance/memory-monitor.js +218 -0
  514. package/dist/viewer/performance/memory-monitor.js.map +1 -0
  515. package/dist/viewer/performance/refresh-throttle.d.ts +90 -0
  516. package/dist/viewer/performance/refresh-throttle.js +266 -0
  517. package/dist/viewer/performance/refresh-throttle.js.map +1 -0
  518. package/dist/viewer/performance/response-buffer-gate.d.ts +108 -0
  519. package/dist/viewer/performance/response-buffer-gate.js +170 -0
  520. package/dist/viewer/performance/response-buffer-gate.js.map +1 -0
  521. package/dist/viewer/performance/update-batcher.d.ts +79 -0
  522. package/dist/viewer/performance/update-batcher.js +119 -0
  523. package/dist/viewer/performance/update-batcher.js.map +1 -0
  524. package/dist/viewer/polling/batched-tick-dispatcher.d.ts +83 -0
  525. package/dist/viewer/polling/batched-tick-dispatcher.js +183 -0
  526. package/dist/viewer/polling/batched-tick-dispatcher.js.map +1 -0
  527. package/dist/viewer/polling/index.d.ts +13 -0
  528. package/dist/viewer/polling/index.js +16 -0
  529. package/dist/viewer/polling/index.js.map +1 -0
  530. package/dist/viewer/polling/polling-manager.d.ts +82 -0
  531. package/dist/viewer/polling/polling-manager.js +254 -0
  532. package/dist/viewer/polling/polling-manager.js.map +1 -0
  533. package/dist/viewer/polling/polling-restart.d.ts +45 -0
  534. package/dist/viewer/polling/polling-restart.js +98 -0
  535. package/dist/viewer/polling/polling-restart.js.map +1 -0
  536. package/dist/viewer/polling/polling-state.d.ts +182 -0
  537. package/dist/viewer/polling/polling-state.js +306 -0
  538. package/dist/viewer/polling/polling-state.js.map +1 -0
  539. package/dist/viewer/polling/tab-visibility.d.ts +112 -0
  540. package/dist/viewer/polling/tab-visibility.js +276 -0
  541. package/dist/viewer/polling/tab-visibility.js.map +1 -0
  542. package/dist/viewer/polling/tick-timer.d.ts +70 -0
  543. package/dist/viewer/polling/tick-timer.js +168 -0
  544. package/dist/viewer/polling/tick-timer.js.map +1 -0
  545. package/dist/viewer/polling/tick-visibility-gate.d.ts +92 -0
  546. package/dist/viewer/polling/tick-visibility-gate.js +146 -0
  547. package/dist/viewer/polling/tick-visibility-gate.js.map +1 -0
  548. package/dist/viewer/route-state.d.ts +8 -0
  549. package/dist/viewer/route-state.js +77 -0
  550. package/dist/viewer/route-state.js.map +1 -0
  551. package/dist/viewer/schema-compat.d.ts +17 -0
  552. package/dist/viewer/schema-compat.js +49 -0
  553. package/dist/viewer/schema-compat.js.map +1 -0
  554. package/dist/viewer/types.d.ts +66 -0
  555. package/dist/viewer/types.js +2 -0
  556. package/dist/viewer/types.js.map +1 -0
  557. package/dist/viewer/usage/constants.d.ts +14 -0
  558. package/dist/viewer/usage/constants.js +14 -0
  559. package/dist/viewer/usage/constants.js.map +1 -0
  560. package/dist/viewer/usage/index.d.ts +7 -0
  561. package/dist/viewer/usage/index.js +8 -0
  562. package/dist/viewer/usage/index.js.map +1 -0
  563. package/dist/viewer/utils.d.ts +25 -0
  564. package/dist/viewer/utils.js +48 -0
  565. package/dist/viewer/utils.js.map +1 -0
  566. package/dist/viewer/validate.d.ts +23 -0
  567. package/dist/viewer/validate.js +275 -0
  568. package/dist/viewer/validate.js.map +1 -0
  569. package/dist/viewer/views/analysis.d.ts +10 -0
  570. package/dist/viewer/views/analysis.js +109 -0
  571. package/dist/viewer/views/analysis.js.map +1 -0
  572. package/dist/viewer/views/architecture.d.ts +10 -0
  573. package/dist/viewer/views/architecture.js +44 -0
  574. package/dist/viewer/views/architecture.js.map +1 -0
  575. package/dist/viewer/views/domain-hench.d.ts +13 -0
  576. package/dist/viewer/views/domain-hench.js +14 -0
  577. package/dist/viewer/views/domain-hench.js.map +1 -0
  578. package/dist/viewer/views/domain-rex.d.ts +17 -0
  579. package/dist/viewer/views/domain-rex.js +18 -0
  580. package/dist/viewer/views/domain-rex.js.map +1 -0
  581. package/dist/viewer/views/domain-settings.d.ts +12 -0
  582. package/dist/viewer/views/domain-settings.js +13 -0
  583. package/dist/viewer/views/domain-settings.js.map +1 -0
  584. package/dist/viewer/views/domain-sourcevision.d.ts +20 -0
  585. package/dist/viewer/views/domain-sourcevision.js +21 -0
  586. package/dist/viewer/views/domain-sourcevision.js.map +1 -0
  587. package/dist/viewer/views/enrichment-thresholds.d.ts +11 -0
  588. package/dist/viewer/views/enrichment-thresholds.js +12 -0
  589. package/dist/viewer/views/enrichment-thresholds.js.map +1 -0
  590. package/dist/viewer/views/feature-toggles.d.ts +13 -0
  591. package/dist/viewer/views/feature-toggles.js +185 -0
  592. package/dist/viewer/views/feature-toggles.js.map +1 -0
  593. package/dist/viewer/views/files.d.ts +13 -0
  594. package/dist/viewer/views/files.js +174 -0
  595. package/dist/viewer/views/files.js.map +1 -0
  596. package/dist/viewer/views/graph.d.ts +12 -0
  597. package/dist/viewer/views/graph.js +316 -0
  598. package/dist/viewer/views/graph.js.map +1 -0
  599. package/dist/viewer/views/hench-config.d.ts +39 -0
  600. package/dist/viewer/views/hench-config.js +473 -0
  601. package/dist/viewer/views/hench-config.js.map +1 -0
  602. package/dist/viewer/views/hench-runs.d.ts +19 -0
  603. package/dist/viewer/views/hench-runs.js +460 -0
  604. package/dist/viewer/views/hench-runs.js.map +1 -0
  605. package/dist/viewer/views/hench-templates.d.ts +17 -0
  606. package/dist/viewer/views/hench-templates.js +262 -0
  607. package/dist/viewer/views/hench-templates.js.map +1 -0
  608. package/dist/viewer/views/integration-config.d.ts +73 -0
  609. package/dist/viewer/views/integration-config.js +524 -0
  610. package/dist/viewer/views/integration-config.js.map +1 -0
  611. package/dist/viewer/views/notion-config.d.ts +16 -0
  612. package/dist/viewer/views/notion-config.js +357 -0
  613. package/dist/viewer/views/notion-config.js.map +1 -0
  614. package/dist/viewer/views/overview.d.ts +10 -0
  615. package/dist/viewer/views/overview.js +187 -0
  616. package/dist/viewer/views/overview.js.map +1 -0
  617. package/dist/viewer/views/pr-markdown.d.ts +3 -0
  618. package/dist/viewer/views/pr-markdown.js +350 -0
  619. package/dist/viewer/views/pr-markdown.js.map +1 -0
  620. package/dist/viewer/views/prd.d.ts +34 -0
  621. package/dist/viewer/views/prd.js +257 -0
  622. package/dist/viewer/views/prd.js.map +1 -0
  623. package/dist/viewer/views/problems.d.ts +8 -0
  624. package/dist/viewer/views/problems.js +50 -0
  625. package/dist/viewer/views/problems.js.map +1 -0
  626. package/dist/viewer/views/rex-dashboard.d.ts +14 -0
  627. package/dist/viewer/views/rex-dashboard.js +334 -0
  628. package/dist/viewer/views/rex-dashboard.js.map +1 -0
  629. package/dist/viewer/views/routes.d.ts +8 -0
  630. package/dist/viewer/views/routes.js +216 -0
  631. package/dist/viewer/views/routes.js.map +1 -0
  632. package/dist/viewer/views/sourcevision-tabs.d.ts +18 -0
  633. package/dist/viewer/views/sourcevision-tabs.js +14 -0
  634. package/dist/viewer/views/sourcevision-tabs.js.map +1 -0
  635. package/dist/viewer/views/suggestions.d.ts +8 -0
  636. package/dist/viewer/views/suggestions.js +36 -0
  637. package/dist/viewer/views/suggestions.js.map +1 -0
  638. package/dist/viewer/views/task-audit.d.ts +18 -0
  639. package/dist/viewer/views/task-audit.js +413 -0
  640. package/dist/viewer/views/task-audit.js.map +1 -0
  641. package/dist/viewer/views/token-usage.d.ts +10 -0
  642. package/dist/viewer/views/token-usage.js +410 -0
  643. package/dist/viewer/views/token-usage.js.map +1 -0
  644. package/dist/viewer/views/validation.d.ts +11 -0
  645. package/dist/viewer/views/validation.js +475 -0
  646. package/dist/viewer/views/validation.js.map +1 -0
  647. package/dist/viewer/views/view-registry.d.ts +27 -0
  648. package/dist/viewer/views/view-registry.js +70 -0
  649. package/dist/viewer/views/view-registry.js.map +1 -0
  650. package/dist/viewer/views/workflow-optimization.d.ts +12 -0
  651. package/dist/viewer/views/workflow-optimization.js +311 -0
  652. package/dist/viewer/views/workflow-optimization.js.map +1 -0
  653. package/dist/viewer/views/zone-types.d.ts +69 -0
  654. package/dist/viewer/views/zone-types.js +5 -0
  655. package/dist/viewer/views/zone-types.js.map +1 -0
  656. package/dist/viewer/views/zones.d.ts +50 -0
  657. package/dist/viewer/views/zones.js +1438 -0
  658. package/dist/viewer/views/zones.js.map +1 -0
  659. package/dist/viewer/visualization/colors.d.ts +16 -0
  660. package/dist/viewer/visualization/colors.js +31 -0
  661. package/dist/viewer/visualization/colors.js.map +1 -0
  662. package/dist/viewer/visualization/flow.d.ts +54 -0
  663. package/dist/viewer/visualization/flow.js +123 -0
  664. package/dist/viewer/visualization/flow.js.map +1 -0
  665. package/dist/viewer/visualization/index.d.ts +34 -0
  666. package/dist/viewer/visualization/index.js +40 -0
  667. package/dist/viewer/visualization/index.js.map +1 -0
  668. package/dist/viewer/visualization/metrics.d.ts +8 -0
  669. package/dist/viewer/visualization/metrics.js +16 -0
  670. package/dist/viewer/visualization/metrics.js.map +1 -0
  671. package/package.json +54 -0
@@ -0,0 +1,1205 @@
1
+ /**
2
+ * Task detail panel for PRD items.
3
+ *
4
+ * Renders inside the existing DetailPanel when a PRD item is selected.
5
+ * Shows description, acceptance criteria, metadata, and provides
6
+ * controls for updating status, priority, and tags.
7
+ */
8
+ import { h, Fragment } from "preact";
9
+ import { useState, useCallback, useEffect, useRef } from "preact/hooks";
10
+ import { formatTimestamp } from "./compute.js";
11
+ import { findItemById } from "./tree-utils.js";
12
+ import { CopyLinkButton } from "../copy-link-button.js";
13
+ import { resolveTaskUtilization } from "./task-utilization.js";
14
+ import { isWorkItem, getLevelLabel, getChildLevel } from "./levels.js";
15
+ // ── Constants ────────────────────────────────────────────────────────
16
+ const STATUS_OPTIONS = [
17
+ { value: "pending", label: "Pending", icon: "○" },
18
+ { value: "in_progress", label: "In Progress", icon: "◐" },
19
+ { value: "completed", label: "Completed", icon: "●" },
20
+ { value: "failing", label: "Failing", icon: "⚠" },
21
+ { value: "blocked", label: "Blocked", icon: "⊘" },
22
+ { value: "deferred", label: "Deferred", icon: "◌" },
23
+ { value: "deleted", label: "Deleted", icon: "✕" },
24
+ ];
25
+ const PRIORITY_OPTIONS = [
26
+ { value: "critical", label: "Critical" },
27
+ { value: "high", label: "High" },
28
+ { value: "medium", label: "Medium" },
29
+ { value: "low", label: "Low" },
30
+ ];
31
+ // Level labels now provided by getLevelLabel() from ./levels.ts
32
+ function formatTokenCount(n) {
33
+ if (n >= 1_000_000)
34
+ return `${(n / 1_000_000).toFixed(1)}M`;
35
+ if (n >= 1_000)
36
+ return `${(n / 1_000).toFixed(1)}k`;
37
+ return String(n);
38
+ }
39
+ // ── Sub-components ───────────────────────────────────────────────────
40
+ /** Status selector — rendered as a row of buttons. */
41
+ function StatusSelector({ current, onChange, onRequestReason, }) {
42
+ return h("div", { class: "task-status-selector", role: "radiogroup", "aria-label": "Task status" }, STATUS_OPTIONS.map((opt) => h("button", {
43
+ key: opt.value,
44
+ class: `task-status-btn prd-status-${opt.value}${current === opt.value ? " active" : ""}`,
45
+ onClick: () => {
46
+ if (opt.value === "failing" && onRequestReason) {
47
+ onRequestReason(opt.value);
48
+ }
49
+ else {
50
+ onChange(opt.value);
51
+ }
52
+ },
53
+ role: "radio",
54
+ "aria-checked": String(current === opt.value),
55
+ title: opt.label,
56
+ }, h("span", { class: "task-status-btn-icon" }, opt.icon), h("span", { class: "task-status-btn-label" }, opt.label))));
57
+ }
58
+ /** Priority selector — rendered as a dropdown. */
59
+ function PrioritySelector({ current, onChange, }) {
60
+ return h("select", {
61
+ class: `task-priority-select${current ? ` prd-priority-${current}` : ""}`,
62
+ value: current || "",
63
+ onChange: (e) => {
64
+ const value = e.target.value;
65
+ if (value)
66
+ onChange(value);
67
+ },
68
+ "aria-label": "Task priority",
69
+ }, h("option", { value: "", disabled: true }, "Set priority"), PRIORITY_OPTIONS.map((opt) => h("option", { key: opt.value, value: opt.value }, opt.label)));
70
+ }
71
+ /** Inline tag editor. */
72
+ function TagEditor({ tags, onUpdate, }) {
73
+ const [adding, setAdding] = useState(false);
74
+ const [newTag, setNewTag] = useState("");
75
+ const handleAdd = useCallback(() => {
76
+ const trimmed = newTag.trim().toLowerCase().replace(/\s+/g, "-");
77
+ if (trimmed && !tags.includes(trimmed)) {
78
+ onUpdate([...tags, trimmed]);
79
+ }
80
+ setNewTag("");
81
+ setAdding(false);
82
+ }, [newTag, tags, onUpdate]);
83
+ const handleRemove = useCallback((tag) => {
84
+ onUpdate(tags.filter((t) => t !== tag));
85
+ }, [tags, onUpdate]);
86
+ return h("div", { class: "task-tag-editor" }, tags.map((tag) => h("span", { key: tag, class: "task-tag" }, tag, h("button", {
87
+ class: "task-tag-remove",
88
+ onClick: () => handleRemove(tag),
89
+ "aria-label": `Remove tag ${tag}`,
90
+ title: "Remove",
91
+ }, "\u00d7"))), adding
92
+ ? h("span", { class: "task-tag-input-wrapper" }, h("input", {
93
+ class: "task-tag-input",
94
+ type: "text",
95
+ value: newTag,
96
+ placeholder: "tag name",
97
+ onInput: (e) => setNewTag(e.target.value),
98
+ onKeyDown: (e) => {
99
+ if (e.key === "Enter") {
100
+ e.preventDefault();
101
+ handleAdd();
102
+ }
103
+ if (e.key === "Escape") {
104
+ setAdding(false);
105
+ setNewTag("");
106
+ }
107
+ },
108
+ // Auto-focus when shown
109
+ ref: (el) => el?.focus(),
110
+ }), h("button", { class: "task-tag-confirm", onClick: handleAdd, title: "Add tag" }, "\u2713"))
111
+ : h("button", {
112
+ class: "task-tag-add-btn",
113
+ onClick: () => setAdding(true),
114
+ title: "Add tag",
115
+ }, "+ tag"));
116
+ }
117
+ /** Dependency (blockedBy) display with navigation. */
118
+ function DependencyList({ blockedBy, allItems, onNavigate, }) {
119
+ if (blockedBy.length === 0)
120
+ return null;
121
+ return h("div", { class: "task-dependencies" }, h("div", { class: "task-section-label" }, "Blocked By"), h("div", { class: "task-dependency-list" }, blockedBy.map((depId) => {
122
+ const dep = findItemById(allItems, depId);
123
+ const isResolved = dep?.status === "completed";
124
+ return h("div", {
125
+ key: depId,
126
+ class: `task-dependency-item${isResolved ? " resolved" : ""}`,
127
+ onClick: onNavigate ? () => onNavigate(depId) : undefined,
128
+ role: onNavigate ? "button" : undefined,
129
+ tabIndex: onNavigate ? 0 : undefined,
130
+ }, h("span", { class: `task-dep-status${isResolved ? " prd-status-completed" : " prd-status-blocked"}` }, isResolved ? "●" : "⊘"), h("span", { class: "task-dep-title" }, dep ? dep.title : depId), dep
131
+ ? h("span", { class: `prd-level-badge prd-level-${dep.level}` }, getLevelLabel(dep.level))
132
+ : null);
133
+ })));
134
+ }
135
+ /** Editable title — click to edit inline. */
136
+ function EditableTitle({ title, onUpdate, }) {
137
+ const [editing, setEditing] = useState(false);
138
+ const [draft, setDraft] = useState(title);
139
+ // Sync draft when item changes externally
140
+ useEffect(() => { setDraft(title); }, [title]);
141
+ const handleSave = useCallback(() => {
142
+ const trimmed = draft.trim();
143
+ if (trimmed && trimmed !== title && onUpdate) {
144
+ onUpdate(trimmed);
145
+ }
146
+ setEditing(false);
147
+ }, [draft, title, onUpdate]);
148
+ if (!onUpdate) {
149
+ return h("div", { class: "task-title-display" }, title);
150
+ }
151
+ if (editing) {
152
+ return h("div", { class: "task-title-edit" }, h("input", {
153
+ class: "task-title-input",
154
+ type: "text",
155
+ value: draft,
156
+ onInput: (e) => setDraft(e.target.value),
157
+ onKeyDown: (e) => {
158
+ if (e.key === "Enter") {
159
+ e.preventDefault();
160
+ handleSave();
161
+ }
162
+ if (e.key === "Escape") {
163
+ setDraft(title);
164
+ setEditing(false);
165
+ }
166
+ },
167
+ onBlur: handleSave,
168
+ ref: (el) => el?.focus(),
169
+ }));
170
+ }
171
+ return h("div", {
172
+ class: "task-title-display editable",
173
+ onClick: () => { setDraft(title); setEditing(true); },
174
+ title: "Click to edit title",
175
+ role: "button",
176
+ tabIndex: 0,
177
+ onKeyDown: (e) => {
178
+ if (e.key === "Enter" || e.key === " ") {
179
+ e.preventDefault();
180
+ setDraft(title);
181
+ setEditing(true);
182
+ }
183
+ },
184
+ }, title, h("span", { class: "task-edit-icon" }, "\u270e"));
185
+ }
186
+ /** Editable description — click to edit with textarea. */
187
+ function EditableDescription({ description, onUpdate, }) {
188
+ const [editing, setEditing] = useState(false);
189
+ const [draft, setDraft] = useState(description ?? "");
190
+ // Sync draft when item changes externally
191
+ useEffect(() => { setDraft(description ?? ""); }, [description]);
192
+ const handleSave = useCallback(() => {
193
+ const trimmed = draft.trim();
194
+ if (trimmed !== (description ?? "") && onUpdate) {
195
+ onUpdate(trimmed);
196
+ }
197
+ setEditing(false);
198
+ }, [draft, description, onUpdate]);
199
+ if (!onUpdate) {
200
+ return description
201
+ ? h("div", { class: "task-description" }, description)
202
+ : null;
203
+ }
204
+ if (editing) {
205
+ return h("div", { class: "task-description-edit" }, h("textarea", {
206
+ class: "task-description-textarea",
207
+ value: draft,
208
+ rows: Math.max(3, (draft.match(/\n/g) ?? []).length + 2),
209
+ onInput: (e) => setDraft(e.target.value),
210
+ onKeyDown: (e) => {
211
+ if (e.key === "Enter" && (e.ctrlKey || e.metaKey)) {
212
+ e.preventDefault();
213
+ handleSave();
214
+ }
215
+ if (e.key === "Escape") {
216
+ setDraft(description ?? "");
217
+ setEditing(false);
218
+ }
219
+ },
220
+ ref: (el) => el?.focus(),
221
+ }), h("div", { class: "task-edit-actions" }, h("button", { class: "task-edit-save", onClick: handleSave }, "Save"), h("button", { class: "task-edit-cancel", onClick: () => { setDraft(description ?? ""); setEditing(false); } }, "Cancel"), h("span", { class: "task-edit-hint" }, "Ctrl+Enter to save")));
222
+ }
223
+ if (!description) {
224
+ return h("button", {
225
+ class: "task-add-description-btn",
226
+ onClick: () => { setDraft(""); setEditing(true); },
227
+ }, "+ Add description");
228
+ }
229
+ return h("div", {
230
+ class: "task-description editable",
231
+ onClick: () => { setDraft(description ?? ""); setEditing(true); },
232
+ title: "Click to edit description",
233
+ role: "button",
234
+ tabIndex: 0,
235
+ onKeyDown: (e) => {
236
+ if (e.key === "Enter" || e.key === " ") {
237
+ e.preventDefault();
238
+ setDraft(description ?? "");
239
+ setEditing(true);
240
+ }
241
+ },
242
+ }, description, h("span", { class: "task-edit-icon" }, "\u270e"));
243
+ }
244
+ /** Editable acceptance criteria — add, edit, remove individual items. */
245
+ function EditableAcceptanceCriteria({ criteria, onUpdate, }) {
246
+ const [editingIndex, setEditingIndex] = useState(null);
247
+ const [editDraft, setEditDraft] = useState("");
248
+ const [adding, setAdding] = useState(false);
249
+ const [addDraft, setAddDraft] = useState("");
250
+ const handleStartEdit = useCallback((i) => {
251
+ setEditingIndex(i);
252
+ setEditDraft(criteria[i]);
253
+ }, [criteria]);
254
+ const handleSaveEdit = useCallback(() => {
255
+ if (editingIndex === null || !onUpdate)
256
+ return;
257
+ const trimmed = editDraft.trim();
258
+ if (!trimmed) {
259
+ // Empty means remove
260
+ onUpdate(criteria.filter((_, i) => i !== editingIndex));
261
+ }
262
+ else if (trimmed !== criteria[editingIndex]) {
263
+ const updated = [...criteria];
264
+ updated[editingIndex] = trimmed;
265
+ onUpdate(updated);
266
+ }
267
+ setEditingIndex(null);
268
+ setEditDraft("");
269
+ }, [editingIndex, editDraft, criteria, onUpdate]);
270
+ const handleRemove = useCallback((i) => {
271
+ if (!onUpdate)
272
+ return;
273
+ onUpdate(criteria.filter((_, idx) => idx !== i));
274
+ }, [criteria, onUpdate]);
275
+ const handleAdd = useCallback(() => {
276
+ const trimmed = addDraft.trim();
277
+ if (trimmed && onUpdate) {
278
+ onUpdate([...criteria, trimmed]);
279
+ }
280
+ setAddDraft("");
281
+ setAdding(false);
282
+ }, [addDraft, criteria, onUpdate]);
283
+ if (criteria.length === 0 && !onUpdate)
284
+ return null;
285
+ return h("div", { class: "task-acceptance-criteria" }, h("div", { class: "task-section-label" }, "Acceptance Criteria"), criteria.length > 0
286
+ ? h("ul", { class: "task-criteria-list" }, criteria.map((criterion, i) => editingIndex === i
287
+ ? h("li", { key: i, class: "task-criterion editing" }, h("input", {
288
+ class: "task-criterion-input",
289
+ type: "text",
290
+ value: editDraft,
291
+ onInput: (e) => setEditDraft(e.target.value),
292
+ onKeyDown: (e) => {
293
+ if (e.key === "Enter") {
294
+ e.preventDefault();
295
+ handleSaveEdit();
296
+ }
297
+ if (e.key === "Escape") {
298
+ setEditingIndex(null);
299
+ setEditDraft("");
300
+ }
301
+ },
302
+ onBlur: handleSaveEdit,
303
+ ref: (el) => el?.focus(),
304
+ }))
305
+ : h("li", {
306
+ key: i,
307
+ class: `task-criterion${onUpdate ? " editable" : ""}`,
308
+ onClick: onUpdate ? () => handleStartEdit(i) : undefined,
309
+ role: onUpdate ? "button" : undefined,
310
+ tabIndex: onUpdate ? 0 : undefined,
311
+ onKeyDown: onUpdate
312
+ ? (e) => { if (e.key === "Enter" || e.key === " ") {
313
+ e.preventDefault();
314
+ handleStartEdit(i);
315
+ } }
316
+ : undefined,
317
+ }, h("span", { class: "task-criterion-text" }, criterion), onUpdate
318
+ ? h("button", {
319
+ class: "task-criterion-remove",
320
+ onClick: (e) => { e.stopPropagation(); handleRemove(i); },
321
+ "aria-label": `Remove criterion ${i + 1}`,
322
+ title: "Remove",
323
+ }, "\u00d7")
324
+ : null)))
325
+ : null,
326
+ // Add new criterion
327
+ onUpdate
328
+ ? adding
329
+ ? h("div", { class: "task-criterion-add-form" }, h("input", {
330
+ class: "task-criterion-add-input",
331
+ type: "text",
332
+ value: addDraft,
333
+ placeholder: "New acceptance criterion...",
334
+ onInput: (e) => setAddDraft(e.target.value),
335
+ onKeyDown: (e) => {
336
+ if (e.key === "Enter" && addDraft.trim()) {
337
+ e.preventDefault();
338
+ handleAdd();
339
+ }
340
+ if (e.key === "Escape") {
341
+ setAdding(false);
342
+ setAddDraft("");
343
+ }
344
+ },
345
+ ref: (el) => el?.focus(),
346
+ }), h("button", { class: "task-criterion-add-confirm", onClick: handleAdd, disabled: !addDraft.trim() }, "\u2713"), h("button", { class: "task-criterion-add-cancel", onClick: () => { setAdding(false); setAddDraft(""); } }, "\u00d7"))
347
+ : h("button", { class: "task-criterion-add-btn", onClick: () => setAdding(true) }, "+ criterion")
348
+ : null);
349
+ }
350
+ /** Children summary — shows direct children with their status. */
351
+ function ChildrenSummary({ children, onNavigate, }) {
352
+ if (children.length === 0)
353
+ return null;
354
+ const statusIcons = {
355
+ completed: "●",
356
+ in_progress: "◐",
357
+ pending: "○",
358
+ blocked: "⊘",
359
+ deferred: "◌",
360
+ };
361
+ return h("div", { class: "task-children-summary" }, h("div", { class: "task-section-label" }, `Children (${children.length})`), h("div", { class: "task-children-list" }, children.map((child) => h("div", {
362
+ key: child.id,
363
+ class: "task-child-item",
364
+ onClick: onNavigate ? () => onNavigate(child.id) : undefined,
365
+ role: onNavigate ? "button" : undefined,
366
+ tabIndex: onNavigate ? 0 : undefined,
367
+ }, h("span", { class: `prd-status-icon prd-status-${child.status}` }, statusIcons[child.status] || "○"), h("span", { class: `prd-level-badge prd-level-${child.level}` }, getLevelLabel(child.level)), h("span", { class: "task-child-title" }, child.title)))));
368
+ }
369
+ // ── Add Child Form ────────────────────────────────────────────────────
370
+ // Child level inference now provided by getChildLevel() from ./levels.ts
371
+ /** Inline form for adding a child item within the detail panel. */
372
+ function AddChildForm({ parentId, parentLevel, onSubmit, onCancel, }) {
373
+ const [title, setTitle] = useState("");
374
+ const [description, setDescription] = useState("");
375
+ const [priority, setPriority] = useState("");
376
+ const [submitting, setSubmitting] = useState(false);
377
+ const [error, setError] = useState(null);
378
+ const [showExtra, setShowExtra] = useState(false);
379
+ const childLevel = getChildLevel(parentLevel);
380
+ if (!childLevel)
381
+ return null;
382
+ const childLabel = getLevelLabel(childLevel);
383
+ const handleSubmit = useCallback(async () => {
384
+ const trimmedTitle = title.trim();
385
+ if (!trimmedTitle) {
386
+ setError("Title is required");
387
+ return;
388
+ }
389
+ setSubmitting(true);
390
+ setError(null);
391
+ try {
392
+ await onSubmit({
393
+ title: trimmedTitle,
394
+ parentId,
395
+ level: childLevel,
396
+ description: description.trim() || undefined,
397
+ priority: priority || undefined,
398
+ });
399
+ }
400
+ catch (err) {
401
+ setError(String(err));
402
+ setSubmitting(false);
403
+ }
404
+ }, [title, description, priority, parentId, childLevel, onSubmit]);
405
+ const handleKeyDown = useCallback((e) => {
406
+ if (e.key === "Escape") {
407
+ e.preventDefault();
408
+ onCancel();
409
+ }
410
+ if (e.key === "Enter" && (e.ctrlKey || e.metaKey)) {
411
+ e.preventDefault();
412
+ handleSubmit();
413
+ }
414
+ }, [onCancel, handleSubmit]);
415
+ const handleTitleKeyDown = useCallback((e) => {
416
+ if (e.key === "Enter" && !e.shiftKey && !showExtra) {
417
+ e.preventDefault();
418
+ handleSubmit();
419
+ }
420
+ handleKeyDown(e);
421
+ }, [handleSubmit, handleKeyDown, showExtra]);
422
+ return h("div", { class: "task-add-child-form", onKeyDown: handleKeyDown },
423
+ // Error display
424
+ error
425
+ ? h("div", { class: "task-add-child-error", role: "alert" }, error)
426
+ : null,
427
+ // Main input row
428
+ h("div", { class: "task-add-child-row" }, h("span", { class: `prd-level-badge prd-level-${childLevel}` }, childLabel), h("input", {
429
+ class: "task-add-child-title",
430
+ type: "text",
431
+ value: title,
432
+ placeholder: `New ${childLabel.toLowerCase()} title...`,
433
+ onInput: (e) => {
434
+ setTitle(e.target.value);
435
+ if (error)
436
+ setError(null);
437
+ },
438
+ onKeyDown: handleTitleKeyDown,
439
+ disabled: submitting,
440
+ "aria-label": `New ${childLabel.toLowerCase()} title`,
441
+ ref: (el) => el?.focus(),
442
+ })),
443
+ // Extra fields toggle + actions row
444
+ h("div", { class: "task-add-child-actions" }, h("button", {
445
+ type: "button",
446
+ class: `task-add-child-more${showExtra ? " active" : ""}`,
447
+ onClick: () => setShowExtra(!showExtra),
448
+ title: showExtra ? "Hide extra fields" : "Show description & priority",
449
+ disabled: submitting,
450
+ }, "\u22ef"), h("button", {
451
+ type: "button",
452
+ class: "task-add-child-submit",
453
+ onClick: handleSubmit,
454
+ disabled: submitting || !title.trim(),
455
+ title: `Add ${childLabel.toLowerCase()}`,
456
+ }, submitting ? "\u2026" : `Add ${childLabel}`), h("button", {
457
+ type: "button",
458
+ class: "task-add-child-cancel",
459
+ onClick: onCancel,
460
+ disabled: submitting,
461
+ title: "Cancel (Esc)",
462
+ }, "Cancel")),
463
+ // Extra fields
464
+ showExtra
465
+ ? h("div", { class: "task-add-child-extra" }, h("textarea", {
466
+ class: "task-add-child-description",
467
+ value: description,
468
+ placeholder: "Description (optional)",
469
+ onInput: (e) => setDescription(e.target.value),
470
+ onKeyDown: handleKeyDown,
471
+ rows: 2,
472
+ disabled: submitting,
473
+ }), h("div", { class: "task-add-child-priority-row" }, h("span", { class: "task-add-child-priority-label" }, "Priority:"), h("select", {
474
+ class: "task-add-child-priority",
475
+ value: priority,
476
+ onChange: (e) => setPriority(e.target.value),
477
+ disabled: submitting,
478
+ }, h("option", { value: "" }, "None"), PRIORITY_OPTIONS.map((opt) => h("option", { key: opt.value, value: opt.value }, opt.label)))))
479
+ : null);
480
+ }
481
+ // ── Requirements components ───────────────────────────────────────────
482
+ const CATEGORY_LABELS = {
483
+ technical: "Technical",
484
+ performance: "Performance",
485
+ security: "Security",
486
+ accessibility: "Accessibility",
487
+ compatibility: "Compatibility",
488
+ quality: "Quality",
489
+ };
490
+ const CATEGORY_ICONS = {
491
+ technical: "\u2699", // gear
492
+ performance: "\u26a1", // lightning
493
+ security: "\ud83d\udd12", // lock
494
+ accessibility: "\u267f", // wheelchair
495
+ compatibility: "\ud83d\udd17", // link
496
+ quality: "\u2605", // star
497
+ };
498
+ const VALIDATION_TYPE_LABELS = {
499
+ automated: "Automated",
500
+ manual: "Manual",
501
+ metric: "Metric",
502
+ };
503
+ const CATEGORY_OPTIONS = [
504
+ "technical", "performance", "security", "accessibility", "compatibility", "quality",
505
+ ];
506
+ const VALIDATION_TYPE_OPTIONS = [
507
+ "automated", "manual", "metric",
508
+ ];
509
+ /** Display a single requirement with category badge and validation type. */
510
+ function RequirementItem({ req }) {
511
+ return h("div", { class: `task-requirement-item req-category-${req.category}` }, h("div", { class: "task-requirement-header" }, h("span", { class: `req-category-badge req-cat-${req.category}` }, CATEGORY_ICONS[req.category] || "", " ", CATEGORY_LABELS[req.category] || req.category), h("span", { class: `req-validation-badge req-val-${req.validationType}` }, VALIDATION_TYPE_LABELS[req.validationType] || req.validationType), req.priority
512
+ ? h("span", { class: `prd-priority-badge prd-priority-${req.priority}` }, req.priority)
513
+ : null), h("div", { class: "task-requirement-title" }, req.title), req.description
514
+ ? h("div", { class: "task-requirement-description" }, req.description)
515
+ : null, req.acceptanceCriteria && req.acceptanceCriteria.length > 0
516
+ ? h("ul", { class: "task-requirement-criteria" }, req.acceptanceCriteria.map((c, i) => h("li", { key: i }, c)))
517
+ : null, req.validationCommand
518
+ ? h("div", { class: "task-requirement-command" }, h("span", { class: "label" }, "Validation: "), h("code", null, req.validationCommand), req.threshold !== undefined
519
+ ? h("span", { class: "req-threshold" }, ` (threshold: ${req.threshold})`)
520
+ : null)
521
+ : null);
522
+ }
523
+ /** Requirements list with optional add button. */
524
+ function RequirementsList({ requirements, onAdd, onRemove, }) {
525
+ const [showAdd, setShowAdd] = useState(false);
526
+ const [newTitle, setNewTitle] = useState("");
527
+ const [newCategory, setNewCategory] = useState("technical");
528
+ const [newValidationType, setNewValidationType] = useState("automated");
529
+ const [newDescription, setNewDescription] = useState("");
530
+ const [newCriteria, setNewCriteria] = useState("");
531
+ const [newCommand, setNewCommand] = useState("");
532
+ const [newPriority, setNewPriority] = useState("medium");
533
+ const [filterCategory, setFilterCategory] = useState("all");
534
+ const handleSubmit = useCallback(() => {
535
+ if (!newTitle.trim() || !onAdd)
536
+ return;
537
+ const criteria = newCriteria
538
+ .split("\n")
539
+ .map((c) => c.trim())
540
+ .filter(Boolean);
541
+ const req = {
542
+ title: newTitle.trim(),
543
+ category: newCategory,
544
+ validationType: newValidationType,
545
+ acceptanceCriteria: criteria,
546
+ };
547
+ if (newDescription.trim())
548
+ req.description = newDescription.trim();
549
+ if (newCommand.trim())
550
+ req.validationCommand = newCommand.trim();
551
+ if (newPriority)
552
+ req.priority = newPriority;
553
+ onAdd(req);
554
+ setNewTitle("");
555
+ setNewDescription("");
556
+ setNewCriteria("");
557
+ setNewCommand("");
558
+ setNewPriority("medium");
559
+ setShowAdd(false);
560
+ }, [newTitle, newCategory, newValidationType, newDescription, newCriteria, newCommand, newPriority, onAdd]);
561
+ const filteredReqs = filterCategory === "all"
562
+ ? requirements
563
+ : requirements.filter((r) => r.category === filterCategory);
564
+ if (requirements.length === 0 && !showAdd) {
565
+ return h("div", { class: "task-requirements-empty" }, onAdd
566
+ ? h("button", { class: "task-req-add-btn", onClick: () => setShowAdd(true) }, "+ Add requirement")
567
+ : h("span", { class: "task-requirements-none" }, "No requirements"));
568
+ }
569
+ return h("div", { class: "task-requirements-list" },
570
+ // Filter bar (only if more than 2 requirements)
571
+ requirements.length > 2
572
+ ? h("div", { class: "task-req-filter" }, h("select", {
573
+ class: "task-req-filter-select",
574
+ value: filterCategory,
575
+ onChange: (e) => setFilterCategory(e.target.value),
576
+ "aria-label": "Filter requirements by category",
577
+ }, h("option", { value: "all" }, `All (${requirements.length})`), ...CATEGORY_OPTIONS
578
+ .filter((cat) => requirements.some((r) => r.category === cat))
579
+ .map((cat) => h("option", { key: cat, value: cat }, `${CATEGORY_LABELS[cat]} (${requirements.filter((r) => r.category === cat).length})`))))
580
+ : null,
581
+ // Requirements list
582
+ filteredReqs.map((req) => h("div", { key: req.id, class: "task-requirement-wrapper" }, h(RequirementItem, { req }), onRemove
583
+ ? h("button", {
584
+ class: "task-req-remove-btn",
585
+ onClick: () => onRemove(req.id),
586
+ "aria-label": `Remove requirement ${req.title}`,
587
+ title: "Remove",
588
+ }, "\u00d7")
589
+ : null)),
590
+ // Add button / form
591
+ onAdd
592
+ ? showAdd
593
+ ? h("div", { class: "task-req-add-form" }, h("div", { class: "task-section-label" }, "New Requirement"), h("input", {
594
+ class: "task-req-input",
595
+ type: "text",
596
+ placeholder: "Requirement title",
597
+ value: newTitle,
598
+ onInput: (e) => setNewTitle(e.target.value),
599
+ ref: (el) => el?.focus(),
600
+ }), h("div", { class: "task-req-selectors" }, h("select", {
601
+ class: "task-req-select",
602
+ value: newCategory,
603
+ onChange: (e) => setNewCategory(e.target.value),
604
+ "aria-label": "Requirement category",
605
+ }, CATEGORY_OPTIONS.map((cat) => h("option", { key: cat, value: cat }, CATEGORY_LABELS[cat]))), h("select", {
606
+ class: "task-req-select",
607
+ value: newValidationType,
608
+ onChange: (e) => setNewValidationType(e.target.value),
609
+ "aria-label": "Validation type",
610
+ }, VALIDATION_TYPE_OPTIONS.map((vt) => h("option", { key: vt, value: vt }, VALIDATION_TYPE_LABELS[vt]))), h("select", {
611
+ class: "task-req-select",
612
+ value: newPriority,
613
+ onChange: (e) => setNewPriority(e.target.value),
614
+ "aria-label": "Requirement priority",
615
+ }, h("option", { value: "" }, "No priority"), PRIORITY_OPTIONS.map((opt) => h("option", { key: opt.value, value: opt.value }, opt.label)))), h("textarea", {
616
+ class: "task-req-textarea",
617
+ placeholder: "Description (optional)",
618
+ value: newDescription,
619
+ onInput: (e) => setNewDescription(e.target.value),
620
+ rows: 2,
621
+ }), h("textarea", {
622
+ class: "task-req-textarea",
623
+ placeholder: "Acceptance criteria (one per line)",
624
+ value: newCriteria,
625
+ onInput: (e) => setNewCriteria(e.target.value),
626
+ rows: 3,
627
+ }), h("input", {
628
+ class: "task-req-input",
629
+ type: "text",
630
+ placeholder: "Validation command (optional)",
631
+ value: newCommand,
632
+ onInput: (e) => setNewCommand(e.target.value),
633
+ }), h("div", { class: "task-req-form-actions" }, h("button", {
634
+ class: "task-req-submit-btn",
635
+ onClick: handleSubmit,
636
+ disabled: !newTitle.trim(),
637
+ }, "Add"), h("button", {
638
+ class: "task-req-cancel-btn",
639
+ onClick: () => setShowAdd(false),
640
+ }, "Cancel")))
641
+ : h("button", { class: "task-req-add-btn", onClick: () => setShowAdd(true) }, "+ Add requirement")
642
+ : null);
643
+ }
644
+ /** Statuses that allow triggering Hench execution. */
645
+ const TRIGGERABLE_STATUSES = new Set(["pending", "blocked"]);
646
+ /** Status labels for the execution progress indicator. */
647
+ const EXEC_STATUS_LABELS = {
648
+ starting: "Starting\u2026",
649
+ running: "Running\u2026",
650
+ completed: "Completed",
651
+ failed: "Failed",
652
+ };
653
+ /** Status icons for the execution progress indicator. */
654
+ const EXEC_STATUS_ICONS = {
655
+ starting: "\u25d0", // ◐
656
+ running: "\u25d0", // ◐
657
+ completed: "\u25cf", // ●
658
+ failed: "\u2716", // ✖
659
+ };
660
+ /** Execute task button with real-time progress tracking via WebSocket. */
661
+ function ExecuteTaskButton({ item, onExecute, onPrdChanged, }) {
662
+ const [executing, setExecuting] = useState(false);
663
+ const [resultMessage, setResultMessage] = useState(null);
664
+ const [execProgress, setExecProgress] = useState(null);
665
+ const wsRef = useRef(null);
666
+ const resultTimerRef = useRef(null);
667
+ const isTriggerable = TRIGGERABLE_STATUSES.has(item.status);
668
+ const isTaskLevel = isWorkItem(item.level);
669
+ // Check initial execution status on mount (handles page refresh during execution)
670
+ useEffect(() => {
671
+ if (!isTaskLevel)
672
+ return;
673
+ fetch(`/api/hench/execute/status/${item.id}`)
674
+ .then((res) => res.json())
675
+ .then((data) => {
676
+ if (data.execution && (data.execution.status === "starting" || data.execution.status === "running")) {
677
+ setExecProgress(data.execution);
678
+ setExecuting(true);
679
+ }
680
+ })
681
+ .catch(() => { });
682
+ }, [item.id, isTaskLevel]);
683
+ // Connect to WebSocket for live progress when executing
684
+ useEffect(() => {
685
+ if (!executing)
686
+ return;
687
+ const protocol = window.location.protocol === "https:" ? "wss:" : "ws:";
688
+ const wsUrl = `${protocol}//${window.location.host}`;
689
+ let ws;
690
+ try {
691
+ ws = new WebSocket(wsUrl);
692
+ wsRef.current = ws;
693
+ ws.onmessage = (event) => {
694
+ try {
695
+ const msg = JSON.parse(event.data);
696
+ if (msg.type === "hench:task-execution-progress" && msg.state) {
697
+ const state = msg.state;
698
+ if (state.taskId !== item.id)
699
+ return;
700
+ setExecProgress(state);
701
+ if (state.status === "completed") {
702
+ setExecuting(false);
703
+ setResultMessage("Execution completed");
704
+ // Notify parent to refresh PRD data
705
+ if (onPrdChanged)
706
+ onPrdChanged();
707
+ resultTimerRef.current = setTimeout(() => {
708
+ setResultMessage(null);
709
+ setExecProgress(null);
710
+ }, 5000);
711
+ }
712
+ else if (state.status === "failed") {
713
+ setExecuting(false);
714
+ setResultMessage(state.error || "Execution failed");
715
+ if (onPrdChanged)
716
+ onPrdChanged();
717
+ resultTimerRef.current = setTimeout(() => {
718
+ setResultMessage(null);
719
+ setExecProgress(null);
720
+ }, 8000);
721
+ }
722
+ }
723
+ // Also listen for PRD changes during execution
724
+ if (msg.type === "rex:prd-changed" && onPrdChanged) {
725
+ onPrdChanged();
726
+ }
727
+ }
728
+ catch {
729
+ // Ignore malformed messages
730
+ }
731
+ };
732
+ ws.onclose = () => {
733
+ wsRef.current = null;
734
+ };
735
+ }
736
+ catch {
737
+ // WebSocket not available — fall back to polling
738
+ }
739
+ // Polling fallback: check execution status every 3s
740
+ const pollInterval = setInterval(async () => {
741
+ try {
742
+ const res = await fetch(`/api/hench/execute/status/${item.id}`);
743
+ const data = await res.json();
744
+ if (data.execution) {
745
+ setExecProgress(data.execution);
746
+ if (data.execution.status === "completed" || data.execution.status === "failed") {
747
+ setExecuting(false);
748
+ const isSuccess = data.execution.status === "completed";
749
+ setResultMessage(isSuccess ? "Execution completed" : (data.execution.error || "Execution failed"));
750
+ if (onPrdChanged)
751
+ onPrdChanged();
752
+ resultTimerRef.current = setTimeout(() => {
753
+ setResultMessage(null);
754
+ setExecProgress(null);
755
+ }, isSuccess ? 5000 : 8000);
756
+ }
757
+ }
758
+ else if (executing) {
759
+ // No active execution found — may have completed between polls
760
+ setExecuting(false);
761
+ setExecProgress(null);
762
+ if (onPrdChanged)
763
+ onPrdChanged();
764
+ }
765
+ }
766
+ catch {
767
+ // ignore
768
+ }
769
+ }, 3000);
770
+ return () => {
771
+ clearInterval(pollInterval);
772
+ if (wsRef.current) {
773
+ try {
774
+ wsRef.current.close();
775
+ }
776
+ catch { /* ignore */ }
777
+ wsRef.current = null;
778
+ }
779
+ };
780
+ }, [executing, item.id, onPrdChanged]);
781
+ // Clean up timers on unmount
782
+ useEffect(() => {
783
+ return () => {
784
+ if (resultTimerRef.current)
785
+ clearTimeout(resultTimerRef.current);
786
+ };
787
+ }, []);
788
+ if (!onExecute || !isTaskLevel)
789
+ return null;
790
+ const handleClick = useCallback(async () => {
791
+ if (executing || !isTriggerable)
792
+ return;
793
+ setExecuting(true);
794
+ setResultMessage(null);
795
+ setExecProgress(null);
796
+ if (resultTimerRef.current) {
797
+ clearTimeout(resultTimerRef.current);
798
+ resultTimerRef.current = null;
799
+ }
800
+ try {
801
+ await onExecute(item.id);
802
+ // Initial state will be set via WebSocket / polling
803
+ }
804
+ catch (err) {
805
+ setExecuting(false);
806
+ setResultMessage(err instanceof Error ? err.message : "Failed to start");
807
+ resultTimerRef.current = setTimeout(() => setResultMessage(null), 5000);
808
+ }
809
+ }, [item.id, executing, isTriggerable, onExecute]);
810
+ const isActive = execProgress && (execProgress.status === "starting" || execProgress.status === "running");
811
+ const isDone = execProgress && (execProgress.status === "completed" || execProgress.status === "failed");
812
+ return h("div", { class: "task-execute-section" },
813
+ // Execute / progress button
814
+ h("button", {
815
+ class: `task-execute-btn${executing ? " executing" : ""}${!isTriggerable && !executing ? " disabled" : ""}`,
816
+ onClick: handleClick,
817
+ disabled: !isTriggerable || executing,
818
+ title: !isTriggerable && !executing
819
+ ? `Cannot execute: task is ${item.status}`
820
+ : executing
821
+ ? "Execution in progress\u2026"
822
+ : "Run Hench agent on this task",
823
+ "aria-label": "Execute task with Hench",
824
+ }, h("span", { class: "task-execute-icon" }, isActive ? EXEC_STATUS_ICONS[execProgress.status] : executing ? "\u25d0" : "\u25b6"), h("span", { class: "task-execute-label" }, isActive ? EXEC_STATUS_LABELS[execProgress.status] : executing ? "Starting\u2026" : "Execute")),
825
+ // Result message (shown briefly after completion)
826
+ resultMessage && !isActive
827
+ ? h("span", {
828
+ class: `task-execute-result${resultMessage.startsWith("Execution completed") ? " success" : " error"}`,
829
+ }, resultMessage)
830
+ : null,
831
+ // Live progress indicator (shown during execution)
832
+ isActive
833
+ ? h("div", { class: "task-exec-progress" },
834
+ // Animated progress bar
835
+ h("div", { class: "task-exec-progress-bar" }, h("div", { class: "task-exec-progress-fill" })),
836
+ // Status text
837
+ h("span", { class: `task-exec-status task-exec-status-${execProgress.status}` }, execProgress.status === "running" ? "Agent is working\u2026" : "Initializing\u2026"),
838
+ // Last output snippet
839
+ execProgress.lastOutput
840
+ ? h("div", { class: "task-exec-output" }, h("code", null, execProgress.lastOutput))
841
+ : null)
842
+ : null,
843
+ // Completion summary
844
+ isDone
845
+ ? h("div", { class: `task-exec-done task-exec-done-${execProgress.status}` }, h("span", { class: "task-exec-done-icon" }, execProgress.status === "completed" ? "\u2713" : "\u2717"), h("span", { class: "task-exec-done-text" }, execProgress.status === "completed"
846
+ ? "Task execution completed"
847
+ : `Failed: ${execProgress.error || "Unknown error"}`))
848
+ : null);
849
+ }
850
+ function runTotalTokens(r) {
851
+ return (r.tokenUsage.input ?? 0)
852
+ + (r.tokenUsage.output ?? 0)
853
+ + (r.tokenUsage.cacheCreationInput ?? 0)
854
+ + (r.tokenUsage.cacheReadInput ?? 0);
855
+ }
856
+ function fmtDuration(start, end) {
857
+ if (!end)
858
+ return "running\u2026";
859
+ const ms = new Date(end).getTime() - new Date(start).getTime();
860
+ if (ms < 0)
861
+ return "\u2014";
862
+ const secs = Math.floor(ms / 1000);
863
+ if (secs < 60)
864
+ return `${secs}s`;
865
+ const mins = Math.floor(secs / 60);
866
+ const remainSecs = secs % 60;
867
+ if (mins < 60)
868
+ return `${mins}m ${remainSecs}s`;
869
+ const hours = Math.floor(mins / 60);
870
+ const remainMins = mins % 60;
871
+ return `${hours}h ${remainMins}m`;
872
+ }
873
+ function fmtRelativeTime(iso) {
874
+ const diffMs = Date.now() - new Date(iso).getTime();
875
+ const diffHours = diffMs / (1000 * 60 * 60);
876
+ if (diffHours < 1) {
877
+ const mins = Math.floor(diffMs / (1000 * 60));
878
+ return mins <= 0 ? "just now" : `${mins}m ago`;
879
+ }
880
+ if (diffHours < 24)
881
+ return `${Math.floor(diffHours)}h ago`;
882
+ if (diffHours < 168) {
883
+ return new Date(iso).toLocaleDateString(undefined, { weekday: "short", hour: "numeric", minute: "2-digit" });
884
+ }
885
+ return new Date(iso).toLocaleDateString(undefined, { month: "short", day: "numeric", hour: "numeric", minute: "2-digit" });
886
+ }
887
+ const RUN_STATUS_ICONS = {
888
+ completed: { icon: "\u25cf", color: "var(--green)" },
889
+ failed: { icon: "\u2715", color: "var(--red)" },
890
+ error: { icon: "\u2715", color: "var(--red)" },
891
+ running: { icon: "\u25d0", color: "var(--accent)" },
892
+ in_progress: { icon: "\u25d0", color: "var(--accent)" },
893
+ };
894
+ function getRunStatusConfig(status) {
895
+ return RUN_STATUS_ICONS[status] ?? { icon: "\u25cb", color: "var(--text-dim)" };
896
+ }
897
+ /** Fetches and displays hench runs associated with a task. */
898
+ function HenchRunsList({ taskId, navigateTo, }) {
899
+ const [runs, setRuns] = useState([]);
900
+ const [loading, setLoading] = useState(true);
901
+ const [error, setError] = useState(false);
902
+ useEffect(() => {
903
+ let cancelled = false;
904
+ setLoading(true);
905
+ setError(false);
906
+ fetch(`/api/hench/runs?taskId=${encodeURIComponent(taskId)}`)
907
+ .then((res) => {
908
+ if (!res.ok)
909
+ throw new Error("fetch failed");
910
+ return res.json();
911
+ })
912
+ .then((json) => {
913
+ if (!cancelled) {
914
+ setRuns(json.runs ?? []);
915
+ setLoading(false);
916
+ }
917
+ })
918
+ .catch(() => {
919
+ if (!cancelled) {
920
+ setError(true);
921
+ setLoading(false);
922
+ }
923
+ });
924
+ return () => { cancelled = true; };
925
+ }, [taskId]);
926
+ // Don't render the section at all while loading on first mount, or if no runs
927
+ if (loading)
928
+ return null;
929
+ if (error)
930
+ return null;
931
+ if (runs.length === 0)
932
+ return null;
933
+ return h("div", { class: "task-section task-runs-section" }, h("div", { class: "task-section-label" }, `Hench Runs (${runs.length})`), h("div", { class: "task-runs-list" }, runs.map((run) => {
934
+ const st = getRunStatusConfig(run.status);
935
+ const tokens = runTotalTokens(run);
936
+ return h("div", {
937
+ key: run.id,
938
+ class: `task-run-entry${navigateTo ? " clickable" : ""}`,
939
+ onClick: navigateTo
940
+ ? () => navigateTo("hench-runs", { runId: run.id })
941
+ : undefined,
942
+ role: navigateTo ? "button" : undefined,
943
+ tabIndex: navigateTo ? 0 : undefined,
944
+ onKeyDown: navigateTo
945
+ ? (e) => {
946
+ if (e.key === "Enter" || e.key === " ") {
947
+ e.preventDefault();
948
+ navigateTo("hench-runs", { runId: run.id });
949
+ }
950
+ }
951
+ : undefined,
952
+ title: navigateTo ? "View run details" : undefined,
953
+ },
954
+ // Status icon + timestamp
955
+ h("div", { class: "task-run-header" }, h("span", { class: "task-run-status", style: `color: ${st.color}` }, st.icon), h("span", { class: "task-run-time" }, fmtRelativeTime(run.startedAt))),
956
+ // Metadata chips: duration, turns, tokens
957
+ h("div", { class: "task-run-meta" }, h("span", { class: "task-run-chip" }, fmtDuration(run.startedAt, run.finishedAt)), h("span", { class: "task-run-chip" }, `${run.turns} turns`), h("span", { class: "task-run-chip task-run-chip-tokens" }, formatTokenCount(tokens) + " tokens")));
958
+ })));
959
+ }
960
+ // ── Main component ───────────────────────────────────────────────────
961
+ export function TaskDetail({ item, taskUsage, weeklyBudget, showTokenBudget, allItems, onUpdate, onNavigateToItem, onExecuteTask, onPrdChanged, onAddChild, onRemove, navigateTo }) {
962
+ const [saving, setSaving] = useState(false);
963
+ const [pendingFailStatus, setPendingFailStatus] = useState(false);
964
+ const [failureReason, setFailureReason] = useState("");
965
+ const [editingFailureReason, setEditingFailureReason] = useState(false);
966
+ const [showAddChild, setShowAddChild] = useState(false);
967
+ const [confirmingRemove, setConfirmingRemove] = useState(false);
968
+ const [removing, setRemoving] = useState(false);
969
+ const usageSummary = taskUsage ?? {
970
+ totalTokens: 0,
971
+ runCount: 0,
972
+ utilization: resolveTaskUtilization(0, weeklyBudget),
973
+ };
974
+ const utilization = usageSummary.utilization ?? resolveTaskUtilization(usageSummary.totalTokens, weeklyBudget);
975
+ // Determine if this item can have children added
976
+ const canAddChild = onAddChild != null && getChildLevel(item.level) != null;
977
+ // Reset add-child form and remove confirmation when item changes
978
+ useEffect(() => {
979
+ setShowAddChild(false);
980
+ setConfirmingRemove(false);
981
+ setRemoving(false);
982
+ }, [item.id]);
983
+ const handleStatusChange = useCallback((status) => {
984
+ if (!onUpdate || status === item.status)
985
+ return;
986
+ setSaving(true);
987
+ onUpdate(item.id, { status });
988
+ // Reset saving indicator after brief delay (optimistic)
989
+ setTimeout(() => setSaving(false), 500);
990
+ }, [item.id, item.status, onUpdate]);
991
+ const handleRequestReason = useCallback((status) => {
992
+ if (!onUpdate || (status === item.status && !item.failureReason))
993
+ return;
994
+ setPendingFailStatus(true);
995
+ setFailureReason("");
996
+ }, [item.status, item.failureReason, onUpdate]);
997
+ const handleFailureSubmit = useCallback(() => {
998
+ if (!onUpdate || !failureReason.trim())
999
+ return;
1000
+ setSaving(true);
1001
+ onUpdate(item.id, { status: "failing", failureReason: failureReason.trim() });
1002
+ setPendingFailStatus(false);
1003
+ setEditingFailureReason(false);
1004
+ setFailureReason("");
1005
+ setTimeout(() => setSaving(false), 500);
1006
+ }, [item.id, failureReason, onUpdate]);
1007
+ const handleFailureCancel = useCallback(() => {
1008
+ setPendingFailStatus(false);
1009
+ setEditingFailureReason(false);
1010
+ setFailureReason("");
1011
+ }, []);
1012
+ const handleEditFailureReason = useCallback(() => {
1013
+ setEditingFailureReason(true);
1014
+ setFailureReason(item.failureReason || "");
1015
+ }, [item.failureReason]);
1016
+ const handlePriorityChange = useCallback((priority) => {
1017
+ if (!onUpdate || priority === item.priority)
1018
+ return;
1019
+ onUpdate(item.id, { priority });
1020
+ }, [item.id, item.priority, onUpdate]);
1021
+ const handleTagsUpdate = useCallback((tags) => {
1022
+ if (!onUpdate)
1023
+ return;
1024
+ onUpdate(item.id, { tags });
1025
+ }, [item.id, onUpdate]);
1026
+ return h(Fragment, null,
1027
+ // Level + ID header
1028
+ h("div", { class: "task-meta-header" }, h("span", { class: `prd-level-badge prd-level-${item.level}` }, getLevelLabel(item.level)), h("span", { class: "task-id" }, item.id.slice(0, 8)), h(CopyLinkButton, { path: `/prd/${item.id}`, compact: true })),
1029
+ // Editable title
1030
+ h("div", { class: "task-section" }, h(EditableTitle, {
1031
+ title: item.title,
1032
+ onUpdate: onUpdate
1033
+ ? (title) => onUpdate(item.id, { title })
1034
+ : undefined,
1035
+ })),
1036
+ // Status selector
1037
+ h("div", { class: "task-section" }, h("div", { class: "task-section-label" }, saving ? "Status (saving...)" : "Status"), h(StatusSelector, {
1038
+ current: item.status,
1039
+ onChange: handleStatusChange,
1040
+ onRequestReason: onUpdate ? handleRequestReason : undefined,
1041
+ }),
1042
+ // Execute button (only for tasks/subtasks)
1043
+ h(ExecuteTaskButton, { item, onExecute: onExecuteTask, onPrdChanged }),
1044
+ // Inline failure reason input form
1045
+ (pendingFailStatus || editingFailureReason)
1046
+ ? h("div", { class: "task-failure-input-form" }, h("input", {
1047
+ class: "task-failure-input",
1048
+ type: "text",
1049
+ placeholder: "Describe what's failing...",
1050
+ value: failureReason,
1051
+ onInput: (e) => setFailureReason(e.target.value),
1052
+ onKeyDown: (e) => {
1053
+ if (e.key === "Enter" && failureReason.trim()) {
1054
+ e.preventDefault();
1055
+ handleFailureSubmit();
1056
+ }
1057
+ if (e.key === "Escape") {
1058
+ handleFailureCancel();
1059
+ }
1060
+ },
1061
+ ref: (el) => el?.focus(),
1062
+ }), h("div", { class: "task-failure-form-actions" }, h("button", {
1063
+ class: "task-failure-submit-btn",
1064
+ onClick: handleFailureSubmit,
1065
+ disabled: !failureReason.trim(),
1066
+ }, "Set Failing"), h("button", {
1067
+ class: "task-failure-cancel-btn",
1068
+ onClick: handleFailureCancel,
1069
+ }, "Cancel")))
1070
+ : null),
1071
+ // Description (editable)
1072
+ h("div", { class: "task-section" }, h("div", { class: "task-section-label" }, "Description"), h(EditableDescription, {
1073
+ description: item.description,
1074
+ onUpdate: onUpdate
1075
+ ? (description) => onUpdate(item.id, { description: description || undefined })
1076
+ : undefined,
1077
+ })),
1078
+ // Acceptance criteria (editable)
1079
+ (item.acceptanceCriteria && item.acceptanceCriteria.length > 0) || onUpdate
1080
+ ? h("div", { class: "task-section" }, h(EditableAcceptanceCriteria, {
1081
+ criteria: item.acceptanceCriteria ?? [],
1082
+ onUpdate: onUpdate
1083
+ ? (criteria) => onUpdate(item.id, { acceptanceCriteria: criteria })
1084
+ : undefined,
1085
+ }))
1086
+ : null,
1087
+ // Aggregated token usage across runs
1088
+ item.level === "task" || item.level === "subtask"
1089
+ ? h("div", { class: "task-section task-usage-section" }, h("div", { class: "task-section-label" }, "Usage"), h("div", { class: "task-usage-row" }, h("span", { class: "label" }, "Total Tokens"), h("span", { class: "task-usage-value" }, `${formatTokenCount(usageSummary.totalTokens)} tokens`)),
1090
+ // Budget-specific fields: weekly utilization percentage and budget reason
1091
+ showTokenBudget
1092
+ ? h("div", { class: "task-usage-row" }, h("span", { class: "label" }, "Weekly Utilization"), h("span", { class: "task-usage-value", "data-utilization-reason": utilization.reason }, utilization.label))
1093
+ : null, h("div", { class: "task-usage-hint", "data-utilization-reason": showTokenBudget ? utilization.reason : undefined }, `${usageSummary.runCount} associated run${usageSummary.runCount === 1 ? "" : "s"}${showTokenBudget ? ` | reason: ${utilization.reason}` : ""}`))
1094
+ : null,
1095
+ // Associated hench runs (tasks/subtasks only)
1096
+ isWorkItem(item.level)
1097
+ ? h(HenchRunsList, { taskId: item.id, navigateTo })
1098
+ : null,
1099
+ // Failure reason
1100
+ item.status === "failing" && item.failureReason && !editingFailureReason
1101
+ ? h("div", { class: "task-section task-failure-reason" }, h("div", { class: "task-failure-reason-header" }, h("div", { class: "task-section-label prd-status-failing" }, "Failure Reason"), onUpdate
1102
+ ? h("button", {
1103
+ class: "task-failure-edit-btn",
1104
+ onClick: handleEditFailureReason,
1105
+ title: "Edit reason",
1106
+ }, "Edit")
1107
+ : null), h("div", { class: "task-failure-text" }, item.failureReason))
1108
+ : null,
1109
+ // Priority selector
1110
+ h("div", { class: "task-section" }, h("div", { class: "task-section-label" }, "Priority"), h(PrioritySelector, { current: item.priority, onChange: handlePriorityChange })),
1111
+ // Tags editor
1112
+ h("div", { class: "task-section" }, h("div", { class: "task-section-label" }, "Tags"), h(TagEditor, { tags: item.tags ?? [], onUpdate: handleTagsUpdate })),
1113
+ // Requirements
1114
+ h("div", { class: "task-section" }, h("div", { class: "task-section-label" }, `Requirements (${(item.requirements ?? []).length})`), h(RequirementsList, {
1115
+ requirements: item.requirements ?? [],
1116
+ onAdd: onUpdate
1117
+ ? (req) => {
1118
+ // Optimistic update: add with a temporary id
1119
+ const tempId = "req-" + Date.now().toString(36);
1120
+ const newReq = { ...req, id: tempId };
1121
+ const existing = item.requirements ?? [];
1122
+ onUpdate(item.id, { requirements: [...existing, newReq] });
1123
+ }
1124
+ : undefined,
1125
+ onRemove: onUpdate
1126
+ ? (reqId) => {
1127
+ const existing = item.requirements ?? [];
1128
+ onUpdate(item.id, { requirements: existing.filter((r) => r.id !== reqId) });
1129
+ }
1130
+ : undefined,
1131
+ })),
1132
+ // Dependencies
1133
+ item.blockedBy && item.blockedBy.length > 0
1134
+ ? h("div", { class: "task-section" }, h(DependencyList, {
1135
+ blockedBy: item.blockedBy,
1136
+ allItems,
1137
+ onNavigate: onNavigateToItem,
1138
+ }))
1139
+ : null,
1140
+ // Children + Add child
1141
+ canAddChild || (item.children && item.children.length > 0)
1142
+ ? h("div", { class: "task-section" }, item.children && item.children.length > 0
1143
+ ? h(ChildrenSummary, {
1144
+ children: item.children,
1145
+ onNavigate: onNavigateToItem,
1146
+ })
1147
+ : null,
1148
+ // Add child button or form
1149
+ canAddChild
1150
+ ? showAddChild
1151
+ ? h(AddChildForm, {
1152
+ parentId: item.id,
1153
+ parentLevel: item.level,
1154
+ onSubmit: async (data) => {
1155
+ await onAddChild(data);
1156
+ setShowAddChild(false);
1157
+ },
1158
+ onCancel: () => setShowAddChild(false),
1159
+ })
1160
+ : h("button", {
1161
+ class: "task-add-child-btn",
1162
+ onClick: () => setShowAddChild(true),
1163
+ title: `Add ${getLevelLabel(getChildLevel(item.level) ?? "child")} to this ${getLevelLabel(item.level)}`,
1164
+ }, `+ Add ${getLevelLabel(getChildLevel(item.level) ?? "child")}`)
1165
+ : null)
1166
+ : null,
1167
+ // Timestamps
1168
+ (item.startedAt || item.completedAt)
1169
+ ? h("div", { class: "task-section task-timestamps" }, h("div", { class: "task-section-label" }, "Timeline"), item.startedAt
1170
+ ? h("div", { class: "detail-row" }, h("span", { class: "label" }, "Started"), h("span", null, formatTimestamp(item.startedAt)))
1171
+ : null, item.completedAt
1172
+ ? h("div", { class: "detail-row" }, h("span", { class: "label" }, "Completed"), h("span", null, formatTimestamp(item.completedAt)))
1173
+ : null)
1174
+ : null,
1175
+ // Delete / Remove item
1176
+ onRemove
1177
+ ? h("div", { class: "task-section task-danger-zone" }, h("div", { class: "task-section-label task-danger-label" }, "Danger Zone"), confirmingRemove
1178
+ ? h("div", { class: "task-remove-confirm" }, h("p", { class: "task-remove-confirm-msg" }, `Are you sure you want to delete this ${getLevelLabel(item.level)}`, (item.children && item.children.length > 0)
1179
+ ? ` and all its ${item.children.length} descendant${item.children.length !== 1 ? "s" : ""}?`
1180
+ : "?"), h("div", { class: "task-remove-confirm-actions" }, h("button", {
1181
+ class: "task-remove-confirm-btn",
1182
+ onClick: async () => {
1183
+ setRemoving(true);
1184
+ try {
1185
+ await onRemove(item.id);
1186
+ }
1187
+ catch {
1188
+ setRemoving(false);
1189
+ setConfirmingRemove(false);
1190
+ }
1191
+ },
1192
+ disabled: removing,
1193
+ }, removing ? "Deleting..." : "Yes, Delete"), h("button", {
1194
+ class: "task-remove-cancel-btn",
1195
+ onClick: () => setConfirmingRemove(false),
1196
+ disabled: removing,
1197
+ }, "Cancel")))
1198
+ : h("button", {
1199
+ class: "task-remove-btn",
1200
+ onClick: () => setConfirmingRemove(true),
1201
+ title: `Delete this ${getLevelLabel(item.level)} and all its descendants`,
1202
+ }, h("span", { class: "task-remove-btn-icon" }, "\u2717"), `Delete ${getLevelLabel(item.level)}`))
1203
+ : null);
1204
+ }
1205
+ //# sourceMappingURL=task-detail.js.map