@taskforcehq/taskforce 0.3.305 → 0.3.307

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 (440) hide show
  1. package/README.md +0 -2
  2. package/dist/Taskforce.module.css +603 -107
  3. package/dist/TaskforceCore.js +216 -43
  4. package/dist/TaskforceCore.test.js +611 -28
  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 +46 -2
  14. package/dist/compat/workspaceSyncCompat.test.js +38 -1
  15. package/dist/components/activity/EntityActivityTimeline.d.ts +34 -0
  16. package/dist/components/activity/EntityActivityTimeline.js +81 -0
  17. package/dist/components/context/ContextAttachmentManager.d.ts +14 -0
  18. package/dist/components/context/ContextAttachmentManager.js +561 -0
  19. package/dist/components/features/AgentsModule.d.ts +14 -1
  20. package/dist/components/features/AgentsModule.js +366 -18
  21. package/dist/components/features/AgentsModule.test.js +507 -8
  22. package/dist/components/features/AnnotatedAttachmentWorkspace.d.ts +3 -1
  23. package/dist/components/features/AnnotatedAttachmentWorkspace.js +118 -4
  24. package/dist/components/features/AnnotatedAttachmentWorkspace.test.js +52 -0
  25. package/dist/components/features/DocumentIndex.d.ts +2 -2
  26. package/dist/components/features/DocumentIndex.js +3 -4
  27. package/dist/components/features/DocumentViewer.d.ts +7 -1
  28. package/dist/components/features/DocumentViewer.js +62 -4
  29. package/dist/components/features/DocumentWorkspace.d.ts +10 -2
  30. package/dist/components/features/DocumentWorkspace.js +15 -4
  31. package/dist/components/features/DocumentWorkspace.test.js +51 -1
  32. package/dist/components/features/TaskSettings.d.ts +2 -0
  33. package/dist/components/features/TaskSettings.js +83 -78
  34. package/dist/components/features/TaskSettings.test.js +171 -82
  35. package/dist/components/features/documentWorkspaceModel.d.ts +1 -1
  36. package/dist/components/task/TaskActionHeader.js +11 -5
  37. package/dist/components/task/TaskActionHeader.test.js +10 -0
  38. package/dist/components/task/TaskCard.js +24 -29
  39. package/dist/components/task/TaskCard.test.js +38 -3
  40. package/dist/components/task/TaskContextUpload.js +4 -526
  41. package/dist/components/task/TaskContextUpload.test.js +3 -3
  42. package/dist/components/task/TaskForm.js +61 -116
  43. package/dist/components/task/TaskForm.test.js +110 -8
  44. package/dist/components/task/TaskKanban.d.ts +2 -1
  45. package/dist/components/task/TaskKanban.js +94 -21
  46. package/dist/components/task/TaskKanban.test.js +36 -0
  47. package/dist/components/task/TaskList.js +5 -1
  48. package/dist/components/task/TaskList.test.js +4 -0
  49. package/dist/components/ui/EditableAvatarButton.d.ts +18 -0
  50. package/dist/components/ui/EditableAvatarButton.js +18 -0
  51. package/dist/components/ui/HierarchyActionBadge.d.ts +10 -0
  52. package/dist/components/ui/HierarchyActionBadge.js +5 -0
  53. package/dist/components/views/AppShellHeader.d.ts +3 -1
  54. package/dist/components/views/AppShellHeader.js +27 -11
  55. package/dist/components/views/AppShellHeader.test.js +18 -1
  56. package/dist/components/views/PlanComparisonPage.d.ts +9 -1
  57. package/dist/components/views/PlanComparisonPage.js +78 -14
  58. package/dist/components/views/PlanComparisonPage.test.js +257 -0
  59. package/dist/components/views/PlansPage.js +127 -36
  60. package/dist/components/views/PlansPage.test.js +294 -12
  61. package/dist/components/views/StandaloneLayout.js +529 -592
  62. package/dist/components/views/WidgetView.js +10 -8
  63. package/dist/components/views/panels/PlanningDrawer.d.ts +25 -5
  64. package/dist/components/views/panels/PlanningDrawer.js +270 -55
  65. package/dist/components/views/panels/PlanningDrawer.test.d.ts +1 -0
  66. package/dist/components/views/panels/PlanningDrawer.test.js +529 -0
  67. package/dist/components/views/planningScope.d.ts +20 -0
  68. package/dist/components/views/planningScope.js +25 -0
  69. package/dist/components/views/planningScope.test.d.ts +1 -0
  70. package/dist/components/views/planningScope.test.js +60 -0
  71. package/dist/components/views/standalone/modals/AccountHubModal.d.ts +26 -1
  72. package/dist/components/views/standalone/modals/AccountHubModal.js +76 -2
  73. package/dist/components/views/standalone/modals/AccountHubModal.test.js +20 -3
  74. package/dist/components/views/standalone/modals/AvatarImageManagerModal.d.ts +20 -0
  75. package/dist/components/views/standalone/modals/AvatarImageManagerModal.js +127 -0
  76. package/dist/components/views/standalone/modals/EditProfileModal.d.ts +2 -9
  77. package/dist/components/views/standalone/modals/EditProfileModal.js +3 -2
  78. package/dist/components/views/standalone/modals/SyncEnableWarningModal.d.ts +9 -0
  79. package/dist/components/views/standalone/modals/SyncEnableWarningModal.js +6 -0
  80. package/dist/components/views/standalone/modals/SyncEnableWarningModal.test.d.ts +1 -0
  81. package/dist/components/views/standalone/modals/SyncEnableWarningModal.test.js +24 -0
  82. package/dist/components/views/standalone/modals/SyncStatusModal.d.ts +2 -1
  83. package/dist/components/views/standalone/modals/SyncStatusModal.js +2 -2
  84. package/dist/config/envSchema.js +1 -0
  85. package/dist/core/AiProfileService.d.ts +46 -2
  86. package/dist/core/AiProfileService.js +1073 -82
  87. package/dist/core/AiProfileService.test.js +19 -1
  88. package/dist/core/AiProfiles.test.js +1249 -44
  89. package/dist/core/AttachmentLinkService.d.ts +3 -0
  90. package/dist/core/AttachmentLinkService.js +110 -23
  91. package/dist/core/AuthIdentityService.d.ts +107 -0
  92. package/dist/core/AuthIdentityService.js +284 -0
  93. package/dist/core/AuthTokenService.d.ts +4 -0
  94. package/dist/core/AuthTokenService.js +34 -6
  95. package/dist/core/AuthTokenService.test.js +3 -3
  96. package/dist/core/EntitlementsPolicy.test.js +2 -2
  97. package/dist/core/GlobalSettingsService.js +127 -6
  98. package/dist/core/McpTokenService.d.ts +12 -0
  99. package/dist/core/McpTokenService.js +44 -1
  100. package/dist/core/PlanEntitlementService.d.ts +23 -2
  101. package/dist/core/PlanEntitlementService.js +343 -57
  102. package/dist/core/PlanFeatureCatalog.test.d.ts +1 -0
  103. package/dist/core/PlanFeatureCatalog.test.js +84 -0
  104. package/dist/core/PlanVersionPolicy.test.js +35 -2
  105. package/dist/core/PlanningEntities.test.js +418 -4
  106. package/dist/core/SignupMonetizationSettings.test.js +199 -27
  107. package/dist/core/SyncReconciliationService.js +2 -65
  108. package/dist/core/SystemAdmin.test.js +42 -42
  109. package/dist/core/TaskActivityService.d.ts +28 -1
  110. package/dist/core/TaskActivityService.js +227 -46
  111. package/dist/core/TaskActivitySync.test.d.ts +1 -0
  112. package/dist/core/TaskActivitySync.test.js +90 -0
  113. package/dist/core/TaskAttachmentsCanonical.test.js +51 -1
  114. package/dist/core/TaskAuditTimeline.test.js +96 -0
  115. package/dist/core/Taskforce.d.ts +415 -40
  116. package/dist/core/Taskforce.ids.test.js +58 -0
  117. package/dist/core/Taskforce.js +1543 -266
  118. package/dist/core/Taskforce.listTasksSlim.test.d.ts +1 -0
  119. package/dist/core/Taskforce.listTasksSlim.test.js +91 -0
  120. package/dist/core/Taskforce.mcpAuth.test.js +109 -0
  121. package/dist/core/UserProfileAvatarDrafts.test.js +30 -3
  122. package/dist/core/WorkspaceLifecycleService.js +0 -5
  123. package/dist/core/WorkspacePermissions.test.js +25 -1
  124. package/dist/core/WorkspacePlanMode.test.js +117 -16
  125. package/dist/core/shared.d.ts +1 -1
  126. package/dist/core/shared.js +5 -1
  127. package/dist/core/types.d.ts +75 -2
  128. package/dist/hooks/auth/useTaskforceAuthBootstrap.js +28 -2
  129. package/dist/hooks/bootstrapTimeouts.d.ts +4 -0
  130. package/dist/hooks/bootstrapTimeouts.js +8 -0
  131. package/dist/hooks/bootstrapTimeouts.test.d.ts +1 -0
  132. package/dist/hooks/bootstrapTimeouts.test.js +12 -0
  133. package/dist/hooks/sync/bootstrap.js +2 -0
  134. package/dist/hooks/sync/controlPlane.d.ts +37 -0
  135. package/dist/hooks/sync/controlPlane.js +78 -0
  136. package/dist/hooks/sync/controlPlane.test.d.ts +1 -0
  137. package/dist/hooks/sync/controlPlane.test.js +121 -0
  138. package/dist/hooks/sync/lifecyclePolicy.d.ts +45 -0
  139. package/dist/hooks/sync/lifecyclePolicy.js +93 -0
  140. package/dist/hooks/sync/lifecyclePolicy.test.d.ts +1 -0
  141. package/dist/hooks/sync/lifecyclePolicy.test.js +124 -0
  142. package/dist/hooks/sync/orchestratorShared.d.ts +13 -4
  143. package/dist/hooks/sync/orchestratorShared.js +129 -48
  144. package/dist/hooks/sync/orchestratorShared.test.js +50 -1
  145. package/dist/hooks/sync/pullLifecycle.d.ts +31 -0
  146. package/dist/hooks/sync/pullLifecycle.js +24 -0
  147. package/dist/hooks/sync/pullLifecycle.test.d.ts +1 -0
  148. package/dist/hooks/sync/pullLifecycle.test.js +80 -0
  149. package/dist/hooks/sync/recovery.d.ts +16 -2
  150. package/dist/hooks/sync/recovery.js +179 -53
  151. package/dist/hooks/sync/recovery.test.d.ts +1 -0
  152. package/dist/hooks/sync/recovery.test.js +292 -0
  153. package/dist/hooks/sync/transfers.d.ts +16 -3
  154. package/dist/hooks/sync/transfers.js +210 -66
  155. package/dist/hooks/sync/transfers.test.d.ts +1 -0
  156. package/dist/hooks/sync/transfers.test.js +928 -0
  157. package/dist/hooks/sync/useRecentSyncEvents.d.ts +21 -0
  158. package/dist/hooks/sync/useRecentSyncEvents.js +92 -0
  159. package/dist/hooks/sync/useRecentSyncEvents.test.d.ts +1 -0
  160. package/dist/hooks/sync/useRecentSyncEvents.test.js +124 -0
  161. package/dist/hooks/sync/useSyncStatusActions.d.ts +30 -0
  162. package/dist/hooks/sync/useSyncStatusActions.js +88 -0
  163. package/dist/hooks/sync/useSyncStatusActions.test.d.ts +1 -0
  164. package/dist/hooks/sync/useSyncStatusActions.test.js +133 -0
  165. package/dist/hooks/sync/useSyncStatusControls.d.ts +25 -0
  166. package/dist/hooks/sync/useSyncStatusControls.js +60 -0
  167. package/dist/hooks/sync/useSyncStatusControls.test.d.ts +1 -0
  168. package/dist/hooks/sync/useSyncStatusControls.test.js +88 -0
  169. package/dist/hooks/useFilterSort.js +13 -13
  170. package/dist/hooks/useFilterSort.test.js +87 -1
  171. package/dist/hooks/useSyncOrchestrator.aiProfiles.test.js +12 -0
  172. package/dist/hooks/useSyncOrchestrator.d.ts +9 -2
  173. package/dist/hooks/useSyncOrchestrator.js +409 -183
  174. package/dist/hooks/useSyncOrchestrator.retry-closure.test.js +407 -0
  175. package/dist/hooks/useTaskData.js +8 -3
  176. package/dist/hooks/useTaskData.test.js +45 -0
  177. package/dist/hooks/useTaskforce.d.ts +145 -52
  178. package/dist/hooks/useTaskforce.js +592 -253
  179. package/dist/hooks/useTaskforce.runtime-routing.test.d.ts +1 -0
  180. package/dist/hooks/useTaskforce.runtime-routing.test.js +246 -0
  181. package/dist/hooks/useTaskforce.sync-behavior.test.js +2468 -201
  182. package/dist/hooks/useWorkspaceSyncController.d.ts +1 -0
  183. package/dist/hooks/useWorkspaceSyncController.js +13 -2
  184. package/dist/hooks/useWorkspaceSyncController.test.js +48 -0
  185. package/dist/hooks/workspace/useTaskforceWorkspaceBootstrap.d.ts +16 -0
  186. package/dist/hooks/workspace/useTaskforceWorkspaceBootstrap.js +328 -40
  187. package/dist/localization/locales/en-US.d.ts +0 -1
  188. package/dist/localization/locales/en-US.js +0 -1
  189. package/dist/localization/locales/es-419.js +0 -1
  190. package/dist/localization/locales/pt-BR.js +0 -1
  191. package/dist/mcp/canonicalAssetHelpers.d.ts +16 -4
  192. package/dist/mcp/canonicalAssetHelpers.js +18 -10
  193. package/dist/mcp/canonicalAssetHelpers.test.d.ts +1 -0
  194. package/dist/mcp/canonicalAssetHelpers.test.js +67 -0
  195. package/dist/mcp/clientIntegrations.d.ts +1 -0
  196. package/dist/mcp/clientIntegrations.js +25 -0
  197. package/dist/mcp/clientIntegrations.test.js +14 -3
  198. package/dist/mcp/clientProfileDefaults.js +3 -3
  199. package/dist/mcp/collaborationRegistrar.js +7 -7
  200. package/dist/mcp/documentAssetRegistrar.js +116 -10
  201. package/dist/mcp/runtime.d.ts +3 -0
  202. package/dist/mcp/runtime.js +1099 -185
  203. package/dist/mcp/runtime.test.js +1491 -140
  204. package/dist/mcp/taskAttachmentTypes.d.ts +1 -1
  205. package/dist/mcp/taskPlanningRegistrar.js +48 -0
  206. package/dist/mcp/toolRegistry.d.ts +56 -0
  207. package/dist/mcp/toolRegistry.js +8 -0
  208. package/dist/mcp/toolRegistry.test.js +4 -0
  209. package/dist/migrations/taskSchemaMigrations.d.ts +1 -1
  210. package/dist/migrations/taskSchemaMigrations.js +192 -61
  211. package/dist/migrations/taskSchemaMigrations.test.js +15 -0
  212. package/dist/resources/templates/workflow-sources/workflowDocs.mjs +6 -6
  213. package/dist/resources/templates/workflows/collaborate.yaml +1 -1
  214. package/dist/resources/templates/workflows/evaluate.yaml +2 -2
  215. package/dist/resources/templates/workflows/plan.yaml +1 -1
  216. package/dist/resources/templates/workflows/review.yaml +2 -2
  217. package/dist/server/aiProfileAvatarAssets.d.ts +49 -0
  218. package/dist/server/aiProfileAvatarAssets.js +319 -0
  219. package/dist/server/aiProfileAvatarAssets.test.d.ts +1 -0
  220. package/dist/server/aiProfileAvatarAssets.test.js +213 -0
  221. package/dist/server/annotatedAttachmentsRoutes.test.js +69 -10
  222. package/dist/server/auth/providers/apple.d.ts +33 -0
  223. package/dist/server/auth/providers/apple.js +82 -0
  224. package/dist/server/auth/providers/appleClientSecret.d.ts +37 -0
  225. package/dist/server/auth/providers/appleClientSecret.js +65 -0
  226. package/dist/server/auth/providers/github.d.ts +26 -0
  227. package/dist/server/auth/providers/github.js +86 -0
  228. package/dist/server/auth/providers/google.d.ts +21 -0
  229. package/dist/server/auth/providers/google.js +56 -0
  230. package/dist/server/auth/providers/types.d.ts +21 -0
  231. package/dist/server/auth/providers/types.js +1 -0
  232. package/dist/server/auth.d.ts +2 -0
  233. package/dist/server/auth.js +6 -3
  234. package/dist/server/documentReviewRoutes.test.js +36 -3
  235. package/dist/server/index.cookieProxy.test.js +1 -0
  236. package/dist/server/index.d.ts +12 -0
  237. package/dist/server/index.js +124 -9
  238. package/dist/server/index.rateLimit.test.js +3 -0
  239. package/dist/server/index.test.js +111 -3
  240. package/dist/server/routes/admin.d.ts +3 -0
  241. package/dist/server/routes/admin.js +459 -28
  242. package/dist/server/routes/annotatedAttachments.js +51 -0
  243. package/dist/server/routes/auth.d.ts +26 -2
  244. package/dist/server/routes/auth.js +1170 -71
  245. package/dist/server/routes/authSupport.d.ts +5 -1
  246. package/dist/server/routes/authSupport.js +13 -2
  247. package/dist/server/routes/billing.js +372 -35
  248. package/dist/server/routes/billing.test.js +595 -12
  249. package/dist/server/routes/documents.d.ts +1 -0
  250. package/dist/server/routes/documents.js +182 -70
  251. package/dist/server/routes/shared.d.ts +28 -2
  252. package/dist/server/routes/shared.js +69 -14
  253. package/dist/server/routes/sync.integration.test.js +658 -39
  254. package/dist/server/routes/sync.js +74 -608
  255. package/dist/server/routes/syncAuxRoutes.d.ts +15 -0
  256. package/dist/server/routes/syncAuxRoutes.js +91 -0
  257. package/dist/server/routes/syncAuxRoutes.test.d.ts +1 -0
  258. package/dist/server/routes/syncAuxRoutes.test.js +158 -0
  259. package/dist/server/routes/syncPullApplyRoutes.d.ts +36 -0
  260. package/dist/server/routes/syncPullApplyRoutes.js +204 -0
  261. package/dist/server/routes/syncPushRoutes.d.ts +24 -0
  262. package/dist/server/routes/syncPushRoutes.js +212 -0
  263. package/dist/server/routes/syncRouteGuards.d.ts +36 -0
  264. package/dist/server/routes/syncRouteGuards.js +67 -0
  265. package/dist/server/routes/syncRouteGuards.test.d.ts +1 -0
  266. package/dist/server/routes/syncRouteGuards.test.js +94 -0
  267. package/dist/server/routes/syncRouteTypes.d.ts +13 -0
  268. package/dist/server/routes/syncRouteTypes.js +1 -0
  269. package/dist/server/routes/syncSnapshotRoutes.d.ts +66 -0
  270. package/dist/server/routes/syncSnapshotRoutes.js +182 -0
  271. package/dist/server/routes/tasks.js +55 -10
  272. package/dist/server/routes/workspaces.js +36 -5
  273. package/dist/server/routes.js +147 -11
  274. package/dist/server/routes.test.js +2704 -183
  275. package/dist/services/workflowExportSnapshots.test.js +2 -0
  276. package/dist/shared/aiProfileSeatScope.d.ts +5 -0
  277. package/dist/shared/aiProfileSeatScope.js +21 -0
  278. package/dist/shared/aiProfileSurfaceType.d.ts +1 -1
  279. package/dist/shared/aiProfileSurfaceType.js +10 -8
  280. package/dist/shared/passwordPolicy.d.ts +13 -0
  281. package/dist/shared/passwordPolicy.js +84 -0
  282. package/dist/shared/passwordPolicy.test.d.ts +1 -0
  283. package/dist/shared/passwordPolicy.test.js +34 -0
  284. package/dist/storage/objectStorage.d.ts +1 -0
  285. package/dist/storage/objectStorage.js +2 -1
  286. package/dist/storage/objectStorageClient.d.ts +1 -0
  287. package/dist/storage/objectStorageClient.js +41 -8
  288. package/dist/storage/objectStorageClient.test.js +26 -1
  289. package/dist/storage/objectStorageConfig.js +3 -1
  290. package/dist/storage/objectStorageConfig.test.js +13 -1
  291. package/dist/storage/workspaceAssetStore.d.ts +15 -3
  292. package/dist/storage/workspaceAssetStore.js +132 -44
  293. package/dist/storage/workspaceAssetStore.test.js +93 -6
  294. package/dist/styles/fonts.css +70 -0
  295. package/dist/sync/collaborationSyncPayload.d.ts +6 -1
  296. package/dist/sync/collaborationSyncPayload.js +19 -11
  297. package/dist/sync/collaborationSyncPayload.test.js +14 -1
  298. package/dist/sync/collaborationSyncState.d.ts +3 -3
  299. package/dist/sync/collaborationSyncState.js +19 -12
  300. package/dist/sync/contentSyncPayload.d.ts +5 -1
  301. package/dist/sync/contentSyncPayload.js +9 -0
  302. package/dist/sync/contentSyncPayload.test.js +39 -0
  303. package/dist/sync/contentSyncState.d.ts +9 -9
  304. package/dist/sync/contentSyncState.js +248 -107
  305. package/dist/sync/planningSyncPayload.js +5 -0
  306. package/dist/sync/planningSyncPayload.test.js +24 -0
  307. package/dist/sync/syncApplyHandlers.d.ts +11 -4
  308. package/dist/sync/syncApplyHandlers.js +115 -42
  309. package/dist/sync/syncApplyHandlers.test.js +251 -4
  310. package/dist/sync/syncFieldSemantics.d.ts +12 -0
  311. package/dist/sync/syncFieldSemantics.js +55 -0
  312. package/dist/sync/syncResourceAdapters.d.ts +46 -0
  313. package/dist/sync/syncResourceAdapters.js +79 -0
  314. package/dist/sync/syncService.d.ts +6 -0
  315. package/dist/sync/syncService.js +65 -1
  316. package/dist/sync/syncService.test.js +96 -3
  317. package/dist/sync/taskSyncChangeKey.d.ts +2 -0
  318. package/dist/sync/taskSyncChangeKey.js +143 -0
  319. package/dist/sync/taskSyncPayload.js +3 -8
  320. package/dist/sync/workspacePullFeed.d.ts +4 -0
  321. package/dist/sync/workspacePullFeed.js +132 -72
  322. package/dist/sync/workspacePullFeed.test.js +92 -0
  323. package/dist/sync/workspaceRepair.js +5 -2
  324. package/dist/sync/workspaceSyncModel.d.ts +73 -6
  325. package/dist/sync/workspaceSyncModel.js +342 -73
  326. package/dist/sync/workspaceSyncModel.test.js +559 -14
  327. package/dist/sync/workspaceSyncState.d.ts +17 -0
  328. package/dist/sync/workspaceSyncState.js +2 -1
  329. package/dist/sync/workspaceSyncSurface.js +3 -8
  330. package/dist/types.d.ts +50 -2
  331. package/dist/ui/assets/AgentsModule-2y82_3DO.js +1 -0
  332. package/dist/ui/assets/AnnotatedAttachmentWorkspace-BTLZZCiQ.js +3 -0
  333. package/dist/ui/assets/AnnotatedAttachmentWorkspace-BVKew-mH.css +1 -0
  334. package/dist/ui/assets/ContextAttachmentManager-D2epuNnG.js +3 -0
  335. package/dist/ui/assets/CourierPrime-Bold-BuLj_dpw.woff2 +0 -0
  336. package/dist/ui/assets/CourierPrime-BoldItalic-BrK2pUkZ.woff2 +0 -0
  337. package/dist/ui/assets/CourierPrime-Italic-DFUZK5Ey.woff2 +0 -0
  338. package/dist/ui/assets/CourierPrime-Regular-BsKphzVf.woff2 +0 -0
  339. package/dist/ui/assets/DocumentWorkspace-D4rJKXzd.css +1 -0
  340. package/dist/ui/assets/DocumentWorkspace-sVa-3Do6.js +252 -0
  341. package/dist/ui/assets/EntityActivityTimeline-DyqvvhuF.css +1 -0
  342. package/dist/ui/assets/EntityActivityTimeline-Zods27y_.js +1 -0
  343. package/dist/ui/assets/{InitiativesModule-Dhm7M8e7.js → InitiativesModule-D4XseTwW.js} +1 -1
  344. package/dist/ui/assets/Noir_medium-bxQwKGzB.woff2 +0 -0
  345. package/dist/ui/assets/Noir_regular-ojf2kxlG.woff2 +0 -0
  346. package/dist/ui/assets/PlansPage-D5AcbO0L.js +1 -0
  347. package/dist/ui/assets/PlansPage-Dvqsz5zc.css +1 -0
  348. package/dist/ui/assets/RussoOne-Regular-Cu_qq_qC.woff2 +0 -0
  349. package/dist/ui/assets/SpecialElite-Regular-Di6gSvAS.woff2 +0 -0
  350. package/dist/ui/assets/TaskContextUpload-uC5qx4Bv.js +1 -0
  351. package/dist/ui/assets/TaskSettings-CqvPAsTv.js +9 -0
  352. package/dist/ui/assets/{WorkflowsModule-BcS4afRn.js → WorkflowsModule-oOA9bcdK.js} +4 -4
  353. package/dist/ui/assets/documentReferences-DPZuTgHh.js +1 -0
  354. package/dist/ui/assets/index-607WPo_X.js +4 -0
  355. package/dist/ui/assets/index-CYyodXsg.css +1 -0
  356. package/dist/ui/assets/{vendor-icons-BSUaRwF8.js → vendor-icons-B32hGuF2.js} +1 -1
  357. package/dist/ui/fonts/CourierPrime-Bold.ttf +0 -0
  358. package/dist/ui/fonts/CourierPrime-Bold.woff2 +0 -0
  359. package/dist/ui/fonts/CourierPrime-BoldItalic.ttf +0 -0
  360. package/dist/ui/fonts/CourierPrime-BoldItalic.woff2 +0 -0
  361. package/dist/ui/fonts/CourierPrime-Italic.ttf +0 -0
  362. package/dist/ui/fonts/CourierPrime-Italic.woff2 +0 -0
  363. package/dist/ui/fonts/CourierPrime-Regular.ttf +0 -0
  364. package/dist/ui/fonts/CourierPrime-Regular.woff2 +0 -0
  365. package/dist/ui/fonts/Noir_medium.otf +0 -0
  366. package/dist/ui/fonts/Noir_medium.woff2 +0 -0
  367. package/dist/ui/fonts/Noir_regular.otf +0 -0
  368. package/dist/ui/fonts/Noir_regular.woff2 +0 -0
  369. package/dist/ui/fonts/RussoOne-Regular.woff2 +0 -0
  370. package/dist/ui/fonts/SpecialElite-Regular.ttf +0 -0
  371. package/dist/ui/fonts/SpecialElite-Regular.woff2 +0 -0
  372. package/dist/ui/index.html +3 -3
  373. package/dist/ui/product/agent manager.png +0 -0
  374. package/dist/ui/product/agent_manager_02.png +0 -0
  375. package/dist/utils/accountProfileSummaryCache.d.ts +5 -0
  376. package/dist/utils/aiProfileEvents.d.ts +7 -0
  377. package/dist/utils/aiProfileEvents.js +6 -0
  378. package/dist/utils/assignees.d.ts +8 -0
  379. package/dist/utils/assignees.js +10 -1
  380. package/dist/utils/assignees.test.d.ts +1 -0
  381. package/dist/utils/assignees.test.js +25 -0
  382. package/dist/utils/avatarDisplay.d.ts +1 -0
  383. package/dist/utils/avatarDisplay.js +15 -0
  384. package/dist/utils/avatarUpload.d.ts +27 -0
  385. package/dist/utils/avatarUpload.js +132 -0
  386. package/dist/utils/avatarUpload.test.d.ts +1 -0
  387. package/dist/utils/avatarUpload.test.js +40 -0
  388. package/dist/utils/bootstrapTrace.d.ts +7 -0
  389. package/dist/utils/bootstrapTrace.js +29 -0
  390. package/dist/utils/commercialLifecyclePresentation.js +3 -0
  391. package/dist/utils/contextAssetEvents.d.ts +2 -0
  392. package/dist/utils/contextFiles.d.ts +3 -1
  393. package/dist/utils/contextFiles.js +2 -1
  394. package/dist/utils/syncEventDetails.d.ts +3 -0
  395. package/dist/utils/syncEventDetails.js +64 -0
  396. package/dist/utils/syncEventDetails.test.d.ts +1 -0
  397. package/dist/utils/syncEventDetails.test.js +23 -0
  398. package/dist/utils/syncEventPresentation.d.ts +15 -0
  399. package/dist/utils/syncEventPresentation.js +129 -0
  400. package/dist/utils/syncEventPresentation.test.d.ts +1 -0
  401. package/dist/utils/syncEventPresentation.test.js +64 -0
  402. package/dist/utils/syncStatusPresentation.d.ts +72 -0
  403. package/dist/utils/syncStatusPresentation.js +217 -0
  404. package/dist/utils/syncStatusPresentation.test.d.ts +1 -0
  405. package/dist/utils/syncStatusPresentation.test.js +164 -0
  406. package/dist/utils/taskActivity.d.ts +9 -1
  407. package/dist/utils/taskActivity.js +290 -22
  408. package/dist/utils/taskActivity.test.js +141 -8
  409. package/dist/utils/taskSearch.d.ts +22 -0
  410. package/dist/utils/taskSearch.js +87 -0
  411. package/dist/utils/taskSearch.test.d.ts +1 -0
  412. package/dist/utils/taskSearch.test.js +69 -0
  413. package/dist/utils/taskUtils.js +10 -3
  414. package/dist/utils/taskUtils.test.d.ts +1 -0
  415. package/dist/utils/taskUtils.test.js +19 -0
  416. package/dist/utils/workspaceSyncPresentation.d.ts +4 -0
  417. package/dist/utils/workspaceSyncPresentation.js +68 -2
  418. package/dist/utils/workspaceSyncPresentation.test.js +36 -1
  419. package/package.json +3 -2
  420. package/scripts/export-sqlite-to-postgres.mjs +7 -9
  421. package/scripts/run-e2e-realtime.sh +17 -3
  422. package/scripts/run-smoke.sh +17 -5
  423. package/scripts/run-soak.sh +17 -5
  424. package/src/resources/templates/workflow-sources/workflowDocs.mjs +6 -6
  425. package/src/resources/templates/workflows/collaborate.yaml +1 -1
  426. package/src/resources/templates/workflows/evaluate.yaml +2 -2
  427. package/src/resources/templates/workflows/plan.yaml +1 -1
  428. package/src/resources/templates/workflows/review.yaml +2 -2
  429. package/dist/ui/assets/AgentsModule-N2MnQBZk.js +0 -1
  430. package/dist/ui/assets/AnnotatedAttachmentWorkspace-BG6P_GVW.js +0 -3
  431. package/dist/ui/assets/AnnotatedAttachmentWorkspace-BaS2VwIr.css +0 -1
  432. package/dist/ui/assets/DocumentWorkspace-B3nV_Gc5.css +0 -1
  433. package/dist/ui/assets/DocumentWorkspace-DRbDMwh8.js +0 -252
  434. package/dist/ui/assets/PlansPage-C2dd2n1X.css +0 -1
  435. package/dist/ui/assets/PlansPage-Cq0zXj3I.js +0 -1
  436. package/dist/ui/assets/RussoOne-Regular-C3BxZIj7.ttf +0 -0
  437. package/dist/ui/assets/TaskSettings-ln0aF7xc.js +0 -9
  438. package/dist/ui/assets/index-C2-OXZV7.css +0 -1
  439. package/dist/ui/assets/index-DcSI5F86.js +0 -6
  440. package/scripts/migrate-planning-model.ts +0 -233
