@taskforcehq/taskforce 0.3.304 → 0.3.306

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 (383) hide show
  1. package/README.md +140 -247
  2. package/dist/Taskforce.module.css +295 -89
  3. package/dist/TaskforceCore.js +378 -111
  4. package/dist/TaskforceCore.test.js +1221 -107
  5. package/dist/assets/fonts/CourierPrime-Bold.woff2 +0 -0
  6. package/dist/assets/fonts/CourierPrime-BoldItalic.woff2 +0 -0
  7. package/dist/assets/fonts/CourierPrime-Italic.woff2 +0 -0
  8. package/dist/assets/fonts/CourierPrime-Regular.woff2 +0 -0
  9. package/dist/assets/fonts/Noir_medium.woff2 +0 -0
  10. package/dist/assets/fonts/Noir_regular.woff2 +0 -0
  11. package/dist/assets/fonts/RussoOne-Regular.woff2 +0 -0
  12. package/dist/assets/fonts/SpecialElite-Regular.woff2 +0 -0
  13. package/dist/compat/workspaceSyncCompat.js +52 -2
  14. package/dist/compat/workspaceSyncCompat.test.js +38 -1
  15. package/dist/components/context/ContextAttachmentManager.d.ts +14 -0
  16. package/dist/components/context/ContextAttachmentManager.js +549 -0
  17. package/dist/components/features/AgentsModule.d.ts +10 -1
  18. package/dist/components/features/AgentsModule.js +260 -15
  19. package/dist/components/features/AgentsModule.test.js +255 -5
  20. package/dist/components/features/DocumentIndex.d.ts +1 -1
  21. package/dist/components/features/DocumentIndex.js +1 -1
  22. package/dist/components/features/DocumentViewer.d.ts +6 -2
  23. package/dist/components/features/DocumentViewer.js +90 -10
  24. package/dist/components/features/DocumentWorkspace.d.ts +5 -22
  25. package/dist/components/features/DocumentWorkspace.js +12 -155
  26. package/dist/components/features/DocumentWorkspace.test.js +226 -6
  27. package/dist/components/features/TaskSettings.js +29 -3
  28. package/dist/components/features/TaskSettings.test.js +42 -1
  29. package/dist/components/features/documentWorkspaceModel.d.ts +24 -0
  30. package/dist/components/features/documentWorkspaceModel.js +57 -0
  31. package/dist/components/features/useDocumentWorkspaceController.d.ts +34 -0
  32. package/dist/components/features/useDocumentWorkspaceController.js +113 -0
  33. package/dist/components/task/TaskCard.js +19 -28
  34. package/dist/components/task/TaskCard.test.js +38 -3
  35. package/dist/components/task/TaskContextUpload.js +4 -526
  36. package/dist/components/task/TaskForm.d.ts +8 -8
  37. package/dist/components/task/TaskForm.js +362 -230
  38. package/dist/components/task/TaskForm.test.js +558 -381
  39. package/dist/components/task/TaskKanban.d.ts +2 -1
  40. package/dist/components/task/TaskKanban.js +96 -14
  41. package/dist/components/task/TaskKanban.test.js +36 -0
  42. package/dist/components/ui/EditableAvatarButton.d.ts +18 -0
  43. package/dist/components/ui/EditableAvatarButton.js +18 -0
  44. package/dist/components/views/PlanComparisonPage.d.ts +9 -1
  45. package/dist/components/views/PlanComparisonPage.js +70 -17
  46. package/dist/components/views/PlanComparisonPage.test.js +289 -41
  47. package/dist/components/views/PlansPage.js +239 -106
  48. package/dist/components/views/PlansPage.test.js +593 -42
  49. package/dist/components/views/StandaloneLayout.js +310 -554
  50. package/dist/components/views/panels/FilterToolbar.test.js +6 -4
  51. package/dist/components/views/panels/PlanningDrawer.d.ts +8 -1
  52. package/dist/components/views/panels/PlanningDrawer.js +18 -11
  53. package/dist/components/views/panels/PlanningDrawer.test.d.ts +1 -0
  54. package/dist/components/views/panels/PlanningDrawer.test.js +141 -0
  55. package/dist/components/views/planningScope.d.ts +20 -0
  56. package/dist/components/views/planningScope.js +25 -0
  57. package/dist/components/views/planningScope.test.d.ts +1 -0
  58. package/dist/components/views/planningScope.test.js +60 -0
  59. package/dist/components/views/standalone/modals/AccountHubModal.d.ts +27 -14
  60. package/dist/components/views/standalone/modals/AccountHubModal.js +87 -15
  61. package/dist/components/views/standalone/modals/AccountHubModal.test.js +42 -2
  62. package/dist/components/views/standalone/modals/AvatarImageManagerModal.d.ts +20 -0
  63. package/dist/components/views/standalone/modals/AvatarImageManagerModal.js +127 -0
  64. package/dist/components/views/standalone/modals/EditProfileModal.d.ts +2 -9
  65. package/dist/components/views/standalone/modals/EditProfileModal.js +3 -2
  66. package/dist/components/views/standalone/modals/SyncEnableWarningModal.d.ts +9 -0
  67. package/dist/components/views/standalone/modals/SyncEnableWarningModal.js +6 -0
  68. package/dist/components/views/standalone/modals/SyncEnableWarningModal.test.d.ts +1 -0
  69. package/dist/components/views/standalone/modals/SyncEnableWarningModal.test.js +24 -0
  70. package/dist/components/views/standalone/modals/SyncStatusModal.d.ts +2 -1
  71. package/dist/components/views/standalone/modals/SyncStatusModal.js +2 -2
  72. package/dist/config/envSchema.js +1 -1
  73. package/dist/core/AiProfileService.d.ts +45 -3
  74. package/dist/core/AiProfileService.js +774 -63
  75. package/dist/core/AiProfileService.test.js +22 -2
  76. package/dist/core/AiProfiles.test.js +761 -2
  77. package/dist/core/AttachmentLinkService.js +57 -15
  78. package/dist/core/AuthIdentityService.d.ts +107 -0
  79. package/dist/core/AuthIdentityService.js +284 -0
  80. package/dist/core/AuthTokenService.d.ts +4 -0
  81. package/dist/core/AuthTokenService.js +34 -6
  82. package/dist/core/AuthTokenService.test.d.ts +1 -0
  83. package/dist/core/AuthTokenService.test.js +78 -0
  84. package/dist/core/EntitlementsPolicy.test.js +77 -15
  85. package/dist/core/GlobalSettingsService.js +115 -6
  86. package/dist/core/PlanEntitlementService.d.ts +30 -3
  87. package/dist/core/PlanEntitlementService.js +454 -93
  88. package/dist/core/PlanFeatureCatalog.test.d.ts +1 -0
  89. package/dist/core/PlanFeatureCatalog.test.js +84 -0
  90. package/dist/core/PlanVersionPolicy.test.js +54 -28
  91. package/dist/core/PlanningEntities.test.js +52 -0
  92. package/dist/core/SignupMonetizationSettings.test.js +241 -44
  93. package/dist/core/SyncReconciliationService.js +2 -65
  94. package/dist/core/SystemAdmin.test.js +244 -88
  95. package/dist/core/TaskAttachmentsCanonical.test.js +51 -1
  96. package/dist/core/TaskTaxonomyValidation.test.js +53 -0
  97. package/dist/core/Taskforce.d.ts +228 -14
  98. package/dist/core/Taskforce.js +814 -201
  99. package/dist/core/Taskforce.listTasksSlim.test.d.ts +1 -0
  100. package/dist/core/Taskforce.listTasksSlim.test.js +91 -0
  101. package/dist/core/UserProfileAvatarDrafts.test.js +30 -3
  102. package/dist/core/WorkspaceLifecycleService.js +0 -5
  103. package/dist/core/WorkspacePermissions.test.js +25 -1
  104. package/dist/core/WorkspacePlanMode.test.js +128 -27
  105. package/dist/core/shared.d.ts +3 -1
  106. package/dist/core/shared.js +16 -1
  107. package/dist/core/types.d.ts +68 -2
  108. package/dist/hooks/auth/useTaskforceAuthBootstrap.js +26 -1
  109. package/dist/hooks/sync/auth.js +3 -1
  110. package/dist/hooks/sync/bootstrap.js +2 -0
  111. package/dist/hooks/sync/controlPlane.d.ts +37 -0
  112. package/dist/hooks/sync/controlPlane.js +78 -0
  113. package/dist/hooks/sync/controlPlane.test.d.ts +1 -0
  114. package/dist/hooks/sync/controlPlane.test.js +121 -0
  115. package/dist/hooks/sync/lifecyclePolicy.d.ts +45 -0
  116. package/dist/hooks/sync/lifecyclePolicy.js +93 -0
  117. package/dist/hooks/sync/lifecyclePolicy.test.d.ts +1 -0
  118. package/dist/hooks/sync/lifecyclePolicy.test.js +124 -0
  119. package/dist/hooks/sync/orchestratorShared.d.ts +10 -1
  120. package/dist/hooks/sync/orchestratorShared.js +68 -47
  121. package/dist/hooks/sync/orchestratorShared.test.js +50 -1
  122. package/dist/hooks/sync/pullLifecycle.d.ts +31 -0
  123. package/dist/hooks/sync/pullLifecycle.js +24 -0
  124. package/dist/hooks/sync/pullLifecycle.test.d.ts +1 -0
  125. package/dist/hooks/sync/pullLifecycle.test.js +80 -0
  126. package/dist/hooks/sync/recovery.d.ts +16 -2
  127. package/dist/hooks/sync/recovery.js +171 -53
  128. package/dist/hooks/sync/recovery.test.d.ts +1 -0
  129. package/dist/hooks/sync/recovery.test.js +292 -0
  130. package/dist/hooks/sync/transfers.d.ts +16 -3
  131. package/dist/hooks/sync/transfers.js +186 -65
  132. package/dist/hooks/sync/transfers.test.d.ts +1 -0
  133. package/dist/hooks/sync/transfers.test.js +396 -0
  134. package/dist/hooks/sync/useRecentSyncEvents.d.ts +21 -0
  135. package/dist/hooks/sync/useRecentSyncEvents.js +92 -0
  136. package/dist/hooks/sync/useRecentSyncEvents.test.d.ts +1 -0
  137. package/dist/hooks/sync/useRecentSyncEvents.test.js +124 -0
  138. package/dist/hooks/sync/useSyncStatusActions.d.ts +30 -0
  139. package/dist/hooks/sync/useSyncStatusActions.js +88 -0
  140. package/dist/hooks/sync/useSyncStatusActions.test.d.ts +1 -0
  141. package/dist/hooks/sync/useSyncStatusActions.test.js +133 -0
  142. package/dist/hooks/sync/useSyncStatusControls.d.ts +25 -0
  143. package/dist/hooks/sync/useSyncStatusControls.js +60 -0
  144. package/dist/hooks/sync/useSyncStatusControls.test.d.ts +1 -0
  145. package/dist/hooks/sync/useSyncStatusControls.test.js +88 -0
  146. package/dist/hooks/useSyncOrchestrator.aiProfiles.test.js +12 -0
  147. package/dist/hooks/useSyncOrchestrator.d.ts +16 -6
  148. package/dist/hooks/useSyncOrchestrator.js +424 -175
  149. package/dist/hooks/useSyncOrchestrator.retry-closure.test.js +487 -10
  150. package/dist/hooks/useTaskData.js +8 -3
  151. package/dist/hooks/useTaskData.test.js +45 -0
  152. package/dist/hooks/useTaskMutations.d.ts +2 -1
  153. package/dist/hooks/useTaskMutations.js +12 -1
  154. package/dist/hooks/useTaskMutations.test.js +30 -1
  155. package/dist/hooks/useTaskforce.d.ts +65 -4
  156. package/dist/hooks/useTaskforce.js +564 -243
  157. package/dist/hooks/useTaskforce.runtime-routing.test.d.ts +1 -0
  158. package/dist/hooks/useTaskforce.runtime-routing.test.js +152 -0
  159. package/dist/hooks/useTaskforce.sync-behavior.test.js +2454 -208
  160. package/dist/hooks/useWorkspaceSyncController.d.ts +4 -1
  161. package/dist/hooks/useWorkspaceSyncController.js +17 -2
  162. package/dist/hooks/useWorkspaceSyncController.test.js +1 -0
  163. package/dist/hooks/workspace/useTaskforceWorkspaceBootstrap.d.ts +16 -0
  164. package/dist/hooks/workspace/useTaskforceWorkspaceBootstrap.js +316 -35
  165. package/dist/localization/locales/en-US.d.ts +0 -1
  166. package/dist/localization/locales/en-US.js +0 -1
  167. package/dist/localization/locales/es-419.js +0 -1
  168. package/dist/localization/locales/pt-BR.js +0 -1
  169. package/dist/mcp/adminWorkspaceRegistrar.d.ts +2 -0
  170. package/dist/mcp/adminWorkspaceRegistrar.js +154 -0
  171. package/dist/mcp/collaborationRegistrar.d.ts +2 -0
  172. package/dist/mcp/collaborationRegistrar.js +71 -0
  173. package/dist/mcp/documentAssetRegistrar.d.ts +2 -0
  174. package/dist/mcp/documentAssetRegistrar.js +346 -0
  175. package/dist/mcp/runtime.d.ts +2 -0
  176. package/dist/mcp/runtime.js +2433 -3534
  177. package/dist/mcp/runtime.test.js +440 -2
  178. package/dist/mcp/taskPlanningRegistrar.d.ts +2 -0
  179. package/dist/mcp/taskPlanningRegistrar.js +418 -0
  180. package/dist/mcp/toolCatalog.d.ts +35 -0
  181. package/dist/mcp/toolCatalog.js +3 -0
  182. package/dist/migrations/taskSchemaMigrations.d.ts +1 -1
  183. package/dist/migrations/taskSchemaMigrations.js +171 -61
  184. package/dist/migrations/taskSchemaMigrations.test.js +15 -0
  185. package/dist/resources/templates/workflow-sources/workflowDocs.mjs +6 -6
  186. package/dist/resources/templates/workflows/collaborate.yaml +1 -1
  187. package/dist/resources/templates/workflows/evaluate.yaml +2 -2
  188. package/dist/resources/templates/workflows/plan.yaml +1 -1
  189. package/dist/resources/templates/workflows/review.yaml +2 -2
  190. package/dist/server/annotatedAttachmentsRoutes.test.js +3 -3
  191. package/dist/server/auth/providers/apple.d.ts +33 -0
  192. package/dist/server/auth/providers/apple.js +82 -0
  193. package/dist/server/auth/providers/appleClientSecret.d.ts +37 -0
  194. package/dist/server/auth/providers/appleClientSecret.js +65 -0
  195. package/dist/server/auth/providers/github.d.ts +26 -0
  196. package/dist/server/auth/providers/github.js +86 -0
  197. package/dist/server/auth/providers/google.d.ts +21 -0
  198. package/dist/server/auth/providers/google.js +56 -0
  199. package/dist/server/auth/providers/types.d.ts +21 -0
  200. package/dist/server/auth/providers/types.js +1 -0
  201. package/dist/server/auth.d.ts +2 -0
  202. package/dist/server/auth.js +6 -3
  203. package/dist/server/documentReviewRoutes.test.js +36 -3
  204. package/dist/server/index.cookieProxy.test.js +1 -0
  205. package/dist/server/index.d.ts +12 -0
  206. package/dist/server/index.js +120 -9
  207. package/dist/server/index.rateLimit.test.js +3 -0
  208. package/dist/server/index.test.js +106 -3
  209. package/dist/server/routes/admin.d.ts +4 -5
  210. package/dist/server/routes/admin.js +410 -80
  211. package/dist/server/routes/annotatedAttachments.d.ts +1 -1
  212. package/dist/server/routes/annotatedAttachments.js +1 -1
  213. package/dist/server/routes/auth.d.ts +16 -6
  214. package/dist/server/routes/auth.js +1015 -118
  215. package/dist/server/routes/authSupport.d.ts +30 -0
  216. package/dist/server/routes/authSupport.js +81 -0
  217. package/dist/server/routes/billing.d.ts +1 -1
  218. package/dist/server/routes/billing.js +276 -34
  219. package/dist/server/routes/billing.test.js +760 -52
  220. package/dist/server/routes/documentReviews.d.ts +1 -1
  221. package/dist/server/routes/documentReviews.js +1 -1
  222. package/dist/server/routes/documents.d.ts +5 -2
  223. package/dist/server/routes/documents.js +242 -74
  224. package/dist/server/routes/primitives.d.ts +11 -0
  225. package/dist/server/routes/primitives.js +73 -0
  226. package/dist/server/routes/resources.d.ts +1 -1
  227. package/dist/server/routes/resources.js +1 -1
  228. package/dist/server/routes/shared.d.ts +29 -3
  229. package/dist/server/routes/shared.js +69 -4
  230. package/dist/server/routes/sync.d.ts +1 -1
  231. package/dist/server/routes/sync.integration.test.js +437 -39
  232. package/dist/server/routes/sync.js +62 -606
  233. package/dist/server/routes/syncAuxRoutes.d.ts +15 -0
  234. package/dist/server/routes/syncAuxRoutes.js +91 -0
  235. package/dist/server/routes/syncAuxRoutes.test.d.ts +1 -0
  236. package/dist/server/routes/syncAuxRoutes.test.js +158 -0
  237. package/dist/server/routes/syncPullApplyRoutes.d.ts +36 -0
  238. package/dist/server/routes/syncPullApplyRoutes.js +204 -0
  239. package/dist/server/routes/syncPushRoutes.d.ts +24 -0
  240. package/dist/server/routes/syncPushRoutes.js +212 -0
  241. package/dist/server/routes/syncRouteGuards.d.ts +36 -0
  242. package/dist/server/routes/syncRouteGuards.js +67 -0
  243. package/dist/server/routes/syncRouteGuards.test.d.ts +1 -0
  244. package/dist/server/routes/syncRouteGuards.test.js +94 -0
  245. package/dist/server/routes/syncRouteTypes.d.ts +13 -0
  246. package/dist/server/routes/syncRouteTypes.js +1 -0
  247. package/dist/server/routes/syncSnapshotRoutes.d.ts +66 -0
  248. package/dist/server/routes/syncSnapshotRoutes.js +180 -0
  249. package/dist/server/routes/tasks.d.ts +1 -1
  250. package/dist/server/routes/tasks.js +44 -2
  251. package/dist/server/routes/workspaces.d.ts +1 -1
  252. package/dist/server/routes/workspaces.js +37 -6
  253. package/dist/server/routes.d.ts +2 -9
  254. package/dist/server/routes.js +136 -165
  255. package/dist/server/routes.test.js +2457 -162
  256. package/dist/services/workflowExportSnapshots.test.js +2 -0
  257. package/dist/shared/aiProfileSeatScope.d.ts +5 -0
  258. package/dist/shared/aiProfileSeatScope.js +21 -0
  259. package/dist/shared/passwordPolicy.d.ts +13 -0
  260. package/dist/shared/passwordPolicy.js +84 -0
  261. package/dist/shared/passwordPolicy.test.d.ts +1 -0
  262. package/dist/shared/passwordPolicy.test.js +34 -0
  263. package/dist/storage/workspaceAssetStore.d.ts +15 -3
  264. package/dist/storage/workspaceAssetStore.js +132 -44
  265. package/dist/storage/workspaceAssetStore.test.js +93 -6
  266. package/dist/styles/fonts.css +70 -0
  267. package/dist/sync/collaborationSyncPayload.d.ts +6 -1
  268. package/dist/sync/collaborationSyncPayload.js +17 -11
  269. package/dist/sync/collaborationSyncPayload.test.js +13 -1
  270. package/dist/sync/contentSyncState.d.ts +4 -8
  271. package/dist/sync/contentSyncState.js +33 -77
  272. package/dist/sync/syncApplyHandlers.d.ts +7 -0
  273. package/dist/sync/syncApplyHandlers.js +28 -13
  274. package/dist/sync/syncApplyHandlers.test.js +72 -3
  275. package/dist/sync/syncFieldSemantics.d.ts +12 -0
  276. package/dist/sync/syncFieldSemantics.js +55 -0
  277. package/dist/sync/syncResourceAdapters.d.ts +44 -0
  278. package/dist/sync/syncResourceAdapters.js +77 -0
  279. package/dist/sync/syncService.d.ts +6 -0
  280. package/dist/sync/syncService.js +60 -0
  281. package/dist/sync/syncService.test.js +71 -3
  282. package/dist/sync/taskSyncChangeKey.d.ts +2 -0
  283. package/dist/sync/taskSyncChangeKey.js +143 -0
  284. package/dist/sync/taskSyncPayload.js +3 -8
  285. package/dist/sync/workspacePullFeed.js +4 -3
  286. package/dist/sync/workspacePullFeed.test.js +43 -0
  287. package/dist/sync/workspaceRepair.js +5 -2
  288. package/dist/sync/workspaceSyncModel.d.ts +48 -0
  289. package/dist/sync/workspaceSyncModel.js +195 -31
  290. package/dist/sync/workspaceSyncModel.test.js +325 -12
  291. package/dist/sync/workspaceSyncState.d.ts +19 -0
  292. package/dist/sync/workspaceSyncState.js +3 -1
  293. package/dist/sync/workspaceSyncSurface.js +3 -8
  294. package/dist/types.d.ts +17 -0
  295. package/dist/ui/assets/AgentsModule-DrLawwNl.js +1 -0
  296. package/dist/ui/assets/{AnnotatedAttachmentWorkspace-BlS-cqnl.js → AnnotatedAttachmentWorkspace-Z9_whf7F.js} +1 -1
  297. package/dist/ui/assets/CourierPrime-Bold-BuLj_dpw.woff2 +0 -0
  298. package/dist/ui/assets/CourierPrime-BoldItalic-BrK2pUkZ.woff2 +0 -0
  299. package/dist/ui/assets/CourierPrime-Italic-DFUZK5Ey.woff2 +0 -0
  300. package/dist/ui/assets/CourierPrime-Regular-BsKphzVf.woff2 +0 -0
  301. package/dist/ui/assets/DocumentWorkspace-B03MaSkw.js +252 -0
  302. package/dist/ui/assets/DocumentWorkspace-Dhdso07p.css +1 -0
  303. package/dist/ui/assets/{InitiativesModule-BjR6iBf9.js → InitiativesModule-9E6ParrY.js} +1 -1
  304. package/dist/ui/assets/Noir_medium-bxQwKGzB.woff2 +0 -0
  305. package/dist/ui/assets/Noir_regular-ojf2kxlG.woff2 +0 -0
  306. package/dist/ui/assets/PlansPage-CvfnMiWq.css +1 -0
  307. package/dist/ui/assets/PlansPage-DRXscYxH.js +1 -0
  308. package/dist/ui/assets/RussoOne-Regular-Cu_qq_qC.woff2 +0 -0
  309. package/dist/ui/assets/SpecialElite-Regular-Di6gSvAS.woff2 +0 -0
  310. package/dist/ui/assets/TaskSettings-DZX4jk7e.js +9 -0
  311. package/dist/ui/assets/{WorkflowsModule-DnFqdUME.js → WorkflowsModule-BA7jcEpH.js} +1 -1
  312. package/dist/ui/assets/index-9eT8e0Lu.css +1 -0
  313. package/dist/ui/assets/index-C6k75jr8.js +6 -0
  314. package/dist/ui/assets/{vendor-icons-I7ZwG1z_.js → vendor-icons-DuEd_65c.js} +1 -1
  315. package/dist/ui/fonts/CourierPrime-Bold.ttf +0 -0
  316. package/dist/ui/fonts/CourierPrime-Bold.woff2 +0 -0
  317. package/dist/ui/fonts/CourierPrime-BoldItalic.ttf +0 -0
  318. package/dist/ui/fonts/CourierPrime-BoldItalic.woff2 +0 -0
  319. package/dist/ui/fonts/CourierPrime-Italic.ttf +0 -0
  320. package/dist/ui/fonts/CourierPrime-Italic.woff2 +0 -0
  321. package/dist/ui/fonts/CourierPrime-Regular.ttf +0 -0
  322. package/dist/ui/fonts/CourierPrime-Regular.woff2 +0 -0
  323. package/dist/ui/fonts/Noir_medium.otf +0 -0
  324. package/dist/ui/fonts/Noir_medium.woff2 +0 -0
  325. package/dist/ui/fonts/Noir_regular.otf +0 -0
  326. package/dist/ui/fonts/Noir_regular.woff2 +0 -0
  327. package/dist/ui/fonts/RussoOne-Regular.woff2 +0 -0
  328. package/dist/ui/fonts/SpecialElite-Regular.ttf +0 -0
  329. package/dist/ui/fonts/SpecialElite-Regular.woff2 +0 -0
  330. package/dist/ui/index.html +3 -3
  331. package/dist/utils/accountProfileSummaryCache.d.ts +5 -0
  332. package/dist/utils/assignees.d.ts +2 -0
  333. package/dist/utils/assignees.js +2 -0
  334. package/dist/utils/avatarUpload.d.ts +27 -0
  335. package/dist/utils/avatarUpload.js +132 -0
  336. package/dist/utils/avatarUpload.test.d.ts +1 -0
  337. package/dist/utils/avatarUpload.test.js +40 -0
  338. package/dist/utils/bootstrapTrace.d.ts +7 -0
  339. package/dist/utils/bootstrapTrace.js +29 -0
  340. package/dist/utils/commercialLifecyclePresentation.d.ts +19 -0
  341. package/dist/utils/commercialLifecyclePresentation.js +199 -0
  342. package/dist/utils/contextAssetEvents.d.ts +2 -0
  343. package/dist/utils/syncEventDetails.d.ts +3 -0
  344. package/dist/utils/syncEventDetails.js +64 -0
  345. package/dist/utils/syncEventDetails.test.d.ts +1 -0
  346. package/dist/utils/syncEventDetails.test.js +23 -0
  347. package/dist/utils/syncEventPresentation.d.ts +15 -0
  348. package/dist/utils/syncEventPresentation.js +129 -0
  349. package/dist/utils/syncEventPresentation.test.d.ts +1 -0
  350. package/dist/utils/syncEventPresentation.test.js +64 -0
  351. package/dist/utils/syncStatusPresentation.d.ts +72 -0
  352. package/dist/utils/syncStatusPresentation.js +217 -0
  353. package/dist/utils/syncStatusPresentation.test.d.ts +1 -0
  354. package/dist/utils/syncStatusPresentation.test.js +164 -0
  355. package/dist/utils/taskActivity.d.ts +2 -0
  356. package/dist/utils/taskActivity.js +228 -22
  357. package/dist/utils/taskActivity.test.js +119 -8
  358. package/dist/utils/workspaceSyncPresentation.d.ts +4 -0
  359. package/dist/utils/workspaceSyncPresentation.js +68 -2
  360. package/dist/utils/workspaceSyncPresentation.test.js +36 -1
  361. package/package.json +10 -2
  362. package/scripts/check-cloud-mcp.mjs +83 -0
  363. package/scripts/export-sqlite-to-postgres.mjs +7 -9
  364. package/scripts/playwright-auth.sh +5 -1
  365. package/scripts/run-billing-performance-smoke.sh +24 -0
  366. package/scripts/run-e2e-realtime.sh +17 -3
  367. package/scripts/run-smoke.sh +17 -5
  368. package/scripts/run-soak.sh +17 -5
  369. package/src/resources/templates/workflow-sources/workflowDocs.mjs +6 -6
  370. package/src/resources/templates/workflows/collaborate.yaml +1 -1
  371. package/src/resources/templates/workflows/evaluate.yaml +2 -2
  372. package/src/resources/templates/workflows/plan.yaml +1 -1
  373. package/src/resources/templates/workflows/review.yaml +2 -2
  374. package/dist/ui/assets/AgentsModule-BLWVbuKP.js +0 -1
  375. package/dist/ui/assets/DocumentWorkspace-BH_6DF6x.js +0 -250
  376. package/dist/ui/assets/DocumentWorkspace-C_8T8oz-.css +0 -1
  377. package/dist/ui/assets/PlansPage-C2dd2n1X.css +0 -1
  378. package/dist/ui/assets/PlansPage-CSDQ4sLa.js +0 -1
  379. package/dist/ui/assets/RussoOne-Regular-C3BxZIj7.ttf +0 -0
  380. package/dist/ui/assets/TaskSettings-CEDy34Jo.js +0 -9
  381. package/dist/ui/assets/index-DXUdtH1B.js +0 -6
  382. package/dist/ui/assets/index-Ii0B0rTO.css +0 -1
  383. package/scripts/migrate-planning-model.ts +0 -233
