@taskforcehq/taskforce 0.3.327 → 0.3.329

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 (360) hide show
  1. package/dist/Taskforce.module.css +257 -87
  2. package/dist/TaskforceCore.js +237 -230
  3. package/dist/annotatedAttachments/AnnotatedAttachmentSessionStore.js +1 -1
  4. package/dist/cli.js +34 -0
  5. package/dist/cliCloudTargetGuard.d.ts +5 -0
  6. package/dist/cliCloudTargetGuard.js +11 -0
  7. package/dist/components/activity/EntityActivityTimeline.js +15 -5
  8. package/dist/components/context/AttachmentPreviewHost.js +1 -1
  9. package/dist/components/context/ContextAttachmentManager.d.ts +3 -0
  10. package/dist/components/context/ContextAttachmentManager.js +116 -20
  11. package/dist/components/features/AnnotatedAttachmentWorkspace.js +24 -8
  12. package/dist/components/features/TaskSettings.d.ts +12 -1
  13. package/dist/components/features/TaskSettings.js +147 -40
  14. package/dist/components/features/agentCapabilities/AgentCapabilitiesPrototype.d.ts +1 -1
  15. package/dist/components/features/planning/PlanningEntitySummary.d.ts +3 -1
  16. package/dist/components/features/planning/PlanningEntitySummary.js +3 -2
  17. package/dist/components/features/planning/PlanningLinkPicker.d.ts +3 -1
  18. package/dist/components/features/planning/PlanningLinkPicker.js +5 -3
  19. package/dist/components/features/planning/PlanningModule.js +28 -14
  20. package/dist/components/features/planning/PlanningTaskOrderList.d.ts +1 -1
  21. package/dist/components/features/planning/PlanningTaskOrderList.js +17 -6
  22. package/dist/components/features/planning/PlanningVisualIdentity.d.ts +14 -0
  23. package/dist/components/features/planning/PlanningVisualIdentity.js +106 -0
  24. package/dist/components/features/planning/planningTaskArrangement.d.ts +1 -0
  25. package/dist/components/features/planning/planningTaskArrangement.js +5 -5
  26. package/dist/components/features/planning/planningWorkspaceModel.d.ts +11 -0
  27. package/dist/components/features/taskforceAgentEditorModel.js +3 -1
  28. package/dist/components/task/TaskActionHeader.d.ts +2 -1
  29. package/dist/components/task/TaskActionHeader.js +2 -2
  30. package/dist/components/task/TaskCard.d.ts +5 -0
  31. package/dist/components/task/TaskCard.js +101 -13
  32. package/dist/components/task/TaskContextUpload.d.ts +3 -0
  33. package/dist/components/task/TaskContextUpload.js +2 -2
  34. package/dist/components/task/TaskForm.d.ts +4 -0
  35. package/dist/components/task/TaskForm.js +32 -7
  36. package/dist/components/task/TaskImageReviews.js +28 -19
  37. package/dist/components/task/TaskKanban.d.ts +6 -1
  38. package/dist/components/task/TaskKanban.js +152 -6
  39. package/dist/components/task/TaskList.d.ts +5 -0
  40. package/dist/components/task/TaskList.js +3 -3
  41. package/dist/components/task/TaskStatusActions.js +11 -2
  42. package/dist/components/task/TaskWorkstreamPicker.js +2 -1
  43. package/dist/components/ui/DocumentTypeIcon.d.ts +7 -0
  44. package/dist/components/ui/DocumentTypeIcon.js +8 -0
  45. package/dist/components/ui/PasswordInput.d.ts +6 -0
  46. package/dist/components/ui/PasswordInput.js +11 -0
  47. package/dist/components/ui/TaxonomyDropdown.d.ts +9 -1
  48. package/dist/components/ui/TaxonomyDropdown.js +30 -3
  49. package/dist/components/ui/TopNoticeLayer.js +1 -1
  50. package/dist/components/views/AppShellHeader.js +32 -11
  51. package/dist/components/views/PlanComparisonPage.js +12 -0
  52. package/dist/components/views/StandaloneLayout.js +68 -39
  53. package/dist/components/views/WidgetView.js +10 -7
  54. package/dist/components/views/panels/PlanningDrawer.d.ts +7 -1
  55. package/dist/components/views/panels/PlanningDrawer.js +51 -54
  56. package/dist/components/views/standalone/modals/AccountSettingsModal.js +26 -17
  57. package/dist/config/envSchema.js +13 -0
  58. package/dist/config/localServiceCloudTarget.d.ts +10 -0
  59. package/dist/config/localServiceCloudTarget.js +76 -0
  60. package/dist/config/localServiceRuntimeContract.d.ts +13 -0
  61. package/dist/config/localServiceRuntimeContract.js +135 -0
  62. package/dist/core/AiProfileService.d.ts +1 -0
  63. package/dist/core/AiProfileService.js +48 -9
  64. package/dist/core/AttachmentLinkService.js +7 -4
  65. package/dist/core/DeletedTaskLifecycleService.d.ts +5 -0
  66. package/dist/core/DeletedTaskLifecycleService.js +55 -3
  67. package/dist/core/McpTokenService.d.ts +4 -0
  68. package/dist/core/McpTokenService.js +52 -0
  69. package/dist/core/TaskAttachmentLinksDurableMutationService.d.ts +8 -2
  70. package/dist/core/TaskAttachmentLinksDurableMutationService.js +51 -18
  71. package/dist/core/Taskforce.d.ts +124 -2
  72. package/dist/core/Taskforce.js +1175 -87
  73. package/dist/core/types.d.ts +8 -1
  74. package/dist/documentReviews/DocumentReviewCommentStore.d.ts +11 -0
  75. package/dist/documentReviews/DocumentReviewCommentStore.js +35 -2
  76. package/dist/documentReviews/service.d.ts +4 -0
  77. package/dist/documentReviews/service.js +141 -46
  78. package/dist/hooks/sync/useDurableLocalOutboxDrain.d.ts +1 -1
  79. package/dist/hooks/sync/useDurableTaskCreateMutation.js +1 -1
  80. package/dist/hooks/sync/useDurableTaskMetadataMutation.d.ts +1 -0
  81. package/dist/hooks/sync/useDurableTaskMetadataMutation.js +1 -0
  82. package/dist/hooks/sync/useDurableTaskStatusMutation.d.ts +1 -0
  83. package/dist/hooks/tasks/taskHttpMutation.js +1 -0
  84. package/dist/hooks/tasks/useCloudTaskSearch.d.ts +12 -0
  85. package/dist/hooks/tasks/useCloudTaskSearch.js +71 -0
  86. package/dist/hooks/tasks/useTaskDescriptionImageDraftRecovery.d.ts +16 -0
  87. package/dist/hooks/tasks/useTaskDescriptionImageDraftRecovery.js +197 -0
  88. package/dist/hooks/tasks/useTaskEditorNavigation.d.ts +16 -3
  89. package/dist/hooks/tasks/useTaskEditorNavigation.js +72 -13
  90. package/dist/hooks/tasks/useTaskFormActions.d.ts +9 -0
  91. package/dist/hooks/tasks/useTaskFormActions.js +96 -17
  92. package/dist/hooks/ui/useSettingsModel.d.ts +24 -2
  93. package/dist/hooks/ui/useSettingsModel.js +13 -2
  94. package/dist/hooks/ui/useSettingsPersistence.d.ts +12 -1
  95. package/dist/hooks/ui/useSettingsPersistence.js +74 -47
  96. package/dist/hooks/useFilterSort.d.ts +7 -3
  97. package/dist/hooks/useFilterSort.js +15 -5
  98. package/dist/hooks/useTaskData.d.ts +10 -1
  99. package/dist/hooks/useTaskData.js +216 -7
  100. package/dist/hooks/useTaskMutations.d.ts +10 -2
  101. package/dist/hooks/useTaskMutations.js +223 -43
  102. package/dist/hooks/useTaskforce.d.ts +94 -57
  103. package/dist/hooks/useTaskforce.js +428 -16
  104. package/dist/hooks/useUiNotice.d.ts +4 -0
  105. package/dist/hooks/useUiNotice.js +4 -0
  106. package/dist/mcp/adminWorkspaceRegistrar.js +3 -3
  107. package/dist/mcp/canonicalAssetHelpers.d.ts +13 -0
  108. package/dist/mcp/canonicalAssetHelpers.js +39 -5
  109. package/dist/mcp/collaborationRegistrar.js +15 -13
  110. package/dist/mcp/documentAssetRegistrar.js +60 -44
  111. package/dist/mcp/documentHelpers.d.ts +1 -0
  112. package/dist/mcp/documentHelpers.js +7 -3
  113. package/dist/mcp/localServiceProxy.js +4 -1
  114. package/dist/mcp/runtime.d.ts +16 -0
  115. package/dist/mcp/runtime.js +735 -210
  116. package/dist/mcp/taskAttachmentCommandAdapter.d.ts +2 -0
  117. package/dist/mcp/taskAttachmentCommandAdapter.js +14 -8
  118. package/dist/mcp/taskPlanningRegistrar.js +78 -67
  119. package/dist/mcp/toolCallCompatibility.js +38 -1
  120. package/dist/mcp/toolCatalog.d.ts +1 -0
  121. package/dist/mcp/toolRegistry.js +2 -2
  122. package/dist/migrations/taskSchemaMigrations.js +47 -0
  123. package/dist/runtime/appGateDefinitions.d.ts +30 -6
  124. package/dist/runtime/appGateDefinitions.js +6 -6
  125. package/dist/runtime/appGates.d.ts +12 -13
  126. package/dist/runtime/appGates.js +29 -13
  127. package/dist/runtime/nodeAppGates.d.ts +8 -0
  128. package/dist/runtime/nodeAppGates.js +27 -0
  129. package/dist/server/appAccess.d.ts +12 -0
  130. package/dist/server/appAccess.js +21 -0
  131. package/dist/server/authorizedWorkspaceAccess.d.ts +12 -0
  132. package/dist/server/authorizedWorkspaceAccess.js +40 -0
  133. package/dist/server/cloudCollectionRead.d.ts +25 -0
  134. package/dist/server/cloudCollectionRead.js +120 -0
  135. package/dist/server/cloudCollectionReadPool.d.ts +52 -0
  136. package/dist/server/cloudCollectionReadPool.js +140 -0
  137. package/dist/server/cloudCollectionReadWorker.d.ts +1 -0
  138. package/dist/server/cloudCollectionReadWorker.js +36 -0
  139. package/dist/server/index.d.ts +6 -11
  140. package/dist/server/index.js +67 -42
  141. package/dist/server/localServiceLease.d.ts +6 -0
  142. package/dist/server/localServiceLease.js +17 -1
  143. package/dist/server/localWorkspaceSyncServerRuntime.js +10 -3
  144. package/dist/server/rateLimit.d.ts +1 -0
  145. package/dist/server/rateLimit.js +4 -0
  146. package/dist/server/realtimeSyncWs.js +36 -12
  147. package/dist/server/routes/admin.js +1 -0
  148. package/dist/server/routes/agentRoles.js +3 -2
  149. package/dist/server/routes/agentSkills.js +3 -2
  150. package/dist/server/routes/agents.js +118 -16
  151. package/dist/server/routes/annotatedAttachments.js +3 -2
  152. package/dist/server/routes/auth.js +3 -1
  153. package/dist/server/routes/documents.d.ts +5 -2
  154. package/dist/server/routes/documents.js +160 -13
  155. package/dist/server/routes/durableTaskHttpRouters.d.ts +11 -2
  156. package/dist/server/routes/durableTaskHttpRouters.js +2 -2
  157. package/dist/server/routes/localService.js +4 -0
  158. package/dist/server/routes/primitives.d.ts +4 -0
  159. package/dist/server/routes/shared.d.ts +24 -0
  160. package/dist/server/routes/shared.js +114 -18
  161. package/dist/server/routes/sync.js +22 -0
  162. package/dist/server/routes/syncAuxRoutes.js +5 -2
  163. package/dist/server/routes/syncPullApplyRoutes.js +23 -4
  164. package/dist/server/routes/syncV3FeedRoutes.js +22 -18
  165. package/dist/server/routes/syncV3LocalCaptureRoutes.js +48 -4
  166. package/dist/server/routes/syncV3LocalFeedApplyRoutes.d.ts +1 -0
  167. package/dist/server/routes/syncV3LocalFeedApplyRoutes.js +26 -0
  168. package/dist/server/routes/syncV3OperationRoutes.js +27 -2
  169. package/dist/server/routes/tasks.js +299 -97
  170. package/dist/server/routes/workspaces.js +43 -1
  171. package/dist/server/routes.js +35 -7
  172. package/dist/server/workspaceDeletionAdmission.d.ts +23 -0
  173. package/dist/server/workspaceDeletionAdmission.js +50 -0
  174. package/dist/service/localServiceCli.js +52 -25
  175. package/dist/service/localServiceClientLifecycle.js +93 -6
  176. package/dist/services/codexAppServerModelGateway.d.ts +112 -0
  177. package/dist/services/codexAppServerModelGateway.js +438 -0
  178. package/dist/services/taskforceAgentMcpBridge.d.ts +7 -1
  179. package/dist/services/taskforceAgentMcpBridge.js +23 -3
  180. package/dist/shared/planningIdentity.d.ts +20 -0
  181. package/dist/shared/planningIdentity.js +79 -0
  182. package/dist/shared/taskforceAgentDefinition.js +7 -1
  183. package/dist/shared/taskforceAgentModels.d.ts +1 -0
  184. package/dist/shared/taskforceAgentModels.js +30 -1
  185. package/dist/storage/postgresAdapter.d.ts +20 -3
  186. package/dist/storage/postgresAdapter.js +110 -14
  187. package/dist/storage/postgresRequestDiagnostics.d.ts +8 -0
  188. package/dist/storage/postgresRequestDiagnostics.js +24 -0
  189. package/dist/storage/postgresWorker.js +13 -4
  190. package/dist/storage/postgresWorkerProtocol.d.ts +29 -0
  191. package/dist/storage/postgresWorkerProtocol.js +30 -0
  192. package/dist/storage/workspaceAssetStore.d.ts +32 -0
  193. package/dist/storage/workspaceAssetStore.js +70 -0
  194. package/dist/sync/cloudSyncApi.d.ts +4 -1
  195. package/dist/sync/cloudSyncApi.js +7 -2
  196. package/dist/sync/contentSyncState.d.ts +1 -1
  197. package/dist/sync/coordinator/localSyncExecutionPermitStore.d.ts +17 -0
  198. package/dist/sync/coordinator/localSyncExecutionPermitStore.js +77 -23
  199. package/dist/sync/coordinator/localWorkspaceSyncRunner.js +6 -2
  200. package/dist/sync/coordinator/workspaceSyncTransportGateway.js +4 -2
  201. package/dist/sync/engine/workspaceSyncEngineBrowserGatewayRemoteServices.js +20 -2
  202. package/dist/sync/engine/workspaceSyncEngineLocalServices.d.ts +4 -0
  203. package/dist/sync/engine/workspaceSyncEngineLocalServices.js +11 -0
  204. package/dist/sync/engine/workspaceSyncEngineRemoteServices.d.ts +16 -0
  205. package/dist/sync/engine/workspaceSyncEngineRemoteServices.js +2 -1
  206. package/dist/sync/engine/workspaceSyncEngineServerRemoteServices.d.ts +1 -1
  207. package/dist/sync/engine/workspaceSyncEngineServerRemoteServices.js +46 -4
  208. package/dist/sync/engine/workspaceSyncEngineServerRunnerOperations.d.ts +3 -1
  209. package/dist/sync/engine/workspaceSyncEngineServerRunnerOperations.js +51 -11
  210. package/dist/sync/engine/workspaceSyncEngineTransfers.d.ts +1 -1
  211. package/dist/sync/engine/workspaceSyncEngineTransfers.js +8 -3
  212. package/dist/sync/engine/workspaceSyncV2ChangeDispatcher.js +2 -0
  213. package/dist/sync/syncApplyHandlers.d.ts +3 -1
  214. package/dist/sync/syncApplyHandlers.js +29 -0
  215. package/dist/sync/syncService.d.ts +2 -0
  216. package/dist/sync/syncService.js +13 -1
  217. package/dist/sync/taskSyncPayload.d.ts +1 -0
  218. package/dist/sync/taskSyncPayload.js +6 -0
  219. package/dist/sync/v3/combinedFeedCapability.js +4 -0
  220. package/dist/sync/v3/durableTaskAttachmentLinksMutationRouter.d.ts +3 -0
  221. package/dist/sync/v3/durableTaskAttachmentLinksMutationRouter.js +31 -3
  222. package/dist/sync/v3/durableTaskChecklistMutationRouter.d.ts +2 -0
  223. package/dist/sync/v3/durableTaskChecklistMutationRouter.js +4 -0
  224. package/dist/sync/v3/durableTaskHttpBulkMutationRouter.d.ts +1 -0
  225. package/dist/sync/v3/durableTaskHttpBulkMutationRouter.js +47 -12
  226. package/dist/sync/v3/durableTaskHttpRootMutationRouter.d.ts +5 -3
  227. package/dist/sync/v3/durableTaskHttpRootMutationRouter.js +27 -2
  228. package/dist/sync/v3/localInitiativeOutboxService.js +4 -2
  229. package/dist/sync/v3/localOutboxDispatcher.js +5 -1
  230. package/dist/sync/v3/localTaskAttachmentLinksOutboxHandler.js +12 -2
  231. package/dist/sync/v3/localTaskAttachmentLinksOutboxService.d.ts +3 -0
  232. package/dist/sync/v3/localTaskAttachmentLinksOutboxService.js +62 -10
  233. package/dist/sync/v3/localTaskCreateOutboxService.js +15 -6
  234. package/dist/sync/v3/localTaskMetadataClient.d.ts +1 -0
  235. package/dist/sync/v3/localTaskMetadataClient.js +1 -0
  236. package/dist/sync/v3/localTaskMetadataOutboxHandler.js +5 -1
  237. package/dist/sync/v3/localTaskMetadataOutboxService.d.ts +2 -0
  238. package/dist/sync/v3/localTaskMetadataOutboxService.js +7 -0
  239. package/dist/sync/v3/localTaskRestoreOutboxService.js +14 -6
  240. package/dist/sync/v3/localTaskStatusClient.d.ts +1 -0
  241. package/dist/sync/v3/localTaskStatusClient.js +6 -1
  242. package/dist/sync/v3/localTaskStatusOutboxService.d.ts +2 -0
  243. package/dist/sync/v3/localTaskStatusOutboxService.js +54 -13
  244. package/dist/sync/v3/localWorkstreamOutboxService.js +4 -2
  245. package/dist/sync/v3/syncDurabilityStore.d.ts +15 -0
  246. package/dist/sync/v3/syncDurabilityStore.js +201 -43
  247. package/dist/sync/v3/taskAttachmentLinksMutationService.d.ts +7 -4
  248. package/dist/sync/v3/taskAttachmentLinksMutationService.js +37 -12
  249. package/dist/sync/v3/taskChecklistMutationService.d.ts +4 -2
  250. package/dist/sync/v3/taskChecklistMutationService.js +4 -0
  251. package/dist/sync/v3/taskCreateMutationService.d.ts +2 -2
  252. package/dist/sync/v3/taskCreateMutationService.js +7 -4
  253. package/dist/sync/v3/taskMetadataCoexistence.js +4 -0
  254. package/dist/sync/v3/taskMetadataMutationService.d.ts +3 -0
  255. package/dist/sync/v3/taskMetadataMutationService.js +4 -0
  256. package/dist/sync/v3/taskRestoreMutationService.d.ts +2 -2
  257. package/dist/sync/v3/taskRestoreMutationService.js +7 -4
  258. package/dist/sync/v3/taskStatusMutationService.d.ts +2 -0
  259. package/dist/sync/v3/taskStatusMutationService.js +4 -0
  260. package/dist/sync/v3/workflowAssignmentSync.d.ts +1 -0
  261. package/dist/sync/v3/workflowAssignmentSync.js +3 -0
  262. package/dist/sync/v3/workspaceSyncV2ProjectionExclusions.d.ts +1 -1
  263. package/dist/sync/v3/workspaceSyncV2ProjectionExclusions.js +14 -12
  264. package/dist/sync/v3/workspaceSyncV3AttachmentApplyObligationStore.d.ts +11 -0
  265. package/dist/sync/v3/workspaceSyncV3AttachmentApplyObligationStore.js +27 -10
  266. package/dist/sync/v3/workspaceSyncV3AttachmentAssetHydration.d.ts +2 -1
  267. package/dist/sync/v3/workspaceSyncV3AttachmentAssetHydration.js +52 -10
  268. package/dist/sync/v3/workspaceSyncV3CloudOperationHandler.js +10 -1
  269. package/dist/sync/v3/workspaceSyncV3CombinedFeedOrchestrator.js +15 -4
  270. package/dist/sync/v3/workspaceSyncV3CombinedLocalApply.js +66 -13
  271. package/dist/sync/v3/workspaceSyncV3CombinedRepairBaseline.d.ts +11 -0
  272. package/dist/sync/v3/workspaceSyncV3CombinedRepairBaseline.js +57 -22
  273. package/dist/sync/v3/workspaceSyncV3CombinedRepairMaterialization.d.ts +10 -4
  274. package/dist/sync/v3/workspaceSyncV3CombinedRepairMaterialization.js +124 -51
  275. package/dist/sync/v3/workspaceSyncV3CombinedRepairSnapshot.d.ts +10 -0
  276. package/dist/sync/v3/workspaceSyncV3CombinedRepairSnapshot.js +120 -28
  277. package/dist/sync/v3/workspaceSyncV3CombinedWireContract.d.ts +3 -1
  278. package/dist/sync/v3/workspaceSyncV3CombinedWireContract.js +18 -0
  279. package/dist/sync/v3/workspaceSyncV3CoverageRegistry.js +6 -0
  280. package/dist/sync/v3/workspaceSyncV3FeedProfile.d.ts +1 -1
  281. package/dist/sync/v3/workspaceSyncV3LegacyBoundaryAttestation.d.ts +1 -0
  282. package/dist/sync/v3/workspaceSyncV3LegacyBoundaryAttestation.js +23 -10
  283. package/dist/sync/v3/workspaceSyncV3NestedTaskApply.d.ts +3 -0
  284. package/dist/sync/v3/workspaceSyncV3NestedTaskApply.js +64 -13
  285. package/dist/sync/v3/workspaceSyncV3NestedTaskCodecs.d.ts +9 -0
  286. package/dist/sync/v3/workspaceSyncV3NestedTaskCodecs.js +26 -0
  287. package/dist/sync/v3/workspaceSyncV3NestedTaskProjection.d.ts +15 -5
  288. package/dist/sync/v3/workspaceSyncV3NestedTaskProjection.js +50 -14
  289. package/dist/sync/v3/workspaceSyncV3NestedTaskRepairSnapshot.d.ts +2 -1
  290. package/dist/sync/v3/workspaceSyncV3NestedTaskRepairSnapshot.js +11 -3
  291. package/dist/sync/v3/workspaceSyncV3OperationProtocol.d.ts +3 -0
  292. package/dist/sync/v3/workspaceSyncV3OperatorDiagnostics.d.ts +2 -0
  293. package/dist/sync/v3/workspaceSyncV3OperatorDiagnostics.js +19 -7
  294. package/dist/sync/v3/workspaceSyncV3RepairSqlBatching.d.ts +2 -0
  295. package/dist/sync/v3/workspaceSyncV3RepairSqlBatching.js +10 -0
  296. package/dist/sync/v3/workspaceSyncV3TaskCommentAudit.js +9 -2
  297. package/dist/sync/v3/workspaceSyncV3TaskCoverCoverage.d.ts +7 -0
  298. package/dist/sync/v3/workspaceSyncV3TaskCoverCoverage.js +37 -0
  299. package/dist/sync/v3/workspaceSyncV3TaskCoverWireContract.d.ts +6 -0
  300. package/dist/sync/v3/workspaceSyncV3TaskCoverWireContract.js +9 -0
  301. package/dist/sync/v3/workspaceSyncV3TaskRootProjection.d.ts +1 -1
  302. package/dist/sync/v3/workspaceSyncV3TaskRootProjection.js +1 -1
  303. package/dist/sync/workspacePullFeed.d.ts +6 -1
  304. package/dist/sync/workspacePullFeed.js +174 -162
  305. package/dist/sync/workspaceRepair.d.ts +3 -0
  306. package/dist/sync/workspaceRepair.js +67 -6
  307. package/dist/sync/workspaceSyncModel.d.ts +11 -0
  308. package/dist/sync/workspaceSyncModel.js +27 -2
  309. package/dist/types.d.ts +55 -1
  310. package/dist/types.js +6 -0
  311. package/dist/ui/assets/{AiIdentityRosterCard-DB1BLO_j.js → AiIdentityRosterCard-BZM4aQnM.js} +1 -1
  312. package/dist/ui/assets/AiProfilesModule-BbAEWtHH.js +1 -0
  313. package/dist/ui/assets/AnnotatedAttachmentWorkspace-B68Wg-RV.js +3 -0
  314. package/dist/ui/assets/AnnotatedAttachmentWorkspace-TzYu7pkG.css +1 -0
  315. package/dist/ui/assets/AssetTraySortControl-C8Rztyal.js +1 -0
  316. package/dist/ui/assets/ContextAttachmentManager-Cj3c7X9y.js +3 -0
  317. package/dist/ui/assets/{DocumentWorkspace-BC7sy5TG.js → DocumentWorkspace-B5uvzems.js} +4 -4
  318. package/dist/ui/assets/EntityActivityTimeline-BVFvCB5R.js +1 -0
  319. package/dist/ui/assets/PlanningModule-B5DWQOGz.js +1 -0
  320. package/dist/ui/assets/PlanningModule-CoIR9hbV.css +1 -0
  321. package/dist/ui/assets/PlansPage-AY4cGWco.js +1 -0
  322. package/dist/ui/assets/TaskContextUpload-DZxTVesW.js +1 -0
  323. package/dist/ui/assets/TaskContextUpload-qmBeUw3u.css +1 -0
  324. package/dist/ui/assets/TaskSettings-BeWAuyZj.js +12 -0
  325. package/dist/ui/assets/{TaskforceAgentsModule-CkAxW19K.js → TaskforceAgentsModule-BifCYtjR.js} +6 -6
  326. package/dist/ui/assets/WorkflowManagerModule-DEr5di_r.js +1 -0
  327. package/dist/ui/assets/index-I5zhdtgt.js +7 -0
  328. package/dist/ui/assets/index-VRFsx3wz.css +1 -0
  329. package/dist/ui/assets/{vendor-icons-B8ZNsH1g.js → vendor-icons-D9Lpw-j4.js} +1 -1
  330. package/dist/ui/index.html +3 -3
  331. package/dist/utils/constants.d.ts +2 -2
  332. package/dist/utils/constants.js +1 -1
  333. package/dist/utils/contextAttachmentUpload.d.ts +2 -0
  334. package/dist/utils/contextAttachmentUpload.js +7 -0
  335. package/dist/utils/contextFiles.d.ts +1 -1
  336. package/dist/utils/contextFiles.js +4 -4
  337. package/dist/utils/taskActivity.d.ts +3 -2
  338. package/dist/utils/taskActivity.js +19 -2
  339. package/dist/utils/taskEditorReconciliation.d.ts +40 -0
  340. package/dist/utils/taskEditorReconciliation.js +217 -0
  341. package/dist/utils/taskNormalization.d.ts +1 -0
  342. package/dist/utils/taskNormalization.js +39 -0
  343. package/dist/utils/taskSearch.js +2 -2
  344. package/dist/utils/uiNotice.d.ts +4 -0
  345. package/package.json +21 -4
  346. package/dist/ui/assets/AiProfilesModule-DPxQeF9N.js +0 -1
  347. package/dist/ui/assets/AnnotatedAttachmentWorkspace-CBRZ8rca.css +0 -1
  348. package/dist/ui/assets/AnnotatedAttachmentWorkspace-DHNYaNg6.js +0 -3
  349. package/dist/ui/assets/AssetTraySortControl-DphbyHVc.js +0 -1
  350. package/dist/ui/assets/ContextAttachmentManager-CMxSzHqc.js +0 -3
  351. package/dist/ui/assets/EntityActivityTimeline-pJ2ZjZ-k.js +0 -1
  352. package/dist/ui/assets/PlanningModule-CeTqXMMz.css +0 -1
  353. package/dist/ui/assets/PlanningModule-t5GbvVR9.js +0 -1
  354. package/dist/ui/assets/PlansPage-BVH_NfgT.js +0 -1
  355. package/dist/ui/assets/TaskContextUpload-Ds4LnDzV.css +0 -1
  356. package/dist/ui/assets/TaskContextUpload-bl4fOOUz.js +0 -1
  357. package/dist/ui/assets/TaskSettings-xVsxSa0g.js +0 -12
  358. package/dist/ui/assets/WorkflowManagerModule-BKqIGVnF.js +0 -1
  359. package/dist/ui/assets/index-BzJlVnNS.css +0 -1
  360. package/dist/ui/assets/index-Cx7vZJHL.js +0 -7