@@ -1,8 +1,8 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
- import React, { useMemo, useCallback, useState, useEffect, useRef, useLayoutEffect, Suspense } from 'react';
2
+ import React, { useMemo, useCallback, useState, useEffect, useRef, Suspense } from 'react';
3
3
  import { createPortal } from 'react-dom';
4
4
  import { useLocation, useNavigate } from 'react-router-dom';
5
- import { Settings, Plus, Filter, Layers, User, Maximize2, Minimize2, Check, ChevronLeft, ArrowUp, ArrowDown, ArrowUpDown, AlertTriangle, Save, Loader2, Folder, FileText, LogOut, NotebookTabs, ClipboardCheck, Cloud, CloudOff, Users, ClockAlert, ChevronsUp, ChevronsDown, EyeOff, Zap, HelpCircle, Bot, Image, Workflow } from 'lucide-react';
5
+ import { Settings, Plus, Filter, Layers, User, Maximize2, Minimize2, Check, ChevronLeft, ArrowUp, ArrowDown, ArrowUpDown, AlertTriangle, Save, Loader2, Folder, FileText, LogOut, NotebookTabs, ClipboardCheck, Cloud, CloudOff, Users, ClockAlert, ChevronsUp, ChevronsDown, EyeOff, Zap, HelpCircle, Bot, Image, Workflow, Plug } from 'lucide-react';
6
6
  import { STATUS_OPTIONS, getStatusDefinition } from '../../utils/constants';
7
7
  import styles from '../../Taskforce.module.css';
8
8
  import shellStyles from './WorkspaceShell.module.css';
@@ -12,9 +12,11 @@ import folderBrowserStyles from './FolderBrowser.module.css';
12
12
  import modalStyles from '../ui/Modal.module.css';
13
13
  import { CreateWorkspaceConfirmModal } from './standalone/modals/CreateWorkspaceConfirmModal';
14
14
  import { AccountHubModal } from './standalone/modals/AccountHubModal';
15
+ import { AvatarImageManagerModal } from './standalone/modals/AvatarImageManagerModal';
15
16
  import { EditProfileModal } from './standalone/modals/EditProfileModal';
16
17
  import { HelpModal } from './standalone/modals/HelpModal';
17
18
  import { ScheduleWarningModal } from './standalone/modals/ScheduleWarningModal';
19
+ import { SyncEnableWarningModal } from './standalone/modals/SyncEnableWarningModal';
18
20
  import { SyncStatusModal } from './standalone/modals/SyncStatusModal';
19
21
  import { TeamManagementModal } from './standalone/modals/TeamManagementModal';
20
22
  import { Modal } from '../ui/Modal';
@@ -27,14 +29,17 @@ import { resolveDraftDefaultsForGrouping } from '../../utils/taskDraftDefaults';
27
29
  import { formatDate } from '../../utils/formatting';
28
30
  import { resolveHeaderWorkspaceSyncPresentation } from '../../utils/workspaceSyncPresentation';
29
31
  import { getTaskReferenceLabel, parseTaskReference } from '../../utils/taskReferences';
32
+ import { normalizeTaskSearchQuery, taskMatchesSearch } from '../../utils/taskSearch';
30
33
  import { formatInitiativeReference, formatWorkstreamReference, parseWorkstreamReference, resolveInitiativeReference } from '../../utils/planningReferences';
31
34
  import { DEFAULT_TYPE_VALUE } from '../../shared/taxonomyDefaults.js';
32
35
  import { getImageReferenceLabel } from '../../utils/imageReferences';
36
+ import { isPlanningEntityVisibleForScope, scopePlanningSummaries } from './planningScope';
37
+ import { buildReferenceMismatchSummaries, buildSyncDiagnosticsSummary, buildSyncEventRows, getActiveReferenceMismatchCount, resolveSyncStageLabel, resolveSyncStatusMeta, } from '../../utils/syncStatusPresentation';
33
38
  import { buildCustomTaxonomySortOptions } from '../../utils/taxonomySorting.js';
34
39
  import { getLocale, t } from '../../localization';
35
40
  import { resolveAnnotatedAttachmentDetail } from '../../utils/annotatedAttachments';
36
41
  import { ACCOUNT_PROFILE_SUMMARY_PATH, invalidateAccountProfileSummaryCache, loadAccountProfileSummaryCached } from '../../utils/accountProfileSummaryCache';
37
- import { fetchTaskforceApi } from '../../utils/taskforceApiClient';
42
+ import { prepareAvatarUploadFile } from '../../utils/avatarUpload';
38
43
  import { resolveCommercialLifecyclePresentation } from '../../utils/commercialLifecyclePresentation';
39
44
  import { AppShellHeader } from './AppShellHeader';
40
45
  import { getTaskforceCloudEnvironmentConfig, parseTaskforceCloudEnvironment, resolveTaskforceCloudEnvironmentFromBaseUrl } from '../../config/cloudEnvironment';