@@ -1,42 +1,44 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
- import React from 'react';
3
- import { DndContext, PointerSensor, closestCenter, useSensor, useSensors } from '@dnd-kit/core';
4
- import { SortableContext, arrayMove, useSortable, verticalListSortingStrategy } from '@dnd-kit/sortable';
5
- import { CSS } from '@dnd-kit/utilities';
6
- import * as Icons from 'lucide-react';
7
- import { HelpCircle, X, Send, Check, ChevronDown, ChevronRight, User, ClipboardList, Plus, Bot, GripVertical } from 'lucide-react';
8
- import styles from '../../Taskforce.module.css';
9
- import formStyles from './TaskForm.module.css';
10
- import { DEFAULT_CATEGORY_LABEL, DEFAULT_CATEGORY_VALUE } from '../../shared/taxonomyDefaults.js';
11
- import { LevelSelector } from '../ui/LevelSelector';
12
- import { TaxonomyDropdown } from '../ui/TaxonomyDropdown';
13
- import { Markdown } from '../ui/Markdown';
14
- import { IconMap, resolveColor } from '../../utils/constants';
15
- import { formatDate } from '../../utils/formatting';
16
- import { t } from '../../localization';
17
- import { TaskContextUpload } from './TaskContextUpload';
18
- import { summarizeTaskChange, summarizeTaskEvent } from '../../utils/taskActivity';
19
- import { ensureAssigneeOptionPresent, getAssigneeKind, getAssigneeLabel, } from '../../utils/assignees';
20
- import { getTaskReferenceLabel } from '../../utils/taskReferences';
21
- import { getVisibleTaskFormTaxonomies } from '../../utils/taxonomyFormDefaults.js';
22
- import { getInitiativeReferenceLabel, getWorkstreamReferenceLabel } from '../../utils/planningReferences';
23
- function SortableChecklistRow({ id, item, onToggle, onRemove }) {
2
+ import React from "react";
3
+ import { DndContext, PointerSensor, closestCenter, useSensor, useSensors, } from "@dnd-kit/core";
4
+ import { SortableContext, arrayMove, useSortable, verticalListSortingStrategy, } from "@dnd-kit/sortable";
5
+ import { CSS } from "@dnd-kit/utilities";
6
+ import * as Icons from "lucide-react";
7
+ import { HelpCircle, X, Send, Check, ChevronDown, ChevronRight, User, ClipboardList, Plus, Bot, GripVertical, } from "lucide-react";
8
+ import styles from "../../Taskforce.module.css";
9
+ import formStyles from "./TaskForm.module.css";
10
+ import { DEFAULT_CATEGORY_LABEL, DEFAULT_CATEGORY_VALUE, } from "../../shared/taxonomyDefaults.js";
11
+ import { LevelSelector } from "../ui/LevelSelector";
12
+ import { TaxonomyDropdown } from "../ui/TaxonomyDropdown";
13
+ import { Markdown } from "../ui/Markdown";
14
+ import { IconMap, resolveColor } from "../../utils/constants";
15
+ import { formatDate } from "../../utils/formatting";
16
+ import { t } from "../../localization";
17
+ import { TaskContextUpload } from "./TaskContextUpload";
18
+ import { getTaskActivityItems, getTaskEventPrimaryChangeField, summarizeTaskChange, summarizeTaskEvent, } from "../../utils/taskActivity";
19
+ import { ensureAssigneeOptionPresent, getAssigneeKind, getAssigneeLabel, } from "../../utils/assignees";
20
+ import { getTaskReferenceLabel } from "../../utils/taskReferences";
21
+ import { getVisibleTaskFormTaxonomies } from "../../utils/taxonomyFormDefaults.js";
22
+ import { getInitiativeReferenceLabel, getWorkstreamReferenceLabel, } from "../../utils/planningReferences";
23
+ function SortableChecklistRow({ id, item, onToggle, onRemove, }) {
24
24
  const { attributes, listeners, setNodeRef, transform, transition, isDragging, } = useSortable({ id });
25
25
  const style = {
26
26
  transform: CSS.Transform.toString(transform),
27
27
  transition,
28
28
  opacity: isDragging ? 0.88 : 1,
29
29
  };
30
- return (_jsxs("div", { ref: setNodeRef, style: style, className: `${formStyles.checklistRow} ${isDragging ? formStyles.checklistRowDragging : ''}`.trim(), children: [_jsx("button", { type: "button", className: formStyles.checklistHandleBtn, title: "Reorder checklist item", "aria-label": "Reorder checklist item", ...attributes, ...listeners, children: _jsx(GripVertical, { size: 14 }) }), _jsx("button", { type: "button", className: `${formStyles.checklistCheckboxBtn} ${item.isCompleted ? formStyles.checklistCheckboxBtnChecked : ''}`.trim(), onClick: onToggle, title: item.isCompleted ? 'Mark incomplete' : 'Mark complete', "aria-label": item.isCompleted ? 'Mark checklist item incomplete' : 'Mark checklist item complete', children: item.isCompleted ? _jsx(Check, { size: 14, strokeWidth: 2.1 }) : null }), _jsx("span", { className: `${formStyles.checklistItemText} ${item.isCompleted ? formStyles.checklistItemTextCompleted : ''}`.trim(), children: item.title }), _jsx("button", { type: "button", className: formStyles.checklistRemoveBtn, onClick: onRemove, title: "Remove checklist item", "aria-label": "Remove checklist item", children: _jsx(X, { size: 15, strokeWidth: 1.9 }) })] }));
30
+ return (_jsxs("div", { ref: setNodeRef, style: style, className: `${formStyles.checklistRow} ${isDragging ? formStyles.checklistRowDragging : ""}`.trim(), children: [_jsx("button", { type: "button", className: formStyles.checklistHandleBtn, title: "Reorder checklist item", "aria-label": "Reorder checklist item", ...attributes, ...listeners, children: _jsx(GripVertical, { size: 14 }) }), _jsx("button", { type: "button", className: `${formStyles.checklistCheckboxBtn} ${item.isCompleted ? formStyles.checklistCheckboxBtnChecked : ""}`.trim(), onClick: onToggle, title: item.isCompleted ? "Mark incomplete" : "Mark complete", "aria-label": item.isCompleted
31
+ ? "Mark checklist item incomplete"
32
+ : "Mark checklist item complete", children: item.isCompleted ? _jsx(Check, { size: 14, strokeWidth: 2.1 }) : null }), _jsx("span", { className: `${formStyles.checklistItemText} ${item.isCompleted ? formStyles.checklistItemTextCompleted : ""}`.trim(), children: item.title }), _jsx("button", { type: "button", className: formStyles.checklistRemoveBtn, onClick: onRemove, title: "Remove checklist item", "aria-label": "Remove checklist item", children: _jsx(X, { size: 15, strokeWidth: 1.9 }) })] }));
31
33
  }
32
34
  function hexToRgba(hex, opacity) {
33
35
  const result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
34
36
  if (!result)
35
- return '';
37
+ return "";
36
38
  return `rgba(${parseInt(result[1], 16)}, ${parseInt(result[2], 16)}, ${parseInt(result[3], 16)}, ${opacity})`;
37
39
  }
38
40
  export function TaskForm(props) {
39
- const { editingTaskId, error, title, description, category, type, priority, complexity, manualComplexityEnabled = false, assignee, scheduledDate, dueDate, workstreamInput = '', checklistItems, comments, newCommentText, contextFiles, currentWorkspaceId, apiBaseUrl, descriptionFocused, showMarkdownHelp, showChecklist = false, checklistEnabled = true, showComments, formTaxonomies, onTaxonomyChange, onOpenSettings, categories, types, priorities, taxonomyDisplayLabels, assigneeOptions, taxonomies, workstreams = [], initiatives = [], copiedId, onTitleChange, onDescriptionChange, onCategoryChange, onTypeChange, onPriorityChange, onComplexityChange, onAssigneeChange, onScheduledDateChange, onDueDateChange, onWorkstreamInputChange = () => { }, onChecklistItemsChange, onNewCommentTextChange, onDescriptionFocusedChange, onShowMarkdownHelpChange, onShowChecklistChange = () => { }, onShowCommentsChange, onSubmit, onAddComment, onAddContextFile, onRemoveContextFile, onUpdateContextCaption, onCopyId, onToggleInProgress, onToggleReview, onToggleComplete, onToggleCancel, onSetStatus, onArchiveTask, onUnarchive, onOpenTaskById, currentTask, commentsEndRef, } = props;
41
+ const { editingTaskId, error, title, description, category, type, priority, complexity, manualComplexityEnabled = false, assignee, scheduledDate, dueDate, workstreamInput = "", checklistItems, comments, newCommentText, contextFiles, currentWorkspaceId, apiBaseUrl, descriptionFocused, showMarkdownHelp, showChecklist = false, checklistEnabled = true, formTaxonomies, onTaxonomyChange, onOpenSettings, categories, types, priorities, taxonomyDisplayLabels, assigneeOptions, taxonomies, workstreams = [], initiatives = [], copiedId, onTitleChange, onDescriptionChange, onCategoryChange, onTypeChange, onPriorityChange, onComplexityChange, onAssigneeChange, onScheduledDateChange, onDueDateChange, onWorkstreamInputChange = () => { }, onChecklistItemsChange, onNewCommentTextChange, onDescriptionFocusedChange, onShowMarkdownHelpChange, onShowChecklistChange = () => { }, onSubmit, onAddComment, onAddContextFile, onRemoveContextFile, onUpdateContextCaption, onCopyId, onToggleInProgress, onToggleReview, onToggleComplete, onToggleCancel, onSetStatus, onArchiveTask, onUnarchive, onOpenTaskById, currentTask, commentsEndRef, } = props;
40
42
  const currentTaskReferenceLabel = getTaskReferenceLabel(currentTask);
41
43
  const visibleTaxonomies = React.useMemo(() => getVisibleTaskFormTaxonomies(taxonomies, formTaxonomies), [taxonomies, formTaxonomies]);
42
44
  const categoryOptions = React.useMemo(() => {
@@ -44,40 +46,40 @@ export function TaskForm(props) {
44
46
  const defaultOption = {
45
47
  value: DEFAULT_CATEGORY_VALUE,
46
48
  label: configuredDefault?.label || DEFAULT_CATEGORY_LABEL,
47
- icon: configuredDefault?.icon || 'HelpCircle',
48
- color: configuredDefault?.color || 'var(--text-secondary)'
49
+ icon: configuredDefault?.icon || "HelpCircle",
50
+ color: configuredDefault?.color || "var(--text-secondary)",
49
51
  };
50
52
  const selectedCategory = categories.find((cat) => cat.value === category);
51
53
  const availableCategories = categories.filter((cat) => !cat.disabled || cat.value === selectedCategory?.value);
52
54
  const nonDefault = availableCategories
53
- .filter(cat => cat.value !== DEFAULT_CATEGORY_VALUE)
54
- .map(cat => ({
55
+ .filter((cat) => cat.value !== DEFAULT_CATEGORY_VALUE)
56
+ .map((cat) => ({
55
57
  value: cat.value,
56
58
  label: cat.disabled ? `${cat.label} (Legacy)` : cat.label,
57
59
  icon: cat.icon,
58
- color: cat.color
60
+ color: cat.color,
59
61
  }));
60
62
  return [defaultOption, ...nonDefault];
61
63
  }, [categories, category]);
62
64
  const typeOptions = React.useMemo(() => {
63
65
  const selectedType = types.find((entry) => entry.value === type);
64
- const availableTypes = types.filter((entry) => entry.status !== 'retired' || entry.value === selectedType?.value);
66
+ const availableTypes = types.filter((entry) => entry.status !== "retired" || entry.value === selectedType?.value);
65
67
  return availableTypes.map((entry) => ({
66
68
  value: entry.value,
67
- label: entry.status === 'retired' ? `${entry.label} (Retired)` : entry.label,
68
- icon: entry.icon || 'Box',
69
- color: entry.color || 'violet-500'
69
+ label: entry.status === "retired" ? `${entry.label} (Retired)` : entry.label,
70
+ icon: entry.icon || "Box",
71
+ color: entry.color || "violet-500",
70
72
  }));
71
73
  }, [types, type]);
72
74
  const formatMetaDate = (value) => {
73
75
  if (!value)
74
76
  return null;
75
77
  const formatted = formatDate(value, {
76
- month: 'short',
77
- day: 'numeric',
78
- year: 'numeric',
79
- hour: '2-digit',
80
- minute: '2-digit'
78
+ month: "short",
79
+ day: "numeric",
80
+ year: "numeric",
81
+ hour: "2-digit",
82
+ minute: "2-digit",
81
83
  });
82
84
  return formatted || null;
83
85
  };
@@ -87,29 +89,32 @@ export function TaskForm(props) {
87
89
  const todayDateOnly = React.useMemo(() => {
88
90
  const now = new Date();
89
91
  const y = now.getFullYear();
90
- const m = String(now.getMonth() + 1).padStart(2, '0');
91
- const d = String(now.getDate()).padStart(2, '0');
92
+ const m = String(now.getMonth() + 1).padStart(2, "0");
93
+ const d = String(now.getDate()).padStart(2, "0");
92
94
  return `${y}-${m}-${d}`;
93
95
  }, []);
94
96
  const dueBeforeScheduledWarning = Boolean(dueDate && scheduledDate && dueDate < scheduledDate);
95
97
  const overdueWarning = Boolean(dueDate &&
96
98
  dueDate < todayDateOnly &&
97
- (!currentTask || (currentTask.status !== 'done' && currentTask.status !== 'cancelled')));
98
- const completedChecklistCount = checklistItems.filter(item => item.isCompleted).length;
99
- const [newChecklistItemText, setNewChecklistItemText] = React.useState('');
99
+ (!currentTask ||
100
+ (currentTask.status !== "done" && currentTask.status !== "cancelled")));
101
+ const completedChecklistCount = checklistItems.filter((item) => item.isCompleted).length;
102
+ const [newChecklistItemText, setNewChecklistItemText] = React.useState("");
100
103
  const [manualWorkstreamEntry, setManualWorkstreamEntry] = React.useState(false);
101
104
  const isArchivedReadOnly = Boolean(currentTask?.isArchived);
102
105
  const isDeletedReadOnly = Boolean(currentTask?.isDeleted);
103
- const workstreamOptions = React.useMemo(() => (workstreams.map((workstream) => {
106
+ const workstreamOptions = React.useMemo(() => workstreams.map((workstream) => {
104
107
  const reference = getWorkstreamReferenceLabel(workstream);
105
108
  return {
106
109
  value: reference || workstream.id,
107
- label: reference ? `${reference} · ${workstream.title}` : workstream.title,
108
- icon: 'Folder',
109
- color: 'var(--text-secondary)',
110
+ label: reference
111
+ ? `${reference} · ${workstream.title}`
112
+ : workstream.title,
113
+ icon: "Folder",
114
+ color: "var(--text-secondary)",
110
115
  };
111
- })), [workstreams]);
112
- const hasMatchingWorkstreamSelection = React.useMemo(() => (workstreamOptions.some((option) => String(option.value) === String(workstreamInput || '').trim())), [workstreamInput, workstreamOptions]);
116
+ }), [workstreams]);
117
+ const hasMatchingWorkstreamSelection = React.useMemo(() => workstreamOptions.some((option) => String(option.value) === String(workstreamInput || "").trim()), [workstreamInput, workstreamOptions]);
113
118
  React.useEffect(() => {
114
119
  if (editingTaskId) {
115
120
  if (manualWorkstreamEntry)
@@ -126,7 +131,12 @@ export function TaskForm(props) {
126
131
  if (!hasMatchingWorkstreamSelection && !manualWorkstreamEntry) {
127
132
  setManualWorkstreamEntry(true);
128
133
  }
129
- }, [editingTaskId, hasMatchingWorkstreamSelection, manualWorkstreamEntry, workstreamInput]);
134
+ }, [
135
+ editingTaskId,
136
+ hasMatchingWorkstreamSelection,
137
+ manualWorkstreamEntry,
138
+ workstreamInput,
139
+ ]);
130
140
  const isReadOnly = isArchivedReadOnly || isDeletedReadOnly;
131
141
  const formRef = React.useRef(null);
132
142
  const checklistSensors = useSensors(useSensor(PointerSensor, {
@@ -140,8 +150,8 @@ export function TaskForm(props) {
140
150
  form.scrollTop = 0;
141
151
  const container = form.parentElement;
142
152
  if (container) {
143
- if (typeof container.scrollTo === 'function') {
144
- container.scrollTo({ top: 0, left: 0, behavior: 'auto' });
153
+ if (typeof container.scrollTo === "function") {
154
+ container.scrollTo({ top: 0, left: 0, behavior: "auto" });
145
155
  }
146
156
  else {
147
157
  container.scrollTop = 0;
@@ -161,7 +171,7 @@ export function TaskForm(props) {
161
171
  ...checklistItems,
162
172
  {
163
173
  id: `checklist-draft-${now}-${checklistItems.length}`,
164
- taskId: editingTaskId || '',
174
+ taskId: editingTaskId || "",
165
175
  title: text,
166
176
  isCompleted: false,
167
177
  order: checklistItems.length,
@@ -169,7 +179,7 @@ export function TaskForm(props) {
169
179
  updatedAt: now,
170
180
  },
171
181
  ]);
172
- setNewChecklistItemText('');
182
+ setNewChecklistItemText("");
173
183
  };
174
184
  const handleChecklistReorder = React.useCallback((event) => {
175
185
  const { active, over } = event;
@@ -188,25 +198,27 @@ export function TaskForm(props) {
188
198
  onChecklistItemsChange(reordered);
189
199
  }, [checklistItems, onChecklistItemsChange]);
190
200
  const formatAiAuthorLabel = (author) => {
191
- const raw = String(author || '').trim();
201
+ const raw = String(author || "").trim();
192
202
  const cleaned = author
193
203
  .trim()
194
- .replace(/^ai-profile-/i, '')
195
- .replace(/[-_]+/g, ' ')
196
- .replace(/\s+/g, ' ');
197
- if (!cleaned || cleaned === 'ai' || raw.toLowerCase().startsWith('ai-profile-'))
198
- return 'AI Agent';
204
+ .replace(/^ai-profile-/i, "")
205
+ .replace(/[-_]+/g, " ")
206
+ .replace(/\s+/g, " ");
207
+ if (!cleaned ||
208
+ cleaned === "ai" ||
209
+ raw.toLowerCase().startsWith("ai-profile-"))
210
+ return "AI Agent";
199
211
  const label = cleaned
200
- .split(' ')
212
+ .split(" ")
201
213
  .map((part) => part.charAt(0).toUpperCase() + part.slice(1))
202
- .join(' ');
214
+ .join(" ");
203
215
  return `AI Agent - ${label}`;
204
216
  };
205
217
  const resolvedAssigneeOptions = ensureAssigneeOptionPresent(assigneeOptions, assignee);
206
218
  const currentAssigneeKind = getAssigneeKind(assignee);
207
219
  const currentAssigneeLabel = getAssigneeLabel(assignee, resolvedAssigneeOptions);
208
220
  const actorOptionByValue = React.useMemo(() => new Map(resolvedAssigneeOptions.map((option) => [String(option.value), option])), [resolvedAssigneeOptions]);
209
- const currentAssigneeOption = actorOptionByValue.get(String(assignee || 'unassigned'));
221
+ const currentAssigneeOption = actorOptionByValue.get(String(assignee || "unassigned"));
210
222
  const currentAssigneeColor = currentTask?.assigneeActor?.color || currentAssigneeOption?.color;
211
223
  const actorFallbackById = React.useMemo(() => {
212
224
  const entries = [
@@ -223,28 +235,18 @@ export function TaskForm(props) {
223
235
  initiative.id,
224
236
  getInitiativeReferenceLabel(initiative) || initiative.id,
225
237
  ])), [initiatives]);
226
- const taskActivityItems = React.useMemo(() => {
227
- if (Array.isArray(currentTask?.activity) && currentTask.activity.length > 0) {
228
- return currentTask.activity;
229
- }
230
- return comments.map((comment) => ({
231
- id: `comment:${comment.id}`,
232
- type: 'comment',
233
- timestamp: comment.timestamp,
234
- comment
235
- }));
236
- }, [comments, currentTask?.activity]);
238
+ const taskActivityItems = React.useMemo(() => getTaskActivityItems({ activity: currentTask?.activity, comments }), [comments, currentTask?.activity]);
237
239
  const formatActivityFieldValue = (field, value) => {
238
- const normalizedValue = String(value || '').trim();
240
+ const normalizedValue = String(value || "").trim();
239
241
  if (!normalizedValue)
240
242
  return undefined;
241
- if (field === 'assignee') {
243
+ if (field === "assignee") {
242
244
  return getAssigneeLabel(normalizedValue, resolvedAssigneeOptions);
243
245
  }
244
- if (field === 'workstreamId') {
246
+ if (field === "workstreamId") {
245
247
  return workstreamLabelById.get(normalizedValue);
246
248
  }
247
- if (field === 'initiativeId') {
249
+ if (field === "initiativeId") {
248
250
  return initiativeLabelById.get(normalizedValue);
249
251
  }
250
252
  return undefined;
@@ -252,26 +254,81 @@ export function TaskForm(props) {
252
254
  const resolveActorLabel = React.useCallback((actorId, actorType, actorLabel) => {
253
255
  if (actorLabel && actorLabel.trim())
254
256
  return actorLabel.trim();
255
- const raw = String(actorId || '').trim();
257
+ const raw = String(actorId || "").trim();
256
258
  if (!raw)
257
- return actorType === 'ai' ? 'AI Agent' : t('taskForm.you');
259
+ return actorType === "ai" ? "AI Agent" : t("taskForm.you");
258
260
  const fallback = actorFallbackById.get(raw);
259
261
  if (fallback?.label)
260
262
  return fallback.label;
261
- const option = actorOptionByValue.get(raw) || actorOptionByValue.get(raw.toLowerCase());
263
+ const option = actorOptionByValue.get(raw) ||
264
+ actorOptionByValue.get(raw.toLowerCase());
262
265
  if (option?.label)
263
266
  return option.label;
264
- if (actorType === 'ai')
267
+ if (actorType === "ai")
265
268
  return formatAiAuthorLabel(raw);
266
269
  return getAssigneeLabel(raw, resolvedAssigneeOptions) || raw;
267
- }, [actorFallbackById, actorOptionByValue, formatAiAuthorLabel, resolvedAssigneeOptions]);
270
+ }, [
271
+ actorFallbackById,
272
+ actorOptionByValue,
273
+ formatAiAuthorLabel,
274
+ resolvedAssigneeOptions,
275
+ ]);
276
+ const resolveActivityActorPresentation = React.useCallback(({ actorId, actorType, actorProfile, fallbackKind, }) => {
277
+ const actorRef = String(actorId || "").trim();
278
+ const normalizedActor = actorRef.toLowerCase();
279
+ const actorFallback = actorFallbackById.get(actorRef);
280
+ const actorOption = actorOptionByValue.get(actorRef) ||
281
+ actorOptionByValue.get(normalizedActor);
282
+ const resolvedActor = actorProfile || actorFallback || actorOption || null;
283
+ const resolvedKind = actorType === "system"
284
+ ? "system"
285
+ : actorType === "ai" || resolvedActor?.kind === "ai"
286
+ ? "agent"
287
+ : actorType === "human" || resolvedActor?.kind === "human"
288
+ ? "member"
289
+ : actorOption?.kind === "agent"
290
+ ? "agent"
291
+ : actorOption?.kind === "member"
292
+ ? "member"
293
+ : fallbackKind ||
294
+ (normalizedActor === "" ||
295
+ normalizedActor === "user" ||
296
+ normalizedActor === "human"
297
+ ? "member"
298
+ : "agent");
299
+ const label = resolvedKind === "system"
300
+ ? "System"
301
+ : resolveActorLabel(actorRef, resolvedKind === "agent" ? "ai" : "human", resolvedActor?.label || null);
302
+ const color = resolvedActor?.color;
303
+ const iconName = String(resolvedActor?.icon ||
304
+ (resolvedKind === "agent"
305
+ ? "Bot"
306
+ : resolvedKind === "member"
307
+ ? "User"
308
+ : "ClipboardList"));
309
+ const ActorIcon = IconMap[iconName] ||
310
+ (resolvedKind === "agent"
311
+ ? Bot
312
+ : resolvedKind === "member"
313
+ ? User
314
+ : ClipboardList);
315
+ return { kind: resolvedKind, label, color, ActorIcon };
316
+ }, [
317
+ actorFallbackById,
318
+ actorOptionByValue,
319
+ resolveActorLabel,
320
+ ]);
268
321
  const latestActivityActorLabel = React.useMemo(() => {
269
- const latestItem = taskActivityItems[0];
322
+ const latestItem = taskActivityItems[taskActivityItems.length - 1];
270
323
  if (!latestItem)
271
324
  return null;
272
- if (latestItem.type === 'comment') {
325
+ if (latestItem.type === "comment") {
273
326
  const comment = latestItem.comment;
274
- return resolveActorLabel(comment.author, comment.actor?.kind === 'ai' ? 'ai' : comment.actor?.kind === 'human' ? 'human' : null, comment.actor?.label || null);
327
+ return resolveActorLabel(comment.author, comment.actor?.kind === "ai"
328
+ ? "ai"
329
+ : comment.actor?.kind === "human"
330
+ ? "human"
331
+ : null, comment.actor?.label || null);
275
332
  }
276
333
  const event = latestItem.event;
277
334
  return resolveActorLabel(event.actor, event.actorType, event.actorProfile?.label || null);
@@ -281,158 +338,233 @@ export function TaskForm(props) {
281
338
  if (!completedAt)
282
339
  return null;
283
340
  for (const item of taskActivityItems) {
284
- if (item.type !== 'event')
341
+ if (item.type !== "event")
285
342
  continue;
286
343
  const event = item.event;
287
344
  const nextStatus = event.details?.changes?.status?.to;
288
- if (nextStatus === 'done' || nextStatus === 'cancelled') {
345
+ if (nextStatus === "done" || nextStatus === "cancelled") {
289
346
  return resolveActorLabel(event.actor, event.actorType, event.actorProfile?.label || null);
290
347
  }
291
348
  }
292
349
  return latestActivityActorLabel;
293
- }, [currentTask?.completedAt, latestActivityActorLabel, resolveActorLabel, taskActivityItems]);
294
- const createdByLabel = currentTask?.createdByActor?.label
295
- || resolveActorLabel(currentTask?.createdBy || null, currentTask?.createdByActor?.kind === 'ai' ? 'ai' : currentTask?.createdByActor?.kind === 'human' ? 'human' : null, currentTask?.createdByActor?.label || null);
350
+ }, [
351
+ currentTask?.completedAt,
352
+ latestActivityActorLabel,
353
+ resolveActorLabel,
354
+ taskActivityItems,
355
+ ]);
356
+ const createdByLabel = currentTask?.createdByActor?.label ||
357
+ resolveActorLabel(currentTask?.createdBy || null, currentTask?.createdByActor?.kind === "ai"
358
+ ? "ai"
359
+ : currentTask?.createdByActor?.kind === "human"
360
+ ? "human"
361
+ : null, currentTask?.createdByActor?.label || null);
296
362
  const updatedByLabel = latestActivityActorLabel;
297
363
  const activityCount = taskActivityItems.length;
364
+ React.useLayoutEffect(() => {
365
+ if (!editingTaskId)
366
+ return;
367
+ const endNode = commentsEndRef.current;
368
+ if (!endNode)
369
+ return;
370
+ const thread = endNode.parentElement;
371
+ const scrollToLatest = () => {
372
+ if (typeof endNode.scrollIntoView === "function") {
373
+ endNode.scrollIntoView({ block: "end" });
374
+ return;
375
+ }
376
+ if (thread) {
377
+ thread.scrollTop = thread.scrollHeight;
378
+ }
379
+ };
380
+ scrollToLatest();
381
+ const frame = window.requestAnimationFrame(scrollToLatest);
382
+ return () => window.cancelAnimationFrame(frame);
383
+ }, [activityCount, commentsEndRef, currentTask?.id, editingTaskId]);
384
+ const activitySidebar = editingTaskId ? (_jsxs("aside", { className: formStyles.activitySidebar, "aria-label": t("taskForm.commentsSectionTitle"), children: [_jsxs("div", { className: formStyles.activitySidebarHeader, children: [_jsx("h2", { className: formStyles.activitySidebarTitle, children: t("taskForm.commentsSectionTitle") }), _jsx("span", { className: formStyles.activitySidebarCount, children: activityCount })] }), _jsxs("div", { className: `${formStyles.commentPanel} ${formStyles.activitySidebarPanel}`.trim(), children: [_jsxs("div", { className: formStyles.commentThread, children: [activityCount === 0 && (_jsx("div", { className: formStyles.emptyComments, children: t("taskForm.noComments") })), taskActivityItems.map((item) => {
385
+ if (item.type === "comment") {
386
+ const c = item.comment;
387
+ const actorPresentation = resolveActivityActorPresentation({
388
+ actorId: c.author,
389
+ actorType: c.actor?.kind === "ai"
390
+ ? "ai"
391
+ : c.actor?.kind === "human"
392
+ ? "human"
393
+ : null,
394
+ actorProfile: c.actor,
395
+ });
396
+ const commentKind = actorPresentation.kind === "agent" ? "agent" : "member";
397
+ const { ActorIcon: CommentActorIcon } = actorPresentation;
398
+ const authorColor = actorPresentation.color;
399
+ const authorLabel = actorPresentation.label;
400
+ const commentAccentStyle = authorColor && hexToRgba(authorColor, 0.35)
401
+ ? {
402
+ background: `linear-gradient(135deg, ${hexToRgba(authorColor, 0.18)} 0%, ${hexToRgba(authorColor, 0.1)} 100%)`,
403
+ borderColor: hexToRgba(authorColor, 0.35),
404
+ }
405
+ : undefined;
406
+ return (_jsxs("div", { className: `${formStyles.comment} ${commentKind === "agent" ? formStyles.commentAi : formStyles.commentUser}`, children: [_jsxs("div", { className: formStyles.commentHeader, children: [_jsx("span", { className: formStyles.commentAuthor, style: authorColor ? { color: authorColor } : undefined, children: _jsxs(_Fragment, { children: [_jsx(CommentActorIcon, { size: 12, strokeWidth: 1.5 }), " ", authorLabel] }) }), _jsx("span", { className: formStyles.commentTime, children: formatDate(c.timestamp, {
407
+ month: "short",
408
+ day: "numeric",
409
+ hour: "2-digit",
410
+ minute: "2-digit",
411
+ }) })] }), _jsx("div", { className: formStyles.commentText, style: commentAccentStyle, children: _jsx(Markdown, { onTaskIdClick: onOpenTaskById, children: c.text }) })] }, c.id));
412
+ }
413
+ const event = item.event;
414
+ const actorPresentation = resolveActivityActorPresentation({
415
+ actorId: event.actor,
416
+ actorType: event.actorType,
417
+ actorProfile: event.actorProfile,
418
+ fallbackKind: "system",
419
+ });
420
+ const { ActorIcon: EventActorIcon } = actorPresentation;
421
+ const authorColor = actorPresentation.color;
422
+ const authorLabel = actorPresentation.label;
423
+ const eventKind = actorPresentation.kind;
424
+ const changeEntries = Object.entries(event.details?.changes || {});
425
+ const primaryChangeField = getTaskEventPrimaryChangeField(event);
426
+ const displayedChangeEntries = changeEntries.length > 1 && primaryChangeField
427
+ ? changeEntries.filter(([field]) => field !== primaryChangeField)
428
+ : changeEntries.length > 1
429
+ ? changeEntries
430
+ : [];
431
+ return (_jsxs("div", { className: `${formStyles.comment} ${formStyles.activityEvent} ${eventKind === "system" ? formStyles.activityEventSystem : ""}`, children: [_jsxs("div", { className: `${formStyles.commentHeader} ${formStyles.activityEventHeader}`, children: [_jsx("span", { className: formStyles.commentAuthor, style: authorColor ? { color: authorColor } : undefined, children: _jsxs(_Fragment, { children: [_jsx(EventActorIcon, { size: 12, strokeWidth: 1.5 }), " ", authorLabel] }) }), _jsx("span", { className: formStyles.commentTime, children: formatDate(event.createdAt, {
432
+ month: "short",
433
+ day: "numeric",
434
+ hour: "2-digit",
435
+ minute: "2-digit",
436
+ }) })] }), _jsxs("div", { className: formStyles.activityEventText, children: [_jsx("div", { children: summarizeTaskEvent(event, formatActivityFieldValue) }), displayedChangeEntries.length > 0 && (_jsx("div", { className: formStyles.activityEventChanges, children: displayedChangeEntries.map(([field, change]) => (_jsx("div", { className: formStyles.activityEventChange, children: summarizeTaskChange(field, change, formatActivityFieldValue) }, field))) }))] })] }, item.id));
437
+ }), _jsx("div", { ref: commentsEndRef })] }), _jsxs("div", { className: formStyles.commentInputArea, children: [_jsx("textarea", { className: formStyles.commentInput, placeholder: t("taskForm.commentPlaceholder"), value: newCommentText, onChange: (e) => onNewCommentTextChange(e.target.value), onKeyDown: (e) => {
438
+ if (e.key === "Enter" && !e.shiftKey) {
439
+ e.preventDefault();
440
+ onAddComment();
441
+ }
442
+ }, rows: 1 }), _jsx("button", { type: "button", className: formStyles.sendCommentBtn, onClick: onAddComment, disabled: !newCommentText.trim(), "aria-label": "Send comment", title: "Send comment", children: _jsx(Send, { size: 16 }) })] })] })] })) : null;
298
443
  return (_jsxs("form", { ref: formRef, onSubmit: onSubmit, className: `${styles.form} ${styles.appScrollbar} tf-scrollbar`, children: [error && _jsx("div", { className: formStyles.error, children: error }), isReadOnly && (_jsx("div", { className: `${formStyles.formNotice} tf-text-helper`, children: isDeletedReadOnly
299
- ? 'Deleted tasks are read-only. Restore the task to make changes.'
300
- : 'Archived tasks are read-only. Unarchive the task to make changes.' })), _jsxs("fieldset", { disabled: isReadOnly, className: formStyles.formFieldsetReset, children: [_jsxs("div", { className: styles.field, children: [_jsxs("div", { className: styles.labelRow, children: [_jsx("label", { className: styles.label, children: "Title" }), assignee && (_jsx("span", { title: `Assigned to: ${currentAssigneeLabel}`, className: [
301
- formStyles.assigneeIndicator,
302
- currentAssigneeKind === 'agent'
303
- ? formStyles.assigneeIndicatorAgent
304
- : currentAssigneeKind === 'member'
305
- ? formStyles.assigneeIndicatorUser
306
- : formStyles.assigneeIndicatorUnassigned
307
- ].join(' '), style: currentAssigneeColor ? { color: currentAssigneeColor } : undefined, children: currentAssigneeKind === 'agent'
308
- ? _jsx(Bot, { size: 20 })
309
- : currentAssigneeKind === 'member'
310
- ? _jsx(User, { size: 20 })
311
- : _jsx(HelpCircle, { size: 20 }) }))] }), _jsx("input", { type: "text", value: title, onChange: (e) => onTitleChange(e.target.value), placeholder: "What needs to be done?", className: styles.input, required: true, maxLength: 255, autoFocus: true })] }), _jsxs("div", { className: styles.field, children: [_jsxs("div", { className: styles.labelRow, children: [_jsx("label", { className: styles.label, children: "Description" }), _jsx("button", { type: "button", className: styles.helpLink, onClick: () => onShowMarkdownHelpChange(!showMarkdownHelp), title: "Markdown Help", tabIndex: -1, children: _jsx(HelpCircle, { size: 14 }) })] }), showMarkdownHelp && (_jsxs("div", { className: formStyles.markdownHelp, children: [_jsxs("div", { className: formStyles.helpHeader, children: [_jsx("span", { children: "Markdown Guide" }), _jsx("button", { onClick: () => onShowMarkdownHelpChange(false), className: formStyles.helpClose, children: _jsx(X, { size: 12 }) })] }), _jsxs("div", { className: formStyles.helpGrid, children: [_jsx("div", { children: _jsx("code", { children: "**bold**" }) }), _jsx("div", { children: _jsx("code", { children: "_italic_" }) }), _jsx("div", { children: _jsx("code", { children: "- list" }) }), _jsx("div", { children: _jsx("code", { children: "1. list" }) }), _jsx("div", { className: formStyles.helpGridItem, children: _jsx("code", { children: "`inline code`" }) }), _jsxs("div", { className: formStyles.helpGridItem, children: [_jsx("code", { children: "```" }), _jsx("br", {}), _jsx("code", { children: "code block" }), _jsx("br", {}), _jsx("code", { children: "```" })] }), _jsx("div", { children: _jsx("code", { children: "[link](url)" }) })] })] })), descriptionFocused || !description ? (_jsx("textarea", { className: `${styles.textarea} ${formStyles.descriptionSurface}`, placeholder: "What needs to be done? (Markdown supported)", value: description, onChange: (e) => onDescriptionChange(e.target.value), onFocus: () => onDescriptionFocusedChange(true), onBlur: () => onDescriptionFocusedChange(false), rows: 9, autoFocus: descriptionFocused })) : (_jsx("div", { className: `${styles.textarea} ${formStyles.descriptionSurface} ${formStyles.markdownPreview} ${formStyles.markdownPreviewContainer}`, onClick: () => onDescriptionFocusedChange(true), children: _jsx(Markdown, { onTaskIdClick: onOpenTaskById, children: description }) }))] }), _jsxs("div", { className: formStyles.compactMetaSection, children: [_jsxs("div", { className: formStyles.compactMetaGrid, children: [_jsxs("div", { className: `${styles.field} ${formStyles.compactMetaField}`, children: [_jsx("label", { id: "category-label", className: styles.label, children: taxonomyDisplayLabels?.category || 'Category' }), _jsx(TaxonomyDropdown, { value: category, options: categoryOptions, onChange: (value) => onCategoryChange(String(value)), required: true, ariaLabelledBy: "category-label" })] }), _jsxs("div", { className: `${styles.field} ${formStyles.compactMetaField}`, children: [_jsx("label", { id: "type-label", className: styles.label, children: taxonomyDisplayLabels?.type || 'Type' }), _jsx(TaxonomyDropdown, { value: type, options: typeOptions, onChange: (value) => onTypeChange(String(value)), ariaLabelledBy: "type-label" })] }), _jsxs("div", { className: `${styles.field} ${formStyles.compactMetaField}`, children: [_jsx("label", { htmlFor: "task-form-scheduled-date", className: styles.label, children: t('taskForm.scheduledLabel') }), _jsx("input", { id: "task-form-scheduled-date", type: "date", value: scheduledDate, onChange: (e) => onScheduledDateChange(e.target.value), className: `${styles.input} ${styles.taskFormDateInput}` })] }), _jsx("div", { className: formStyles.compactMetaSpacer, "aria-hidden": "true" }), _jsxs("div", { className: `${styles.field} ${formStyles.compactMetaField}`, children: [_jsx("label", { id: "assignee-label", className: styles.label, children: "Assigned To" }), _jsx(TaxonomyDropdown, { value: assignee || 'unassigned', options: resolvedAssigneeOptions, onChange: (value) => onAssigneeChange(String(value)), ariaLabelledBy: "assignee-label" })] }), _jsx("div", { className: formStyles.compactMetaField, children: _jsx(LevelSelector, { label: taxonomyDisplayLabels?.priority || 'Priority', options: priorities, value: priority, onChange: onPriorityChange, type: "priority", showSelectedLabel: false }) }), _jsxs("div", { className: `${styles.field} ${formStyles.compactMetaField}`, children: [_jsx("label", { htmlFor: "task-form-due-date", className: styles.label, children: t('taskForm.dueLabel') }), _jsx("input", { id: "task-form-due-date", type: "date", value: dueDate, onChange: (e) => onDueDateChange(e.target.value), className: `${styles.input} ${styles.taskFormDateInput}` }), dueBeforeScheduledWarning && (_jsx("span", { className: `${formStyles.taskFormDateWarning} ${formStyles.compactMetaFieldHint}`, children: t('taskForm.dueBeforeScheduled') })), overdueWarning && (_jsx("span", { className: `${formStyles.taskFormDateError} ${formStyles.compactMetaFieldHint}`, children: t('taskForm.overdue') }))] }), manualComplexityEnabled && (_jsx("div", { className: formStyles.compactMetaField, children: _jsx(LevelSelector, { label: "Complexity", options: [
312
- { value: 1, label: 'Tiny', color: '#10b981', icon: 'Gauge' },
313
- { value: 2, label: 'Low', color: '#14b8a6', icon: 'Gauge' },
314
- { value: 3, label: 'Medium', color: '#3b82f6', icon: 'Gauge' },
315
- { value: 4, label: 'High', color: '#8b5cf6', icon: 'Gauge' },
316
- { value: 5, label: 'Epic', color: '#d946ef', icon: 'Gauge' },
317
- ], value: complexity, onChange: onComplexityChange, type: "general" }) }))] }), !editingTaskId && (_jsxs("div", { className: formStyles.createWorkstreamRow, children: [_jsx("label", { className: styles.label, children: "Attach to workstream" }), _jsxs("div", { className: styles.pathInputGroup, children: [manualWorkstreamEntry ? (_jsx("input", { type: "text", className: styles.input, value: workstreamInput, onChange: (e) => onWorkstreamInputChange(e.target.value), placeholder: "Type a workstream name or reference", "aria-label": "Attach to workstream" })) : (_jsx(TaxonomyDropdown, { value: workstreamInput, options: workstreamOptions, onChange: (value) => onWorkstreamInputChange(String(value)), placeholder: "Select workstream...", ariaLabel: "Attach to workstream", className: formStyles.createWorkstreamDropdown })), _jsx("button", { type: "button", className: styles.secondaryHeaderBtn, onClick: () => {
318
- if (manualWorkstreamEntry) {
319
- setManualWorkstreamEntry(false);
320
- if (!hasMatchingWorkstreamSelection) {
321
- onWorkstreamInputChange('');
322
- }
323
- return;
324
- }
325
- setManualWorkstreamEntry(true);
326
- }, "aria-label": manualWorkstreamEntry ? 'Use workstream dropdown' : 'Enter workstream by name', title: manualWorkstreamEntry ? 'Use workstream dropdown' : 'Enter workstream by name', children: _jsx(Plus, { size: 14 }) })] })] }))] }), _jsxs("div", { className: formStyles.sectionBlock, children: [_jsx("div", { className: formStyles.taxonomyFieldsGrid, children: visibleTaxonomies.map(tax => {
327
- if (tax.id === 'approach' && tax.isSystem)
328
- return null; // Workflow stays hidden until the dedicated refactor lands.
329
- const selectedRawValue = formTaxonomies[tax.id];
330
- const selectedValues = Array.isArray(selectedRawValue)
331
- ? selectedRawValue.map((value) => String(value))
332
- : (selectedRawValue !== undefined && selectedRawValue !== null && selectedRawValue !== ''
333
- ? [String(selectedRawValue)]
334
- : []);
335
- const selectableOptions = tax.options.filter((option) => (option.status !== 'retired' || selectedValues.includes(String(option.value))));
336
- return (_jsxs("div", { className: styles.field, children: [_jsx("label", { className: styles.label, children: tax.label }), tax.description && (_jsx("div", { className: `${formStyles.settingsHint} tf-text-helper`, children: tax.description })), tax.widgetType === 'level' ? (_jsx(LevelSelector, { label: tax.label, options: selectableOptions.map((option) => ({
337
- ...option,
338
- label: option.status === 'retired' ? `${option.label} (Retired)` : option.label
339
- })), value: formTaxonomies[tax.id] || '', onChange: (val) => onTaxonomyChange(tax.id, val), type: "general", hideLabel: true })) : (tax.multiSelect ? (_jsx("div", { className: `${styles.dropdownList} ${formStyles.softSectionSurface}`, children: selectableOptions.map((opt) => {
340
- const optionValue = String(opt.value);
341
- const isSelected = selectedValues.includes(optionValue);
342
- return (_jsxs("button", { type: "button", className: `${formStyles.specialistChip} ${isSelected ? formStyles.specialistChipActive : ''}`, onClick: () => {
343
- const nextValues = isSelected
344
- ? selectedValues.filter((value) => value !== optionValue)
345
- : [...selectedValues, optionValue];
346
- onTaxonomyChange(tax.id, nextValues);
347
- }, title: isSelected ? 'Click to remove' : 'Click to add', children: [isSelected && _jsx(Check, { size: 12 }), opt.status === 'retired' ? `${opt.label} (Retired)` : opt.label] }, optionValue));
348
- }) })) : (_jsxs("div", { className: styles.fieldIconWrapper, style: {
349
- '--field-color': 'var(--text-primary)',
350
- '--field-border': 'rgba(255, 255, 255, 0.1)',
351
- '--field-bg': 'rgba(255, 255, 255, 0.02)'
352
- }, children: [(() => {
353
- const selectedOption = tax.options.find(o => String(o.value) === String(formTaxonomies[tax.id]));
354
- const IconComp = selectedOption?.icon && Icons[selectedOption.icon] ? Icons[selectedOption.icon] : HelpCircle;
355
- const color = selectedOption?.color || 'var(--text-secondary)';
356
- return (_jsx("div", { className: styles.fieldIcon, style: { color: resolveColor(color) }, children: _jsx(IconComp, { size: 16 }) }));
357
- })(), _jsxs("select", { value: formTaxonomies[tax.id] || '', onChange: (e) => onTaxonomyChange(tax.id, e.target.value), className: `${styles.select} ${styles.field_dynamic}`, required: tax.isRequired, children: [_jsxs("option", { value: "", children: ["Select ", tax.label, "..."] }), selectableOptions.map(opt => (_jsx("option", { value: opt.value, children: opt.status === 'retired' ? `${opt.label} (Retired)` : opt.label }, opt.value)))] })] })))] }, tax.id));
358
- }) }), checklistEnabled && (_jsxs("div", { className: styles.specialistSection, children: [_jsxs("div", { className: styles.toggleHeading, onClick: () => onShowChecklistChange(!showChecklist), title: showChecklist ? 'Hide checklist' : 'Show checklist', children: [_jsxs("label", { className: styles.label, children: ["Checklist ", checklistItems.length > 0 && `(${completedChecklistCount}/${checklistItems.length})`] }), showChecklist ? _jsx(ChevronDown, { size: 14 }) : _jsx(ChevronRight, { size: 14 })] }), showChecklist && (_jsxs("div", { className: `${styles.dropdownList} ${formStyles.stackedList} ${formStyles.stackedListSpaced}`, children: [_jsxs("div", { className: styles.pathInputGroup, children: [_jsx("input", { type: "text", className: styles.input, placeholder: "Add checklist item", value: newChecklistItemText, onChange: (e) => setNewChecklistItemText(e.target.value), onKeyDown: (e) => {
359
- if (e.key === 'Enter') {
360
- e.preventDefault();
361
- handleAddChecklistItem();
362
- }
363
- } }), _jsx("button", { type: "button", className: styles.secondaryHeaderBtn, onClick: handleAddChecklistItem, disabled: !newChecklistItemText.trim(), children: "Add" })] }), checklistItems.length > 0 ? (_jsx(DndContext, { sensors: checklistSensors, collisionDetection: closestCenter, onDragEnd: handleChecklistReorder, children: _jsx(SortableContext, { items: checklistItems.map((item) => item.id), strategy: verticalListSortingStrategy, children: checklistItems.map((item, index) => (_jsx(SortableChecklistRow, { id: item.id || `checklist-${index}`, item: item, onToggle: () => {
364
- const now = new Date().toISOString();
365
- onChecklistItemsChange(checklistItems.map((entry, entryIndex) => entryIndex === index
366
- ? { ...entry, isCompleted: !entry.isCompleted, updatedAt: now }
367
- : entry));
368
- }, onRemove: () => onChecklistItemsChange(checklistItems
369
- .filter((_, entryIndex) => entryIndex !== index)
370
- .map((entry, entryIndex) => ({ ...entry, order: entryIndex }))) }, item.id || `checklist-${index}`))) }) })) : (_jsx("div", { className: `${formStyles.settingsHint} tf-text-helper`, children: "No checklist items yet." }))] }))] }))] }), _jsxs("div", { children: [_jsx(TaskContextUpload, { taskId: editingTaskId, taskReferenceLabel: currentTaskReferenceLabel, workspaceId: currentWorkspaceId, apiBaseUrl: apiBaseUrl, contextFiles: contextFiles, onAddContextFile: onAddContextFile, onRemoveContextFile: onRemoveContextFile, onUpdateContextCaption: onUpdateContextCaption }), editingTaskId && (_jsxs("div", { className: styles.specialistSection, children: [_jsxs("div", { className: styles.toggleHeading, onClick: () => onShowCommentsChange(!showComments), title: showComments ? t('taskForm.hideComments') : t('taskForm.showComments'), children: [_jsxs("label", { className: styles.label, children: [t('taskForm.commentsSectionTitle'), " ", activityCount > 0 && `(${activityCount})`] }), showComments ? _jsx(ChevronDown, { size: 14 }) : _jsx(ChevronRight, { size: 14 })] }), showComments && (_jsxs("div", { className: formStyles.commentPanel, children: [_jsxs("div", { className: formStyles.commentThread, children: [activityCount === 0 && (_jsx("div", { className: formStyles.emptyComments, children: t('taskForm.noComments') })), taskActivityItems.map((item) => {
371
- if (item.type === 'comment') {
372
- const c = item.comment;
373
- const authorRef = String(c.author || '').trim();
374
- const normalizedAuthor = authorRef.toLowerCase();
375
- const actorFallback = actorFallbackById.get(authorRef);
376
- const actorOption = actorOptionByValue.get(authorRef) || actorOptionByValue.get(normalizedAuthor);
377
- const resolvedActor = c.actor || actorFallback || actorOption || null;
378
- const commentKind = c.actor?.kind === 'human'
379
- ? 'member'
380
- : c.actor?.kind === 'ai'
381
- ? 'agent'
382
- : actorFallback?.kind === 'human'
383
- ? 'member'
384
- : actorFallback?.kind === 'ai'
385
- ? 'agent'
386
- : actorOption?.kind || ((normalizedAuthor === '' || normalizedAuthor === 'user' || normalizedAuthor === 'human') ? 'member' : 'agent');
387
- const authorLabel = c.actor?.label
388
- || actorFallback?.label
389
- || actorOption?.label
390
- || (authorRef ? getAssigneeLabel(authorRef, resolvedAssigneeOptions) : '')
391
- || (commentKind === 'agent' ? formatAiAuthorLabel(authorRef || 'ai') : t('taskForm.you'));
392
- const authorColor = c.actor?.color || actorFallback?.color || actorOption?.color;
393
- const iconName = String(resolvedActor?.icon || (commentKind === 'agent' ? 'Bot' : 'User'));
394
- const CommentActorIcon = IconMap[iconName]
395
- || (commentKind === 'agent' ? Bot : User);
396
- return (_jsxs("div", { className: `${formStyles.comment} ${commentKind === 'agent' ? formStyles.commentAi : formStyles.commentUser}`, children: [_jsxs("div", { className: formStyles.commentHeader, children: [_jsx("span", { className: formStyles.commentAuthor, style: authorColor ? { color: authorColor } : undefined, children: _jsxs(_Fragment, { children: [_jsx(CommentActorIcon, { size: 12, strokeWidth: 1.5 }), " ", authorLabel] }) }), _jsx("span", { className: formStyles.commentTime, children: formatDate(c.timestamp, {
397
- month: 'short', day: 'numeric', hour: '2-digit', minute: '2-digit'
398
- }) })] }), _jsx("div", { className: formStyles.commentText, style: authorColor ? {
399
- background: `linear-gradient(135deg, ${hexToRgba(authorColor, 0.18)} 0%, ${hexToRgba(authorColor, 0.10)} 100%)`,
400
- borderColor: hexToRgba(authorColor, 0.35),
401
- } : undefined, children: _jsx(Markdown, { onTaskIdClick: onOpenTaskById, children: c.text }) })] }, c.id));
402
- }
403
- const event = item.event;
404
- const actorRef = String(event.actor || '').trim();
405
- const normalizedAuthor = actorRef.toLowerCase();
406
- const actorFallback = actorFallbackById.get(actorRef);
407
- const actorOption = actorOptionByValue.get(actorRef) || actorOptionByValue.get(normalizedAuthor);
408
- const resolvedActor = event.actorProfile || actorFallback || actorOption || null;
409
- const eventKind = event.actorType === 'ai'
410
- ? 'agent'
411
- : event.actorType === 'human'
412
- ? 'member'
413
- : 'system';
414
- const authorLabel = event.actorProfile?.label
415
- || actorFallback?.label
416
- || actorOption?.label
417
- || (eventKind === 'system'
418
- ? 'System'
419
- : actorRef
420
- ? getAssigneeLabel(actorRef, resolvedAssigneeOptions)
421
- : eventKind === 'agent'
422
- ? formatAiAuthorLabel('ai')
423
- : t('taskForm.you'));
424
- const authorColor = event.actorProfile?.color || actorFallback?.color || actorOption?.color;
425
- const iconName = String(resolvedActor?.icon || (eventKind === 'agent' ? 'Bot' : eventKind === 'member' ? 'User' : 'ClipboardList'));
426
- const EventActorIcon = IconMap[iconName]
427
- || (eventKind === 'agent' ? Bot : eventKind === 'member' ? User : ClipboardList);
428
- const changeEntries = Object.entries(event.details?.changes || {});
429
- return (_jsxs("div", { className: `${formStyles.comment} ${formStyles.activityEvent} ${eventKind === 'system' ? formStyles.activityEventSystem : ''}`, children: [_jsxs("div", { className: `${formStyles.commentHeader} ${formStyles.activityEventHeader}`, children: [_jsx("span", { className: formStyles.commentAuthor, style: authorColor ? { color: authorColor } : undefined, children: _jsxs(_Fragment, { children: [_jsx(EventActorIcon, { size: 12, strokeWidth: 1.5 }), " ", authorLabel] }) }), _jsx("span", { className: formStyles.commentTime, children: formatDate(event.createdAt, {
430
- month: 'short', day: 'numeric', hour: '2-digit', minute: '2-digit'
431
- }) })] }), _jsxs("div", { className: formStyles.activityEventText, children: [_jsx("div", { children: summarizeTaskEvent(event, formatActivityFieldValue) }), changeEntries.length > 1 && (_jsx("div", { className: formStyles.activityEventChanges, children: changeEntries.map(([field, change]) => (_jsx("span", { className: formStyles.activityEventChange, children: summarizeTaskChange(field, change, formatActivityFieldValue) }, field))) }))] })] }, item.id));
432
- }), _jsx("div", { ref: commentsEndRef })] }), _jsxs("div", { className: formStyles.commentInputArea, children: [_jsx("textarea", { className: formStyles.commentInput, placeholder: t('taskForm.commentPlaceholder'), value: newCommentText, onChange: (e) => onNewCommentTextChange(e.target.value), onKeyDown: (e) => {
433
- if (e.key === 'Enter' && !e.shiftKey) {
434
- e.preventDefault();
435
- onAddComment();
444
+ ? "Deleted tasks are read-only. Restore the task to make changes."
445
+ : "Archived tasks are read-only. Unarchive the task to make changes." })), _jsxs("fieldset", { disabled: isReadOnly, className: `${formStyles.formFieldsetReset} ${editingTaskId ? formStyles.formLayoutWithSidebar : formStyles.formLayout}`.trim(), children: [_jsxs("div", { className: formStyles.formMainColumn, children: [_jsxs("div", { className: styles.field, children: [_jsxs("div", { className: styles.labelRow, children: [_jsx("label", { className: styles.label, children: "Title" }), assignee && (_jsx("span", { title: `Assigned to: ${currentAssigneeLabel}`, className: [
446
+ formStyles.assigneeIndicator,
447
+ currentAssigneeKind === "agent"
448
+ ? formStyles.assigneeIndicatorAgent
449
+ : currentAssigneeKind === "member"
450
+ ? formStyles.assigneeIndicatorUser
451
+ : formStyles.assigneeIndicatorUnassigned,
452
+ ].join(" "), style: currentAssigneeColor
453
+ ? { color: currentAssigneeColor }
454
+ : undefined, children: currentAssigneeKind === "agent" ? (_jsx(Bot, { size: 20 })) : currentAssigneeKind === "member" ? (_jsx(User, { size: 20 })) : (_jsx(HelpCircle, { size: 20 })) }))] }), _jsx("input", { type: "text", value: title, onChange: (e) => onTitleChange(e.target.value), placeholder: "What needs to be done?", className: styles.input, required: true, maxLength: 255, autoFocus: true })] }), _jsxs("div", { className: styles.field, children: [_jsxs("div", { className: styles.labelRow, children: [_jsx("label", { className: styles.label, children: "Description" }), _jsx("button", { type: "button", className: styles.helpLink, onClick: () => onShowMarkdownHelpChange(!showMarkdownHelp), title: "Markdown Help", tabIndex: -1, children: _jsx(HelpCircle, { size: 14 }) })] }), showMarkdownHelp && (_jsxs("div", { className: formStyles.markdownHelp, children: [_jsxs("div", { className: formStyles.helpHeader, children: [_jsx("span", { children: "Markdown Guide" }), _jsx("button", { onClick: () => onShowMarkdownHelpChange(false), className: formStyles.helpClose, children: _jsx(X, { size: 12 }) })] }), _jsxs("div", { className: formStyles.helpGrid, children: [_jsx("div", { children: _jsx("code", { children: "**bold**" }) }), _jsx("div", { children: _jsx("code", { children: "_italic_" }) }), _jsx("div", { children: _jsx("code", { children: "- list" }) }), _jsx("div", { children: _jsx("code", { children: "1. list" }) }), _jsx("div", { className: formStyles.helpGridItem, children: _jsx("code", { children: "`inline code`" }) }), _jsxs("div", { className: formStyles.helpGridItem, children: [_jsx("code", { children: "```" }), _jsx("br", {}), _jsx("code", { children: "code block" }), _jsx("br", {}), _jsx("code", { children: "```" })] }), _jsx("div", { children: _jsx("code", { children: "[link](url)" }) })] })] })), descriptionFocused || !description ? (_jsx("textarea", { className: `${styles.textarea} ${formStyles.descriptionSurface}`, placeholder: "What needs to be done? (Markdown supported)", value: description, onChange: (e) => onDescriptionChange(e.target.value), onFocus: () => onDescriptionFocusedChange(true), onBlur: () => onDescriptionFocusedChange(false), rows: 9, autoFocus: descriptionFocused })) : (_jsx("div", { className: `${styles.textarea} ${formStyles.descriptionSurface} ${formStyles.markdownPreview} ${formStyles.markdownPreviewContainer}`, onClick: () => onDescriptionFocusedChange(true), children: _jsx(Markdown, { onTaskIdClick: onOpenTaskById, children: description }) }))] }), _jsxs("div", { className: formStyles.compactMetaSection, children: [_jsxs("div", { className: formStyles.compactMetaGrid, children: [_jsxs("div", { className: `${styles.field} ${formStyles.compactMetaField}`, children: [_jsx("label", { id: "category-label", className: styles.label, children: taxonomyDisplayLabels?.category || "Category" }), _jsx(TaxonomyDropdown, { value: category, options: categoryOptions, onChange: (value) => onCategoryChange(String(value)), required: true, ariaLabelledBy: "category-label" })] }), _jsxs("div", { className: `${styles.field} ${formStyles.compactMetaField}`, children: [_jsx("label", { id: "type-label", className: styles.label, children: taxonomyDisplayLabels?.type || "Type" }), _jsx(TaxonomyDropdown, { value: type, options: typeOptions, onChange: (value) => onTypeChange(String(value)), ariaLabelledBy: "type-label" })] }), _jsxs("div", { className: `${styles.field} ${formStyles.compactMetaField}`, children: [_jsx("label", { htmlFor: "task-form-scheduled-date", className: styles.label, children: t("taskForm.scheduledLabel") }), _jsx("input", { id: "task-form-scheduled-date", type: "date", value: scheduledDate, onChange: (e) => onScheduledDateChange(e.target.value), className: `${styles.input} ${styles.taskFormDateInput}` })] }), _jsxs("div", { className: `${styles.field} ${formStyles.compactMetaField}`, children: [_jsx("label", { id: "assignee-label", className: styles.label, children: "Assigned To" }), _jsx(TaxonomyDropdown, { value: assignee || "unassigned", options: resolvedAssigneeOptions, onChange: (value) => onAssigneeChange(String(value)), ariaLabelledBy: "assignee-label" })] }), _jsx("div", { className: formStyles.compactMetaField, children: _jsx(LevelSelector, { label: taxonomyDisplayLabels?.priority || "Priority", options: priorities, value: priority, onChange: onPriorityChange, type: "priority", showSelectedLabel: false }) }), _jsxs("div", { className: `${styles.field} ${formStyles.compactMetaField}`, children: [_jsx("label", { htmlFor: "task-form-due-date", className: styles.label, children: t("taskForm.dueLabel") }), _jsx("input", { id: "task-form-due-date", type: "date", value: dueDate, onChange: (e) => onDueDateChange(e.target.value), className: `${styles.input} ${styles.taskFormDateInput}` }), dueBeforeScheduledWarning && (_jsx("span", { className: `${formStyles.taskFormDateWarning} ${formStyles.compactMetaFieldHint}`, children: t("taskForm.dueBeforeScheduled") })), overdueWarning && (_jsx("span", { className: `${formStyles.taskFormDateError} ${formStyles.compactMetaFieldHint}`, children: t("taskForm.overdue") }))] }), manualComplexityEnabled && (_jsx("div", { className: formStyles.compactMetaField, children: _jsx(LevelSelector, { label: "Complexity", options: [
455
+ {
456
+ value: 1,
457
+ label: "Tiny",
458
+ color: "#10b981",
459
+ icon: "Gauge",
460
+ },
461
+ {
462
+ value: 2,
463
+ label: "Low",
464
+ color: "#14b8a6",
465
+ icon: "Gauge",
466
+ },
467
+ {
468
+ value: 3,
469
+ label: "Medium",
470
+ color: "#3b82f6",
471
+ icon: "Gauge",
472
+ },
473
+ {
474
+ value: 4,
475
+ label: "High",
476
+ color: "#8b5cf6",
477
+ icon: "Gauge",
478
+ },
479
+ {
480
+ value: 5,
481
+ label: "Epic",
482
+ color: "#d946ef",
483
+ icon: "Gauge",
484
+ },
485
+ ], value: complexity, onChange: onComplexityChange, type: "general" }) }))] }), !editingTaskId && (_jsxs("div", { className: formStyles.createWorkstreamRow, children: [_jsx("label", { className: styles.label, children: "Attach to workstream" }), _jsxs("div", { className: styles.pathInputGroup, children: [manualWorkstreamEntry ? (_jsx("input", { type: "text", className: styles.input, value: workstreamInput, onChange: (e) => onWorkstreamInputChange(e.target.value), placeholder: "Type a workstream name or reference", "aria-label": "Attach to workstream" })) : (_jsx(TaxonomyDropdown, { value: workstreamInput, options: workstreamOptions, onChange: (value) => onWorkstreamInputChange(String(value)), placeholder: "Select workstream...", ariaLabel: "Attach to workstream", className: formStyles.createWorkstreamDropdown })), _jsx("button", { type: "button", className: styles.secondaryHeaderBtn, onClick: () => {
486
+ if (manualWorkstreamEntry) {
487
+ setManualWorkstreamEntry(false);
488
+ if (!hasMatchingWorkstreamSelection) {
489
+ onWorkstreamInputChange("");
490
+ }
491
+ return;
436
492
  }
437
- }, rows: 1 }), _jsx("button", { type: "button", className: formStyles.sendCommentBtn, onClick: onAddComment, disabled: !newCommentText.trim(), children: _jsx(Send, { size: 16 }) })] })] }))] })), _jsx("div", { className: `${formStyles.taskFormLifecycle} ${formStyles.taskFormMetaDivider}`, children: _jsx("div", { className: formStyles.taskFormMetaGrid, children: currentTask && (_jsxs(_Fragment, { children: [_jsxs("div", { className: formStyles.taskFormDateRow, children: [_jsx("span", { className: formStyles.taskFormDateLabel, children: t('taskForm.createdLabel') }), _jsxs("span", { className: formStyles.taskFormMetaStack, children: [_jsx("span", { className: formStyles.taskFormDateValue, children: createdLabel || t('taskForm.emptyValue') }), _jsx("span", { className: formStyles.taskFormMetaActor, children: createdByLabel ? `by ${createdByLabel}` : t('taskForm.emptyValue') })] })] }), _jsxs("div", { className: formStyles.taskFormDateRow, children: [_jsx("span", { className: formStyles.taskFormDateLabel, children: t('taskForm.updatedLabel') }), _jsxs("span", { className: formStyles.taskFormMetaStack, children: [_jsx("span", { className: formStyles.taskFormDateValue, children: updatedLabel || t('taskForm.emptyValue') }), _jsx("span", { className: formStyles.taskFormMetaActor, children: updatedByLabel ? `by ${updatedByLabel}` : t('taskForm.emptyValue') })] })] }), _jsxs("div", { className: formStyles.taskFormDateRow, children: [_jsx("span", { className: formStyles.taskFormDateLabel, children: t('taskForm.completedLabel') }), _jsxs("span", { className: formStyles.taskFormMetaStack, children: [_jsx("span", { className: formStyles.taskFormDateValue, children: completedLabel || t('taskForm.emptyValue') }), _jsx("span", { className: formStyles.taskFormMetaActor, children: completedByLabel ? `by ${completedByLabel}` : t('taskForm.emptyValue') })] })] })] })) }) })] }), _jsxs("div", { className: formStyles.keyboardHint, children: ["Press ", _jsx("kbd", { children: navigator.platform.includes('Mac') ? '⌥' : 'Alt' }), " to toggle"] })] })] }));
493
+ setManualWorkstreamEntry(true);
494
+ }, "aria-label": manualWorkstreamEntry
495
+ ? "Use workstream dropdown"
496
+ : "Enter workstream by name", title: manualWorkstreamEntry
497
+ ? "Use workstream dropdown"
498
+ : "Enter workstream by name", children: _jsx(Plus, { size: 14 }) })] })] }))] }), _jsxs("div", { className: formStyles.sectionBlock, children: [_jsx("div", { className: formStyles.taxonomyFieldsGrid, children: visibleTaxonomies.map((tax) => {
499
+ if (tax.id === "approach" && tax.isSystem)
500
+ return null; // Workflow stays hidden until the dedicated refactor lands.
501
+ const selectedRawValue = formTaxonomies[tax.id];
502
+ const selectedValues = Array.isArray(selectedRawValue)
503
+ ? selectedRawValue.map((value) => String(value))
504
+ : selectedRawValue !== undefined &&
505
+ selectedRawValue !== null &&
506
+ selectedRawValue !== ""
507
+ ? [String(selectedRawValue)]
508
+ : [];
509
+ const selectableOptions = tax.options.filter((option) => option.status !== "retired" ||
510
+ selectedValues.includes(String(option.value)));
511
+ return (_jsxs("div", { className: styles.field, children: [_jsx("label", { className: styles.label, children: tax.label }), tax.description && (_jsx("div", { className: `${formStyles.settingsHint} tf-text-helper`, children: tax.description })), tax.widgetType === "level" ? (_jsx(LevelSelector, { label: tax.label, options: selectableOptions.map((option) => ({
512
+ ...option,
513
+ label: option.status === "retired"
514
+ ? `${option.label} (Retired)`
515
+ : option.label,
516
+ })), value: formTaxonomies[tax.id] || "", onChange: (val) => onTaxonomyChange(tax.id, val), type: "general", hideLabel: true })) : tax.multiSelect ? (_jsx("div", { className: `${styles.dropdownList} ${formStyles.softSectionSurface}`, children: selectableOptions.map((opt) => {
517
+ const optionValue = String(opt.value);
518
+ const isSelected = selectedValues.includes(optionValue);
519
+ return (_jsxs("button", { type: "button", className: `${formStyles.specialistChip} ${isSelected ? formStyles.specialistChipActive : ""}`, onClick: () => {
520
+ const nextValues = isSelected
521
+ ? selectedValues.filter((value) => value !== optionValue)
522
+ : [...selectedValues, optionValue];
523
+ onTaxonomyChange(tax.id, nextValues);
524
+ }, title: isSelected ? "Click to remove" : "Click to add", children: [isSelected && _jsx(Check, { size: 12 }), opt.status === "retired"
525
+ ? `${opt.label} (Retired)`
526
+ : opt.label] }, optionValue));
527
+ }) })) : (_jsxs("div", { className: styles.fieldIconWrapper, style: {
528
+ "--field-color": "var(--text-primary)",
529
+ "--field-border": "rgba(255, 255, 255, 0.1)",
530
+ "--field-bg": "rgba(255, 255, 255, 0.02)",
531
+ }, children: [(() => {
532
+ const selectedOption = tax.options.find((o) => String(o.value) ===
533
+ String(formTaxonomies[tax.id]));
534
+ const IconComp = selectedOption?.icon &&
535
+ Icons[selectedOption.icon]
536
+ ? Icons[selectedOption.icon]
537
+ : HelpCircle;
538
+ const color = selectedOption?.color || "var(--text-secondary)";
539
+ return (_jsx("div", { className: styles.fieldIcon, style: { color: resolveColor(color) }, children: _jsx(IconComp, { size: 16 }) }));
540
+ })(), _jsxs("select", { value: formTaxonomies[tax.id] || "", onChange: (e) => onTaxonomyChange(tax.id, e.target.value), className: `${styles.select} ${styles.field_dynamic}`, required: tax.isRequired, children: [_jsxs("option", { value: "", children: ["Select ", tax.label, "..."] }), selectableOptions.map((opt) => (_jsx("option", { value: opt.value, children: opt.status === "retired"
541
+ ? `${opt.label} (Retired)`
542
+ : opt.label }, opt.value)))] })] }))] }, tax.id));
543
+ }) }), checklistEnabled && (_jsxs("div", { className: styles.specialistSection, children: [_jsxs("div", { className: styles.toggleHeading, onClick: () => onShowChecklistChange(!showChecklist), title: showChecklist ? "Hide checklist" : "Show checklist", children: [_jsxs("label", { className: styles.label, children: ["Checklist", " ", checklistItems.length > 0 &&
544
+ `(${completedChecklistCount}/${checklistItems.length})`] }), showChecklist ? (_jsx(ChevronDown, { size: 14 })) : (_jsx(ChevronRight, { size: 14 }))] }), showChecklist && (_jsxs("div", { className: `${styles.dropdownList} ${formStyles.stackedList} ${formStyles.stackedListSpaced}`, children: [_jsxs("div", { className: styles.pathInputGroup, children: [_jsx("input", { type: "text", className: styles.input, placeholder: "Add checklist item", value: newChecklistItemText, onChange: (e) => setNewChecklistItemText(e.target.value), onKeyDown: (e) => {
545
+ if (e.key === "Enter") {
546
+ e.preventDefault();
547
+ handleAddChecklistItem();
548
+ }
549
+ } }), _jsx("button", { type: "button", className: styles.secondaryHeaderBtn, onClick: handleAddChecklistItem, disabled: !newChecklistItemText.trim(), children: "Add" })] }), checklistItems.length > 0 ? (_jsx(DndContext, { sensors: checklistSensors, collisionDetection: closestCenter, onDragEnd: handleChecklistReorder, children: _jsx(SortableContext, { items: checklistItems.map((item) => item.id), strategy: verticalListSortingStrategy, children: checklistItems.map((item, index) => (_jsx(SortableChecklistRow, { id: item.id || `checklist-${index}`, item: item, onToggle: () => {
550
+ const now = new Date().toISOString();
551
+ onChecklistItemsChange(checklistItems.map((entry, entryIndex) => entryIndex === index
552
+ ? {
553
+ ...entry,
554
+ isCompleted: !entry.isCompleted,
555
+ updatedAt: now,
556
+ }
557
+ : entry));
558
+ }, onRemove: () => onChecklistItemsChange(checklistItems
559
+ .filter((_, entryIndex) => entryIndex !== index)
560
+ .map((entry, entryIndex) => ({
561
+ ...entry,
562
+ order: entryIndex,
563
+ }))) }, item.id || `checklist-${index}`))) }) })) : (_jsx("div", { className: `${formStyles.settingsHint} tf-text-helper`, children: "No checklist items yet." }))] }))] }))] }), _jsxs("div", { children: [_jsx(TaskContextUpload, { taskId: editingTaskId, taskReferenceLabel: currentTaskReferenceLabel, workspaceId: currentWorkspaceId, apiBaseUrl: apiBaseUrl, contextFiles: contextFiles, onAddContextFile: onAddContextFile, onRemoveContextFile: onRemoveContextFile, onUpdateContextCaption: onUpdateContextCaption }), _jsx("div", { className: `${formStyles.taskFormLifecycle} ${formStyles.taskFormMetaDivider}`, children: _jsx("div", { className: formStyles.taskFormMetaGrid, children: currentTask && (_jsxs(_Fragment, { children: [_jsxs("div", { className: formStyles.taskFormDateRow, children: [_jsx("span", { className: formStyles.taskFormDateLabel, children: t("taskForm.createdLabel") }), _jsxs("span", { className: formStyles.taskFormMetaStack, children: [_jsx("span", { className: formStyles.taskFormDateValue, children: createdLabel || t("taskForm.emptyValue") }), _jsx("span", { className: formStyles.taskFormMetaActor, children: createdByLabel
564
+ ? `by ${createdByLabel}`
565
+ : t("taskForm.emptyValue") })] })] }), _jsxs("div", { className: formStyles.taskFormDateRow, children: [_jsx("span", { className: formStyles.taskFormDateLabel, children: t("taskForm.updatedLabel") }), _jsxs("span", { className: formStyles.taskFormMetaStack, children: [_jsx("span", { className: formStyles.taskFormDateValue, children: updatedLabel || t("taskForm.emptyValue") }), _jsx("span", { className: formStyles.taskFormMetaActor, children: updatedByLabel
566
+ ? `by ${updatedByLabel}`
567
+ : t("taskForm.emptyValue") })] })] }), _jsxs("div", { className: formStyles.taskFormDateRow, children: [_jsx("span", { className: formStyles.taskFormDateLabel, children: t("taskForm.completedLabel") }), _jsxs("span", { className: formStyles.taskFormMetaStack, children: [_jsx("span", { className: formStyles.taskFormDateValue, children: completedLabel || t("taskForm.emptyValue") }), _jsx("span", { className: formStyles.taskFormMetaActor, children: completedByLabel
568
+ ? `by ${completedByLabel}`
569
+ : t("taskForm.emptyValue") })] })] })] })) }) })] })] }), activitySidebar, _jsxs("div", { className: formStyles.keyboardHint, children: ["Press ", _jsx("kbd", { children: navigator.platform.includes("Mac") ? "⌥" : "Alt" }), " to toggle"] })] })] }));
438
570
  }