package/dist/cli.js CHANGED
@@ -7,6 +7,8 @@ import { fileURLToPath } from 'url';
7
7
  import { TASKFORCE_CLOUD_ENVIRONMENTS } from './config/cloudEnvironment.js';
8
8
  import { resolveDeploymentConfig } from './config/deployment.js';
9
9
  import { resolveOpenPortSearchMax } from './cliOpenPort.js';
10
+ import { assertSourceCheckoutCloudTarget } from './cliCloudTargetGuard.js';
11
+ import { hydrateSourceCheckoutServiceEnvironment } from './config/localServiceRuntimeContract.js';
10
12
  /**
11
13
  * Taskforce CLI
12
14
  *
@@ -185,6 +187,19 @@ async function main() {
185
187
  return;
186
188
  }
187
189
  if (command === 'service') {
190
+ const serviceAction = String(args[1] || '').trim();
191
+ if (!['status', 'stop'].includes(serviceAction)) {
192
+ const projectArgument = serviceAction === 'start' ? args[2] : args[1];
193
+ hydrateSourceCheckoutServiceEnvironment({
194
+ env: process.env,
195
+ projectRoot: projectArgument || process.cwd(),
196
+ packageRoot: getCliInstallationPaths().packageRoot,
197
+ });
198
+ assertSourceCheckoutCloudTarget({
199
+ packageRoot: getCliInstallationPaths().packageRoot,
200
+ env: process.env,
201
+ });
202
+ }
188
203
  const { runLocalServiceCli } = await import('./service/localServiceCli.js');
189
204
  process.exitCode = await runLocalServiceCli(args.slice(1), {
190
205
  appVersion: getCliVersion(),
@@ -252,6 +267,15 @@ async function main() {
252
267
  // 1. Handle MCP Command
253
268
  if (command === 'mcp') {
254
269
  const projectPath = args[1] || process.cwd();
270
+ hydrateSourceCheckoutServiceEnvironment({
271
+ env: process.env,
272
+ projectRoot: projectPath,
273
+ packageRoot: getCliInstallationPaths().packageRoot,
274
+ });
275
+ assertSourceCheckoutCloudTarget({
276
+ packageRoot: getCliInstallationPaths().packageRoot,
277
+ env: process.env,
278
+ });
255
279
  // Set the path in argv so the mcp server can pick it up
256
280
  process.argv[2] = projectPath;
257
281
  try {
@@ -287,6 +311,11 @@ async function main() {
287
311
  }
288
312
  // 2. Handle Open Command
289
313
  const { preferredPort, preset: openPreset } = parseOpenCommandOptions(args);
314
+ assertSourceCheckoutCloudTarget({
315
+ packageRoot: getCliInstallationPaths().packageRoot,
316
+ env: process.env,
317
+ explicitPreset: openPreset,
318
+ });
290
319
  // Check if Taskforce is installed in the project
291
320
  let projectRoot = findProjectRoot();
292
321
  if (!projectRoot) {
@@ -324,6 +353,11 @@ async function main() {
324
353
  else if (hasExternalCloudEndpoint) {
325
354
  console.log('\x1b[36m[Taskforce] Using cloud endpoint from environment variables\x1b[0m');
326
355
  }
356
+ hydrateSourceCheckoutServiceEnvironment({
357
+ env: process.env,
358
+ projectRoot,
359
+ packageRoot: getCliInstallationPaths().packageRoot,
360
+ });
327
361
  const { openTaskforceViaLocalService, shouldOpenViaLocalService } = await import('./service/localServiceOpen.js');
328
362
  if (shouldOpenViaLocalService(process.env)) {
329
363
  console.log(`\x1b[36m✨ Discovering the shared Taskforce service...\x1b[0m`);
@@ -0,0 +1,5 @@
1
+ export declare function assertSourceCheckoutCloudTarget(input: {
2
+ packageRoot: string;
3
+ env: NodeJS.ProcessEnv;
4
+ explicitPreset?: string | null;
5
+ }): void;
@@ -0,0 +1,11 @@
1
+ import { existsSync } from 'fs';
2
+ import { join } from 'path';
3
+ import { resolveDeploymentConfig } from './config/deployment.js';
4
+ export function assertSourceCheckoutCloudTarget(input) {
5
+ if (!existsSync(join(input.packageRoot, '.git')))
6
+ return;
7
+ if (input.explicitPreset || resolveDeploymentConfig(input.env).cloudBaseUrl)
8
+ return;
9
+ throw new Error('Refusing to default a Taskforce source checkout to production. '
10
+ + 'Use the performance/staging development script or provide an explicit cloud target.');
11
+ }
@@ -24,6 +24,17 @@ export function EntityActivityTimeline({ activity, comments, formatFieldValue, r
24
24
  normalizedCurrentActorLabel !== 'user' &&
25
25
  normalizedCurrentActorLabel !== 'human' &&
26
26
  normalizedCurrentActorLabel !== normalizedCurrentActorId);
27
+ const resolveCanonicalCurrentActorLabel = React.useCallback((presentation) => {
28
+ const normalizedLabel = String(presentation.label || '').trim().toLowerCase();
29
+ const isLegacyCurrentUserLabel = normalizedLabel === 'you'
30
+ || normalizedLabel === 'user'
31
+ || normalizedLabel === 'human';
32
+ return presentation.isCurrentActorHint === true
33
+ && hasCanonicalCurrentActorLabel
34
+ && isLegacyCurrentUserLabel
35
+ ? currentActorLabel
36
+ : presentation.label;
37
+ }, [currentActorLabel, hasCanonicalCurrentActorLabel]);
27
38
  return (_jsxs("div", { ref: listRef, className: [timelineStyles.thread, listClassName].filter(Boolean).join(' '), onScroll: onScroll, children: [items.length === 0 ? (_jsx("div", { className: [timelineStyles.empty, emptyClassName].filter(Boolean).join(' '), children: emptyMessage })) : null, items.map((item) => {
28
39
  const isTargetEntry = Boolean(normalizedTargetEntryId
29
40
  && (item.id === normalizedTargetEntryId
@@ -60,10 +71,8 @@ export function EntityActivityTimeline({ activity, comments, formatFieldValue, r
60
71
  ? commentActorIds.includes(normalizedCurrentActorId)
61
72
  : actorPresentation.isCurrentActorHint === true
62
73
  : commentKind === 'member';
63
- const authorLabel = isCurrentActor &&
64
- actorPresentation.label.trim().toLowerCase() === 'you' &&
65
- hasCanonicalCurrentActorLabel
66
- ? currentActorLabel
74
+ const authorLabel = isCurrentActor
75
+ ? resolveCanonicalCurrentActorLabel(actorPresentation)
67
76
  : actorPresentation.label;
68
77
  const { ActorIcon } = actorPresentation;
69
78
  const authorColor = actorPresentation.kind === 'system'
@@ -96,6 +105,7 @@ export function EntityActivityTimeline({ activity, comments, formatFieldValue, r
96
105
  actorProfile: item.event.actorProfile || null,
97
106
  fallbackKind: 'system',
98
107
  });
108
+ const authorLabel = resolveCanonicalCurrentActorLabel(actorPresentation);
99
109
  const { ActorIcon } = actorPresentation;
100
110
  const authorColor = actorPresentation.color;
101
111
  const changeEntries = Object.entries(item.event.details?.changes || {});
@@ -109,7 +119,7 @@ export function EntityActivityTimeline({ activity, comments, formatFieldValue, r
109
119
  timelineStyles.item,
110
120
  timelineStyles.event,
111
121
  actorPresentation.kind === 'system' ? timelineStyles.eventSystem : '',
112
- ].filter(Boolean).join(' '), children: _jsxs("div", { className: timelineStyles.eventMain, children: [_jsxs("span", { className: timelineStyles.author, style: authorColor ? { color: authorColor } : undefined, children: [_jsx(ActorIcon, { size: 12, strokeWidth: 1.5 }), actorPresentation.label] }), _jsxs("div", { className: timelineStyles.eventText, children: [_jsx("div", { children: summarizeEntityEvent(item.event, formatFieldValue) }), displayedChangeEntries.length > 0 ? (_jsx("div", { className: timelineStyles.eventChanges, children: displayedChangeEntries.map(([field, change]) => (_jsx("div", { className: timelineStyles.eventChange, children: summarizeTaskChange(field, change, formatFieldValue) }, field))) })) : null] }), _jsx("time", { className: timelineStyles.time, dateTime: item.event.createdAt, children: formatDate(item.event.createdAt, {
122
+ ].filter(Boolean).join(' '), children: _jsxs("div", { className: timelineStyles.eventMain, children: [_jsxs("span", { className: timelineStyles.author, style: authorColor ? { color: authorColor } : undefined, children: [_jsx(ActorIcon, { size: 12, strokeWidth: 1.5 }), authorLabel] }), _jsxs("div", { className: timelineStyles.eventText, children: [_jsx("div", { children: summarizeEntityEvent(item.event, formatFieldValue) }), displayedChangeEntries.length > 0 ? (_jsx("div", { className: timelineStyles.eventChanges, children: displayedChangeEntries.map(([field, change]) => (_jsx("div", { className: timelineStyles.eventChange, children: summarizeTaskChange(field, change, formatFieldValue) }, field))) })) : null] }), _jsx("time", { className: timelineStyles.time, dateTime: item.event.createdAt, children: formatDate(item.event.createdAt, {
113
123
  month: 'short',
114
124
  day: 'numeric',
115
125
  hour: '2-digit',
@@ -218,5 +218,5 @@ export function AttachmentPreviewHost({ theme }) {
218
218
  ? `Copied ${referenceLabel}`
219
219
  : `Copy ${referenceLabel}`, className: styles.referenceBadge }))] })] }), _jsx("button", { type: "button", className: "tf-control-icon", "aria-label": "Close preview", title: "Close preview", onClick: () => setPreview(null), children: _jsx(X, { size: 18 }) })] }), _jsxs("div", { className: styles.content, children: [isImage && _jsx("img", { className: styles.image, src: path, alt: attachmentName }), isPdf && (_jsx("iframe", { className: styles.pdf, src: path, title: `Preview ${attachmentName}` })), isText && textLoading && _jsx("div", { className: styles.message, children: "Loading preview\u2026" }), isText && textError && _jsx("div", { className: styles.message, children: textError }), isText && !textLoading && !textError && (isMarkdown
220
220
  ? _jsx("div", { className: styles.textPreview, children: _jsx(Markdown, { variant: "detail", children: textContent }) })
221
- : _jsx("pre", { className: styles.codePreview, children: textContent })), !isImage && !isPdf && !isText && (_jsxs("div", { className: styles.unavailable, children: [_jsx(FileText, { size: 34, "aria-hidden": "true" }), _jsx("strong", { children: "Preview unavailable" }), _jsx("span", { children: "This file type can be opened or downloaded instead." })] }))] }), _jsxs("footer", { className: styles.footer, children: [_jsxs("div", { className: styles.secondaryActions, children: [!isExternal && (_jsxs("a", { className: styles.secondaryLink, href: buildDownloadUrl(path, attachmentName), target: "_blank", rel: "noreferrer", children: [_jsx(Download, { size: 14 }), " Download"] })), (isExternal || (!canOpenImageNotes && !canOpenDocumentManager)) && (_jsxs("a", { className: styles.secondaryLink, href: path, target: "_blank", rel: "noreferrer", children: [_jsx(ExternalLink, { size: 14 }), " Open original"] }))] }), (canOpenImageNotes || canOpenDocumentManager) && (_jsxs("button", { type: "button", className: "tf-button-primary tf-button-compact", onClick: openManager, children: [canOpenImageNotes ? _jsx(PencilLine, { size: 14 }) : _jsx(ExternalLink, { size: 14 }), canOpenImageNotes ? 'Open in Image Notes' : 'Open in Documents'] }))] })] }) }), document.body);
221
+ : _jsx("pre", { className: styles.codePreview, children: textContent })), !isImage && !isPdf && !isText && (_jsxs("div", { className: styles.unavailable, children: [_jsx(FileText, { size: 34, "aria-hidden": "true" }), _jsx("strong", { children: "Preview unavailable" }), _jsx("span", { children: "This file type can be opened or downloaded instead." })] }))] }), _jsxs("footer", { className: styles.footer, children: [_jsxs("div", { className: styles.secondaryActions, children: [!isExternal && (_jsxs("a", { className: styles.secondaryLink, href: buildDownloadUrl(path, attachmentName), target: "_blank", rel: "noreferrer", children: [_jsx(Download, { size: 14 }), " Download"] })), (isExternal || (!canOpenImageNotes && !canOpenDocumentManager)) && (_jsxs("a", { className: styles.secondaryLink, href: path, target: "_blank", rel: "noreferrer", children: [_jsx(ExternalLink, { size: 14 }), " Open original"] }))] }), (canOpenImageNotes || canOpenDocumentManager) && (_jsxs("button", { type: "button", className: "tf-button-primary tf-button-compact", onClick: openManager, children: [canOpenImageNotes ? _jsx(PencilLine, { size: 14 }) : _jsx(ExternalLink, { size: 14 }), canOpenImageNotes ? 'Open in Image Notes' : 'Open'] }))] })] }) }), document.body);
222
222
  }
@@ -11,6 +11,9 @@ export interface ContextAttachmentManagerProps {
11
11
  onAddAttachment: (file: AttachmentItem) => void;
12
12
  onRemoveAttachment: (index: number) => void;
13
13
  onUpdateAttachmentCaption: (index: number, caption: string) => void;
14
+ cardCoverAssetId?: string | null;
15
+ onSetCardCover?: (assetId: string | null) => void | Promise<void>;
16
+ cardCoverDisabled?: boolean;
14
17
  readOnly?: boolean;
15
18
  defaultExpanded?: boolean;
16
19
  supplementalContent?: React.ReactNode;
@@ -1,15 +1,17 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { useEffect, useLayoutEffect, useMemo, useRef, useState } from 'react';
3
- import { AlertTriangle, Check, Copy, Download, ExternalLink, Eye, FileUp, Link2, Loader2, MoreHorizontal, PencilLine, Plus, Trash2, X } from 'lucide-react';
3
+ import { createPortal } from 'react-dom';
4
+ import { AlertTriangle, Check, Copy, Download, ExternalLink, Eye, FileUp, Link2, Loader2, MoreHorizontal, PanelTop, PencilLine, Plus, Trash2, X } from 'lucide-react';
4
5
  import styles from '../../Taskforce.module.css';
5
6
  import { DisclosureSectionHeader } from '../ui/DisclosureSectionHeader';
6
- import { dispatchOpenMarkdownDocument, isMarkdownContextFile, resolveContextFsPath } from '../../utils/contextFiles';
7
+ import { dispatchOpenMarkdownDocument, isMarkdownContextFile, resolveContextAssetId, resolveContextFsPath } from '../../utils/contextFiles';
7
8
  import { canOpenAnnotatedAttachment, dispatchOpenAnnotatedAttachment } from '../../utils/annotatedAttachments';
8
9
  import { dispatchTaskforceContextAssetsMutated } from '../../utils/contextAssetEvents';
9
10
  import { getDocumentReferenceLabel } from '../../utils/documentReferences';
10
11
  import { uploadContextAttachment } from '../../utils/contextAttachmentUpload';
11
12
  import { dispatchPreviewContextAttachment } from '../../utils/contextAttachmentPreview';
12
13
  import { ReferenceBadgeButton } from '../ui/ReferenceBadgeButton';
14
+ import { DocumentTypeIcon } from '../ui/DocumentTypeIcon';
13
15
  const ACCEPTED_CONTEXT_FILE_TYPES = [
14
16
  '.png', '.jpg', '.jpeg', '.webp', '.gif',
15
17
  '.pdf', '.txt', '.md', '.csv', '.json', '.doc', '.docx',
@@ -75,7 +77,7 @@ function getAttachmentTypeLabel(item) {
75
77
  return /^https?:\/\//i.test(path) ? 'LINK' : 'FILE';
76
78
  }
77
79
  export function ContextAttachmentManager(props) {
78
- const { ownerType = 'task', ownerId, ownerReferenceLabel, workspaceId, attachments, onAddAttachment, onRemoveAttachment, onUpdateAttachmentCaption, readOnly = false, defaultExpanded = true, supplementalContent, } = props;
80
+ const { ownerType = 'task', ownerId, ownerReferenceLabel, workspaceId, attachments, onAddAttachment, onRemoveAttachment, onUpdateAttachmentCaption, cardCoverAssetId = null, onSetCardCover, cardCoverDisabled = false, readOnly = false, defaultExpanded = true, supplementalContent, } = props;
79
81
  const inputRef = useRef(null);
80
82
  const managerRef = useRef(null);
81
83
  const [uploading, setUploading] = useState(false);
@@ -83,10 +85,13 @@ export function ContextAttachmentManager(props) {
83
85
  const [linkValue, setLinkValue] = useState('');
84
86
  const [linkError, setLinkError] = useState(null);
85
87
  const [contextNotice, setContextNotice] = useState(null);
88
+ const [cardCoverBusyAssetId, setCardCoverBusyAssetId] = useState(null);
86
89
  const [showContextUpload, setShowContextUpload] = useState(defaultExpanded);
87
90
  const [showAddMenu, setShowAddMenu] = useState(false);
88
91
  const [showLinkComposer, setShowLinkComposer] = useState(false);
89
92
  const [openActionsIndex, setOpenActionsIndex] = useState(null);
93
+ const [openImageActionsIndex, setOpenImageActionsIndex] = useState(null);
94
+ const [imageActionsMenuPosition, setImageActionsMenuPosition] = useState({ top: 0, left: 0 });
90
95
  const [captionEditor, setCaptionEditor] = useState(null);
91
96
  const [copiedDocumentReference, setCopiedDocumentReference] = useState(null);
92
97
  const copyResetTimerRef = useRef(null);
@@ -94,6 +99,9 @@ export function ContextAttachmentManager(props) {
94
99
  if (copyResetTimerRef.current !== null)
95
100
  window.clearTimeout(copyResetTimerRef.current);
96
101
  }, []);
102
+ useEffect(() => {
103
+ setCardCoverBusyAssetId(null);
104
+ }, [ownerId, ownerType, workspaceId]);
97
105
  const copyDocumentReference = async (referenceLabel) => {
98
106
  try {
99
107
  await navigator.clipboard.writeText(referenceLabel);
@@ -121,6 +129,9 @@ export function ContextAttachmentManager(props) {
121
129
  const addFirstActionRef = useRef(null);
122
130
  const attachmentActionsTriggerRef = useRef(null);
123
131
  const attachmentFirstActionRef = useRef(null);
132
+ const imageActionsTriggerRef = useRef(null);
133
+ const imageActionsMenuRef = useRef(null);
134
+ const imageFirstActionRef = useRef(null);
124
135
  const captionFocusReturnRef = useRef(null);
125
136
  const linkInputRef = useRef(null);
126
137
  const contextKeyRef = useRef(new Set());
@@ -171,6 +182,7 @@ export function ContextAttachmentManager(props) {
171
182
  setShowAddMenu(false);
172
183
  setShowLinkComposer(false);
173
184
  setOpenActionsIndex(null);
185
+ setOpenImageActionsIndex(null);
174
186
  setCaptionEditor(null);
175
187
  setIsDropActive(false);
176
188
  setDragDepth(0);
@@ -217,7 +229,7 @@ export function ContextAttachmentManager(props) {
217
229
  return () => window.clearTimeout(timeoutId);
218
230
  }, [contextNotice]);
219
231
  useEffect(() => {
220
- if (!showAddMenu && openActionsIndex === null)
232
+ if (!showAddMenu && openActionsIndex === null && openImageActionsIndex === null)
221
233
  return;
222
234
  const handlePointerDown = (event) => {
223
235
  const target = event.target;
@@ -227,10 +239,15 @@ export function ContextAttachmentManager(props) {
227
239
  if (openActionsIndex !== null && !target?.closest('[data-context-actions-menu="true"]')) {
228
240
  setOpenActionsIndex(null);
229
241
  }
242
+ if (openImageActionsIndex !== null
243
+ && !imageActionsTriggerRef.current?.contains(target)
244
+ && !imageActionsMenuRef.current?.contains(target)) {
245
+ setOpenImageActionsIndex(null);
246
+ }
230
247
  };
231
248
  document.addEventListener('pointerdown', handlePointerDown);
232
249
  return () => document.removeEventListener('pointerdown', handlePointerDown);
233
- }, [openActionsIndex, showAddMenu]);
250
+ }, [openActionsIndex, openImageActionsIndex, showAddMenu]);
234
251
  useEffect(() => {
235
252
  if (!showLinkComposer)
236
253
  return;
@@ -246,12 +263,45 @@ export function ContextAttachmentManager(props) {
246
263
  return;
247
264
  attachmentFirstActionRef.current?.focus();
248
265
  }, [openActionsIndex]);
266
+ useEffect(() => {
267
+ if (openImageActionsIndex === null)
268
+ return;
269
+ imageFirstActionRef.current?.focus();
270
+ }, [openImageActionsIndex]);
271
+ useLayoutEffect(() => {
272
+ if (openImageActionsIndex === null)
273
+ return;
274
+ const positionMenu = () => {
275
+ const triggerRect = imageActionsTriggerRef.current?.getBoundingClientRect();
276
+ if (!triggerRect)
277
+ return;
278
+ const menuRect = imageActionsMenuRef.current?.getBoundingClientRect();
279
+ const menuWidth = menuRect?.width || 150;
280
+ const menuHeight = menuRect?.height || 108;
281
+ const gap = 4;
282
+ const viewportPadding = 8;
283
+ const left = Math.max(viewportPadding, Math.min(triggerRect.right - menuWidth, window.innerWidth - menuWidth - viewportPadding));
284
+ const preferredTop = triggerRect.bottom + gap;
285
+ const top = preferredTop + menuHeight <= window.innerHeight - viewportPadding
286
+ ? preferredTop
287
+ : Math.max(viewportPadding, triggerRect.top - menuHeight - gap);
288
+ setImageActionsMenuPosition({ top, left });
289
+ };
290
+ positionMenu();
291
+ window.addEventListener('resize', positionMenu);
292
+ window.addEventListener('scroll', positionMenu, true);
293
+ return () => {
294
+ window.removeEventListener('resize', positionMenu);
295
+ window.removeEventListener('scroll', positionMenu, true);
296
+ };
297
+ }, [openImageActionsIndex]);
249
298
  useEffect(() => {
250
299
  if (!readOnly)
251
300
  return;
252
301
  setShowAddMenu(false);
253
302
  setShowLinkComposer(false);
254
303
  setOpenActionsIndex(null);
304
+ setOpenImageActionsIndex(null);
255
305
  setCaptionEditor(null);
256
306
  setIsDropActive(false);
257
307
  setDragDepth(0);
@@ -572,6 +622,7 @@ export function ContextAttachmentManager(props) {
572
622
  setShowAddMenu(false);
573
623
  setShowLinkComposer(false);
574
624
  setOpenActionsIndex(null);
625
+ setOpenImageActionsIndex(null);
575
626
  setCaptionEditor(null);
576
627
  setIsDropActive(false);
577
628
  setDragDepth(0);
@@ -623,6 +674,16 @@ export function ContextAttachmentManager(props) {
623
674
  const isExternalUrl = /^https?:\/\//i.test(path);
624
675
  const canAnnotate = ownerType === 'task'
625
676
  && canOpenAnnotatedAttachment(item, { taskId: trimmedOwnerId });
677
+ const assetId = typeof item === 'string' ? '' : String(item.assetId || '').trim();
678
+ const isCardCover = Boolean(assetId && assetId === cardCoverAssetId);
679
+ const canSetCardCover = ownerType === 'task'
680
+ && Boolean(trimmedOwnerId)
681
+ && Boolean(assetId)
682
+ && typeof item !== 'string'
683
+ && item.linkRole === 'image'
684
+ && Boolean(onSetCardCover)
685
+ && !readOnly;
686
+ const hasImageActionMenu = Boolean(fsPath || !isExternalUrl || !readOnly);
626
687
  return (_jsxs("div", { className: styles.attachmentCard, children: [_jsxs("div", { className: styles.attachmentPreviewContainer, children: [isImage ? (_jsx("button", { type: "button", className: styles.attachmentPreviewButton, "aria-label": `Open ${caption || displayName}`, onClick: () => {
627
688
  if (dispatchPreviewContextAttachment(item, {
628
689
  ownerType,
@@ -650,22 +711,56 @@ export function ContextAttachmentManager(props) {
650
711
  if (dispatchOpenMarkdownDocument(markdownTarget, fsPath))
651
712
  return;
652
713
  window.open(path, '_blank');
653
- }, title: "Open file", children: [_jsx(FileUp, { size: 16 }), _jsx("span", { children: caption || fsPath || path.split('/').pop() || 'Context file' })] })), _jsxs("div", { className: styles.brokenImagePlaceholder, children: [_jsx(AlertTriangle, { size: 16 }), _jsx("span", { children: "Preview unavailable" })] }), _jsxs("div", { className: styles.attachmentActions, children: [canAnnotate && (_jsx("button", { type: "button", className: styles.attachmentActionBtn, onClick: (e) => {
714
+ }, title: "Open file", children: [_jsx(FileUp, { size: 16 }), _jsx("span", { children: caption || fsPath || path.split('/').pop() || 'Context file' })] })), _jsxs("div", { className: styles.brokenImagePlaceholder, children: [_jsx(AlertTriangle, { size: 16 }), _jsx("span", { children: "Preview unavailable" })] }), _jsxs("div", { className: styles.attachmentActions, children: [canSetCardCover && (_jsx("button", { type: "button", className: `${styles.attachmentActionBtn} ${isCardCover ? styles.attachmentActionBtnActive : ''}`, "aria-pressed": isCardCover, "aria-label": isCardCover ? 'Remove from task card' : 'Show on task card', onClick: (event) => {
715
+ event.stopPropagation();
716
+ setCardCoverBusyAssetId(assetId);
717
+ setUploadError(null);
718
+ void Promise.resolve(onSetCardCover?.(isCardCover ? null : assetId))
719
+ .then(() => {
720
+ setContextNotice(isCardCover
721
+ ? 'Removed image from task card.'
722
+ : 'Image shown on task card.');
723
+ })
724
+ .catch((error) => {
725
+ setUploadError(error instanceof Error
726
+ ? error.message
727
+ : 'Unable to update the task card image.');
728
+ })
729
+ .finally(() => setCardCoverBusyAssetId(null));
730
+ }, disabled: cardCoverDisabled || cardCoverBusyAssetId !== null, title: cardCoverDisabled
731
+ ? 'Attachment is still saving'
732
+ : isCardCover ? 'Remove from task card' : 'Show on task card', children: cardCoverBusyAssetId === assetId
733
+ ? _jsx(Loader2, { size: 12, className: styles.spinner })
734
+ : _jsx(PanelTop, { size: 12 }) })), canAnnotate && (_jsx("button", { type: "button", className: styles.attachmentActionBtn, "aria-label": "Annotate image", onClick: (e) => {
654
735
  e.stopPropagation();
655
736
  dispatchOpenAnnotatedAttachment(item, {
656
737
  taskId: trimmedOwnerId,
657
738
  taskReferenceLabel: ownerReferenceLabel,
658
739
  });
659
- }, title: "Annotate", children: _jsx(PencilLine, { size: 12 }) })), fsPath && (_jsx("button", { type: "button", className: styles.attachmentActionBtn, onClick: (e) => {
660
- e.stopPropagation();
661
- navigator.clipboard.writeText(fsPath);
662
- }, title: `Copy path: ${fsPath}`, children: _jsx(Copy, { size: 12 }) })), !isExternalUrl && (_jsx("button", { type: "button", className: styles.attachmentActionBtn, onClick: (e) => {
663
- e.stopPropagation();
664
- window.open(buildDownloadUrl(path, caption || displayName), '_blank');
665
- }, title: "Download", children: _jsx(Download, { size: 12 }) })), !readOnly && (_jsx("button", { type: "button", className: styles.attachmentActionBtn, onClick: (e) => {
666
- e.stopPropagation();
667
- onRemoveAttachment(idx);
668
- }, title: "Delete", children: _jsx(Trash2, { size: 12 }) }))] })] }), captionEditor?.index === idx ? (_jsxs("div", { className: styles.contextCaptionEditor, onBlur: (event) => {
740
+ }, title: "Annotate", children: _jsx(PencilLine, { size: 12 }) })), hasImageActionMenu && (_jsx("button", { type: "button", className: styles.attachmentActionBtn, "aria-label": `Actions for ${displayName}`, "aria-expanded": openImageActionsIndex === idx, onClick: (event) => {
741
+ event.stopPropagation();
742
+ imageActionsTriggerRef.current = event.currentTarget;
743
+ setOpenActionsIndex(null);
744
+ setOpenImageActionsIndex(openImageActionsIndex === idx ? null : idx);
745
+ }, title: "More actions", children: _jsx(MoreHorizontal, { size: 12 }) })), openImageActionsIndex === idx && createPortal(_jsxs("div", { ref: imageActionsMenuRef, className: `${styles.contextMenu} ${styles.attachmentImageActionsMenu}`, role: "group", "aria-label": `Actions for ${displayName}`, style: imageActionsMenuPosition, onClick: (event) => event.stopPropagation(), onKeyDown: (event) => {
746
+ if (event.key !== 'Escape')
747
+ return;
748
+ event.preventDefault();
749
+ event.stopPropagation();
750
+ setOpenImageActionsIndex(null);
751
+ imageActionsTriggerRef.current?.focus();
752
+ }, children: [fsPath && (_jsxs("button", { ref: imageFirstActionRef, type: "button", onClick: () => {
753
+ void navigator.clipboard.writeText(fsPath);
754
+ setOpenImageActionsIndex(null);
755
+ window.requestAnimationFrame(() => imageActionsTriggerRef.current?.focus());
756
+ }, children: [_jsx(Copy, { size: 14 }), " Copy path"] })), !isExternalUrl && (_jsxs("button", { ref: fsPath ? undefined : imageFirstActionRef, type: "button", onClick: () => {
757
+ window.open(buildDownloadUrl(path, caption || displayName), '_blank');
758
+ setOpenImageActionsIndex(null);
759
+ window.requestAnimationFrame(() => imageActionsTriggerRef.current?.focus());
760
+ }, children: [_jsx(Download, { size: 14 }), " Download"] })), !readOnly && (_jsxs("button", { ref: !fsPath && isExternalUrl ? imageFirstActionRef : undefined, type: "button", className: styles.contextDestructiveMenuItem, onClick: () => {
761
+ onRemoveAttachment(idx);
762
+ setOpenImageActionsIndex(null);
763
+ }, children: [_jsx(Trash2, { size: 14 }), " Delete"] }))] }), document.body)] })] }), captionEditor?.index === idx ? (_jsxs("div", { className: styles.contextCaptionEditor, onBlur: (event) => {
669
764
  if (!event.currentTarget.contains(event.relatedTarget)) {
670
765
  commitCaptionEdit(false);
671
766
  }
@@ -700,8 +795,9 @@ export function ContextAttachmentManager(props) {
700
795
  : { ...item, taskId: trimmedOwnerId })
701
796
  : item;
702
797
  const canOpenInDocumentManager = isMarkdownContextFile(markdownTarget, fsPath)
703
- && Boolean(resolveContextFsPath(markdownTarget, fsPath));
704
- return (_jsxs("div", { className: styles.documentAttachmentItem, children: [_jsxs("div", { className: styles.documentAttachmentRow, children: [_jsxs("div", { className: styles.documentAttachmentMain, children: [documentReferenceLabel && (_jsx(ReferenceBadgeButton, { label: documentReferenceLabel, copied: copiedDocumentReference === documentReferenceLabel, title: `Copy document reference ${documentReferenceLabel}`, ariaLabel: `Copy document reference ${documentReferenceLabel}`, className: styles.documentAttachmentReference, onClick: () => { void copyDocumentReference(documentReferenceLabel); } })), _jsx("button", { type: "button", className: styles.documentAttachmentLink, onClick: () => {
798
+ && Boolean(resolveContextFsPath(markdownTarget, fsPath)
799
+ || resolveContextAssetId(markdownTarget));
800
+ return (_jsxs("div", { className: styles.documentAttachmentItem, children: [_jsxs("div", { className: styles.documentAttachmentRow, children: [_jsxs("div", { className: styles.documentAttachmentMain, children: [_jsx(DocumentTypeIcon, { label: fileType }), documentReferenceLabel && (_jsx(ReferenceBadgeButton, { label: documentReferenceLabel, copied: copiedDocumentReference === documentReferenceLabel, title: `Copy document reference ${documentReferenceLabel}`, ariaLabel: `Copy document reference ${documentReferenceLabel}`, className: styles.documentAttachmentReference, onClick: () => { void copyDocumentReference(documentReferenceLabel); } })), _jsx("button", { type: "button", className: styles.documentAttachmentLink, onClick: () => {
705
801
  if (dispatchPreviewContextAttachment(item, {
706
802
  ownerType,
707
803
  ownerId: trimmedOwnerId || null,
@@ -712,7 +808,7 @@ export function ContextAttachmentManager(props) {
712
808
  if (dispatchOpenMarkdownDocument(markdownTarget, fsPath))
713
809
  return;
714
810
  window.open(path, '_blank');
715
- }, title: displayName, "aria-label": `${caption || displayName} ${fileType}`, children: _jsx("span", { className: styles.documentAttachmentName, children: caption || displayName }) }), _jsxs("span", { className: styles.documentAttachmentType, children: [_jsxs("svg", { className: styles.documentAttachmentTypeOutline, viewBox: "0 0 28 28", "aria-hidden": "true", focusable: "false", children: [_jsx("path", { d: "M2 0.75h18.25l7 7V26q0 1.25-1.25 1.25H2Q0.75 27.25 0.75 26V2Q0.75 0.75 2 0.75Z" }), _jsx("path", { className: styles.documentAttachmentTypeFoldFill, d: "M20.25 0.75v7h7Z" }), _jsx("path", { className: styles.documentAttachmentTypeFoldLine, d: "M20.25 0.75v7h7" })] }), _jsx("span", { className: styles.documentAttachmentTypeLabel, children: fileType })] })] }), (!readOnly || Boolean(fsPath) || !isExternalUrl) && (_jsxs("div", { className: styles.contextMenuControl, "data-context-actions-menu": "true", onKeyDown: (event) => {
811
+ }, title: displayName, "aria-label": `${caption || displayName} ${fileType}`, children: _jsx("span", { className: styles.documentAttachmentName, children: caption || displayName }) })] }), (!readOnly || Boolean(fsPath) || !isExternalUrl) && (_jsxs("div", { className: styles.contextMenuControl, "data-context-actions-menu": "true", onKeyDown: (event) => {
716
812
  if (event.key !== 'Escape' || openActionsIndex !== idx)
717
813
  return;
718
814
  event.preventDefault();
@@ -737,7 +833,7 @@ export function ContextAttachmentManager(props) {
737
833
  window.open(path, '_blank');
738
834
  }
739
835
  setOpenActionsIndex(null);
740
- }, children: [_jsx(ExternalLink, { size: 14 }), " Open in Documents"] })), !readOnly && (_jsxs("button", { type: "button", onClick: () => {
836
+ }, children: [_jsx(ExternalLink, { size: 14 }), " Open"] })), !readOnly && (_jsxs("button", { type: "button", onClick: () => {
741
837
  captionFocusReturnRef.current = attachmentActionsTriggerRef.current;
742
838
  startCaptionEdit(idx, caption);
743
839
  setOpenActionsIndex(null);
@@ -427,6 +427,7 @@ export function AnnotatedAttachmentWorkspaceShell({ runtimeMode = 'local', apiBa
427
427
  const [draftGlobalInstruction, setDraftGlobalInstruction] = useState('');
428
428
  const [draftAnnotations, setDraftAnnotations] = useState([]);
429
429
  const [selectedAnnotationId, setSelectedAnnotationId] = useState(null);
430
+ const [hoveredAnnotationId, setHoveredAnnotationId] = useState(null);
430
431
  const [isAnnotationEditorCollapsed, setIsAnnotationEditorCollapsed] = useState(false);
431
432
  const [activeAnnotationId, setActiveAnnotationId] = useState(null);
432
433
  const [defaultMarkerColor, setDefaultMarkerColor] = useState(ANNOTATION_COLORS[DEFAULT_MARKER_TYPE]);
@@ -1218,6 +1219,18 @@ export function AnnotatedAttachmentWorkspaceShell({ runtimeMode = 'local', apiBa
1218
1219
  }
1219
1220
  }, [flushPendingDraft, isTargetControlled, loadSessions, onOpenTarget, resetForTargetLoad]);
1220
1221
  const applySessionLocally = useCallback((session) => {
1222
+ if (isTargetControlled && activeTarget && activeTarget.taskId !== session.taskId) {
1223
+ const nextTarget = { ...activeTarget };
1224
+ if (session.taskId) {
1225
+ nextTarget.taskId = session.taskId;
1226
+ nextTarget.taskReferenceLabel = resolveTaskReferenceLabel?.(session.taskId) || session.taskId;
1227
+ }
1228
+ else {
1229
+ delete nextTarget.taskId;
1230
+ delete nextTarget.taskReferenceLabel;
1231
+ }
1232
+ onOpenTarget?.(nextTarget, { sessionId: session.id });
1233
+ }
1221
1234
  setSessions((current) => {
1222
1235
  const existingIndex = current.findIndex((item) => item.id === session.id);
1223
1236
  if (existingIndex === -1)
@@ -1228,7 +1241,7 @@ export function AnnotatedAttachmentWorkspaceShell({ runtimeMode = 'local', apiBa
1228
1241
  });
1229
1242
  setSelectedSessionId(session.id);
1230
1243
  syncDraftFromSession(session, activeTarget);
1231
- }, [activeTarget, syncDraftFromSession]);
1244
+ }, [activeTarget, isTargetControlled, onOpenTarget, resolveTaskReferenceLabel, syncDraftFromSession]);
1232
1245
  const removeSessionLocally = useCallback((sessionId) => {
1233
1246
  setSessions((current) => {
1234
1247
  const remaining = current.filter((session) => session.id !== sessionId);
@@ -2268,8 +2281,9 @@ export function AnnotatedAttachmentWorkspaceShell({ runtimeMode = 'local', apiBa
2268
2281
  const geometry = getArrowGeometry(annotation, canvasMediaSize);
2269
2282
  const color = mapMarkerColor(annotation);
2270
2283
  const isSelected = selectedAnnotationId === annotation.id;
2271
- return (_jsxs(React.Fragment, { children: [isSelected ? (_jsxs(_Fragment, { children: [_jsx("line", { x1: geometry.shaftX1, y1: geometry.shaftY1, x2: geometry.shaftX2, y2: geometry.shaftY2, stroke: "color-mix(in srgb, var(--surface-elevated) 92%, transparent)", strokeWidth: 14, strokeLinecap: "round" }), _jsx("polygon", { points: geometry.headPoints, fill: color, stroke: "color-mix(in srgb, var(--surface-elevated) 92%, transparent)", strokeWidth: 4, strokeLinejoin: "round" })] })) : null, _jsx("line", { x1: geometry.shaftX1, y1: geometry.shaftY1, x2: geometry.shaftX2, y2: geometry.shaftY2, stroke: color, strokeWidth: isSelected ? 10 : 6, strokeLinecap: "round" }), _jsx("polygon", { points: geometry.headPoints, fill: color })] }, annotation.id));
2272
- }) }), _jsx("svg", { className: styles.overlayHitLayer, viewBox: arrowOverlayViewBox, preserveAspectRatio: "none", children: draftAnnotations.filter((annotation) => annotation.kind === 'arrow').map((annotation) => (_jsxs(React.Fragment, { children: [_jsx("line", { x1: annotation.x * canvasMediaSize.width, y1: annotation.y * canvasMediaSize.height, x2: annotation.x2 * canvasMediaSize.width, y2: annotation.y2 * canvasMediaSize.height, className: styles.arrowHitArea, "data-testid": `annotated-arrow-hit-${annotation.id}`, "aria-label": `Arrow marker ${annotationOrderNumbers.get(annotation.id) || 0}`, onPointerDown: (event) => startDragAnnotation(event, annotation), onClick: (event) => {
2284
+ const isEmphasized = isSelected || hoveredAnnotationId === annotation.id;
2285
+ return (_jsxs(React.Fragment, { children: [isEmphasized ? (_jsxs(_Fragment, { children: [_jsx("line", { x1: geometry.shaftX1, y1: geometry.shaftY1, x2: geometry.shaftX2, y2: geometry.shaftY2, stroke: "color-mix(in srgb, var(--surface-elevated) 92%, transparent)", strokeWidth: 14, strokeLinecap: "round" }), _jsx("polygon", { points: geometry.headPoints, fill: color, stroke: "color-mix(in srgb, var(--surface-elevated) 92%, transparent)", strokeWidth: 4, strokeLinejoin: "round" })] })) : null, _jsx("line", { x1: geometry.shaftX1, y1: geometry.shaftY1, x2: geometry.shaftX2, y2: geometry.shaftY2, stroke: color, strokeWidth: isEmphasized ? 10 : 6, strokeLinecap: "round" }), _jsx("polygon", { points: geometry.headPoints, fill: color })] }, annotation.id));
2286
+ }) }), _jsx("svg", { className: styles.overlayHitLayer, viewBox: arrowOverlayViewBox, preserveAspectRatio: "none", children: draftAnnotations.filter((annotation) => annotation.kind === 'arrow').map((annotation) => (_jsxs(React.Fragment, { children: [_jsx("line", { x1: annotation.x * canvasMediaSize.width, y1: annotation.y * canvasMediaSize.height, x2: annotation.x2 * canvasMediaSize.width, y2: annotation.y2 * canvasMediaSize.height, className: styles.arrowHitArea, "data-testid": `annotated-arrow-hit-${annotation.id}`, "aria-label": `Arrow marker ${annotationOrderNumbers.get(annotation.id) || 0}`, onMouseEnter: () => setHoveredAnnotationId(annotation.id), onMouseLeave: () => setHoveredAnnotationId(null), onPointerDown: (event) => startDragAnnotation(event, annotation), onClick: (event) => {
2273
2287
  event.stopPropagation();
2274
2288
  selectAnnotation(annotation.id);
2275
2289
  } }), selectedAnnotationId === annotation.id ? (_jsxs(_Fragment, { children: [_jsx("circle", { cx: annotation.x * canvasMediaSize.width, cy: annotation.y * canvasMediaSize.height, r: arrowHandleGeometry.hitRadius, className: styles.canvasHandleHit, role: "button", tabIndex: 0, "aria-label": "Move arrow tail", onPointerDown: (event) => startArrowHandleDrag(event, annotation, 'tail') }), _jsx("circle", { cx: annotation.x * canvasMediaSize.width, cy: annotation.y * canvasMediaSize.height, r: arrowHandleGeometry.visibleRadius, className: styles.canvasHandleVisible, "aria-hidden": "true" }), _jsx("circle", { cx: annotation.x2 * canvasMediaSize.width, cy: annotation.y2 * canvasMediaSize.height, r: arrowHandleGeometry.hitRadius, className: styles.canvasHandleHit, role: "button", tabIndex: 0, "aria-label": "Move arrow tip", onPointerDown: (event) => startArrowHandleDrag(event, annotation, 'tip') }), _jsx("circle", { cx: annotation.x2 * canvasMediaSize.width, cy: annotation.y2 * canvasMediaSize.height, r: arrowHandleGeometry.visibleRadius, className: styles.canvasHandleVisible, "aria-hidden": "true" })] })) : null] }, `hit-${annotation.id}`))) }), draftAnnotations.filter((annotation) => annotation.kind === 'arrow').map((annotation) => (_jsx("div", { className: `${styles.annotationNumberBadge} ${styles.arrowNumberBadge}`, style: {
@@ -2281,18 +2295,20 @@ export function AnnotatedAttachmentWorkspaceShell({ runtimeMode = 'local', apiBa
2281
2295
  const annotationOrderNumber = annotationOrderNumbers.get(annotation.id) || 0;
2282
2296
  const sharedProps = {
2283
2297
  onPointerDown: (event) => startDragAnnotation(event, annotation),
2298
+ onMouseEnter: () => setHoveredAnnotationId(annotation.id),
2299
+ onMouseLeave: () => setHoveredAnnotationId(null),
2284
2300
  onClick: (event) => {
2285
2301
  event.stopPropagation();
2286
2302
  selectAnnotation(annotation.id);
2287
2303
  },
2288
2304
  };
2289
2305
  if (annotation.kind === 'pin') {
2290
- return (_jsx("button", { type: "button", ...sharedProps, className: `${styles.pin} ${selectedAnnotationId === annotation.id ? styles.selected : ''}`, style: { left: `${annotation.x * 100}%`, top: `${annotation.y * 100}%`, backgroundColor: color }, children: annotationOrderNumber }, annotation.id));
2306
+ return (_jsx("button", { type: "button", ...sharedProps, className: `${styles.pin} ${selectedAnnotationId === annotation.id ? styles.selected : ''} ${hoveredAnnotationId === annotation.id ? styles.markerHovered : ''}`, style: { left: `${annotation.x * 100}%`, top: `${annotation.y * 100}%`, backgroundColor: color }, children: annotationOrderNumber }, annotation.id));
2291
2307
  }
2292
2308
  if (annotation.kind === 'text-note') {
2293
- return (_jsx("button", { type: "button", ...sharedProps, className: `${styles.note} ${selectedAnnotationId === annotation.id ? styles.selected : ''}`, style: { left: `${annotation.x * 100}%`, top: `${annotation.y * 100}%`, backgroundColor: color }, children: annotationOrderNumber }, annotation.id));
2309
+ return (_jsx("button", { type: "button", ...sharedProps, className: `${styles.note} ${selectedAnnotationId === annotation.id ? styles.selected : ''} ${hoveredAnnotationId === annotation.id ? styles.markerHovered : ''}`, style: { left: `${annotation.x * 100}%`, top: `${annotation.y * 100}%`, backgroundColor: color }, children: annotationOrderNumber }, annotation.id));
2294
2310
  }
2295
- return (_jsxs("div", { className: `${styles.box} ${selectedAnnotationId === annotation.id ? styles.selected : ''}`, style: {
2311
+ return (_jsxs("div", { className: `${styles.box} ${selectedAnnotationId === annotation.id ? styles.selected : ''} ${hoveredAnnotationId === annotation.id ? styles.markerHovered : ''}`, style: {
2296
2312
  left: `${annotation.x * 100}%`,
2297
2313
  top: `${annotation.y * 100}%`,
2298
2314
  width: `${annotation.width * 100}%`,
@@ -2311,7 +2327,7 @@ export function AnnotatedAttachmentWorkspaceShell({ runtimeMode = 'local', apiBa
2311
2327
  const instructionTypeLabel = MARKER_TYPE_OPTIONS.find((option) => option.value === (annotation.markerType || DEFAULT_MARKER_TYPE))?.label || 'Review';
2312
2328
  const isSelected = selectedAnnotationId === annotation.id;
2313
2329
  const isExpanded = isSelected && !isAnnotationEditorCollapsed;
2314
- return (_jsx(SortableAnnotationCard, { id: annotation.id, children: ({ attributes, listeners, setNodeRef, transform, transition, isDragging }) => (_jsxs("div", { className: `tf-surface-elevated ${styles.annotationCard} ${isSelected ? styles.annotationCardActive : ''} ${isDragging ? styles.annotationCardDragging : ''}`, ref: (node) => {
2330
+ return (_jsx(SortableAnnotationCard, { id: annotation.id, children: ({ attributes, listeners, setNodeRef, transform, transition, isDragging }) => (_jsxs("div", { className: `tf-surface-elevated ${styles.annotationCard} ${isSelected ? styles.annotationCardActive : ''} ${hoveredAnnotationId === annotation.id ? styles.annotationCardHovered : ''} ${isDragging ? styles.annotationCardDragging : ''}`, "data-testid": `annotation-card-${annotation.id}`, ref: (node) => {
2315
2331
  setNodeRef(node);
2316
2332
  if (node)
2317
2333
  annotationCardRefs.current.set(annotation.id, node);
@@ -2325,7 +2341,7 @@ export function AnnotatedAttachmentWorkspaceShell({ runtimeMode = 'local', apiBa
2325
2341
  if (!event.currentTarget.contains(event.relatedTarget)) {
2326
2342
  setIsAnnotationEditorCollapsed(true);
2327
2343
  }
2328
- }, children: [_jsxs("div", { className: styles.annotationMeta, children: [_jsx("button", { type: "button", className: styles.annotationDragHandle, "aria-label": `Reorder ${annotationSequenceLabel}`, title: "Drag to reorder. Press Space, then use the arrow keys to reorder with the keyboard.", ...attributes, ...listeners, children: _jsx(GripVertical, { size: 16 }) }), _jsx("button", { type: "button", className: styles.annotationCardButton, onClick: () => {
2344
+ }, onMouseEnter: () => setHoveredAnnotationId(annotation.id), onMouseLeave: () => setHoveredAnnotationId(null), children: [_jsxs("div", { className: styles.annotationMeta, children: [_jsx("button", { type: "button", className: styles.annotationDragHandle, "aria-label": `Reorder ${annotationSequenceLabel}`, title: "Drag to reorder. Press Space, then use the arrow keys to reorder with the keyboard.", ...attributes, ...listeners, children: _jsx(GripVertical, { size: 16 }) }), _jsx("button", { type: "button", className: styles.annotationCardButton, onClick: () => {
2329
2345
  toggleAnnotation(annotation.id);
2330
2346
  }, "aria-label": annotationKindLabel, "aria-expanded": isExpanded, children: _jsx("span", { className: `tf-heading-card ${styles.annotationTitle}`, children: annotationSequenceLabel }) }), _jsx("span", { className: `tf-text-meta ${styles.annotationKind}`, "aria-hidden": "true", children: _jsx(AnnotationKindIcon, { size: 16 }) })] }), isExpanded ? (_jsxs("div", { className: styles.annotationInstructionEditor, children: [_jsx("label", { className: "tf-field-label", htmlFor: "annotated-marker-instruction", children: "Marker instruction" }), _jsx("textarea", { id: "annotated-marker-instruction", ref: isSelected ? selectedInstructionTextareaRef : null, className: `tf-field-shell ${styles.textArea} ${styles.annotationInstructionField}`, value: annotation.instruction, onChange: (event) => {
2331
2347
  updateDraftAnnotation(annotation.id, (current) => ({
@@ -23,6 +23,11 @@ export interface TaskSettingsModel {
23
23
  manualComplexityEnabled: boolean;
24
24
  checklistDropdownEnabled?: boolean;
25
25
  showTaskCardStatusLabel?: boolean;
26
+ showTaskCardDescription?: boolean;
27
+ showTaskCardAttachments?: boolean;
28
+ showTaskCardChecklistProgress?: boolean;
29
+ showTaskCardLatestActivity?: boolean;
30
+ tintTaskCardActivityBackground?: boolean;
26
31
  initialSection?: 'categories' | 'types' | 'general' | 'documents' | 'agents' | 'taxonomy' | 'mcp' | 'integrations' | 'priorities' | 'initiatives' | 'appearance-application' | 'appearance-task-cards' | 'appearance-task-forms' | null;
27
32
  setupState?: {
28
33
  mode: 'core' | 'operations';
@@ -38,15 +43,21 @@ export interface TaskSettingsModel {
38
43
  onThemeChange: (theme: TaskforceTheme) => void;
39
44
  onSaveTheme: (theme: TaskforceTheme) => Promise<boolean>;
40
45
  onSaveGlobalTheme: (theme: TaskforceTheme) => Promise<boolean>;
41
- onResetProjectToGlobal: () => Promise<boolean>;
46
+ onUseGlobalThemeDefault: () => Promise<boolean>;
42
47
  onKeyShortcutChange: (value: string) => void;
43
48
  onJsonBackupEnabledChange: (value: boolean) => Promise<boolean>;
44
49
  onSaveGlobalJsonBackupEnabled: (value: boolean) => Promise<boolean>;
50
+ onUseGlobalJsonBackupDefault: () => Promise<boolean>;
45
51
  onSaveGlobalWeekStartsOn: (value: 'sunday' | 'monday') => Promise<boolean>;
46
52
  onSaveLocale?: (value: string) => Promise<boolean>;
47
53
  onManualComplexityEnabledChange: (value: boolean) => Promise<boolean>;
48
54
  onChecklistDropdownEnabledChange?: (value: boolean) => Promise<boolean>;
49
55
  onShowTaskCardStatusLabelChange?: (value: boolean) => Promise<boolean>;
56
+ onShowTaskCardDescriptionChange?: (value: boolean) => Promise<boolean>;
57
+ onShowTaskCardAttachmentsChange?: (value: boolean) => Promise<boolean>;
58
+ onShowTaskCardChecklistProgressChange?: (value: boolean) => Promise<boolean>;
59
+ onShowTaskCardLatestActivityChange?: (value: boolean) => Promise<boolean>;
60
+ onTintTaskCardActivityBackgroundChange?: (value: boolean) => Promise<boolean>;
50
61
  onSaveSettings: () => Promise<boolean>;
51
62
  onSaveSetupMode?: (mode: 'core' | 'operations') => Promise<boolean>;
52
63
  onSaveWorkspaceProfile?: (input: {