@@ -47,6 +52,9 @@ import { EmptyWorkspaceFilterBar, WorkspaceHeaderActions, WorkspaceHeaderDivider
47
52
  import { ScheduleSidebar, SCHEDULE_SIDEBAR_WIDTH_PX } from './panels/ScheduleSidebar';
48
53
  import { DOCUMENT_ATTACHMENT_FILTER_OPTIONS, DOCUMENT_TYPE_FILTER_OPTIONS, } from '../features/documentWorkspaceFilters';
49
54
  import { canManageTeamWorkspace as computeCanManageTeamWorkspace, canOpenTeamManagement as computeCanOpenTeamManagement } from './teamManagementAccess';
55
+ import { useRecentSyncEvents } from '../../hooks/sync/useRecentSyncEvents';
56
+ import { useSyncStatusActions } from '../../hooks/sync/useSyncStatusActions';
57
+ import { useSyncStatusControls } from '../../hooks/sync/useSyncStatusControls';
50
58
  import { toDateOnlyLocal, parseDateOnlyLocal, startOfWeek, getDayOffsetFromWeekStart, addDays, dateOnlyToIsoWeekKey, SCHEDULE_DAY_KEYS, SCHEDULE_DAY_LABELS, } from './panels/scheduleUtils';
51
59
  import { AGENTS_WORKSPACE_FEATURE_KEY, ANNOTATED_ATTACHMENTS_WORKSPACE_FEATURE_KEY, DOCUMENT_WORKSPACE_FEATURE_KEY, INITIATIVES_WORKSPACE_FEATURE_KEY, WORKFLOWS_WORKSPACE_FEATURE_KEY, resolveFeatureAccess, } from '../../runtime/appGates';
52
60
  // Heavy feature modules — lazy-loaded to reduce the initial app chunk size.
@@ -60,72 +68,6 @@ const PlansPage = React.lazy(() => import('./PlansPage').then(m => ({ default: m
60
68
  const PROFILE_AVATAR_ACCEPT = 'image/png,image/jpeg,image/webp,image/gif';
61
69
  const PROFILE_AVATAR_MAX_BYTES = 5 * 1024 * 1024;
62
70
  const resolveSameOriginTaskforcePath = (path) => path;
63
- function getSyncEventStableKey(event, index = 0) {
64
- const explicitId = String(event.id ?? '').trim();
65
- if (explicitId)
66
- return `id:${explicitId}`;
67
- return [
68
- String(event.occurredAt || '').trim() || 'unknown-time',
69
- String(event.eventType || '').trim() || 'sync',
70
- String(event.status || '').trim() || 'unknown-status',
71
- String(event.statusCode ?? ''),
72
- String(event.changeCount ?? ''),
73
- String(event.requestMs ?? ''),
74
- String(event.errorMessage || '').trim(),
75
- JSON.stringify(event.details || null),
76
- String(index)
77
- ].join('|');
78
- }
79
- function mergeRecentSyncEvents(previous, incoming, limit = 15) {
80
- const merged = new Map();
81
- for (const [index, event] of previous.entries()) {
82
- merged.set(getSyncEventStableKey(event, index), event);
83
- }
84
- for (const [index, event] of incoming.entries()) {
85
- merged.set(getSyncEventStableKey(event, index), event);
86
- }
87
- return Array.from(merged.values())
88
- .sort((left, right) => String(right.occurredAt || '').localeCompare(String(left.occurredAt || '')))
89
- .slice(0, Math.max(1, limit));
90
- }
91
- function areSyncEventListsEqual(left, right) {
92
- if (left.length !== right.length)
93
- return false;
94
- for (let index = 0; index < left.length; index += 1) {
95
- if (getSyncEventStableKey(left[index], index) !== getSyncEventStableKey(right[index], index)) {
96
- return false;
97
- }
98
- }
99
- return true;
100
- }
101
- function getActiveReferenceMismatchCount(events) {
102
- let count = 0;
103
- for (const event of events) {
104
- const message = String(event.errorMessage || '').toLowerCase();
105
- if (message.includes('reference number mismatch')) {
106
- count += 1;
107
- continue;
108
- }
109
- if (String(event.status || '').toLowerCase() === 'success') {
110
- break;
111
- }
112
- }
113
- return count;
114
- }
115
- function getActiveReferenceMismatchEvents(events) {
116
- const mismatches = [];
117
- for (const event of events) {
118
- const message = String(event.errorMessage || '').toLowerCase();
119
- if (message.includes('reference number mismatch')) {
120
- mismatches.push(event);
121
- continue;
122
- }
123
- if (String(event.status || '').toLowerCase() === 'success') {
124
- break;
125
- }
126
- }
127
- return mismatches;
128
- }
129
71
  function resolveConnectedEnvironmentLabel(explicitEnvironment, candidates) {
130
72
  const parsedExplicitEnvironment = parseTaskforceCloudEnvironment(explicitEnvironment);
131
73
  if (parsedExplicitEnvironment) {
@@ -203,7 +145,7 @@ export function StandaloneLayout(props) {
203
145
  // Form Actions
204
146
  handleAddComment, handleSetWorkstreamForCurrentTask, handleOpenTaskById,
205
147
  // Other Props for Form
206
- taxonomies, activeTypes, priorities, taxonomyDisplayLabels, approaches, copiedId, handleCopyId, handleToggleInProgress, handleToggleComplete, handleToggleReview, handleToggleCancel, handleArchiveTask, handleDelete, handleUnarchive, handleRestoreDeletedTask, handlePermanentlyDeleteDeletedTask, handleEmptyDeletedTasks, fetchTasks, searchQuery, setSearchQuery, filterCategories, setFilterCategories, filterTypes, setFilterTypes, filterPriorities, setFilterPriorities, filterAssignees, setFilterAssignees, assigneeOptions, taskScope, setTaskScope,
148
+ taxonomies, activeTypes, priorities, taxonomyDisplayLabels, approaches, copiedId, handleCopyId, handleToggleInProgress, handleToggleComplete, handleToggleReview, handleToggleCancel, handleArchiveTask, handleDelete, handleUnarchive, handleRestoreDeletedTask, handlePermanentlyDeleteDeletedTask, handleEmptyDeletedTasks, fetchTasks, searchQuery, setSearchQuery, filterCategories, setFilterCategories, filterTypes, setFilterTypes, filterPriorities, setFilterPriorities, filterAssignees, setFilterAssignees, hasInitedFilters, assigneeOptions, taskScope, setTaskScope,
207
149
  // filterStatus, setFilterStatus, // This was duplicated, removed one instance
208
150
  sortBy, setSortBy, clearFilters, // setSearchQuery, // This was duplicated, removed one instance
209
151
  // filterCategories, setFilterCategories, // This was duplicated, removed one instance
@@ -214,7 +156,7 @@ export function StandaloneLayout(props) {
214
156
  // clearFilters, // This was duplicated, removed one instance
215
157
  // activeTypes, // This was duplicated, removed one instance
216
158
  // Settings State & Props
217
- settingsModel, configLoaded, currentTheme, setCurrentTheme, pathSaved, saveSettings, keyShortcut, setKeyShortcut, globalWeekStartsOn, locale, jsonBackupEnabled, setJsonBackupEnabled, mcpHostRoot, setMcpHostRoot, settingsSection, setSettingsSection, runtimeMode, workspaceSwitchingEnabled, cloudAuthConfigured, authRequiredForApi, authBlocked, isAuthenticated, authUserId, authWorkspaceId, authUserEmail, authUserDisplayName, authUserAvatarUrl, authSessionResolved, realtimeSyncEnabled, realtimeSyncFlagSource, workspaceCloudSyncEnabled, workspaceSyncPhase, workspaceSyncStatus, workspaceSyncSummary, workspaceSyncRecommendedAction, workspaceSyncBusy, workspaceSyncPendingChanges, saveWorkspaceCloudSyncSettings, pushNotice, userGlobalSyncStatus, workspaceLastSuccessfulSyncAt, workspaceLastPullAt, workspaceLastPushAt, workspaceLastErrorAt, userGlobalSyncError, workspaceLastErrorMessage, retryWorkspaceCloudSync, resetWorkspaceSyncCursorAndPull, getWorkspaceSyncDiagnostics, currentWorkspaceId, currentWorkspaceRole, availableWorkspaces, switchWorkspace, updateCurrentUserProfile, resolveCloudAuthUrl = resolveSameOriginTaskforcePath, logout, projectRoot, projectName, mcpScriptPath, serverHostRoot,
159
+ settingsModel, configLoaded, currentTheme, setCurrentTheme, pathSaved, saveSettings, keyShortcut, setKeyShortcut, globalWeekStartsOn, locale, jsonBackupEnabled, setJsonBackupEnabled, mcpHostRoot, setMcpHostRoot, settingsSection, setSettingsSection, runtimeMode, workspaceSwitchingEnabled, cloudAuthConfigured, authRequiredForApi, authBlocked, isAuthenticated, authUserId, authWorkspaceId, authUserEmail, authUserDisplayName, authUserAvatarUrl, authSessionResolved, realtimeSyncEnabled, realtimeSyncFlagSource, workspaceCloudSyncEnabled, workspaceSyncPhase, workspaceSyncStatus, workspaceSyncSummary, workspaceSyncRecommendedAction, workspaceSyncBusy, workspaceSyncPendingChanges, saveWorkspaceCloudSyncSettings, pushNotice, userGlobalSyncStatus, workspaceLastSuccessfulSyncAt, workspaceLastPullAt, workspaceLastPushAt, workspaceLastErrorAt, userGlobalSyncError, workspaceLastErrorMessage, retryWorkspaceCloudSync, resetWorkspaceSyncCursorAndPull, getWorkspaceSyncDiagnostics, currentWorkspaceId, currentWorkspaceRole, availableWorkspaces, switchWorkspace, updateCurrentUserProfile, resolveCloudAuthUrl = resolveSameOriginTaskforcePath, logout, fetchLoginMethods, unlinkLoginMethod, addPasswordToAccount, beginOAuthLogin, beginOAuthLink, availableAuthProviders, projectRoot, projectName, mcpScriptPath, serverHostRoot,
218
160
  // Browser Props
219
161
  showFolderBrowser, setShowFolderBrowser, folders, files, currentBrowsePath, fetchFolders, browserTarget, setBrowserTarget, handleSelectPath, handleAddPath, handleRemovePath,
220
162
  // Settings Managers
@@ -270,6 +212,9 @@ export function StandaloneLayout(props) {
270
212
  const [showFilters, setShowFilters] = useState(false);
271
213
  const [taskModalFullscreen, setTaskModalFullscreen] = useState(false);
272
214
  const [planningDrawerOpen, setPlanningDrawerOpen] = useState(false);
215
+ const [documentTrayOpen, setDocumentTrayOpen] = useState(true);
216
+ const [imageTrayOpen, setImageTrayOpen] = useState(true);
217
+ const [agentTrayOpen, setAgentTrayOpen] = useState(true);
273
218
  const [planningTreeCollapsed, setPlanningTreeCollapsed] = useState(false);
274
219
  const [planningPrimaryCollapsed, setPlanningPrimaryCollapsed] = useState(false);
275
220
  const [planningSecondaryCollapsed, setPlanningSecondaryCollapsed] = useState(false);
@@ -284,6 +229,13 @@ export function StandaloneLayout(props) {
284
229
  const [planningDraftDescription, setPlanningDraftDescription] = useState('');
285
230
  const [planningDraftOwner, setPlanningDraftOwner] = useState('');
286
231
  const [planningDraftInitiativeId, setPlanningDraftInitiativeId] = useState('');
232
+ const [isSubmittingPlanningEditor, setIsSubmittingPlanningEditor] = useState(false);
233
+ const taskContextAttachmentDraftRef = useRef({
234
+ taskId: null,
235
+ attachments: [],
236
+ });
237
+ const planningContextAttachmentDraftsRef = useRef(new Map());
238
+ const planningContextAttachmentUpdateQueueRef = useRef(new Map());
287
239
  const [documentTypeFilters, setDocumentTypeFilters] = useState(() => DOCUMENT_TYPE_FILTER_OPTIONS.map((option) => option.value));
288
240
  const [documentAttachmentFilters, setDocumentAttachmentFilters] = useState(() => DOCUMENT_ATTACHMENT_FILTER_OPTIONS.map((option) => option.value));
289
241
  const [requestedDocPath, setRequestedDocPath] = useState(null);
@@ -293,10 +245,32 @@ export function StandaloneLayout(props) {
293
245
  const [requestedAnnotatedOpenVersion, setRequestedAnnotatedOpenVersion] = useState(0);
294
246
  const [canonicalAnnotatedImageReferenceLabels, setCanonicalAnnotatedImageReferenceLabels] = useState({});
295
247
  const [layoutStateReady, setLayoutStateReady] = useState(false);
248
+ const planningEditorSubmitInFlightRef = useRef(false);
296
249
  const loadedLayoutStateWorkspaceRef = useRef('');
297
250
  const requestedAnnotatedTargetRef = useRef(null);
298
251
  const requestedAnnotatedSessionIdRef = useRef(null);
299
252
  const annotatedImageReferenceFetchesRef = useRef(new Set());
253
+ useEffect(() => {
254
+ taskContextAttachmentDraftRef.current = {
255
+ taskId: editingTaskId || null,
256
+ attachments,
257
+ };
258
+ }, [editingTaskId, attachments]);
259
+ const updateTaskContextAttachments = useCallback((getNextAttachments) => {
260
+ const taskId = editingTaskId || null;
261
+ const draft = taskContextAttachmentDraftRef.current;
262
+ const currentAttachments = draft.taskId === taskId ? draft.attachments : attachments;
263
+ const nextAttachments = getNextAttachments(currentAttachments);
264
+ taskContextAttachmentDraftRef.current = {
265
+ taskId,
266
+ attachments: nextAttachments,
267
+ };
268
+ setAttachments(nextAttachments);
269
+ setAttachmentsDirty(true);
270
+ if (taskId) {
271
+ void handleUpdateTask(taskId, { attachments: nextAttachments });
272
+ }
273
+ }, [attachments, editingTaskId, handleUpdateTask, setAttachments, setAttachmentsDirty]);
300
274
  const workspaceUiStateHeaders = useMemo(() => {
301
275
  const headers = {};
302
276
  const workspaceIdHeader = String(currentWorkspaceId || '').trim();
@@ -307,7 +281,33 @@ export function StandaloneLayout(props) {
307
281
  }, [runtimeMode, currentWorkspaceId]);
308
282
  const layoutStateWorkspaceKey = useMemo(() => `${runtimeMode}:${String(currentWorkspaceId || 'default').trim() || 'default'}`, [runtimeMode, currentWorkspaceId]);
309
283
  const annotatedLayoutCacheKey = useMemo(() => `taskforce:annotate-layout:${layoutStateWorkspaceKey}`, [layoutStateWorkspaceKey]);
284
+ const taskOriginWorkspaceModuleKey = useMemo(() => `taskforce:task-origin-workspace-module:${layoutStateWorkspaceKey}`, [layoutStateWorkspaceKey]);
310
285
  const annotatedWorkspaceReady = useMemo(() => (runtimeMode === 'cloud' ? authSessionResolved : configLoaded), [authSessionResolved, configLoaded, runtimeMode]);
286
+ const openedTaskOriginWorkspaceModuleRef = useRef(false);
287
+ const taskOriginWorkspaceModuleRestoreHandledRef = useRef(false);
288
+ const rememberTaskOriginWorkspaceModule = useCallback((taskId) => {
289
+ const normalizedTaskId = String(taskId || '').trim();
290
+ if (!normalizedTaskId || typeof window === 'undefined')
291
+ return;
292
+ openedTaskOriginWorkspaceModuleRef.current = true;
293
+ try {
294
+ window.sessionStorage.setItem(taskOriginWorkspaceModuleKey, normalizedTaskId);
295
+ }
296
+ catch {
297
+ // ignore cache write failure
298
+ }
299
+ }, [taskOriginWorkspaceModuleKey]);
300
+ const clearTaskOriginWorkspaceModule = useCallback(() => {
301
+ openedTaskOriginWorkspaceModuleRef.current = false;
302
+ if (typeof window === 'undefined')
303
+ return;
304
+ try {
305
+ window.sessionStorage.removeItem(taskOriginWorkspaceModuleKey);
306
+ }
307
+ catch {
308
+ // ignore cache clear failure
309
+ }
310
+ }, [taskOriginWorkspaceModuleKey]);
311
311
  useEffect(() => {
312
312
  requestedAnnotatedTargetRef.current = requestedAnnotatedTarget;
313
313
  }, [requestedAnnotatedTarget]);
@@ -354,6 +354,7 @@ export function StandaloneLayout(props) {
354
354
  pushNotice(documentsWorkspaceUnavailableMessage, 'error');
355
355
  return;
356
356
  }
357
+ rememberTaskOriginWorkspaceModule(customEvent.detail?.taskId);
357
358
  setRequestedDocPath(fsPath || null);
358
359
  setRequestedDocAssetId(assetId);
359
360
  setActiveWorkspaceModule('docs');
@@ -362,7 +363,7 @@ export function StandaloneLayout(props) {
362
363
  return () => {
363
364
  window.removeEventListener('taskforce:open-markdown-document', handleOpenMarkdownDocument);
364
365
  };
365
- }, [documentsWorkspaceEnabled, documentsWorkspaceUnavailableMessage, pushNotice, setActiveWorkspaceModule]);
366
+ }, [documentsWorkspaceEnabled, documentsWorkspaceUnavailableMessage, pushNotice, rememberTaskOriginWorkspaceModule, setActiveWorkspaceModule]);
366
367
  useEffect(() => {
367
368
  if (resolvedWorkspaceModule !== activeWorkspaceModule) {
368
369
  setActiveWorkspaceModule(resolvedWorkspaceModule);
@@ -393,6 +394,29 @@ export function StandaloneLayout(props) {
393
394
  // ignore cache read failure
394
395
  }
395
396
  }, [annotatedLayoutCacheKey]);
397
+ useEffect(() => {
398
+ if (taskOriginWorkspaceModuleRestoreHandledRef.current)
399
+ return;
400
+ if (resolvedWorkspaceModule !== 'docs' && resolvedWorkspaceModule !== 'annotate')
401
+ return;
402
+ taskOriginWorkspaceModuleRestoreHandledRef.current = true;
403
+ if (openedTaskOriginWorkspaceModuleRef.current || typeof window === 'undefined')
404
+ return;
405
+ try {
406
+ const taskId = String(window.sessionStorage.getItem(taskOriginWorkspaceModuleKey) || '').trim();
407
+ if (!taskId)
408
+ return;
409
+ window.sessionStorage.removeItem(taskOriginWorkspaceModuleKey);
410
+ setActiveWorkspaceModule('tasks');
411
+ }
412
+ catch {
413
+ // ignore cache read failure
414
+ }
415
+ }, [resolvedWorkspaceModule, setActiveWorkspaceModule, taskOriginWorkspaceModuleKey]);
416
+ useEffect(() => {
417
+ taskOriginWorkspaceModuleRestoreHandledRef.current = false;
418
+ openedTaskOriginWorkspaceModuleRef.current = false;
419
+ }, [taskOriginWorkspaceModuleKey]);
396
420
  useEffect(() => {
397
421
  if (loadedLayoutStateWorkspaceRef.current === layoutStateWorkspaceKey)
398
422
  return;
@@ -442,6 +466,15 @@ export function StandaloneLayout(props) {
442
466
  if (typeof state.planningDrawerOpen === 'boolean') {
443
467
  setPlanningDrawerOpen(state.planningDrawerOpen);
444
468
  }
469
+ if (typeof state.documentTrayOpen === 'boolean') {
470
+ setDocumentTrayOpen(state.documentTrayOpen);
471
+ }
472
+ if (typeof state.imageTrayOpen === 'boolean') {
473
+ setImageTrayOpen(state.imageTrayOpen);
474
+ }
475
+ if (typeof state.agentTrayOpen === 'boolean') {
476
+ setAgentTrayOpen(state.agentTrayOpen);
477
+ }
445
478
  if (typeof state.planningTreeCollapsed === 'boolean') {
446
479
  setPlanningTreeCollapsed(state.planningTreeCollapsed);
447
480
  }
@@ -517,6 +550,9 @@ export function StandaloneLayout(props) {
517
550
  showFilters,
518
551
  documentTypeFilters,
519
552
  documentAttachmentFilters,
553
+ documentTrayOpen,
554
+ imageTrayOpen,
555
+ agentTrayOpen,
520
556
  planningDrawerOpen,
521
557
  planningTreeCollapsed,
522
558
  planningPrimaryCollapsed,
@@ -546,6 +582,9 @@ export function StandaloneLayout(props) {
546
582
  showFilters,
547
583
  documentTypeFilters,
548
584
  documentAttachmentFilters,
585
+ documentTrayOpen,
586
+ imageTrayOpen,
587
+ agentTrayOpen,
549
588
  planningDrawerOpen,
550
589
  planningTreeCollapsed,
551
590
  planningPrimaryCollapsed,
@@ -623,6 +662,7 @@ export function StandaloneLayout(props) {
623
662
  setRequestedAnnotatedTarget(detail);
624
663
  setRequestedAnnotatedSessionId(null);
625
664
  setRequestedAnnotatedOpenVersion((current) => current + 1);
665
+ rememberTaskOriginWorkspaceModule(detail.taskId);
626
666
  setActiveWorkspaceModule('annotate');
627
667
  };
628
668
  window.addEventListener('taskforce:open-annotated-attachment', handleOpenAnnotatedAttachment);
@@ -634,6 +674,7 @@ export function StandaloneLayout(props) {
634
674
  annotatedAttachmentsWorkspaceEnabled,
635
675
  annotatedAttachmentsWorkspaceUnavailableMessage,
636
676
  pushNotice,
677
+ rememberTaskOriginWorkspaceModule,
637
678
  setActiveWorkspaceModule
638
679
  ]);
639
680
  useEffect(() => {
@@ -729,7 +770,14 @@ export function StandaloneLayout(props) {
729
770
  }, [props.deletedTasks]);
730
771
  const filteredDeletedTasks = useMemo(() => {
731
772
  const deletedTasks = deletedScopeAllTasks;
732
- const searchNeedle = searchQuery.trim().toLowerCase();
773
+ const normalizedSearch = normalizeTaskSearchQuery(searchQuery);
774
+ const searchContext = {
775
+ categories: activeCategories,
776
+ types: activeTypes,
777
+ priorities,
778
+ assigneeOptions: props.assigneeOptions,
779
+ taxonomies
780
+ };
733
781
  const allCategoriesSelected = activeCategories.every((taskCategory) => filterCategories.includes(taskCategory.value));
734
782
  const allTypesSelected = activeTypes.every((taskType) => filterTypes.includes(taskType.value));
735
783
  const selectedPriorities = new Set(filterPriorities.map((value) => String(value)));
@@ -737,12 +785,7 @@ export function StandaloneLayout(props) {
737
785
  const selectedStatuses = new Set(filterStatus.map((value) => String(value)));
738
786
  const allStatusesSelected = STATUS_OPTIONS.every((statusOption) => selectedStatuses.has(String(statusOption.value)));
739
787
  return deletedTasks.filter((task) => {
740
- const referenceLabel = (getTaskReferenceLabel(task) || task.id).toLowerCase();
741
- const matchesSearch = !searchNeedle
742
- || task.title.toLowerCase().includes(searchNeedle)
743
- || String(task.description || '').toLowerCase().includes(searchNeedle)
744
- || task.id.toLowerCase().includes(searchNeedle)
745
- || referenceLabel.includes(searchNeedle);
788
+ const matchesSearch = taskMatchesSearch(task, normalizedSearch, searchContext);
746
789
  const matchesCategory = allCategoriesSelected || filterCategories.includes(task.category);
747
790
  const matchesType = allTypesSelected || filterTypes.includes(task.type || DEFAULT_TYPE_VALUE);
748
791
  const matchesPriority = allPrioritiesSelected || selectedPriorities.has(String(task.priority));
@@ -836,94 +879,144 @@ export function StandaloneLayout(props) {
836
879
  : taskScope === 'deleted'
837
880
  ? deletedScopeAllTasks
838
881
  : props.tasks.filter((task) => !task.isArchived);
839
- const summarizeTasks = (scopeTasks) => {
840
- const taskCount = scopeTasks.length;
841
- const completedTaskCount = scopeTasks.filter((task) => task.isArchived || task.status === 'done' || task.status === 'cancelled').length;
882
+ const planningPreviewTasks = taskScope === 'deleted'
883
+ ? deletedScopeAllTasks
884
+ : Array.from([...props.tasks, ...archivedScopeAllTasks]
885
+ .reduce((map, task) => map.set(task.id, task), new Map())
886
+ .values());
887
+ const summarizeTaskCounts = (taskCount, completedTaskCount) => {
842
888
  return {
843
889
  progressPercent: taskCount > 0 ? Math.round((completedTaskCount / taskCount) * 100) : 0,
844
890
  taskCount,
845
891
  completedTaskCount,
846
892
  };
847
893
  };
894
+ const useBootstrapTaskSummaries = taskScope === 'open'
895
+ && props.planningBootstrapTaskSummaries.length > 0
896
+ && (props.loadingTasks || props.tasks.length === 0);
897
+ const workstreamTaskSummaryById = new Map();
898
+ const workstreamTaskPreviewsById = new Map();
899
+ if (useBootstrapTaskSummaries) {
900
+ props.planningBootstrapTaskSummaries.forEach((summary) => {
901
+ workstreamTaskSummaryById.set(summary.workstreamId, summary);
902
+ workstreamTaskPreviewsById.set(summary.workstreamId, summary.tasks || []);
903
+ });
904
+ }
905
+ else {
906
+ planningScopedTasks.forEach((task) => {
907
+ const workstreamId = String(task.workstreamId || '').trim();
908
+ if (!workstreamId)
909
+ return;
910
+ const current = workstreamTaskSummaryById.get(workstreamId) || {
911
+ workstreamId,
912
+ taskCount: 0,
913
+ completedTaskCount: 0,
914
+ tasks: [],
915
+ };
916
+ current.taskCount += 1;
917
+ if (task.isArchived || task.status === 'done' || task.status === 'cancelled') {
918
+ current.completedTaskCount += 1;
919
+ }
920
+ workstreamTaskSummaryById.set(workstreamId, current);
921
+ });
922
+ planningPreviewTasks.forEach((task) => {
923
+ const workstreamId = String(task.workstreamId || '').trim();
924
+ if (!workstreamId)
925
+ return;
926
+ const current = workstreamTaskPreviewsById.get(workstreamId) || [];
927
+ current.push({
928
+ id: task.id,
929
+ referenceNumber: task.referenceNumber ?? null,
930
+ title: task.title,
931
+ status: task.status || null,
932
+ isArchived: Boolean(task.isArchived),
933
+ });
934
+ workstreamTaskPreviewsById.set(workstreamId, current);
935
+ });
936
+ }
848
937
  const workstreamTaskIds = new Map();
849
938
  const workstreamInitiativeIds = new Map();
850
939
  const initiativeTaskIds = new Map();
851
- const workstreamById = new Map();
852
- const initiativeById = new Map();
940
+ const workstreamsByInitiativeId = new Map();
853
941
  const buildWorkstreamSummary = (workstream) => {
854
- const scopedTasks = planningScopedTasks.filter((task) => (task.workstreamId || null) === workstream.id);
855
- const taskPreviews = scopedTasks.map((task) => ({
856
- id: task.id,
857
- referenceNumber: task.referenceNumber ?? null,
858
- title: task.title,
859
- status: task.status || null,
860
- }));
942
+ const taskSummary = workstreamTaskSummaryById.get(workstream.id);
943
+ const taskPreviews = workstreamTaskPreviewsById.get(workstream.id) || taskSummary?.tasks || [];
861
944
  const summary = {
862
945
  id: workstream.id,
863
946
  referenceNumber: workstream.referenceNumber ?? null,
864
947
  title: workstream.title,
865
948
  description: String(workstream.description || '').trim() || undefined,
949
+ ownerId: workstream.ownerId ? String(workstream.ownerId) : null,
866
950
  ownerLabel: workstream.ownerId ? assigneeLabelByValue.get(String(workstream.ownerId)) || String(workstream.ownerId) : null,
867
- ...summarizeTasks(scopedTasks),
951
+ ...summarizeTaskCounts(taskSummary?.taskCount || 0, taskSummary?.completedTaskCount || 0),
868
952
  initiativeId: workstream.initiativeId || null,
869
953
  commentCount: Array.isArray(workstream.comments) ? workstream.comments.length : 0,
870
954
  attachmentCount: Array.isArray(workstream.attachments) ? workstream.attachments.length : 0,
955
+ attachments: Array.isArray(workstream.attachments) ? workstream.attachments : [],
871
956
  isArchived: Boolean(workstream.isArchived),
872
957
  tasks: taskPreviews,
873
958
  };
874
- workstreamTaskIds.set(workstream.id, scopedTasks.map((task) => task.id));
875
- if (workstream.initiativeId)
959
+ workstreamTaskIds.set(workstream.id, taskPreviews.map((task) => task.id));
960
+ if (workstream.initiativeId) {
876
961
  workstreamInitiativeIds.set(workstream.id, workstream.initiativeId);
877
- workstreamById.set(summary.id, summary);
962
+ const current = workstreamsByInitiativeId.get(workstream.initiativeId) || [];
963
+ current.push(summary);
964
+ workstreamsByInitiativeId.set(workstream.initiativeId, current);
965
+ }
878
966
  return summary;
879
967
  };
880
968
  const allWorkstreamSummaries = props.workstreams.map(buildWorkstreamSummary);
969
+ const scopedWorkstreamSummaries = allWorkstreamSummaries.filter((workstream) => (isPlanningEntityVisibleForScope(taskScope, Boolean(workstream.isArchived))));
881
970
  const allInitiatives = props.initiatives.map((initiative) => {
882
- const workstreams = allWorkstreamSummaries
883
- .filter((workstream) => (workstream.initiativeId || null) === initiative.id);
884
- const initiativeScopedTasks = workstreams
885
- .flatMap((workstream) => workstreamTaskIds.get(workstream.id) || [])
886
- .map((taskId) => hierarchyTaskById.get(taskId))
887
- .filter((task) => Boolean(task));
971
+ const allWorkstreamsForInitiative = workstreamsByInitiativeId.get(initiative.id) || [];
972
+ const workstreams = allWorkstreamsForInitiative
973
+ .filter((workstream) => isPlanningEntityVisibleForScope(taskScope, Boolean(workstream.isArchived)));
974
+ const taskCount = workstreams.reduce((sum, workstream) => sum + workstream.taskCount, 0);
975
+ const completedTaskCount = workstreams.reduce((sum, workstream) => sum + workstream.completedTaskCount, 0);
888
976
  const initiativeSummary = {
889
977
  id: initiative.id,
890
978
  referenceNumber: initiative.referenceNumber ?? null,
891
979
  title: initiative.title,
892
980
  description: String(initiative.description || '').trim() || undefined,
981
+ ownerId: initiative.ownerId ? String(initiative.ownerId) : null,
893
982
  ownerLabel: initiative.ownerId ? assigneeLabelByValue.get(String(initiative.ownerId)) || String(initiative.ownerId) : null,
894
- ...summarizeTasks(initiativeScopedTasks),
983
+ ...summarizeTaskCounts(taskCount, completedTaskCount),
895
984
  workstreamCount: workstreams.length,
896
985
  workstreams,
986
+ allWorkstreams: allWorkstreamsForInitiative,
897
987
  commentCount: Array.isArray(initiative.comments) ? initiative.comments.length : 0,
898
988
  attachmentCount: Array.isArray(initiative.attachments) ? initiative.attachments.length : 0,
989
+ attachments: Array.isArray(initiative.attachments) ? initiative.attachments : [],
899
990
  isArchived: Boolean(initiative.isArchived),
900
991
  };
901
- initiativeTaskIds.set(initiative.id, initiativeScopedTasks.map((task) => task.id));
902
- initiativeById.set(initiativeSummary.id, initiativeSummary);
903
992
  return initiativeSummary;
904
993
  });
905
- const initiatives = taskScope === 'deleted'
906
- ? []
907
- : allInitiatives.filter((initiative) => taskScope === 'archived'
908
- ? Boolean(initiative.isArchived)
909
- : !initiative.isArchived);
910
- const standaloneWorkstreams = allWorkstreamSummaries
911
- .filter((workstream) => !workstream.initiativeId)
912
- .filter((workstream) => taskScope === 'deleted'
913
- ? false
914
- : taskScope === 'archived'
915
- ? Boolean(workstream.isArchived)
916
- : !workstream.isArchived);
994
+ const { initiatives, standaloneWorkstreams, visibleWorkstreams, initiativeById, workstreamById, } = scopePlanningSummaries(allInitiatives, scopedWorkstreamSummaries, taskScope);
995
+ const allStandaloneWorkstreams = allWorkstreamSummaries.filter((workstream) => !workstream.initiativeId);
996
+ initiatives.forEach((initiative) => {
997
+ initiativeTaskIds.set(initiative.id, initiative.workstreams.flatMap((workstream) => (workstream.tasks || []).map((task) => task.id)));
998
+ });
917
999
  return {
918
1000
  initiatives,
919
1001
  standaloneWorkstreams,
1002
+ allStandaloneWorkstreams,
920
1003
  workstreamTaskIds,
921
1004
  workstreamInitiativeIds,
922
1005
  initiativeTaskIds,
923
1006
  workstreamById,
924
1007
  initiativeById,
925
1008
  };
926
- }, [archivedScopeAllTasks, assigneeLabelByValue, deletedScopeAllTasks, hierarchyTaskById, props.initiatives, props.tasks, props.workstreams, taskScope]);
1009
+ }, [
1010
+ archivedScopeAllTasks,
1011
+ assigneeLabelByValue,
1012
+ deletedScopeAllTasks,
1013
+ props.initiatives,
1014
+ props.loadingTasks,
1015
+ props.planningBootstrapTaskSummaries,
1016
+ props.tasks,
1017
+ props.workstreams,
1018
+ taskScope,
1019
+ ]);
927
1020
  const initiativeFilterOptions = useMemo(() => planningStructure.initiatives.map((initiative) => {
928
1021
  const referenceLabel = formatInitiativeReference(initiative);
929
1022
  return {
@@ -1198,17 +1291,7 @@ export function StandaloneLayout(props) {
1198
1291
  const [showAccountMenu, setShowAccountMenu] = useState(false);
1199
1292
  const [showAccountHub, setShowAccountHub] = useState(false);
1200
1293
  const [showEditProfileModal, setShowEditProfileModal] = useState(false);
1201
- const [showSyncStatusModal, setShowSyncStatusModal] = useState(false);
1202
- const [workspaceSyncToggleBusy, setWorkspaceSyncToggleBusy] = useState(false);
1203
- const [workspaceSyncError, setWorkspaceSyncError] = useState(null);
1204
- const [workspaceSyncRepairBusy, setWorkspaceSyncRepairBusy] = useState(false);
1205
- const [workspaceSyncRepairQueued, setWorkspaceSyncRepairQueued] = useState(false);
1206
- const [workspaceSyncCopied, setWorkspaceSyncCopied] = useState(false);
1207
- const [syncRecentEvents, setSyncRecentEvents] = useState([]);
1208
- const [syncRecentEventsLoading, setSyncRecentEventsLoading] = useState(false);
1209
- const [syncRecentEventsError, setSyncRecentEventsError] = useState(null);
1210
- const syncEventsListRef = useRef(null);
1211
- const pendingSyncEventsScrollRestoreRef = useRef(null);
1294
+ const [showAvatarPhotoManager, setShowAvatarPhotoManager] = useState(false);
1212
1295
  const [workspaceActionBusy, setWorkspaceActionBusy] = useState(false);
1213
1296
  const [workspaceActionError, setWorkspaceActionError] = useState('');
1214
1297
  const [showCreateWorkspaceConfirm, setShowCreateWorkspaceConfirm] = useState(false);
@@ -1249,7 +1332,6 @@ export function StandaloneLayout(props) {
1249
1332
  const [profileSaveNotice, setProfileSaveNotice] = useState(null);
1250
1333
  const TEAM_AUDIT_PAGE_SIZE = 25;
1251
1334
  const accountMenuRef = useRef(null);
1252
- const profileAvatarInputRef = useRef(null);
1253
1335
  const accountSurfaceOpenStartedAtRef = useRef(null);
1254
1336
  const accountSurfacePendingRef = useRef({
1255
1337
  billing: false,
@@ -1274,21 +1356,11 @@ export function StandaloneLayout(props) {
1274
1356
  const canManageCloudWorkspaces = runtimeMode === 'cloud' && workspaceSwitchingEnabled && isAuthenticated;
1275
1357
  const canAccessCloudTeamManagement = isAuthenticated && (runtimeMode === 'cloud' || cloudAuthConfigured);
1276
1358
  const canManageWorkspaceSync = runtimeMode === 'local' && isAuthenticated;
1277
- const handleWorkspaceSyncToggle = useCallback(async (enabled) => {
1278
- if (!canManageWorkspaceSync)
1279
- return;
1280
- setWorkspaceSyncError(null);
1281
- setWorkspaceSyncToggleBusy(true);
1282
- const result = await saveWorkspaceCloudSyncSettings({ enabled });
1283
- if (!result.success) {
1284
- setWorkspaceSyncError(result.error || (enabled ? 'Failed to enable sync.' : 'Failed to disable sync.'));
1285
- }
1286
- setWorkspaceSyncToggleBusy(false);
1287
- }, [
1359
+ const { showSyncStatusModal, showSyncEnableWarning, workspaceSyncError, syncControlBusy, openSyncStatusModal, closeSyncStatusModal, handleWorkspaceSyncToggle, cancelSyncEnableWarning, confirmSyncEnableWarning, } = useSyncStatusControls({
1288
1360
  canManageWorkspaceSync,
1289
- saveWorkspaceCloudSyncSettings
1290
- ]);
1291
- const syncControlBusy = workspaceSyncToggleBusy;
1361
+ workspaceCloudSyncEnabled,
1362
+ saveWorkspaceCloudSyncSettings,
1363
+ });
1292
1364
  const currentWorkspaceLabel = useMemo(() => {
1293
1365
  const current = availableWorkspaces.find((workspace) => workspace.id === currentWorkspaceId);
1294
1366
  const namedWorkspace = String(current?.name || '').trim();
@@ -1348,23 +1420,21 @@ export function StandaloneLayout(props) {
1348
1420
  if (!normalizedDraftId || !cloudAuthConfigured)
1349
1421
  return;
1350
1422
  try {
1351
- const baseUrl = runtimeMode === 'local'
1352
- ? (props.config?.cloudAuthBaseUrl || props.config?.apiBaseUrl || '')
1353
- : (props.config?.apiBaseUrl || props.config?.cloudAuthBaseUrl || '');
1354
- await fetchTaskforceApi('/api/taskforce/auth/profile/avatar/discard', {
1423
+ await fetch(resolveCloudAuthUrl('/api/taskforce/auth/profile/avatar/discard'), {
1355
1424
  method: 'POST',
1356
1425
  headers: { 'Content-Type': 'application/json' },
1357
1426
  credentials: 'include',
1358
1427
  body: JSON.stringify({ draftId: normalizedDraftId })
1359
- }, baseUrl);
1428
+ });
1360
1429
  }
1361
1430
  catch {
1362
1431
  // Best-effort cleanup only.
1363
1432
  }
1364
- }, [cloudAuthConfigured, props.config?.apiBaseUrl, props.config?.cloudAuthBaseUrl, runtimeMode]);
1433
+ }, [cloudAuthConfigured, resolveCloudAuthUrl]);
1365
1434
  const closeEditProfileModal = useCallback(() => {
1366
1435
  const pendingDraftId = profileAvatarDraftId;
1367
1436
  setShowEditProfileModal(false);
1437
+ setShowAvatarPhotoManager(false);
1368
1438
  setProfileSaveError(null);
1369
1439
  setProfileSaveNotice(null);
1370
1440
  setProfileAvatarDraftId(null);
@@ -1375,47 +1445,53 @@ export function StandaloneLayout(props) {
1375
1445
  }, [authAvatarUrl, discardProfileAvatarDraft, profileAvatarDraftId]);
1376
1446
  const handleProfileAvatarSelected = useCallback(async (file) => {
1377
1447
  if (!file || !cloudAuthConfigured)
1378
- return;
1448
+ return false;
1379
1449
  if (!file.type.startsWith('image/')) {
1380
1450
  setProfileSaveError('Profile photo must be an image file.');
1381
1451
  setProfileSaveNotice(null);
1382
- return;
1383
- }
1384
- if (file.size > PROFILE_AVATAR_MAX_BYTES) {
1385
- setProfileSaveError('Profile photo must be 5 MB or smaller.');
1386
- setProfileSaveNotice(null);
1387
- return;
1452
+ return false;
1388
1453
  }
1389
1454
  setProfileAvatarBusy(true);
1390
1455
  setProfileSaveError(null);
1391
1456
  setProfileSaveNotice(null);
1392
1457
  try {
1393
- const baseUrl = runtimeMode === 'local'
1394
- ? (props.config?.cloudAuthBaseUrl || props.config?.apiBaseUrl || '')
1395
- : (props.config?.apiBaseUrl || props.config?.cloudAuthBaseUrl || '');
1396
- const initRes = await fetchTaskforceApi('/api/taskforce/auth/profile/avatar/init', {
1458
+ const preparedAvatar = await prepareAvatarUploadFile(file, {
1459
+ maxBytes: PROFILE_AVATAR_MAX_BYTES
1460
+ });
1461
+ if (preparedAvatar.exceededLimit) {
1462
+ const message = file.type === 'image/gif'
1463
+ ? 'Animated GIF profile photos must be 5 MB or smaller.'
1464
+ : 'Profile photo must be 5 MB or smaller.';
1465
+ throw new Error(message);
1466
+ }
1467
+ const uploadFile = preparedAvatar.file;
1468
+ const initRes = await fetch(resolveCloudAuthUrl('/api/taskforce/auth/profile/avatar/init'), {
1397
1469
  method: 'POST',
1398
1470
  headers: { 'Content-Type': 'application/json' },
1399
1471
  credentials: 'include',
1400
1472
  body: JSON.stringify({
1401
- originalName: file.name,
1402
- mimeType: file.type,
1403
- size: file.size
1473
+ originalName: uploadFile.name,
1474
+ mimeType: uploadFile.type,
1475
+ size: uploadFile.size
1404
1476
  })
1405
- }, baseUrl);
1477
+ });
1406
1478
  const initData = await initRes.json().catch(() => ({}));
1407
- if (!initRes.ok || !initData?.success || typeof initData?.uploadUrl !== 'string' || typeof initData?.relativePath !== 'string') {
1479
+ if (!initRes.ok || !initData?.success || typeof initData?.draftId !== 'string' || typeof initData?.relativePath !== 'string') {
1408
1480
  throw new Error(initData?.error || 'Failed to start avatar upload.');
1409
1481
  }
1410
- const uploadRes = await fetch(initData.uploadUrl, {
1411
- method: String(initData.method || 'PUT'),
1412
- headers: initData.headers || { 'Content-Type': file.type || 'application/octet-stream' },
1413
- body: file
1482
+ const uploadRes = await fetch(resolveCloudAuthUrl('/api/taskforce/auth/profile/avatar/upload'), {
1483
+ method: 'POST',
1484
+ headers: {
1485
+ 'Content-Type': uploadFile.type || 'application/octet-stream',
1486
+ 'x-taskforce-avatar-draft-id': initData.draftId
1487
+ },
1488
+ credentials: 'include',
1489
+ body: uploadFile
1414
1490
  });
1415
1491
  if (!uploadRes.ok) {
1416
1492
  throw new Error('Failed to upload avatar.');
1417
1493
  }
1418
- const finalizeRes = await fetchTaskforceApi('/api/taskforce/auth/profile/avatar/finalize', {
1494
+ const finalizeRes = await fetch(resolveCloudAuthUrl('/api/taskforce/auth/profile/avatar/finalize'), {
1419
1495
  method: 'POST',
1420
1496
  headers: { 'Content-Type': 'application/json' },
1421
1497
  credentials: 'include',
@@ -1423,7 +1499,7 @@ export function StandaloneLayout(props) {
1423
1499
  draftId: initData.draftId,
1424
1500
  relativePath: initData.relativePath
1425
1501
  })
1426
- }, baseUrl);
1502
+ });
1427
1503
  const finalizeData = await finalizeRes.json().catch(() => ({}));
1428
1504
  if (!finalizeRes.ok || !finalizeData?.success || typeof finalizeData?.draftId !== 'string') {
1429
1505
  throw new Error(finalizeData?.error || 'Failed to finalize avatar upload.');
@@ -1431,27 +1507,24 @@ export function StandaloneLayout(props) {
1431
1507
  const previousDraftId = profileAvatarDraftId;
1432
1508
  setProfileAvatarDraftId(finalizeData.draftId);
1433
1509
  setProfileAvatarPreviewUrl(typeof finalizeData?.avatarUrl === 'string' ? finalizeData.avatarUrl : '');
1434
- setProfileSaveNotice('Profile photo ready to save.');
1510
+ setProfileSaveNotice(null);
1435
1511
  if (previousDraftId && previousDraftId !== finalizeData.draftId) {
1436
1512
  void discardProfileAvatarDraft(previousDraftId);
1437
1513
  }
1514
+ return true;
1438
1515
  }
1439
1516
  catch (error) {
1440
1517
  setProfileSaveError(error instanceof Error ? error.message : 'Failed to upload profile photo.');
1518
+ return false;
1441
1519
  }
1442
1520
  finally {
1443
1521
  setProfileAvatarBusy(false);
1444
- if (profileAvatarInputRef.current) {
1445
- profileAvatarInputRef.current.value = '';
1446
- }
1447
1522
  }
1448
1523
  }, [
1449
1524
  cloudAuthConfigured,
1450
1525
  discardProfileAvatarDraft,
1451
1526
  profileAvatarDraftId,
1452
- props.config?.apiBaseUrl,
1453
- props.config?.cloudAuthBaseUrl,
1454
- runtimeMode
1527
+ resolveCloudAuthUrl
1455
1528
  ]);
1456
1529
  const handleDiscardPendingProfileAvatar = useCallback(() => {
1457
1530
  if (!profileAvatarDraftId)
@@ -1497,6 +1570,7 @@ export function StandaloneLayout(props) {
1497
1570
  setProfileAvatarDraftId(null);
1498
1571
  setProfileAvatarPreviewUrl('');
1499
1572
  setProfileSaveBusy(false);
1573
+ setShowAvatarPhotoManager(false);
1500
1574
  setShowEditProfileModal(false);
1501
1575
  }, [authAvatarUrl, profileAvatarDraftId, profileAvatarPreviewUrl, profileDisplayNameDraft, updateCurrentUserProfile]);
1502
1576
  const teamAdminHeaders = useCallback(() => {
@@ -1815,9 +1889,9 @@ export function StandaloneLayout(props) {
1815
1889
  const target = (!targetRaw || targetRaw === '/login' || !targetRaw.startsWith('/')) ? '/' : targetRaw;
1816
1890
  setShowAccountMenu(false);
1817
1891
  setShowAccountHub(false);
1818
- setShowSyncStatusModal(false);
1892
+ closeSyncStatusModal();
1819
1893
  navigate(`/login?mode=${mode}&next=${encodeURIComponent(target)}`);
1820
- }, [location.hash, location.pathname, location.search, navigate]);
1894
+ }, [closeSyncStatusModal, location.hash, location.pathname, location.search, navigate]);
1821
1895
  const openWorkspaceSetup = useCallback((options) => {
1822
1896
  setShowAccountMenu(false);
1823
1897
  setShowCreateWorkspaceConfirm(false);
@@ -1830,43 +1904,83 @@ export function StandaloneLayout(props) {
1830
1904
  const handlePlansBack = useCallback(async () => {
1831
1905
  if (plansNavigationBusy)
1832
1906
  return;
1907
+ setPlansNavigationBusy(true);
1833
1908
  if (!isAuthenticated) {
1834
- closePlansScreen();
1909
+ try {
1910
+ closePlansScreen();
1911
+ }
1912
+ finally {
1913
+ setPlansNavigationBusy(false);
1914
+ }
1835
1915
  return;
1836
1916
  }
1837
- const summary = await loadAccountProfileSummary({ force: true });
1838
- if (!resolveCommercialLifecyclePresentation(summary).allowReturnToApp) {
1917
+ const currentSummary = accountProfileSummaryRef.current;
1918
+ if (currentSummary && !resolveCommercialLifecyclePresentation(currentSummary).allowReturnToApp) {
1919
+ setPlansNavigationBusy(false);
1839
1920
  return;
1840
1921
  }
1841
- setPlansNavigationBusy(true);
1842
- try {
1843
- const result = await props.continueAfterCommercialOnboarding();
1844
- if (!result.success) {
1845
- pushNotice(result.error || 'Unable to finish onboarding.', 'error');
1846
- if (result.destination === 'login') {
1847
- navigate('/login', { replace: true });
1922
+ closePlansScreen();
1923
+ void (async () => {
1924
+ try {
1925
+ const result = await props.continueAfterCommercialOnboarding();
1926
+ if (!result.success) {
1927
+ pushNotice(result.error || 'Unable to finish onboarding.', 'error');
1928
+ if (result.destination === 'login') {
1929
+ navigate('/login', { replace: true });
1930
+ }
1931
+ return;
1932
+ }
1933
+ if (result.destination === 'setup') {
1934
+ openWorkspaceSetup();
1848
1935
  }
1849
- return;
1850
1936
  }
1851
- if (result.destination === 'setup') {
1852
- openWorkspaceSetup();
1853
- return;
1937
+ finally {
1938
+ setPlansNavigationBusy(false);
1854
1939
  }
1855
- closePlansScreen();
1856
- }
1857
- finally {
1858
- setPlansNavigationBusy(false);
1859
- }
1940
+ })();
1860
1941
  }, [
1942
+ accountProfileSummaryRef,
1861
1943
  closePlansScreen,
1862
1944
  isAuthenticated,
1863
- loadAccountProfileSummary,
1864
1945
  navigate,
1865
1946
  openWorkspaceSetup,
1866
1947
  plansNavigationBusy,
1867
1948
  props,
1868
1949
  pushNotice
1869
1950
  ]);
1951
+ const handleOpenKanbanFromBrand = useCallback(() => {
1952
+ if (isPlansScreen) {
1953
+ void handlePlansBack();
1954
+ return;
1955
+ }
1956
+ handleNavigation(() => {
1957
+ clearTaskOriginWorkspaceModule();
1958
+ setActiveWorkspaceModule('tasks');
1959
+ setPlanningDrawerOpen(false);
1960
+ setPlanningDrawerDetail(null);
1961
+ setPlanningNestedWorkstreamDetailId(null);
1962
+ setPlanningEditor(null);
1963
+ setTaskModalFullscreen(false);
1964
+ if (activeTab === 'add')
1965
+ resetForm();
1966
+ setActiveTab('tasks');
1967
+ if (location.pathname !== '/' || location.search) {
1968
+ navigate('/');
1969
+ }
1970
+ });
1971
+ }, [
1972
+ activeTab,
1973
+ clearTaskOriginWorkspaceModule,
1974
+ handleNavigation,
1975
+ handlePlansBack,
1976
+ isPlansScreen,
1977
+ location.pathname,
1978
+ location.search,
1979
+ navigate,
1980
+ resetForm,
1981
+ setActiveTab,
1982
+ setActiveWorkspaceModule,
1983
+ ]);
1870
1984
  const handleSwitchWorkspace = useCallback(async (workspaceId) => {
1871
1985
  if (!workspaceId || workspaceActionBusy)
1872
1986
  return;
@@ -2046,6 +2160,7 @@ export function StandaloneLayout(props) {
2046
2160
  const formattedLastPullTime = useMemo(() => formatSyncTimestamp(workspaceLastPullAt), [formatSyncTimestamp, workspaceLastPullAt]);
2047
2161
  const formattedLastPushTime = useMemo(() => formatSyncTimestamp(workspaceLastPushAt), [formatSyncTimestamp, workspaceLastPushAt]);
2048
2162
  const rawSyncLastError = workspaceSyncError || workspaceLastErrorMessage || userGlobalSyncError || 'None';
2163
+ const workspaceSyncDiagnostics = getWorkspaceSyncDiagnostics();
2049
2164
  const headerSyncPresentation = useMemo(() => resolveHeaderWorkspaceSyncPresentation({
2050
2165
  runtimeMode,
2051
2166
  isAuthenticated,
@@ -2053,7 +2168,10 @@ export function StandaloneLayout(props) {
2053
2168
  workspaceCloudSyncEnabled,
2054
2169
  workspaceSyncSummary,
2055
2170
  workspaceSyncRecommendedAction,
2056
- workspaceSyncError: rawSyncLastError
2171
+ workspaceSyncError: rawSyncLastError,
2172
+ pushBlockedReason: workspaceSyncDiagnostics.pushBlockedReason || null,
2173
+ pullBlockedReason: workspaceSyncDiagnostics.pullBlockedReason || null,
2174
+ retryBlockedReason: workspaceSyncDiagnostics.retryBlockedReason || null
2057
2175
  }), [
2058
2176
  runtimeMode,
2059
2177
  isAuthenticated,
@@ -2061,75 +2179,24 @@ export function StandaloneLayout(props) {
2061
2179
  workspaceCloudSyncEnabled,
2062
2180
  workspaceSyncSummary,
2063
2181
  workspaceSyncRecommendedAction,
2064
- rawSyncLastError
2182
+ rawSyncLastError,
2183
+ workspaceSyncDiagnostics.pushBlockedReason,
2184
+ workspaceSyncDiagnostics.pullBlockedReason,
2185
+ workspaceSyncDiagnostics.retryBlockedReason
2065
2186
  ]);
2066
2187
  const syncLastError = headerSyncPresentation.lastError;
2067
2188
  const [headerSyncStatus, setHeaderSyncStatus] = useState(headerSyncPresentation.status);
2068
- const syncStageLabel = useMemo(() => {
2069
- if (workspaceSyncRepairBusy) {
2070
- return 'Repairing';
2071
- }
2072
- if (workspaceSyncBusy && workspaceSyncPhase === 'error') {
2073
- return 'Recovering';
2074
- }
2075
- switch (workspaceSyncPhase) {
2076
- case 'provision-local':
2077
- return 'Initial cloud upload';
2078
- case 'attach-cloud':
2079
- return 'Initial cloud pull';
2080
- case 'active':
2081
- return 'Active';
2082
- case 'error':
2083
- return 'Error';
2084
- default:
2085
- return 'Idle';
2086
- }
2087
- }, [workspaceSyncPhase, workspaceSyncRepairBusy]);
2088
- const workspaceSyncDiagnostics = getWorkspaceSyncDiagnostics();
2089
- const syncDiagnosticsSummary = useMemo(() => ([
2090
- {
2091
- label: 'AI profile snapshot',
2092
- value: `${workspaceSyncDiagnostics.aiProfileSnapshotCount} local profile${workspaceSyncDiagnostics.aiProfileSnapshotCount === 1 ? '' : 's'}`
2093
- },
2094
- {
2095
- label: 'AI profile raw response',
2096
- value: `${workspaceSyncDiagnostics.aiProfileSnapshotRawCount} from route`
2097
- },
2098
- {
2099
- label: 'Last pushed AI profiles',
2100
- value: `${workspaceSyncDiagnostics.lastPushedAiProfileCount} tracked`
2101
- },
2102
- {
2103
- label: 'AI watermark map',
2104
- value: `${workspaceSyncDiagnostics.lastPushedAiProfileWatermarkCount} tracked`
2105
- },
2106
- {
2107
- label: 'Document snapshot',
2108
- value: `${workspaceSyncDiagnostics.documentSnapshotCount} local doc${workspaceSyncDiagnostics.documentSnapshotCount === 1 ? '' : 's'}`
2109
- },
2110
- {
2111
- label: 'Asset snapshot',
2112
- value: `${workspaceSyncDiagnostics.assetSnapshotCount} local asset${workspaceSyncDiagnostics.assetSnapshotCount === 1 ? '' : 's'}`
2113
- },
2114
- {
2115
- label: 'Queued full AI sync',
2116
- value: workspaceSyncDiagnostics.forceFullAiProfilePushQueued ? 'Yes' : 'No'
2117
- },
2118
- {
2119
- label: 'AI snapshot last fetch',
2120
- value: workspaceSyncDiagnostics.aiProfileSnapshotLastFetchAt
2121
- ? new Date(workspaceSyncDiagnostics.aiProfileSnapshotLastFetchAt).toLocaleString()
2122
- : 'Never'
2123
- },
2124
- {
2125
- label: 'AI snapshot fetch error',
2126
- value: workspaceSyncDiagnostics.aiProfileSnapshotLastFetchError || 'None'
2127
- },
2128
- {
2129
- label: 'AI snapshot skip reason',
2130
- value: workspaceSyncDiagnostics.aiProfileSnapshotLastSkipReason || 'None'
2131
- }
2132
- ]), [workspaceSyncDiagnostics]);
2189
+ const syncDiagnosticsSummary = useMemo(() => buildSyncDiagnosticsSummary(workspaceSyncDiagnostics), [workspaceSyncDiagnostics]);
2190
+ const { syncRecentEvents, syncRecentEventsLoading, syncRecentEventsError, syncEventsListRef, loadRecentSyncEvents, } = useRecentSyncEvents({
2191
+ isOpen: showSyncStatusModal,
2192
+ workspaceId: currentWorkspaceId,
2193
+ refreshKeys: [
2194
+ workspaceLastPushAt,
2195
+ workspaceLastPullAt,
2196
+ workspaceLastErrorAt,
2197
+ workspaceSyncStatus
2198
+ ]
2199
+ });
2133
2200
  const shouldDelayHeaderSyncing = headerSyncPresentation.actionable
2134
2201
  && workspaceSyncStatus === 'syncing'
2135
2202
  && workspaceSyncPhase === 'active'
@@ -2144,282 +2211,34 @@ export function StandaloneLayout(props) {
2144
2211
  }, 1200);
2145
2212
  return () => window.clearTimeout(timeoutId);
2146
2213
  }, [headerSyncPresentation.status, shouldDelayHeaderSyncing]);
2147
- const formatSyncEventLine = useCallback((event) => {
2148
- const occurredAt = typeof event.occurredAt === 'string' && event.occurredAt.trim().length > 0
2149
- ? event.occurredAt
2150
- : 'unknown-time';
2151
- const eventType = typeof event.eventType === 'string' && event.eventType.trim().length > 0
2152
- ? event.eventType.trim()
2153
- : 'sync';
2154
- const status = event.status === 'error' ? 'failed' : 'succeeded';
2155
- const changeCountValue = Number(event.changeCount);
2156
- const changeCount = Number.isFinite(changeCountValue)
2157
- ? ` (${Math.max(0, Math.floor(changeCountValue))} change${Math.floor(changeCountValue) === 1 ? '' : 's'})`
2158
- : '';
2159
- const requestMsValue = Number(event.requestMs);
2160
- const requestMs = Number.isFinite(requestMsValue)
2161
- ? ` in ${Math.max(0, Math.floor(requestMsValue))}ms`
2162
- : '';
2163
- const statusCodeValue = Number(event.statusCode);
2164
- const statusCode = Number.isFinite(statusCodeValue)
2165
- ? ` [${Math.floor(statusCodeValue)}]`
2166
- : '';
2167
- const errorMessage = typeof event.errorMessage === 'string' && event.errorMessage.trim().length > 0
2168
- ? `: ${event.errorMessage.trim()}`
2169
- : '';
2170
- return `${occurredAt} ${eventType} ${status}${changeCount}${requestMs}${statusCode}${errorMessage}`;
2171
- }, []);
2172
- const syncEventRows = useMemo(() => {
2173
- if (syncRecentEventsLoading && syncRecentEvents.length === 0) {
2174
- return [{
2175
- key: 'loading',
2176
- text: 'Loading recent sync events...',
2177
- tone: 'muted'
2178
- }];
2179
- }
2180
- if (syncRecentEventsError) {
2181
- return [{
2182
- key: 'error',
2183
- text: syncRecentEventsError,
2184
- tone: 'error'
2185
- }];
2186
- }
2187
- if (syncRecentEvents.length === 0) {
2188
- return [{
2189
- key: 'empty',
2190
- text: 'No recent sync events recorded.',
2191
- tone: 'muted'
2192
- }];
2193
- }
2194
- return syncRecentEvents.map((event, index) => ({
2195
- key: getSyncEventStableKey(event, index),
2196
- text: formatSyncEventLine(event),
2197
- tone: event.status === 'error' ? 'error' : 'default'
2198
- }));
2199
- }, [formatSyncEventLine, syncRecentEvents, syncRecentEventsError, syncRecentEventsLoading]);
2214
+ const syncEventRows = useMemo(() => buildSyncEventRows({
2215
+ syncRecentEvents,
2216
+ syncRecentEventsError,
2217
+ syncRecentEventsLoading
2218
+ }), [syncRecentEvents, syncRecentEventsError, syncRecentEventsLoading]);
2200
2219
  const referenceMismatchCount = useMemo(() => getActiveReferenceMismatchCount(syncRecentEvents), [syncRecentEvents]);
2201
- const activeReferenceMismatchEvents = useMemo(() => getActiveReferenceMismatchEvents(syncRecentEvents), [syncRecentEvents]);
2202
- const activeReferenceMismatchSummaries = useMemo(() => (activeReferenceMismatchEvents.map((event, index) => {
2203
- const details = event.details && typeof event.details === 'object' ? event.details : null;
2204
- const pathValue = String(details?.path || '').trim();
2205
- const referenceLabelValue = String(details?.referenceLabel || '').trim();
2206
- const taskTitleValue = String(details?.taskTitle || details?.existingTaskTitle || '').trim();
2207
- const existingRefValue = Number(details?.existingReferenceNumber);
2208
- const incomingRefValue = Number(details?.incomingReferenceNumber);
2209
- const pathLabel = pathValue || taskTitleValue || referenceLabelValue || `Mismatch ${index + 1}`;
2210
- const refsLabel = (Number.isFinite(existingRefValue)
2211
- || Number.isFinite(incomingRefValue))
2212
- ? `Existing ${Number.isFinite(existingRefValue) ? existingRefValue : '?'} vs incoming ${Number.isFinite(incomingRefValue) ? incomingRefValue : '?'}`
2213
- : referenceLabelValue
2214
- ? `Both claimed ${referenceLabelValue}`
2215
- : null;
2216
- return {
2217
- key: getSyncEventStableKey(event, index),
2218
- pathLabel,
2219
- refsLabel,
2220
- };
2221
- })), [activeReferenceMismatchEvents]);
2222
- useLayoutEffect(() => {
2223
- const pendingRestore = pendingSyncEventsScrollRestoreRef.current;
2224
- const listEl = syncEventsListRef.current;
2225
- if (!pendingRestore || !listEl)
2226
- return;
2227
- const delta = listEl.scrollHeight - pendingRestore.scrollHeight;
2228
- listEl.scrollTop = pendingRestore.scrollTop + Math.max(0, delta);
2229
- pendingSyncEventsScrollRestoreRef.current = null;
2230
- }, [syncRecentEvents]);
2231
- const loadRecentSyncEvents = useCallback(async () => {
2232
- const res = await fetch(`/api/taskforce/sync/events?workspace_id=${encodeURIComponent(currentWorkspaceId)}&limit=15`, {
2233
- method: 'GET',
2234
- credentials: 'include'
2235
- });
2236
- if (!res.ok) {
2237
- throw new Error(`Failed to load sync events (${res.status})`);
2238
- }
2239
- const data = await res.json().catch(() => ({}));
2240
- return Array.isArray(data?.events)
2241
- ? data.events.filter((event) => event && typeof event === 'object')
2242
- : [];
2243
- }, [currentWorkspaceId]);
2244
- useEffect(() => {
2245
- if (!showSyncStatusModal)
2246
- return;
2247
- let cancelled = false;
2248
- const isInitialLoad = syncRecentEvents.length === 0;
2249
- if (isInitialLoad) {
2250
- setSyncRecentEventsLoading(true);
2251
- }
2252
- void loadRecentSyncEvents()
2253
- .then((events) => {
2254
- if (cancelled)
2255
- return;
2256
- const listEl = syncEventsListRef.current;
2257
- pendingSyncEventsScrollRestoreRef.current = listEl && listEl.scrollTop > 8
2258
- ? {
2259
- scrollTop: listEl.scrollTop,
2260
- scrollHeight: listEl.scrollHeight
2261
- }
2262
- : null;
2263
- setSyncRecentEvents((current) => {
2264
- const next = current.length === 0 ? events : mergeRecentSyncEvents(current, events, 15);
2265
- return areSyncEventListsEqual(current, next) ? current : next;
2266
- });
2267
- setSyncRecentEventsError(null);
2268
- })
2269
- .catch((error) => {
2270
- if (cancelled)
2271
- return;
2272
- const message = error instanceof Error && error.message.trim().length > 0
2273
- ? error.message.trim()
2274
- : 'Unable to load recent sync events.';
2275
- setSyncRecentEventsError(message);
2276
- if (isInitialLoad) {
2277
- setSyncRecentEvents([]);
2278
- }
2279
- })
2280
- .finally(() => {
2281
- if (cancelled)
2282
- return;
2283
- if (isInitialLoad) {
2284
- setSyncRecentEventsLoading(false);
2285
- }
2286
- });
2287
- return () => {
2288
- cancelled = true;
2289
- };
2290
- }, [
2291
- showSyncStatusModal,
2292
- loadRecentSyncEvents,
2293
- syncRecentEvents.length,
2294
- workspaceLastPushAt,
2295
- workspaceLastPullAt,
2296
- workspaceLastErrorAt,
2297
- workspaceSyncStatus
2298
- ]);
2299
- const syncStatusMeta = useMemo(() => {
2300
- switch (headerSyncStatus) {
2301
- case 'off':
2302
- return {
2303
- label: 'Off',
2304
- icon: 'off',
2305
- color: '#94a3b8',
2306
- border: 'rgba(148, 163, 184, 0.35)',
2307
- background: 'rgba(148, 163, 184, 0.12)'
2308
- };
2309
- case 'syncing':
2310
- return {
2311
- label: 'Syncing',
2312
- icon: 'cloud',
2313
- color: '#60a5fa',
2314
- border: 'rgba(96, 165, 250, 0.35)',
2315
- background: 'rgba(59, 130, 246, 0.12)'
2316
- };
2317
- case 'attention':
2318
- return {
2319
- label: 'Needs Attention',
2320
- icon: 'cloud',
2321
- color: '#fda4af',
2322
- border: 'rgba(253, 164, 175, 0.4)',
2323
- background: 'rgba(239, 68, 68, 0.12)'
2324
- };
2325
- default:
2326
- return {
2327
- label: 'Healthy',
2328
- icon: 'cloud',
2329
- color: '#86efac',
2330
- border: 'rgba(134, 239, 172, 0.4)',
2331
- background: 'rgba(34, 197, 94, 0.12)'
2332
- };
2333
- }
2334
- }, [headerSyncStatus]);
2335
- const handleCopySyncDetails = useCallback(async () => {
2336
- const details = [
2337
- 'Taskforce Sync Manager',
2338
- `Workspace ID: ${currentWorkspaceId}`,
2339
- `Status: ${syncStatusMeta.label}`,
2340
- `Summary: ${headerSyncPresentation.summary}`,
2341
- `Last successful sync: ${formattedLastSyncTime}`,
2342
- `Last pull from cloud: ${formattedLastPullTime}`,
2343
- `Last push to cloud: ${formattedLastPushTime}`,
2344
- `Last error at: ${workspaceSyncDiagnostics.lastErrorAt ? new Date(workspaceSyncDiagnostics.lastErrorAt).toLocaleString() : 'Never'}`,
2345
- `Pending local changes: ${workspaceSyncPendingChanges}`,
2346
- `Last error: ${syncLastError}`,
2347
- `Sync stage: ${syncStageLabel}`,
2348
- `AI profile snapshot: ${workspaceSyncDiagnostics.aiProfileSnapshotCount}`,
2349
- `AI profile raw response: ${workspaceSyncDiagnostics.aiProfileSnapshotRawCount}`,
2350
- `Last pushed AI profiles tracked: ${workspaceSyncDiagnostics.lastPushedAiProfileCount}`,
2351
- `AI profile watermarks tracked: ${workspaceSyncDiagnostics.lastPushedAiProfileWatermarkCount}`,
2352
- `AI snapshot last fetch: ${workspaceSyncDiagnostics.aiProfileSnapshotLastFetchAt ? new Date(workspaceSyncDiagnostics.aiProfileSnapshotLastFetchAt).toLocaleString() : 'Never'}`,
2353
- `AI snapshot fetch error: ${workspaceSyncDiagnostics.aiProfileSnapshotLastFetchError || 'None'}`,
2354
- `AI snapshot skip reason: ${workspaceSyncDiagnostics.aiProfileSnapshotLastSkipReason || 'None'}`,
2355
- `Document snapshot: ${workspaceSyncDiagnostics.documentSnapshotCount}`,
2356
- `Asset snapshot: ${workspaceSyncDiagnostics.assetSnapshotCount}`,
2357
- `Reference mismatches detected: ${referenceMismatchCount}`,
2358
- `Queued full AI sync: ${workspaceSyncDiagnostics.forceFullAiProfilePushQueued ? 'Yes' : 'No'}`
2359
- ];
2360
- try {
2361
- const events = syncRecentEvents.length > 0 ? syncRecentEvents : await loadRecentSyncEvents();
2362
- const recentEventLines = events.map((event) => formatSyncEventLine(event));
2363
- const copyText = [
2364
- ...details,
2365
- '',
2366
- 'Recent sync events',
2367
- ...(recentEventLines.length > 0 ? recentEventLines : ['No recent sync events recorded.'])
2368
- ].join('\n');
2369
- await navigator.clipboard.writeText(copyText);
2370
- setWorkspaceSyncCopied(true);
2371
- window.setTimeout(() => {
2372
- setWorkspaceSyncCopied(false);
2373
- }, 1800);
2374
- pushNotice('Sync details copied to clipboard.', 'success');
2375
- }
2376
- catch {
2377
- setWorkspaceSyncCopied(false);
2378
- pushNotice('Failed to copy sync details.', 'error');
2379
- }
2380
- }, [
2220
+ const activeReferenceMismatchSummaries = useMemo(() => buildReferenceMismatchSummaries(syncRecentEvents), [syncRecentEvents]);
2221
+ const syncStatusMeta = useMemo(() => resolveSyncStatusMeta(headerSyncStatus), [headerSyncStatus]);
2222
+ const { workspaceSyncRepairBusy, workspaceSyncRepairQueued, workspaceSyncCopied, handleCopySyncDetails, handleQueueOrRunRepairSync, } = useSyncStatusActions({
2381
2223
  currentWorkspaceId,
2382
- syncStatusMeta.label,
2383
- headerSyncPresentation.summary,
2224
+ syncStatusLabel: syncStatusMeta.label,
2225
+ workspaceSyncSummary: headerSyncPresentation.summary,
2226
+ workspaceSyncRecommendedAction: headerSyncPresentation.recommendedAction,
2384
2227
  formattedLastSyncTime,
2385
2228
  formattedLastPullTime,
2386
2229
  formattedLastPushTime,
2387
2230
  workspaceSyncDiagnostics,
2388
2231
  workspaceSyncPendingChanges,
2389
2232
  syncLastError,
2390
- syncStageLabel,
2233
+ workspaceSyncPhase,
2391
2234
  referenceMismatchCount,
2392
- pushNotice,
2393
2235
  syncRecentEvents,
2394
2236
  loadRecentSyncEvents,
2395
- formatSyncEventLine
2396
- ]);
2397
- const handleRepairSync = useCallback(async () => {
2398
- setWorkspaceSyncRepairQueued(false);
2399
- setWorkspaceSyncRepairBusy(true);
2400
- try {
2401
- await resetWorkspaceSyncCursorAndPull();
2402
- }
2403
- finally {
2404
- setWorkspaceSyncRepairBusy(false);
2405
- }
2406
- }, [resetWorkspaceSyncCursorAndPull]);
2407
- const handleQueueOrRunRepairSync = useCallback(() => {
2408
- if (workspaceSyncRepairBusy)
2409
- return;
2410
- if (workspaceSyncBusy) {
2411
- setWorkspaceSyncRepairQueued(true);
2412
- return;
2413
- }
2414
- void handleRepairSync();
2415
- }, [handleRepairSync, workspaceSyncBusy, workspaceSyncRepairBusy]);
2416
- useEffect(() => {
2417
- if (!workspaceSyncRepairQueued)
2418
- return;
2419
- if (workspaceSyncBusy || workspaceSyncRepairBusy)
2420
- return;
2421
- void handleRepairSync();
2422
- }, [handleRepairSync, workspaceSyncBusy, workspaceSyncRepairBusy, workspaceSyncRepairQueued]);
2237
+ pushNotice,
2238
+ resetWorkspaceSyncCursorAndPull,
2239
+ workspaceSyncBusy,
2240
+ });
2241
+ const syncStageLabel = useMemo(() => resolveSyncStageLabel(workspaceSyncPhase, workspaceSyncRepairBusy, workspaceSyncBusy), [workspaceSyncBusy, workspaceSyncPhase, workspaceSyncRepairBusy]);
2423
2242
  const selectedDateObj = useMemo(() => parseDateOnlyLocal(scheduleSelectedDate) || new Date(), [scheduleSelectedDate]);
2424
2243
  const scheduleWeekStart = useMemo(() => startOfWeek(selectedDateObj, globalWeekStartsOn), [selectedDateObj, globalWeekStartsOn]);
2425
2244
  const scheduleDates = useMemo(() => {
@@ -2640,6 +2459,8 @@ export function StandaloneLayout(props) {
2640
2459
  setActiveTab('add');
2641
2460
  }, [props.workstreams, resetForm, setActiveTab, setActiveWorkspaceModule, setWorkstreamInput]);
2642
2461
  const openPlanningEditor = useCallback((editor) => {
2462
+ planningEditorSubmitInFlightRef.current = false;
2463
+ setIsSubmittingPlanningEditor(false);
2643
2464
  setPlanningEditor(editor);
2644
2465
  setPlanningPrimaryCollapsed(false);
2645
2466
  setPlanningSecondaryEditor(null);
@@ -2654,6 +2475,8 @@ export function StandaloneLayout(props) {
2654
2475
  setPlanningNestedWorkstreamDetailId(null);
2655
2476
  }, [planningStructure.initiativeById, selectedPlanningInitiativeId]);
2656
2477
  const openPlanningSecondaryEditor = useCallback((editor, initiativeId) => {
2478
+ planningEditorSubmitInFlightRef.current = false;
2479
+ setIsSubmittingPlanningEditor(false);
2657
2480
  setPlanningSecondaryEditor(editor);
2658
2481
  setPlanningSecondaryCollapsed(false);
2659
2482
  setPlanningDraftTitle('');
@@ -2667,6 +2490,8 @@ export function StandaloneLayout(props) {
2667
2490
  setPlanningNestedWorkstreamDetailId(null);
2668
2491
  }, [planningStructure.initiativeById, selectedPlanningInitiativeId]);
2669
2492
  const closePlanningEditor = useCallback(() => {
2493
+ planningEditorSubmitInFlightRef.current = false;
2494
+ setIsSubmittingPlanningEditor(false);
2670
2495
  setPlanningEditor(null);
2671
2496
  setPlanningDraftTitle('');
2672
2497
  setPlanningDraftDescription('');
@@ -2674,6 +2499,8 @@ export function StandaloneLayout(props) {
2674
2499
  setPlanningDraftInitiativeId('');
2675
2500
  }, []);
2676
2501
  const closePlanningSecondaryEditor = useCallback(() => {
2502
+ planningEditorSubmitInFlightRef.current = false;
2503
+ setIsSubmittingPlanningEditor(false);
2677
2504
  setPlanningSecondaryEditor(null);
2678
2505
  setPlanningDraftTitle('');
2679
2506
  setPlanningDraftDescription('');
@@ -2682,8 +2509,10 @@ export function StandaloneLayout(props) {
2682
2509
  }, []);
2683
2510
  const submitPlanningEditor = useCallback(async () => {
2684
2511
  const activeEditor = planningSecondaryEditor || planningEditor;
2685
- if (!activeEditor)
2512
+ if (!activeEditor || planningEditorSubmitInFlightRef.current)
2686
2513
  return;
2514
+ planningEditorSubmitInFlightRef.current = true;
2515
+ setIsSubmittingPlanningEditor(true);
2687
2516
  const isSecondaryEditor = Boolean(planningSecondaryEditor);
2688
2517
  const ownerId = planningDraftOwner.trim() || null;
2689
2518
  try {
@@ -2757,6 +2586,10 @@ export function StandaloneLayout(props) {
2757
2586
  catch (error) {
2758
2587
  pushNotice(error instanceof Error ? error.message : 'Failed to save planning item.', 'error');
2759
2588
  }
2589
+ finally {
2590
+ planningEditorSubmitInFlightRef.current = false;
2591
+ setIsSubmittingPlanningEditor(false);
2592
+ }
2760
2593
  }, [
2761
2594
  closePlanningEditor,
2762
2595
  closePlanningSecondaryEditor,
@@ -2814,13 +2647,6 @@ export function StandaloneLayout(props) {
2814
2647
  setPlanningSecondaryCollapsed(false);
2815
2648
  setPlanningNestedWorkstreamDetailId(workstreamId);
2816
2649
  }, []);
2817
- const ownerValueByLabel = useMemo(() => {
2818
- const map = new Map();
2819
- assigneeOptions.forEach((option) => {
2820
- map.set(option.label, String(option.value));
2821
- });
2822
- return map;
2823
- }, [assigneeOptions]);
2824
2650
  const openPlanningCreateEditor = useCallback((type, initiativeId) => {
2825
2651
  if (type === 'workstream' && initiativeId) {
2826
2652
  openPlanningSecondaryEditor({ mode: 'create', entityType: 'workstream' }, initiativeId);
@@ -2842,7 +2668,7 @@ export function StandaloneLayout(props) {
2842
2668
  setPlanningEditor({ mode: 'edit', entityType: 'initiative', targetId: id });
2843
2669
  setPlanningDraftTitle(item.title);
2844
2670
  setPlanningDraftDescription(item.description || '');
2845
- setPlanningDraftOwner(item.ownerLabel ? (ownerValueByLabel.get(item.ownerLabel) || '') : '');
2671
+ setPlanningDraftOwner(item.ownerId ? String(item.ownerId) : '');
2846
2672
  setPlanningDraftInitiativeId('');
2847
2673
  }
2848
2674
  else {
@@ -2853,12 +2679,12 @@ export function StandaloneLayout(props) {
2853
2679
  setPlanningEditor({ mode: 'edit', entityType: 'workstream', targetId: id });
2854
2680
  setPlanningDraftTitle(item.title);
2855
2681
  setPlanningDraftDescription(item.description || '');
2856
- setPlanningDraftOwner(item.ownerLabel ? (ownerValueByLabel.get(item.ownerLabel) || '') : '');
2682
+ setPlanningDraftOwner(item.ownerId ? String(item.ownerId) : '');
2857
2683
  const initiative = item.initiativeId ? planningStructure.initiativeById.get(item.initiativeId) || null : null;
2858
2684
  setPlanningDraftInitiativeId(initiative ? formatInitiativeReference(initiative) : '');
2859
2685
  }
2860
2686
  setPlanningNestedWorkstreamDetailId(null);
2861
- }, [ownerValueByLabel, planningStructure.initiativeById, planningStructure.standaloneWorkstreams, planningStructure.workstreamById]);
2687
+ }, [planningStructure.initiativeById, planningStructure.standaloneWorkstreams, planningStructure.workstreamById]);
2862
2688
  const handleAssignInitiativeToWorkstream = useCallback(async (workstreamId, initiativeReferenceOverride) => {
2863
2689
  const workstream = props.workstreams.find((item) => item.id === workstreamId);
2864
2690
  if (!workstream) {
@@ -2966,6 +2792,54 @@ export function StandaloneLayout(props) {
2966
2792
  }
2967
2793
  await handleAssignInitiativeToWorkstream(matchedWorkstream.id, formatInitiativeReference(initiative));
2968
2794
  }, [handleAssignInitiativeToWorkstream, planningStructure.initiativeById, props.workstreams, pushNotice]);
2795
+ const getPlanningContextAttachmentDraft = useCallback((entityType, entityId) => {
2796
+ const key = `${entityType}:${entityId}`;
2797
+ const draft = planningContextAttachmentDraftsRef.current.get(key);
2798
+ if (draft)
2799
+ return draft;
2800
+ const entity = entityType === 'initiative'
2801
+ ? props.initiatives.find((initiative) => initiative.id === entityId)
2802
+ : props.workstreams.find((workstream) => workstream.id === entityId);
2803
+ return Array.isArray(entity?.attachments) ? entity.attachments : [];
2804
+ }, [props.initiatives, props.workstreams]);
2805
+ const updatePlanningContextAttachments = useCallback((entityType, entityId, nextAttachments) => {
2806
+ const key = `${entityType}:${entityId}`;
2807
+ planningContextAttachmentDraftsRef.current.set(key, nextAttachments);
2808
+ const previousUpdate = planningContextAttachmentUpdateQueueRef.current.get(key) || Promise.resolve();
2809
+ const nextUpdate = previousUpdate
2810
+ .catch(() => undefined)
2811
+ .then(() => (entityType === 'initiative'
2812
+ ? props.updateInitiative(entityId, { attachments: nextAttachments })
2813
+ : props.updateWorkstream(entityId, { attachments: nextAttachments })));
2814
+ planningContextAttachmentUpdateQueueRef.current.set(key, nextUpdate);
2815
+ void nextUpdate.catch((error) => {
2816
+ pushNotice(error instanceof Error ? error.message : 'Failed to update planning context.', 'error');
2817
+ }).finally(() => {
2818
+ if (planningContextAttachmentUpdateQueueRef.current.get(key) === nextUpdate) {
2819
+ planningContextAttachmentUpdateQueueRef.current.delete(key);
2820
+ }
2821
+ });
2822
+ }, [props, pushNotice]);
2823
+ const handleAddPlanningContextFile = useCallback((entityType, entityId, file) => {
2824
+ const current = getPlanningContextAttachmentDraft(entityType, entityId);
2825
+ updatePlanningContextAttachments(entityType, entityId, [...current, file]);
2826
+ }, [getPlanningContextAttachmentDraft, updatePlanningContextAttachments]);
2827
+ const handleRemovePlanningContextFile = useCallback((entityType, entityId, index) => {
2828
+ const current = getPlanningContextAttachmentDraft(entityType, entityId);
2829
+ updatePlanningContextAttachments(entityType, entityId, current.filter((_, i) => i !== index));
2830
+ }, [getPlanningContextAttachmentDraft, updatePlanningContextAttachments]);
2831
+ const handleUpdatePlanningContextCaption = useCallback((entityType, entityId, index, caption) => {
2832
+ const current = getPlanningContextAttachmentDraft(entityType, entityId);
2833
+ const nextAttachments = current.map((attachment, i) => {
2834
+ if (i !== index)
2835
+ return attachment;
2836
+ if (typeof attachment === 'string') {
2837
+ return { path: attachment, caption, timestamp: new Date().toISOString() };
2838
+ }
2839
+ return { ...attachment, caption };
2840
+ });
2841
+ updatePlanningContextAttachments(entityType, entityId, nextAttachments);
2842
+ }, [getPlanningContextAttachmentDraft, updatePlanningContextAttachments]);
2969
2843
  const handleInitiativeFilterChange = useCallback((initiativeId) => {
2970
2844
  setSelectedPlanningInitiativeId(initiativeId);
2971
2845
  setSelectedPlanningWorkstreamId('');
@@ -2981,8 +2855,10 @@ export function StandaloneLayout(props) {
2981
2855
  pushNotice(`${unavailableLabel} is not available in this build yet.`, 'error');
2982
2856
  return;
2983
2857
  }
2858
+ clearTaskOriginWorkspaceModule();
2984
2859
  setActiveWorkspaceModule(nextModule);
2985
2860
  }, [
2861
+ clearTaskOriginWorkspaceModule,
2986
2862
  pushNotice,
2987
2863
  setActiveWorkspaceModule,
2988
2864
  workspaceModules,
@@ -3002,10 +2878,40 @@ export function StandaloneLayout(props) {
3002
2878
  }
3003
2879
  setPlanningDrawerOpen((current) => !current);
3004
2880
  }, [planningDrawerOpen, resolvedWorkspaceModule, setActiveWorkspaceModule]);
2881
+ const handleDocumentTrayToggle = useCallback(() => {
2882
+ if (resolvedWorkspaceModule !== 'docs') {
2883
+ setActiveWorkspaceModule('docs');
2884
+ setDocumentTrayOpen(true);
2885
+ return;
2886
+ }
2887
+ setDocumentTrayOpen((current) => !current);
2888
+ }, [resolvedWorkspaceModule, setActiveWorkspaceModule]);
2889
+ const handleImageTrayToggle = useCallback(() => {
2890
+ if (resolvedWorkspaceModule !== 'annotate') {
2891
+ setActiveWorkspaceModule('annotate');
2892
+ setImageTrayOpen(true);
2893
+ return;
2894
+ }
2895
+ setImageTrayOpen((current) => !current);
2896
+ }, [resolvedWorkspaceModule, setActiveWorkspaceModule]);
2897
+ const handleAgentTrayToggle = useCallback(() => {
2898
+ if (resolvedWorkspaceModule !== 'agents') {
2899
+ setActiveWorkspaceModule('agents');
2900
+ setAgentTrayOpen(true);
2901
+ return;
2902
+ }
2903
+ setAgentTrayOpen((current) => !current);
2904
+ }, [resolvedWorkspaceModule, setActiveWorkspaceModule]);
3005
2905
  const handleBackToTaskFromAnnotatedAttachment = useCallback((taskId) => {
2906
+ clearTaskOriginWorkspaceModule();
3006
2907
  setActiveWorkspaceModule('tasks');
3007
2908
  handleOpenTaskById(taskId);
3008
- }, [handleOpenTaskById, setActiveWorkspaceModule]);
2909
+ }, [clearTaskOriginWorkspaceModule, handleOpenTaskById, setActiveWorkspaceModule]);
2910
+ const handleBackToTaskFromDocument = useCallback((taskId) => {
2911
+ clearTaskOriginWorkspaceModule();
2912
+ setActiveWorkspaceModule('tasks');
2913
+ handleOpenTaskById(taskId);
2914
+ }, [clearTaskOriginWorkspaceModule, handleOpenTaskById, setActiveWorkspaceModule]);
3009
2915
  const handleAnnotatedAttachmentOpenTarget = useCallback((target, options) => {
3010
2916
  setRequestedAnnotatedTarget(target);
3011
2917
  setRequestedAnnotatedSessionId(options?.sessionId ?? null);
@@ -3196,16 +3102,45 @@ export function StandaloneLayout(props) {
3196
3102
  workflows: _jsx(Workflow, { size: 18 }),
3197
3103
  agents: _jsx(Bot, { size: 18 }),
3198
3104
  };
3199
- return (_jsxs("aside", { className: shellStyles.workspaceToolRail, "aria-label": "Workspace tools", children: [_jsxs("div", { className: shellStyles.workspaceToolRailMain, children: [_jsx("button", { type: "button", className: `${shellStyles.workspaceToolButton} ${(resolvedWorkspaceModule === 'tasks' && planningDrawerOpen) ? shellStyles.workspaceToolButtonActive : ''}`.trim(), onClick: handlePlanningToolToggle, title: resolvedWorkspaceModule === 'tasks' && planningDrawerOpen ? 'Collapse planning trays' : 'Planning', "aria-label": resolvedWorkspaceModule === 'tasks' && planningDrawerOpen ? 'Collapse planning trays' : 'Planning', children: resolvedWorkspaceModule === 'tasks' && planningDrawerOpen ? _jsx(ChevronLeft, { size: 18 }) : _jsx(NotebookTabs, { size: 18 }) }), _jsx("div", { className: shellStyles.workspaceToolRailDivider }), workspaceToolModules.map((module) => (_jsx("button", { type: "button", className: `${shellStyles.workspaceToolButton} ${resolvedWorkspaceModule === module.id ? shellStyles.workspaceToolButtonActive : ''}`.trim(), onClick: () => handleModuleChange(module.id), title: module.label, "aria-label": module.label, children: moduleIcons[module.id] }, module.id)))] }), _jsxs("div", { className: shellStyles.workspaceToolRailBottom, children: [_jsx("div", { className: shellStyles.workspaceToolRailDivider }), _jsx("button", { type: "button", className: `${shellStyles.workspaceToolButton} ${activeTab === 'settings' ? shellStyles.workspaceToolButtonActive : ''}`.trim(), onClick: () => {
3105
+ const trayButtonDisabledClass = shellStyles.workspaceToolButtonDisabled || '';
3106
+ const workspaceTrayButton = (() => {
3107
+ if (resolvedWorkspaceModule === 'docs') {
3108
+ return (_jsx("button", { type: "button", className: `${shellStyles.workspaceToolButton} ${documentTrayOpen ? shellStyles.workspaceToolButtonActive : ''}`.trim(), onClick: handleDocumentTrayToggle, title: documentTrayOpen ? 'Collapse document tray' : 'Document tray', "aria-label": documentTrayOpen ? 'Collapse document tray' : 'Document tray', children: documentTrayOpen ? _jsx(ChevronLeft, { size: 18 }) : _jsx(Folder, { size: 18 }) }));
3109
+ }
3110
+ if (resolvedWorkspaceModule === 'annotate') {
3111
+ return (_jsx("button", { type: "button", className: `${shellStyles.workspaceToolButton} ${imageTrayOpen ? shellStyles.workspaceToolButtonActive : ''}`.trim(), onClick: handleImageTrayToggle, title: imageTrayOpen ? 'Collapse image tray' : 'Image tray', "aria-label": imageTrayOpen ? 'Collapse image tray' : 'Image tray', children: imageTrayOpen ? _jsx(ChevronLeft, { size: 18 }) : _jsx(Folder, { size: 18 }) }));
3112
+ }
3113
+ if (resolvedWorkspaceModule === 'agents') {
3114
+ return (_jsx("button", { type: "button", className: `${shellStyles.workspaceToolButton} ${agentTrayOpen ? shellStyles.workspaceToolButtonActive : ''}`.trim(), onClick: handleAgentTrayToggle, title: agentTrayOpen ? 'Collapse agent tray' : 'MCP settings tray', "aria-label": agentTrayOpen ? 'Collapse agent tray' : 'MCP settings tray', children: agentTrayOpen ? _jsx(ChevronLeft, { size: 18 }) : _jsx(Plug, { size: 18 }) }));
3115
+ }
3116
+ if (resolvedWorkspaceModule === 'workflows') {
3117
+ return (_jsx("button", { type: "button", className: `${shellStyles.workspaceToolButton} ${trayButtonDisabledClass}`.trim(), title: "Workflow tray coming soon", "aria-label": "Workflow tray coming soon", disabled: true, children: _jsx(Workflow, { size: 18 }) }));
3118
+ }
3119
+ return (_jsx("button", { type: "button", className: `${shellStyles.workspaceToolButton} ${(resolvedWorkspaceModule === 'tasks' && planningDrawerOpen) ? shellStyles.workspaceToolButtonActive : ''}`.trim(), onClick: handlePlanningToolToggle, title: resolvedWorkspaceModule === 'tasks' && planningDrawerOpen ? 'Collapse planning trays' : 'Planning', "aria-label": resolvedWorkspaceModule === 'tasks' && planningDrawerOpen ? 'Collapse planning trays' : 'Planning', children: resolvedWorkspaceModule === 'tasks' && planningDrawerOpen ? _jsx(ChevronLeft, { size: 18 }) : _jsx(NotebookTabs, { size: 18 }) }));
3120
+ })();
3121
+ return (_jsxs("aside", { className: shellStyles.workspaceToolRail, "aria-label": "Workspace tools", children: [_jsxs("div", { className: shellStyles.workspaceToolRailMain, children: [workspaceTrayButton, _jsx("div", { className: shellStyles.workspaceToolRailDivider }), workspaceToolModules.map((module) => (_jsx("button", { type: "button", className: `${shellStyles.workspaceToolButton} ${resolvedWorkspaceModule === module.id ? shellStyles.workspaceToolButtonActive : ''}`.trim(), onClick: () => handleModuleChange(module.id), title: module.label, "aria-label": module.label, children: moduleIcons[module.id] }, module.id)))] }), _jsxs("div", { className: shellStyles.workspaceToolRailBottom, children: [_jsx("div", { className: shellStyles.workspaceToolRailDivider }), _jsx("button", { type: "button", className: `${shellStyles.workspaceToolButton} ${activeTab === 'settings' ? shellStyles.workspaceToolButtonActive : ''}`.trim(), onClick: () => {
3200
3122
  setSettingsSection('general');
3201
3123
  setActiveTab('settings');
3202
3124
  }, title: "Workspace Settings", "aria-label": "Workspace Settings", children: _jsx(Settings, { size: 18 }) })] })] }));
3203
- }, [activeTab, handleModuleChange, handlePlanningToolToggle, planningDrawerOpen, resolvedWorkspaceModule, setSettingsSection, setActiveTab, workspaceToolModules]);
3204
- const planningDrawerPortalRef = useRef(null);
3205
- const planningDrawerNode = (_jsx(PlanningDrawer, { open: planningDrawerOpen, leftOffset: WORKSPACE_TOOL_RAIL_WIDTH, initiatives: planningStructure.initiatives, standaloneWorkstreams: planningStructure.standaloneWorkstreams, activeInitiativeId: selectedPlanningInitiativeId, activeWorkstreamId: selectedPlanningWorkstreamId, expandedInitiativeIds: expandedPlanningInitiativeIds, detail: planningDrawerItem, editor: planningEditor, secondaryPane: planningSecondaryPane, assigneeOptions: assigneeOptions.map((option) => ({ value: String(option.value), label: option.label })), draftInitiativeSummary: planningDraftInitiativeSummary, draftTitle: planningDraftTitle, draftDescription: planningDraftDescription, draftOwner: planningDraftOwner, draftInitiativeId: planningDraftInitiativeId, onChangeDraftTitle: setPlanningDraftTitle, onChangeDraftDescription: setPlanningDraftDescription, onChangeDraftOwner: setPlanningDraftOwner, onChangeDraftInitiativeId: setPlanningDraftInitiativeId, onCollapseTreePane: () => setPlanningTreeCollapsed(true), onExpandTreePane: () => setPlanningTreeCollapsed(false), onCollapsePrimaryPane: () => setPlanningPrimaryCollapsed(true), onExpandPrimaryPane: () => setPlanningPrimaryCollapsed(false), onCollapseSecondaryPane: () => setPlanningSecondaryCollapsed(true), onExpandSecondaryPane: () => setPlanningSecondaryCollapsed(false), onBackFromSecondary: () => {
3125
+ }, [activeTab, agentTrayOpen, documentTrayOpen, handleAgentTrayToggle, handleDocumentTrayToggle, handleImageTrayToggle, handleModuleChange, handlePlanningToolToggle, imageTrayOpen, planningDrawerOpen, resolvedWorkspaceModule, setSettingsSection, setActiveTab, workspaceToolModules]);
3126
+ const [planningDrawerPortalElement, setPlanningDrawerPortalElement] = useState(null);
3127
+ const setPlanningDrawerPortalRef = useCallback((node) => {
3128
+ setPlanningDrawerPortalElement(node);
3129
+ }, []);
3130
+ const planningDrawerNode = (_jsx(PlanningDrawer, { open: planningDrawerOpen, leftOffset: WORKSPACE_TOOL_RAIL_WIDTH, initiatives: planningStructure.initiatives, standaloneWorkstreams: planningStructure.standaloneWorkstreams, allStandaloneWorkstreams: planningStructure.allStandaloneWorkstreams, activeInitiativeId: selectedPlanningInitiativeId, activeWorkstreamId: selectedPlanningWorkstreamId, expandedInitiativeIds: expandedPlanningInitiativeIds, detail: planningDrawerItem, editor: planningEditor, secondaryPane: planningSecondaryPane, currentWorkspaceId: currentWorkspaceId, assigneeOptions: assigneeOptions.map((option) => ({
3131
+ value: String(option.value),
3132
+ label: option.label,
3133
+ kind: option.kind,
3134
+ username: option.username ?? null,
3135
+ archivedAt: option.archivedAt ?? null
3136
+ })), draftInitiativeSummary: planningDraftInitiativeSummary, draftTitle: planningDraftTitle, draftDescription: planningDraftDescription, draftOwner: planningDraftOwner, draftInitiativeId: planningDraftInitiativeId, onChangeDraftTitle: setPlanningDraftTitle, onChangeDraftDescription: setPlanningDraftDescription, onChangeDraftOwner: setPlanningDraftOwner, onChangeDraftInitiativeId: setPlanningDraftInitiativeId, onCollapseTreePane: () => setPlanningTreeCollapsed(true), onExpandTreePane: () => setPlanningTreeCollapsed(false), onCollapsePrimaryPane: () => setPlanningPrimaryCollapsed(true), onExpandPrimaryPane: () => setPlanningPrimaryCollapsed(false), onCollapseSecondaryPane: () => setPlanningSecondaryCollapsed(true), onExpandSecondaryPane: () => setPlanningSecondaryCollapsed(false), onBackFromSecondary: () => {
3137
+ planningEditorSubmitInFlightRef.current = false;
3138
+ setIsSubmittingPlanningEditor(false);
3206
3139
  setPlanningSecondaryEditor(null);
3207
3140
  setPlanningNestedWorkstreamDetailId(null);
3208
- }, treeCollapsed: planningTreeCollapsed, primaryCollapsed: planningPrimaryCollapsed, secondaryCollapsed: planningSecondaryCollapsed, onCancelEditor: closePlanningEditor, onSubmitEditor: submitPlanningEditor, onCreateInitiative: () => openPlanningCreateEditor('initiative'), onCreateWorkstream: () => openPlanningCreateEditor('workstream'), onCreateTaskInWorkstream: openAddTaskModalForWorkstream, onCreateWorkstreamInInitiative: (initiativeId) => openPlanningCreateEditor('workstream', initiativeId), onAssignInitiativeToWorkstream: handleAssignInitiativeToWorkstream, onOpenTaskById: handleOpenTaskById, onAttachTaskToWorkstreamByReference: handleAttachTaskToWorkstreamByReference, onAttachWorkstreamToInitiativeByReference: handleAttachWorkstreamToInitiativeByReference, onToggleInitiative: togglePlanningInitiative, onSelectInitiative: selectPlanningInitiative, onSelectWorkstream: selectPlanningWorkstream, onOpenInitiativeDetails: openInitiativeDetails, onOpenWorkstreamDetails: openWorkstreamDetails, onOpenNestedWorkstreamDetails: openNestedWorkstreamDetails, onEditInitiative: (initiativeId) => openPlanningEditDialog('initiative', initiativeId), onEditWorkstream: (workstreamId) => openPlanningEditDialog('workstream', workstreamId), onArchiveInitiative: (initiativeId) => {
3141
+ }, treeCollapsed: planningTreeCollapsed, primaryCollapsed: planningPrimaryCollapsed, secondaryCollapsed: planningSecondaryCollapsed, onCancelEditor: closePlanningEditor, onSubmitEditor: submitPlanningEditor, isSubmittingEditor: isSubmittingPlanningEditor, onCreateInitiative: () => openPlanningCreateEditor('initiative'), onCreateWorkstream: () => openPlanningCreateEditor('workstream'), onCreateTaskInWorkstream: openAddTaskModalForWorkstream, onCreateWorkstreamInInitiative: (initiativeId) => openPlanningCreateEditor('workstream', initiativeId), onAssignInitiativeToWorkstream: handleAssignInitiativeToWorkstream, onOpenTaskById: handleOpenTaskById, onRemoveTaskFromWorkstream: (taskId) => {
3142
+ void handleAssignTaskToWorkstream(taskId, null);
3143
+ }, onAttachTaskToWorkstreamByReference: handleAttachTaskToWorkstreamByReference, onAttachWorkstreamToInitiativeByReference: handleAttachWorkstreamToInitiativeByReference, onAddPlanningContextFile: handleAddPlanningContextFile, onRemovePlanningContextFile: handleRemovePlanningContextFile, onUpdatePlanningContextCaption: handleUpdatePlanningContextCaption, onToggleInitiative: togglePlanningInitiative, onSelectInitiative: selectPlanningInitiative, onSelectWorkstream: selectPlanningWorkstream, onOpenInitiativeDetails: openInitiativeDetails, onOpenWorkstreamDetails: openWorkstreamDetails, onOpenNestedWorkstreamDetails: openNestedWorkstreamDetails, onEditInitiative: (initiativeId) => openPlanningEditDialog('initiative', initiativeId), onEditWorkstream: (workstreamId) => openPlanningEditDialog('workstream', workstreamId), onArchiveInitiative: (initiativeId) => {
3209
3144
  void props.archiveInitiative(initiativeId).then(() => {
3210
3145
  pushNotice('Initiative archived', 'success');
3211
3146
  }).catch((error) => {
@@ -3240,7 +3175,7 @@ export function StandaloneLayout(props) {
3240
3175
  const workspacePrimaryAction = workspaceChromeDefinition.primaryAction
3241
3176
  ? primaryActionRegistry[workspaceChromeDefinition.primaryAction]
3242
3177
  : null;
3243
- return (_jsxs("div", { className: `${shellStyles.standaloneWrapper} ${zenMode ? styles.zenModeEnabled : ''}`, "data-theme": currentTheme, children: [_jsx(AppShellHeader, { projectName: projectName, currentWorkspaceId: currentWorkspaceId, runtimeMode: runtimeMode, theme: currentTheme, meta: isPlansScreen ? null : (_jsxs(_Fragment, { children: [_jsx("span", { className: styles.taskCountBadge, title: activeCountTitle, children: activeCountLabel }), normalizedAuthUserId && normalizedAuthUserId !== 'anonymous' && (_jsxs(_Fragment, { children: [_jsxs("span", { className: styles.taskCountBadge, title: "Tasks assigned to me", children: [_jsx(User, { size: 13, className: styles.taskCountBadgeIcon, "aria-hidden": "true" }), assignedTaskCount] }), _jsxs("span", { className: `${styles.taskCountBadge} ${assignedOverdueTaskCount > 0 ? styles.taskCountBadgeAlert : ''}`.trim(), title: "Tasks overdue", children: [_jsx(ClockAlert, { size: 13, className: styles.taskCountBadgeIcon, "aria-hidden": "true" }), assignedOverdueTaskCount] })] })), shouldShowSyncStatus && (_jsx("button", { className: "tf-control-icon", onClick: () => setShowSyncStatusModal(true), title: `Sync manager: ${syncStatusMeta.label} | Last success: ${formattedLastSyncTime}`, style: {
3178
+ return (_jsxs("div", { className: `${shellStyles.standaloneWrapper} ${zenMode ? styles.zenModeEnabled : ''}`, "data-theme": currentTheme, children: [_jsx(AppShellHeader, { projectName: projectName, currentWorkspaceId: currentWorkspaceId, runtimeMode: runtimeMode, theme: currentTheme, onBrandClick: handleOpenKanbanFromBrand, meta: (_jsxs(_Fragment, { children: [_jsx("span", { className: styles.taskCountBadge, title: activeCountTitle, children: activeCountLabel }), normalizedAuthUserId && normalizedAuthUserId !== 'anonymous' && (_jsxs(_Fragment, { children: [_jsxs("span", { className: styles.taskCountBadge, title: "Tasks assigned to me", children: [_jsx(User, { size: 13, className: styles.taskCountBadgeIcon, "aria-hidden": "true" }), assignedTaskCount] }), _jsxs("span", { className: `${styles.taskCountBadge} ${assignedOverdueTaskCount > 0 ? styles.taskCountBadgeAlert : ''}`.trim(), title: "Tasks overdue", children: [_jsx(ClockAlert, { size: 13, className: styles.taskCountBadgeIcon, "aria-hidden": "true" }), assignedOverdueTaskCount] })] })), shouldShowSyncStatus && (_jsx("button", { className: "tf-control-icon", onClick: openSyncStatusModal, title: `Sync manager: ${syncStatusMeta.label} | Last success: ${formattedLastSyncTime}`, style: {
3244
3179
  marginLeft: '6px',
3245
3180
  height: '24px',
3246
3181
  width: '24px',
@@ -3252,10 +3187,29 @@ export function StandaloneLayout(props) {
3252
3187
  display: 'inline-flex',
3253
3188
  alignItems: 'center',
3254
3189
  justifyContent: 'center'
3255
- }, children: syncStatusMeta.icon === 'off' ? (_jsx(CloudOff, { size: 16 })) : (_jsx(Cloud, { size: 16 })) }))] })), actions: (_jsxs(_Fragment, { children: [isPlansScreen && (_jsx("button", { className: "tf-control-icon", onClick: () => { void handlePlansBack(); }, title: isAuthenticated && !hasActiveCommercialEntitlement ? 'Choose a plan to continue.' : 'Continue to Taskforce', disabled: plansNavigationBusy || (isAuthenticated && !hasActiveCommercialEntitlement), children: hasActiveCommercialEntitlement ? 'Take Me to Taskforce' : 'Back' })), !isPlansScreen && (_jsxs(_Fragment, { children: [workspaceHeaderSections, workspacePrimaryAction] })), !isPlansScreen && _jsx(WorkspaceHeaderActions, { actions: universalDisplayActions }), !isPlansScreen && _jsx(WorkspaceHeaderActions, { actions: universalFilterActions }), accountMenuControl] })) }), _jsx(TopNoticeLayer, { notice: uiNotice, onDismiss: clearNotice }), authBlocked && (_jsx("div", { className: authStyles.authBlockedBanner, children: "Authentication required for this environment. Use the Account menu to sign in." })), !isPlansScreen && showFilters && workspaceFilterBar, isPlansScreen ? (_jsx("div", { className: `${shellStyles.standalonePage} ${shellStyles.standaloneContent} ${styles.appScrollbar} tf-scrollbar`, style: { position: 'relative', display: 'flex', flex: 1, minHeight: 0, overflowY: 'auto', overflowX: 'hidden', scrollbarGutter: 'stable' }, children: _jsx(Suspense, { fallback: _jsx("div", { style: { display: 'flex', alignItems: 'center', justifyContent: 'center', flex: 1 }, children: _jsx(Loader2, { size: 20, className: styles.spinner }) }), children: _jsx(PlansPage, { ...props, currentTheme: currentTheme, projectName: projectName, currentWorkspaceId: currentWorkspaceId, authUserId: authUserId, apiBaseUrl: apiBaseUrl, connectedEnvironmentSource: props.config?.cloudAuthBaseUrl || props.config?.apiBaseUrl || '', resolveCloudAuthUrl: resolveCloudAuthUrl, embedded: true, shellOwnsScroll: true, onAccountProfileSummaryChange: applyPlansAccountProfileSummary, onContinueToTaskforce: handlePlansBack, continueBusy: plansNavigationBusy }) }) })) : resolvedWorkspaceModule === 'docs' && documentsWorkspaceEnabled ? (_jsxs("div", { className: `${shellStyles.standalonePage} ${shellStyles.standaloneContent}`, style: { position: 'relative', display: 'flex', flex: 1, minHeight: 0, overflow: 'hidden', paddingLeft: `${WORKSPACE_TOOL_RAIL_WIDTH}px` }, children: [workspaceToolRail, _jsx(Suspense, { fallback: _jsx("div", { style: { display: 'flex', alignItems: 'center', justifyContent: 'center', flex: 1 }, children: _jsx(Loader2, { size: 20, className: styles.spinner }) }), children: _jsx(DocumentWorkspaceShell, { tasks: tasks, runtimeMode: runtimeMode, apiBaseUrl: props.config?.apiBaseUrl || '', cloudAuthBaseUrl: props.config?.cloudAuthBaseUrl || '', typeFilters: documentTypeFilters, attachmentFilters: documentAttachmentFilters, onTypeFiltersChange: setDocumentTypeFilters, onAttachmentFiltersChange: setDocumentAttachmentFilters, requestedDocPath: requestedDocPath, requestedDocAssetId: requestedDocAssetId, onRequestedDocHandled: () => {
3190
+ }, children: syncStatusMeta.icon === 'off' ? (_jsx(CloudOff, { size: 16 })) : (_jsx(Cloud, { size: 16 })) }))] })), actions: (_jsxs(_Fragment, { children: [isPlansScreen && (_jsxs(_Fragment, { children: [isAuthenticated && accountProfileSummary?.stripeCustomerId && (_jsx("button", { className: "tf-control-icon", onClick: () => { void handleOpenBillingPortal(); }, title: "Manage billing", disabled: billingActionBusy, children: "Manage billing" })), _jsx("button", { className: "tf-control-icon", onClick: () => {
3191
+ if (hasActiveCommercialEntitlement) {
3192
+ void handlePlansBack();
3193
+ return;
3194
+ }
3195
+ closePlansScreen();
3196
+ }, title: hasActiveCommercialEntitlement ? 'Continue to Taskforce' : 'Back', disabled: plansNavigationBusy, children: hasActiveCommercialEntitlement ? 'Take Me to Taskforce' : 'Back' })] })), !isPlansScreen && (_jsxs(_Fragment, { children: [workspaceHeaderSections, workspacePrimaryAction] })), _jsx(WorkspaceHeaderActions, { actions: universalDisplayActions }), !isPlansScreen && _jsx(WorkspaceHeaderActions, { actions: universalFilterActions }), accountMenuControl] })) }), _jsx(TopNoticeLayer, { notice: uiNotice, onDismiss: clearNotice }), authBlocked && (_jsx("div", { className: authStyles.authBlockedBanner, children: "Authentication required for this environment. Use the Account menu to sign in." })), !isPlansScreen && showFilters && workspaceFilterBar, isPlansScreen ? (_jsx("div", { className: `${shellStyles.standalonePage} ${shellStyles.standaloneContent} ${styles.appScrollbar} tf-scrollbar`, style: {
3197
+ position: 'relative',
3198
+ display: 'flex',
3199
+ flex: 1,
3200
+ minHeight: 0,
3201
+ overflowY: 'auto',
3202
+ overflowX: 'hidden',
3203
+ scrollbarGutter: 'stable',
3204
+ background: 'var(--surface-page)'
3205
+ }, children: _jsx(Suspense, { fallback: _jsx("div", { style: { display: 'flex', alignItems: 'center', justifyContent: 'center', flex: 1 }, children: _jsx(Loader2, { size: 20, className: styles.spinner }) }), children: _jsx(PlansPage, { ...props, currentTheme: currentTheme, projectName: projectName, currentWorkspaceId: currentWorkspaceId, authUserId: authUserId, apiBaseUrl: apiBaseUrl, connectedEnvironmentSource: props.config?.cloudAuthBaseUrl || props.config?.apiBaseUrl || '', resolveCloudAuthUrl: resolveCloudAuthUrl, embedded: true, shellOwnsScroll: true, onAccountProfileSummaryChange: applyPlansAccountProfileSummary, onContinueToTaskforce: handlePlansBack, continueBusy: plansNavigationBusy }) }) })) : resolvedWorkspaceModule === 'docs' && documentsWorkspaceEnabled ? (_jsxs("div", { className: `${shellStyles.standalonePage} ${shellStyles.standaloneContent}`, style: { position: 'relative', display: 'flex', flex: 1, minHeight: 0, overflow: 'hidden', paddingLeft: `${WORKSPACE_TOOL_RAIL_WIDTH}px` }, children: [workspaceToolRail, _jsx(Suspense, { fallback: _jsx("div", { style: { display: 'flex', alignItems: 'center', justifyContent: 'center', flex: 1 }, children: _jsx(Loader2, { size: 20, className: styles.spinner }) }), children: _jsx(DocumentWorkspaceShell, { tasks: tasks, runtimeMode: runtimeMode, apiBaseUrl: props.config?.apiBaseUrl || '', cloudAuthBaseUrl: props.config?.cloudAuthBaseUrl || '', typeFilters: documentTypeFilters, attachmentFilters: documentAttachmentFilters, onTypeFiltersChange: setDocumentTypeFilters, onAttachmentFiltersChange: setDocumentAttachmentFilters, documentTrayOpen: documentTrayOpen, onCloseDocumentTray: () => setDocumentTrayOpen(false), requestedDocPath: requestedDocPath, requestedDocAssetId: requestedDocAssetId, onRequestedDocHandled: () => {
3256
3206
  setRequestedDocPath(null);
3257
3207
  setRequestedDocAssetId(null);
3258
- }, enableTaskGeneration: true }) })] })) : resolvedWorkspaceModule === 'annotate' && annotatedAttachmentsWorkspaceEnabled ? (_jsxs("div", { className: `${shellStyles.standalonePage} ${shellStyles.standaloneContent}`, style: { position: 'relative', display: 'flex', flex: 1, minHeight: 0, overflow: 'hidden', paddingLeft: `${WORKSPACE_TOOL_RAIL_WIDTH}px` }, children: [workspaceToolRail, _jsx(Suspense, { fallback: _jsx("div", { style: { display: 'flex', alignItems: 'center', justifyContent: 'center', flex: 1 }, children: _jsx(Loader2, { size: 20, className: styles.spinner }) }), children: _jsx(AnnotatedAttachmentWorkspaceShell, { runtimeMode: runtimeMode, apiBaseUrl: props.config?.apiBaseUrl || '', cloudAuthBaseUrl: props.config?.cloudAuthBaseUrl || '', workspaceId: currentWorkspaceId, sessionLoadReady: annotatedWorkspaceReady, requestedTarget: requestedAnnotatedTarget, requestedSessionId: requestedAnnotatedSessionId, requestedOpenVersion: requestedAnnotatedOpenVersion, resolveTaskReferenceLabel: resolveAnnotatedTaskReferenceLabel, resolveImageReferenceLabel: resolveAnnotatedImageReferenceLabel, onOpenTarget: handleAnnotatedAttachmentOpenTarget, onContextChange: handleAnnotatedAttachmentContextChange, onBackToTask: handleBackToTaskFromAnnotatedAttachment }) })] })) : resolvedWorkspaceModule === 'workflows' ? (_jsxs("div", { className: `${shellStyles.standalonePage} ${shellStyles.standaloneContent}`, style: { position: 'relative', display: 'flex', flex: 1, minHeight: 0, overflow: 'hidden', paddingLeft: `${WORKSPACE_TOOL_RAIL_WIDTH}px` }, children: [workspaceToolRail, _jsx(Suspense, { fallback: _jsx("div", { style: { display: 'flex', alignItems: 'center', justifyContent: 'center', flex: 1 }, children: _jsx(Loader2, { size: 20, className: styles.spinner }) }), children: _jsx(WorkflowsModule, { availableWorkflows: availableWorkflows, availableEnvironments: availableEnvironments, exportEnvironment: exportEnvironment, exportWorkflowsPath: props.exportWorkflowsPath, exportingResource: exportingResource, exportResult: exportResult, onExportEnvironmentChange: props.setExportEnvironment, onExportWorkflows: onExportWorkflows, onRefreshWorkflows: fetchWorkflows, onFetchWorkflowTemplate: props.fetchWorkflowTemplate, onFetchWorkflowOverrideNames: props.fetchWorkflowOverrideNames, onSaveWorkflowTemplateDraft: props.saveWorkflowTemplateDraft, onResetWorkflowTemplateDraft: props.resetWorkflowTemplateDraft }) })] })) : resolvedWorkspaceModule === 'agents' ? (_jsxs("div", { className: `${shellStyles.standalonePage} ${shellStyles.standaloneContent}`, style: { position: 'relative', display: 'flex', flex: 1, minHeight: 0, overflow: 'hidden', paddingLeft: `${WORKSPACE_TOOL_RAIL_WIDTH}px` }, children: [workspaceToolRail, _jsx(Suspense, { fallback: _jsx("div", { style: { display: 'flex', alignItems: 'center', justifyContent: 'center', flex: 1 }, children: _jsx(Loader2, { size: 20, className: styles.spinner }) }), children: _jsx(AgentsModule, { workspaceId: currentWorkspaceId }) })] })) : (_jsxs("div", { className: `${shellStyles.standalonePage} ${shellStyles.standaloneContent} `, style: {
3208
+ }, onBackToTask: handleBackToTaskFromDocument, enableTaskGeneration: true }) })] })) : resolvedWorkspaceModule === 'annotate' && annotatedAttachmentsWorkspaceEnabled ? (_jsxs("div", { className: `${shellStyles.standalonePage} ${shellStyles.standaloneContent}`, style: { position: 'relative', display: 'flex', flex: 1, minHeight: 0, overflow: 'hidden', paddingLeft: `${WORKSPACE_TOOL_RAIL_WIDTH}px` }, children: [workspaceToolRail, _jsx(Suspense, { fallback: _jsx("div", { style: { display: 'flex', alignItems: 'center', justifyContent: 'center', flex: 1 }, children: _jsx(Loader2, { size: 20, className: styles.spinner }) }), children: _jsx(AnnotatedAttachmentWorkspaceShell, { runtimeMode: runtimeMode, apiBaseUrl: props.config?.apiBaseUrl || '', cloudAuthBaseUrl: props.config?.cloudAuthBaseUrl || '', workspaceId: currentWorkspaceId, sessionLoadReady: annotatedWorkspaceReady, requestedTarget: requestedAnnotatedTarget, requestedSessionId: requestedAnnotatedSessionId, requestedOpenVersion: requestedAnnotatedOpenVersion, imageTrayOpen: imageTrayOpen, onCloseImageTray: () => setImageTrayOpen(false), resolveTaskReferenceLabel: resolveAnnotatedTaskReferenceLabel, resolveImageReferenceLabel: resolveAnnotatedImageReferenceLabel, onOpenTarget: handleAnnotatedAttachmentOpenTarget, onContextChange: handleAnnotatedAttachmentContextChange, onBackToTask: handleBackToTaskFromAnnotatedAttachment }) })] })) : resolvedWorkspaceModule === 'workflows' ? (_jsxs("div", { className: `${shellStyles.standalonePage} ${shellStyles.standaloneContent}`, style: { position: 'relative', display: 'flex', flex: 1, minHeight: 0, overflow: 'hidden', paddingLeft: `${WORKSPACE_TOOL_RAIL_WIDTH}px` }, children: [workspaceToolRail, _jsx(Suspense, { fallback: _jsx("div", { style: { display: 'flex', alignItems: 'center', justifyContent: 'center', flex: 1 }, children: _jsx(Loader2, { size: 20, className: styles.spinner }) }), children: _jsx(WorkflowsModule, { availableWorkflows: availableWorkflows, availableEnvironments: availableEnvironments, exportEnvironment: exportEnvironment, exportWorkflowsPath: props.exportWorkflowsPath, exportingResource: exportingResource, exportResult: exportResult, onExportEnvironmentChange: props.setExportEnvironment, onExportWorkflows: onExportWorkflows, onRefreshWorkflows: fetchWorkflows, onFetchWorkflowTemplate: props.fetchWorkflowTemplate, onFetchWorkflowOverrideNames: props.fetchWorkflowOverrideNames, onSaveWorkflowTemplateDraft: props.saveWorkflowTemplateDraft, onResetWorkflowTemplateDraft: props.resetWorkflowTemplateDraft }) })] })) : resolvedWorkspaceModule === 'agents' ? (_jsxs("div", { className: `${shellStyles.standalonePage} ${shellStyles.standaloneContent}`, style: { position: 'relative', display: 'flex', flex: 1, minHeight: 0, overflow: 'hidden', paddingLeft: `${WORKSPACE_TOOL_RAIL_WIDTH}px` }, children: [workspaceToolRail, _jsx(Suspense, { fallback: _jsx("div", { style: { display: 'flex', alignItems: 'center', justifyContent: 'center', flex: 1 }, children: _jsx(Loader2, { size: 20, className: styles.spinner }) }), children: _jsx(AgentsModule, { workspaceId: currentWorkspaceId, cloudAuthConfigured: cloudAuthConfigured, authSessionResolved: authSessionResolved, isAuthenticated: isAuthenticated, cloudAiProfileSeatUsage: accountProfileSummary?.aiProfileSeatUsage ?? null, agentTrayOpen: agentTrayOpen, onCloseAgentTray: () => setAgentTrayOpen(false), mcpSettingsNode: _jsx(TaskSettings, { settingsModel: {
3209
+ ...settingsModel,
3210
+ initialSection: 'mcp',
3211
+ onOpenCloudAuth: openSharedLoginRoute
3212
+ }, renderMode: "mcp-only" }) }) })] })) : (_jsxs("div", { className: `${shellStyles.standalonePage} ${shellStyles.standaloneContent} `, style: {
3259
3213
  position: 'relative',
3260
3214
  opacity: props.loadingTasks ? 0.7 : 1,
3261
3215
  transition: 'opacity 0.2s ease, padding 220ms cubic-bezier(0.4, 0, 0.2, 1)',
@@ -3266,12 +3220,12 @@ export function StandaloneLayout(props) {
3266
3220
  ? `${WORKSPACE_TOOL_RAIL_WIDTH + planningDrawerWidthPx + WORKSPACE_CONTENT_GUTTER}px`
3267
3221
  : `${WORKSPACE_TOOL_RAIL_WIDTH + WORKSPACE_CONTENT_GUTTER}px`,
3268
3222
  paddingRight: (groupBy === 'schedule' && scheduleSidebarOpen) ? `${SCHEDULE_SIDEBAR_WIDTH_PX}px` : 0
3269
- }, children: [workspaceToolRail, _jsx("div", { ref: planningDrawerPortalRef, style: {
3223
+ }, children: [workspaceToolRail, _jsx("div", { ref: setPlanningDrawerPortalRef, style: {
3270
3224
  position: 'absolute',
3271
3225
  inset: 0,
3272
3226
  zIndex: 35,
3273
3227
  pointerEvents: 'none',
3274
- } }), _jsxs("div", { style: { position: 'relative', width: '100%', minWidth: 0, flex: 1, minHeight: 0, display: 'flex', flexDirection: 'column', overflow: 'hidden' }, children: [props.loadingTasks && boardVisibleTasks.length > 0 && (_jsx("div", { className: styles.boardRefreshIndicator, "aria-live": "polite", "aria-label": "Refreshing tasks", children: _jsx(Loader2, { size: 14, className: styles.spinner }) })), _jsx(TaskKanban, { tasks: scopedBoardVisibleTasks, allTasks: scopedKanbanAllTasks, columns: kanbanColumns, groupBy: groupBy, scheduleDates: scheduleDates, searchQuery: searchQuery, filterCategories: filterCategories, filterTypes: filterTypes, filterPriorities: filterPriorities, filterStatus: filterStatus, filterAssignees: filterAssignees, assigneeOptions: assigneeOptions, scheduleFilteredTaskIds: Array.from(scheduleFilteredTaskIds), copiedId: copiedId, taxonomies: taxonomies, types: activeTypes, priorities: priorities, onUpdateTask: handleUpdateTask, onTaskClick: (task) => handleEdit(task), onOpenTaskById: handleOpenTaskById, onCopyId: handleCopyId, onToggleInProgress: handleToggleInProgress, onToggleReview: handleToggleReview, onToggleComplete: handleToggleComplete, onToggleCancel: handleToggleCancel, onSetStatus: handleSetStatus, onArchiveTask: handleArchiveTask, onAddTaskToColumn: handleAddTaskToColumn, showTaskCardStatusLabel: showTaskCardStatusLabel, onScheduleDaySelected: (dateOnly) => {
3228
+ } }), _jsxs("div", { style: { position: 'relative', width: '100%', minWidth: 0, flex: 1, minHeight: 0, display: 'flex', flexDirection: 'column', overflow: 'hidden' }, children: [props.loadingTasks && boardVisibleTasks.length > 0 && (_jsx("div", { className: styles.boardRefreshIndicator, "aria-live": "polite", "aria-label": "Refreshing tasks", children: _jsx(Loader2, { size: 14, className: styles.spinner }) })), _jsx(TaskKanban, { tasks: scopedBoardVisibleTasks, allTasks: scopedKanbanAllTasks, columns: kanbanColumns, groupBy: groupBy, scheduleDates: scheduleDates, searchQuery: searchQuery, filterCategories: filterCategories, filterTypes: filterTypes, filterPriorities: filterPriorities, filterStatus: filterStatus, filterAssignees: filterAssignees, filtersReady: hasInitedFilters, assigneeOptions: assigneeOptions, scheduleFilteredTaskIds: Array.from(scheduleFilteredTaskIds), copiedId: copiedId, taxonomies: taxonomies, types: activeTypes, priorities: priorities, onUpdateTask: handleUpdateTask, onTaskClick: (task) => handleEdit(task), onOpenTaskById: handleOpenTaskById, onCopyId: handleCopyId, onToggleInProgress: handleToggleInProgress, onToggleReview: handleToggleReview, onToggleComplete: handleToggleComplete, onToggleCancel: handleToggleCancel, onSetStatus: handleSetStatus, onArchiveTask: handleArchiveTask, onAddTaskToColumn: handleAddTaskToColumn, showTaskCardStatusLabel: showTaskCardStatusLabel, onScheduleDaySelected: (dateOnly) => {
3275
3229
  setScheduleSelectedDate(dateOnly);
3276
3230
  const parsed = parseDateOnlyLocal(dateOnly);
3277
3231
  if (parsed) {
@@ -3281,8 +3235,8 @@ export function StandaloneLayout(props) {
3281
3235
  if (groupBy !== 'schedule')
3282
3236
  return;
3283
3237
  setScheduleScrollLeft(scrollLeft);
3284
- }, emptyColumnMode: emptyColumnMode, categories: activeCategories, compressed: compressedCards, readOnlyMode: taskScope === 'deleted' ? 'deleted' : taskScope === 'archived' ? 'archived' : null, recentlyChangedTaskIds: props.recentlyChangedTaskIds, sortBy: sortBy, sortOrder: props.sortOrder, planningDropTargets: planningDrawerPortalRef.current
3285
- ? createPortal(planningDrawerNode, planningDrawerPortalRef.current)
3238
+ }, emptyColumnMode: emptyColumnMode, categories: activeCategories, compressed: compressedCards, readOnlyMode: taskScope === 'deleted' ? 'deleted' : taskScope === 'archived' ? 'archived' : null, recentlyChangedTaskIds: props.recentlyChangedTaskIds, sortBy: sortBy, sortOrder: props.sortOrder, planningDropTargets: planningDrawerPortalElement
3239
+ ? createPortal(planningDrawerNode, planningDrawerPortalElement)
3286
3240
  : null, onAssignTaskToWorkstream: handleAssignTaskToWorkstream, onAssignWorkstreamToInitiative: handleAssignInitiativeToWorkstream, workstreams: props.workstreams, initiatives: props.initiatives, onUnarchive: (taskId) => {
3287
3241
  if (taskScope === 'deleted') {
3288
3242
  const deletedRecord = deletedTaskRecordByTaskId.get(taskId);
@@ -3338,41 +3292,17 @@ export function StandaloneLayout(props) {
3338
3292
  setSettingsSection(section);
3339
3293
  setActiveTab('settings');
3340
3294
  }, onSubmit: handleSubmit, commentsEndRef: commentsEndRef, onAddComment: () => handleAddComment(newCommentText), onOpenTaskById: handleOpenTaskById, onAddContextFile: (file) => {
3341
- let nextAttachments = attachments;
3342
- setAttachments((prev) => {
3343
- nextAttachments = [...prev, file];
3344
- return nextAttachments;
3345
- });
3346
- setAttachmentsDirty(true);
3347
- if (editingTaskId) {
3348
- void handleUpdateTask(editingTaskId, { attachments: nextAttachments });
3349
- }
3295
+ updateTaskContextAttachments((current) => [...current, file]);
3350
3296
  }, onRemoveContextFile: async (index) => {
3351
- let nextAttachments = attachments;
3352
- setAttachments((prev) => {
3353
- nextAttachments = prev.filter((_, i) => i !== index);
3354
- return nextAttachments;
3355
- });
3356
- setAttachmentsDirty(true);
3357
- if (editingTaskId) {
3358
- void handleUpdateTask(editingTaskId, { attachments: nextAttachments });
3359
- }
3297
+ updateTaskContextAttachments((current) => current.filter((_, i) => i !== index));
3360
3298
  }, onUpdateContextCaption: (index, caption) => {
3361
- let nextAttachments = attachments;
3362
- setAttachments((prev) => {
3363
- nextAttachments = prev.map((s, i) => {
3364
- if (i !== index)
3365
- return s;
3366
- if (typeof s === 'string')
3367
- return { path: s, caption, timestamp: new Date().toISOString() };
3368
- return { ...s, caption };
3369
- });
3370
- return nextAttachments;
3371
- });
3372
- setAttachmentsDirty(true);
3373
- if (editingTaskId) {
3374
- void handleUpdateTask(editingTaskId, { attachments: nextAttachments });
3375
- }
3299
+ updateTaskContextAttachments((current) => current.map((s, i) => {
3300
+ if (i !== index)
3301
+ return s;
3302
+ if (typeof s === 'string')
3303
+ return { path: s, caption, timestamp: new Date().toISOString() };
3304
+ return { ...s, caption };
3305
+ }));
3376
3306
  }, onCopyId: handleCopyId, onToggleInProgress: handleToggleInProgress, onToggleReview: handleToggleReview, onToggleComplete: handleToggleComplete, onToggleCancel: handleToggleCancel, onSetStatus: handleSetStatus, onArchiveTask: handleArchiveTask, onUnarchive: (taskId) => {
3377
3307
  if (currentTask?.isDeleted) {
3378
3308
  const deletedRecord = deletedTaskRecordByTaskId.get(taskId);
@@ -3382,11 +3312,18 @@ export function StandaloneLayout(props) {
3382
3312
  return;
3383
3313
  }
3384
3314
  void handleUnarchive(taskId);
3385
- }, currentTask: currentTask })] }), _jsx(Modal, { isOpen: activeTab === 'settings', onClose: handleCloseModal, title: "Settings", size: "xl", theme: currentTheme, draggable: true, isSettings: true, closeOnOverlayClick: false, children: _jsx(Suspense, { fallback: _jsx("div", { style: { display: 'flex', alignItems: 'center', justifyContent: 'center', padding: '2rem' }, children: _jsx(Loader2, { size: 20, className: styles.spinner }) }), children: _jsx(TaskSettings, { settingsModel: settingsModel, onSectionChange: setSettingsSection }) }) }), _jsx(ScheduleWarningModal, { prompt: scheduleWarningPrompt, theme: currentTheme, onClose: cancelScheduleWarning, onConfirm: confirmScheduleWarning }), _jsx(HelpModal, { isOpen: showHelpModal, theme: currentTheme, onClose: () => setShowHelpModal(false), onOpenSettings: () => {
3315
+ }, currentTask: currentTask })] }), _jsx(Modal, { isOpen: activeTab === 'settings', onClose: handleCloseModal, title: "Settings", size: "xl", theme: currentTheme, draggable: true, isSettings: true, closeOnOverlayClick: false, children: _jsx(Suspense, { fallback: _jsx("div", { style: { display: 'flex', alignItems: 'center', justifyContent: 'center', padding: '2rem' }, children: _jsx(Loader2, { size: 20, className: styles.spinner }) }), children: _jsx(TaskSettings, { settingsModel: {
3316
+ ...settingsModel,
3317
+ onOpenCloudAuth: openSharedLoginRoute
3318
+ }, onSectionChange: setSettingsSection }) }) }), _jsx(ScheduleWarningModal, { prompt: scheduleWarningPrompt, theme: currentTheme, onClose: cancelScheduleWarning, onConfirm: confirmScheduleWarning }), _jsx(SyncEnableWarningModal, { isOpen: showSyncEnableWarning, theme: currentTheme, onClose: cancelSyncEnableWarning, onConfirm: confirmSyncEnableWarning }), _jsx(HelpModal, { isOpen: showHelpModal, theme: currentTheme, onClose: () => setShowHelpModal(false), onOpenSettings: () => {
3386
3319
  setShowHelpModal(false);
3387
3320
  setActiveTab('settings');
3388
3321
  setSettingsSection('general');
3389
- } }), _jsx(EditProfileModal, { isOpen: showEditProfileModal, theme: currentTheme, onClose: closeEditProfileModal, onSave: () => { void handleSaveProfile(); }, displayName: profileDisplayNameDraft, email: authEmailLabel, avatarDisplayUrl: profileAvatarDisplayUrl, accountBadgeInitial: accountBadgeInitial, avatarInputRef: profileAvatarInputRef, avatarAccept: PROFILE_AVATAR_ACCEPT, avatarDraftId: profileAvatarDraftId, saveBusy: profileSaveBusy, avatarBusy: profileAvatarBusy, saveError: profileSaveError, saveNotice: profileSaveNotice, onDisplayNameChange: setProfileDisplayNameDraft, onAvatarInputChange: (file) => { void handleProfileAvatarSelected(file); }, onStartAvatarUpload: () => profileAvatarInputRef.current?.click(), onDiscardUpload: handleDiscardPendingProfileAvatar, onRemovePhoto: handleRemoveProfileAvatar }), _jsx(AccountHubModal, { isOpen: showAccountHub, theme: currentTheme, runtimeMode: runtimeMode, authRequiredForApi: authRequiredForApi, isAuthenticated: isAuthenticated, hasAuthIdentity: hasAuthIdentity, authIdentityLabel: authIdentityLabel, billingLoading: billingLoading, billingError: billingError, billingActionError: billingActionError, billingNotice: billingNotice, billingActionBusy: billingActionBusy, billingIntervalChoice: billingIntervalChoice, accountProfileSummary: accountProfileSummary, currentWorkspaceId: currentWorkspaceId, canOpenTeamManagement: canOpenTeamManagement, canManageWorkspaceSync: canManageWorkspaceSync, workspaceCloudSyncEnabled: workspaceCloudSyncEnabled, syncStatusLabel: syncStatusMeta.label, workspaceSyncError: workspaceSyncError, syncControlBusy: syncControlBusy, onClose: () => setShowAccountHub(false), onOpenWorkspaceAudit: () => {
3322
+ } }), _jsx(EditProfileModal, { isOpen: showEditProfileModal, theme: currentTheme, onClose: closeEditProfileModal, onSave: () => { void handleSaveProfile(); }, displayName: profileDisplayNameDraft, email: authEmailLabel, avatarDisplayUrl: profileAvatarDisplayUrl, accountBadgeInitial: accountBadgeInitial, saveBusy: profileSaveBusy, avatarBusy: profileAvatarBusy, saveError: profileSaveError, saveNotice: profileSaveNotice, onDisplayNameChange: setProfileDisplayNameDraft, onOpenAvatarManager: () => {
3323
+ setProfileSaveError(null);
3324
+ setProfileSaveNotice(null);
3325
+ setShowAvatarPhotoManager(true);
3326
+ } }), _jsx(AvatarImageManagerModal, { isOpen: showAvatarPhotoManager, theme: currentTheme, title: "Edit Profile Photo", currentImageUrl: profileAvatarDisplayUrl, fallbackInitial: accountBadgeInitial, accept: PROFILE_AVATAR_ACCEPT, busy: profileAvatarBusy, hasPendingImage: Boolean(profileAvatarDraftId), canRemove: Boolean(profileAvatarDisplayUrl), error: profileSaveError, notice: profileSaveNotice, onClose: () => setShowAvatarPhotoManager(false), onApplyImage: handleProfileAvatarSelected, onRemoveImage: handleRemoveProfileAvatar, onDiscardPendingImage: handleDiscardPendingProfileAvatar }), _jsx(AccountHubModal, { isOpen: showAccountHub, theme: currentTheme, runtimeMode: runtimeMode, authRequiredForApi: authRequiredForApi, isAuthenticated: isAuthenticated, hasAuthIdentity: hasAuthIdentity, authIdentityLabel: authIdentityLabel, billingLoading: billingLoading, billingError: billingError, billingActionError: billingActionError, billingNotice: billingNotice, billingActionBusy: billingActionBusy, billingIntervalChoice: billingIntervalChoice, accountProfileSummary: accountProfileSummary, currentWorkspaceId: currentWorkspaceId, canOpenTeamManagement: canOpenTeamManagement, canManageWorkspaceSync: canManageWorkspaceSync, workspaceCloudSyncEnabled: workspaceCloudSyncEnabled, syncStatusLabel: syncStatusMeta.label, workspaceSyncError: workspaceSyncError, syncControlBusy: syncControlBusy, cloudAuthEnabled: cloudAuthConfigured, availableAuthProviders: availableAuthProviders, onFetchLoginMethods: fetchLoginMethods, onUnlinkLoginMethod: unlinkLoginMethod, onAddPassword: addPasswordToAccount, onLinkProvider: (provider) => beginOAuthLink(provider), onClose: () => setShowAccountHub(false), onOpenWorkspaceAudit: () => {
3390
3327
  setShowAccountHub(false);
3391
3328
  void openTeamManagementModal();
3392
3329
  setTeamManagementTab('audit');
@@ -3442,7 +3379,7 @@ export function StandaloneLayout(props) {
3442
3379
  headers: teamAdminHeaders(),
3443
3380
  credentials: 'include'
3444
3381
  }));
3445
- }, onInviteEmailChange: setTeamInviteEmail, onInviteRoleChange: setTeamInviteRole, onInvitePermissionModeChange: setTeamInvitePermissionMode, onSubmitInvite: () => { void submitTeamInvite(); }, onLoadAuditPrevious: () => { void loadTeamAuditPage(teamAuditPage - 1); }, onLoadAuditNext: () => { void loadTeamAuditPage(teamAuditPage + 1); } }), _jsx(CreateWorkspaceConfirmModal, { isOpen: showCreateWorkspaceConfirm, theme: currentTheme, onClose: () => setShowCreateWorkspaceConfirm(false), onConfirm: () => openWorkspaceSetup({ intent: 'create-workspace' }) }), _jsx(SyncStatusModal, { isOpen: showSyncStatusModal, theme: currentTheme, currentWorkspaceLabel: currentWorkspaceLabel, syncStatusMeta: syncStatusMeta, workspaceCloudSyncEnabled: workspaceCloudSyncEnabled, syncControlBusy: syncControlBusy, canManageWorkspaceSync: canManageWorkspaceSync, workspaceSyncSummary: workspaceSyncSummary, workspaceSyncRepairBusy: workspaceSyncRepairBusy, referenceMismatchCount: referenceMismatchCount, syncStageLabel: syncStageLabel, workspaceSyncPendingChanges: workspaceSyncPendingChanges, formattedLastSyncTime: formattedLastSyncTime, formattedLastPullTime: formattedLastPullTime, formattedLastPushTime: formattedLastPushTime, syncLastError: syncLastError, workspaceSyncDiagnostics: workspaceSyncDiagnostics, activeReferenceMismatchSummaries: activeReferenceMismatchSummaries, syncDiagnosticsSummary: syncDiagnosticsSummary, syncEventRows: syncEventRows, syncEventsListRef: syncEventsListRef, workspaceSyncRepairQueued: workspaceSyncRepairQueued, workspaceSyncBusy: workspaceSyncBusy, workspaceSyncCopied: workspaceSyncCopied, runtimeMode: runtimeMode, isAuthenticated: isAuthenticated, onClose: () => setShowSyncStatusModal(false), onToggleWorkspaceSync: (enabled) => {
3382
+ }, onInviteEmailChange: setTeamInviteEmail, onInviteRoleChange: setTeamInviteRole, onInvitePermissionModeChange: setTeamInvitePermissionMode, onSubmitInvite: () => { void submitTeamInvite(); }, onLoadAuditPrevious: () => { void loadTeamAuditPage(teamAuditPage - 1); }, onLoadAuditNext: () => { void loadTeamAuditPage(teamAuditPage + 1); } }), _jsx(CreateWorkspaceConfirmModal, { isOpen: showCreateWorkspaceConfirm, theme: currentTheme, onClose: () => setShowCreateWorkspaceConfirm(false), onConfirm: () => openWorkspaceSetup({ intent: 'create-workspace' }) }), _jsx(SyncStatusModal, { isOpen: showSyncStatusModal, theme: currentTheme, currentWorkspaceLabel: currentWorkspaceLabel, syncStatusMeta: syncStatusMeta, workspaceCloudSyncEnabled: workspaceCloudSyncEnabled, syncControlBusy: syncControlBusy, canManageWorkspaceSync: canManageWorkspaceSync, workspaceSyncSummary: headerSyncPresentation.summary, workspaceSyncRecommendedAction: headerSyncPresentation.recommendedAction, workspaceSyncRepairBusy: workspaceSyncRepairBusy, referenceMismatchCount: referenceMismatchCount, syncStageLabel: syncStageLabel, workspaceSyncPendingChanges: workspaceSyncPendingChanges, formattedLastSyncTime: formattedLastSyncTime, formattedLastPullTime: formattedLastPullTime, formattedLastPushTime: formattedLastPushTime, syncLastError: syncLastError, workspaceSyncDiagnostics: workspaceSyncDiagnostics, activeReferenceMismatchSummaries: activeReferenceMismatchSummaries, syncDiagnosticsSummary: syncDiagnosticsSummary, syncEventRows: syncEventRows, syncEventsListRef: syncEventsListRef, workspaceSyncRepairQueued: workspaceSyncRepairQueued, workspaceSyncBusy: workspaceSyncBusy, workspaceSyncCopied: workspaceSyncCopied, runtimeMode: runtimeMode, isAuthenticated: isAuthenticated, onClose: closeSyncStatusModal, onToggleWorkspaceSync: (enabled) => {
3446
3383
  void handleWorkspaceSyncToggle(enabled);
3447
3384
  }, onRepairSync: handleQueueOrRunRepairSync, onCopyReport: () => { void handleCopySyncDetails(); }, onOpenLogin: () => {
3448
3385
  openSharedLoginRoute('login');