@taskforcehq/taskforce 0.3.328 → 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 (264) hide show
  1. package/dist/Taskforce.module.css +233 -34
  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/features/AnnotatedAttachmentWorkspace.js +24 -8
  8. package/dist/components/features/TaskSettings.d.ts +12 -1
  9. package/dist/components/features/TaskSettings.js +147 -40
  10. package/dist/components/features/agentCapabilities/AgentCapabilitiesPrototype.d.ts +1 -1
  11. package/dist/components/features/planning/PlanningModule.js +2 -5
  12. package/dist/components/features/planning/PlanningTaskOrderList.d.ts +1 -1
  13. package/dist/components/features/planning/PlanningTaskOrderList.js +17 -6
  14. package/dist/components/features/planning/planningTaskArrangement.d.ts +1 -0
  15. package/dist/components/features/planning/planningTaskArrangement.js +5 -5
  16. package/dist/components/features/taskforceAgentEditorModel.js +3 -1
  17. package/dist/components/task/TaskCard.d.ts +5 -0
  18. package/dist/components/task/TaskCard.js +65 -10
  19. package/dist/components/task/TaskImageReviews.js +14 -5
  20. package/dist/components/task/TaskKanban.d.ts +6 -1
  21. package/dist/components/task/TaskKanban.js +152 -6
  22. package/dist/components/task/TaskList.d.ts +5 -0
  23. package/dist/components/task/TaskList.js +3 -3
  24. package/dist/components/ui/PasswordInput.d.ts +6 -0
  25. package/dist/components/ui/PasswordInput.js +11 -0
  26. package/dist/components/ui/TopNoticeLayer.js +1 -1
  27. package/dist/components/views/AppShellHeader.js +32 -11
  28. package/dist/components/views/PlanComparisonPage.js +12 -0
  29. package/dist/components/views/StandaloneLayout.js +7 -31
  30. package/dist/components/views/WidgetView.js +3 -3
  31. package/dist/components/views/panels/PlanningDrawer.js +5 -12
  32. package/dist/components/views/standalone/modals/AccountSettingsModal.js +26 -17
  33. package/dist/config/envSchema.js +12 -0
  34. package/dist/config/localServiceRuntimeContract.d.ts +13 -0
  35. package/dist/config/localServiceRuntimeContract.js +135 -0
  36. package/dist/core/AiProfileService.js +46 -8
  37. package/dist/core/DeletedTaskLifecycleService.d.ts +1 -0
  38. package/dist/core/DeletedTaskLifecycleService.js +37 -6
  39. package/dist/core/TaskAttachmentLinksDurableMutationService.d.ts +3 -2
  40. package/dist/core/TaskAttachmentLinksDurableMutationService.js +33 -9
  41. package/dist/core/Taskforce.d.ts +62 -2
  42. package/dist/core/Taskforce.js +635 -32
  43. package/dist/core/types.d.ts +6 -0
  44. package/dist/hooks/sync/useDurableLocalOutboxDrain.d.ts +1 -1
  45. package/dist/hooks/sync/useDurableTaskCreateMutation.js +1 -1
  46. package/dist/hooks/sync/useDurableTaskMetadataMutation.d.ts +1 -0
  47. package/dist/hooks/sync/useDurableTaskMetadataMutation.js +1 -0
  48. package/dist/hooks/sync/useDurableTaskStatusMutation.d.ts +1 -0
  49. package/dist/hooks/tasks/taskHttpMutation.js +1 -0
  50. package/dist/hooks/tasks/useCloudTaskSearch.d.ts +12 -0
  51. package/dist/hooks/tasks/useCloudTaskSearch.js +71 -0
  52. package/dist/hooks/tasks/useTaskDescriptionImageDraftRecovery.d.ts +16 -0
  53. package/dist/hooks/tasks/useTaskDescriptionImageDraftRecovery.js +197 -0
  54. package/dist/hooks/tasks/useTaskEditorNavigation.d.ts +16 -3
  55. package/dist/hooks/tasks/useTaskEditorNavigation.js +72 -13
  56. package/dist/hooks/tasks/useTaskFormActions.d.ts +6 -0
  57. package/dist/hooks/tasks/useTaskFormActions.js +65 -12
  58. package/dist/hooks/ui/useSettingsModel.d.ts +24 -2
  59. package/dist/hooks/ui/useSettingsModel.js +13 -2
  60. package/dist/hooks/ui/useSettingsPersistence.d.ts +12 -1
  61. package/dist/hooks/ui/useSettingsPersistence.js +74 -47
  62. package/dist/hooks/useFilterSort.d.ts +7 -3
  63. package/dist/hooks/useFilterSort.js +15 -5
  64. package/dist/hooks/useTaskData.d.ts +10 -1
  65. package/dist/hooks/useTaskData.js +216 -7
  66. package/dist/hooks/useTaskMutations.d.ts +10 -2
  67. package/dist/hooks/useTaskMutations.js +223 -43
  68. package/dist/hooks/useTaskforce.d.ts +37 -9
  69. package/dist/hooks/useTaskforce.js +349 -39
  70. package/dist/hooks/useUiNotice.d.ts +4 -0
  71. package/dist/hooks/useUiNotice.js +4 -0
  72. package/dist/mcp/adminWorkspaceRegistrar.js +3 -3
  73. package/dist/mcp/documentAssetRegistrar.js +44 -35
  74. package/dist/mcp/documentHelpers.d.ts +1 -0
  75. package/dist/mcp/documentHelpers.js +7 -3
  76. package/dist/mcp/runtime.js +172 -42
  77. package/dist/mcp/taskPlanningRegistrar.js +41 -38
  78. package/dist/mcp/toolCallCompatibility.js +38 -1
  79. package/dist/mcp/toolCatalog.d.ts +1 -0
  80. package/dist/migrations/taskSchemaMigrations.js +12 -0
  81. package/dist/runtime/appGateDefinitions.d.ts +30 -6
  82. package/dist/runtime/appGateDefinitions.js +6 -6
  83. package/dist/runtime/appGates.d.ts +12 -13
  84. package/dist/runtime/appGates.js +29 -13
  85. package/dist/runtime/nodeAppGates.d.ts +8 -0
  86. package/dist/runtime/nodeAppGates.js +27 -0
  87. package/dist/server/cloudCollectionRead.d.ts +1 -1
  88. package/dist/server/cloudCollectionRead.js +2 -2
  89. package/dist/server/index.d.ts +3 -0
  90. package/dist/server/index.js +15 -3
  91. package/dist/server/localServiceLease.d.ts +3 -0
  92. package/dist/server/localServiceLease.js +9 -1
  93. package/dist/server/localWorkspaceSyncServerRuntime.js +10 -3
  94. package/dist/server/rateLimit.d.ts +1 -0
  95. package/dist/server/rateLimit.js +4 -0
  96. package/dist/server/realtimeSyncWs.js +36 -12
  97. package/dist/server/routes/admin.js +1 -0
  98. package/dist/server/routes/agentRoles.js +3 -2
  99. package/dist/server/routes/agentSkills.js +3 -2
  100. package/dist/server/routes/agents.js +118 -16
  101. package/dist/server/routes/annotatedAttachments.js +3 -2
  102. package/dist/server/routes/auth.js +3 -1
  103. package/dist/server/routes/durableTaskHttpRouters.d.ts +9 -2
  104. package/dist/server/routes/localService.js +2 -0
  105. package/dist/server/routes/primitives.d.ts +2 -0
  106. package/dist/server/routes/syncAuxRoutes.js +5 -2
  107. package/dist/server/routes/syncPullApplyRoutes.js +23 -4
  108. package/dist/server/routes/syncV3FeedRoutes.js +18 -16
  109. package/dist/server/routes/syncV3LocalCaptureRoutes.js +28 -2
  110. package/dist/server/routes/syncV3LocalFeedApplyRoutes.d.ts +1 -0
  111. package/dist/server/routes/syncV3LocalFeedApplyRoutes.js +26 -0
  112. package/dist/server/routes/syncV3OperationRoutes.js +10 -0
  113. package/dist/server/routes/tasks.js +103 -11
  114. package/dist/server/routes/workspaces.js +43 -1
  115. package/dist/server/workspaceDeletionAdmission.d.ts +23 -0
  116. package/dist/server/workspaceDeletionAdmission.js +50 -0
  117. package/dist/service/localServiceCli.js +16 -0
  118. package/dist/service/localServiceClientLifecycle.js +36 -10
  119. package/dist/services/codexAppServerModelGateway.d.ts +112 -0
  120. package/dist/services/codexAppServerModelGateway.js +438 -0
  121. package/dist/services/taskforceAgentMcpBridge.d.ts +7 -1
  122. package/dist/services/taskforceAgentMcpBridge.js +23 -3
  123. package/dist/shared/taskforceAgentDefinition.js +7 -1
  124. package/dist/shared/taskforceAgentModels.d.ts +1 -0
  125. package/dist/shared/taskforceAgentModels.js +30 -1
  126. package/dist/storage/postgresAdapter.d.ts +20 -3
  127. package/dist/storage/postgresAdapter.js +106 -14
  128. package/dist/storage/postgresWorker.js +13 -4
  129. package/dist/storage/postgresWorkerProtocol.d.ts +29 -0
  130. package/dist/storage/postgresWorkerProtocol.js +30 -0
  131. package/dist/sync/cloudSyncApi.d.ts +4 -1
  132. package/dist/sync/cloudSyncApi.js +7 -2
  133. package/dist/sync/contentSyncState.d.ts +1 -1
  134. package/dist/sync/coordinator/localSyncExecutionPermitStore.d.ts +1 -0
  135. package/dist/sync/coordinator/localSyncExecutionPermitStore.js +7 -1
  136. package/dist/sync/coordinator/localWorkspaceSyncRunner.js +6 -2
  137. package/dist/sync/engine/workspaceSyncEngineBrowserGatewayRemoteServices.js +20 -2
  138. package/dist/sync/engine/workspaceSyncEngineLocalServices.d.ts +4 -0
  139. package/dist/sync/engine/workspaceSyncEngineLocalServices.js +11 -0
  140. package/dist/sync/engine/workspaceSyncEngineRemoteServices.d.ts +11 -0
  141. package/dist/sync/engine/workspaceSyncEngineRemoteServices.js +2 -1
  142. package/dist/sync/engine/workspaceSyncEngineServerRemoteServices.js +31 -4
  143. package/dist/sync/engine/workspaceSyncEngineServerRunnerOperations.d.ts +3 -1
  144. package/dist/sync/engine/workspaceSyncEngineServerRunnerOperations.js +25 -8
  145. package/dist/sync/engine/workspaceSyncEngineTransfers.d.ts +1 -1
  146. package/dist/sync/engine/workspaceSyncEngineTransfers.js +8 -3
  147. package/dist/sync/syncService.d.ts +2 -0
  148. package/dist/sync/syncService.js +4 -1
  149. package/dist/sync/v3/combinedFeedCapability.js +4 -0
  150. package/dist/sync/v3/durableTaskAttachmentLinksMutationRouter.d.ts +2 -0
  151. package/dist/sync/v3/durableTaskAttachmentLinksMutationRouter.js +11 -1
  152. package/dist/sync/v3/durableTaskChecklistMutationRouter.d.ts +2 -0
  153. package/dist/sync/v3/durableTaskChecklistMutationRouter.js +4 -0
  154. package/dist/sync/v3/durableTaskHttpBulkMutationRouter.d.ts +1 -0
  155. package/dist/sync/v3/durableTaskHttpBulkMutationRouter.js +30 -8
  156. package/dist/sync/v3/durableTaskHttpRootMutationRouter.d.ts +5 -3
  157. package/dist/sync/v3/durableTaskHttpRootMutationRouter.js +27 -2
  158. package/dist/sync/v3/localTaskAttachmentLinksOutboxService.d.ts +2 -0
  159. package/dist/sync/v3/localTaskAttachmentLinksOutboxService.js +47 -7
  160. package/dist/sync/v3/localTaskCreateOutboxService.js +15 -6
  161. package/dist/sync/v3/localTaskMetadataClient.d.ts +1 -0
  162. package/dist/sync/v3/localTaskMetadataClient.js +1 -0
  163. package/dist/sync/v3/localTaskMetadataOutboxHandler.js +5 -1
  164. package/dist/sync/v3/localTaskMetadataOutboxService.d.ts +2 -0
  165. package/dist/sync/v3/localTaskMetadataOutboxService.js +7 -0
  166. package/dist/sync/v3/localTaskRestoreOutboxService.js +14 -6
  167. package/dist/sync/v3/localTaskStatusClient.d.ts +1 -0
  168. package/dist/sync/v3/localTaskStatusClient.js +6 -1
  169. package/dist/sync/v3/localTaskStatusOutboxService.d.ts +2 -0
  170. package/dist/sync/v3/localTaskStatusOutboxService.js +54 -13
  171. package/dist/sync/v3/syncDurabilityStore.d.ts +7 -0
  172. package/dist/sync/v3/syncDurabilityStore.js +31 -11
  173. package/dist/sync/v3/taskAttachmentLinksMutationService.d.ts +6 -4
  174. package/dist/sync/v3/taskAttachmentLinksMutationService.js +19 -10
  175. package/dist/sync/v3/taskChecklistMutationService.d.ts +4 -2
  176. package/dist/sync/v3/taskChecklistMutationService.js +4 -0
  177. package/dist/sync/v3/taskCreateMutationService.d.ts +2 -2
  178. package/dist/sync/v3/taskCreateMutationService.js +7 -4
  179. package/dist/sync/v3/taskMetadataMutationService.d.ts +3 -0
  180. package/dist/sync/v3/taskMetadataMutationService.js +4 -0
  181. package/dist/sync/v3/taskRestoreMutationService.d.ts +2 -2
  182. package/dist/sync/v3/taskRestoreMutationService.js +7 -4
  183. package/dist/sync/v3/taskStatusMutationService.d.ts +2 -0
  184. package/dist/sync/v3/taskStatusMutationService.js +4 -0
  185. package/dist/sync/v3/workflowAssignmentSync.d.ts +1 -0
  186. package/dist/sync/v3/workflowAssignmentSync.js +3 -0
  187. package/dist/sync/v3/workspaceSyncV2ProjectionExclusions.d.ts +1 -1
  188. package/dist/sync/v3/workspaceSyncV2ProjectionExclusions.js +13 -3
  189. package/dist/sync/v3/workspaceSyncV3AttachmentApplyObligationStore.d.ts +11 -0
  190. package/dist/sync/v3/workspaceSyncV3AttachmentApplyObligationStore.js +27 -10
  191. package/dist/sync/v3/workspaceSyncV3AttachmentAssetHydration.d.ts +2 -1
  192. package/dist/sync/v3/workspaceSyncV3AttachmentAssetHydration.js +52 -10
  193. package/dist/sync/v3/workspaceSyncV3CloudOperationHandler.js +9 -1
  194. package/dist/sync/v3/workspaceSyncV3CombinedFeedOrchestrator.js +8 -3
  195. package/dist/sync/v3/workspaceSyncV3CombinedLocalApply.js +51 -10
  196. package/dist/sync/v3/workspaceSyncV3CombinedRepairBaseline.d.ts +7 -0
  197. package/dist/sync/v3/workspaceSyncV3CombinedRepairBaseline.js +30 -18
  198. package/dist/sync/v3/workspaceSyncV3CombinedRepairMaterialization.d.ts +9 -4
  199. package/dist/sync/v3/workspaceSyncV3CombinedRepairMaterialization.js +98 -47
  200. package/dist/sync/v3/workspaceSyncV3CombinedRepairSnapshot.d.ts +10 -0
  201. package/dist/sync/v3/workspaceSyncV3CombinedRepairSnapshot.js +58 -21
  202. package/dist/sync/v3/workspaceSyncV3CombinedWireContract.d.ts +3 -1
  203. package/dist/sync/v3/workspaceSyncV3CombinedWireContract.js +18 -0
  204. package/dist/sync/v3/workspaceSyncV3CoverageRegistry.js +6 -0
  205. package/dist/sync/v3/workspaceSyncV3FeedProfile.d.ts +1 -1
  206. package/dist/sync/v3/workspaceSyncV3LegacyBoundaryAttestation.d.ts +1 -0
  207. package/dist/sync/v3/workspaceSyncV3LegacyBoundaryAttestation.js +23 -10
  208. package/dist/sync/v3/workspaceSyncV3NestedTaskApply.d.ts +3 -0
  209. package/dist/sync/v3/workspaceSyncV3NestedTaskApply.js +64 -13
  210. package/dist/sync/v3/workspaceSyncV3NestedTaskCodecs.d.ts +9 -0
  211. package/dist/sync/v3/workspaceSyncV3NestedTaskCodecs.js +26 -0
  212. package/dist/sync/v3/workspaceSyncV3NestedTaskProjection.d.ts +15 -5
  213. package/dist/sync/v3/workspaceSyncV3NestedTaskProjection.js +50 -14
  214. package/dist/sync/v3/workspaceSyncV3NestedTaskRepairSnapshot.d.ts +2 -1
  215. package/dist/sync/v3/workspaceSyncV3NestedTaskRepairSnapshot.js +11 -3
  216. package/dist/sync/v3/workspaceSyncV3OperationProtocol.d.ts +2 -0
  217. package/dist/sync/v3/workspaceSyncV3OperatorDiagnostics.d.ts +2 -0
  218. package/dist/sync/v3/workspaceSyncV3OperatorDiagnostics.js +19 -7
  219. package/dist/sync/v3/workspaceSyncV3TaskCommentAudit.js +9 -2
  220. package/dist/sync/v3/workspaceSyncV3TaskCoverCoverage.d.ts +7 -0
  221. package/dist/sync/v3/workspaceSyncV3TaskCoverCoverage.js +37 -0
  222. package/dist/sync/v3/workspaceSyncV3TaskCoverWireContract.d.ts +6 -0
  223. package/dist/sync/v3/workspaceSyncV3TaskCoverWireContract.js +9 -0
  224. package/dist/sync/workspacePullFeed.js +2 -1
  225. package/dist/sync/workspaceRepair.d.ts +3 -0
  226. package/dist/sync/workspaceRepair.js +67 -6
  227. package/dist/types.d.ts +47 -1
  228. package/dist/types.js +6 -0
  229. package/dist/ui/assets/{AiIdentityRosterCard-MZs7lVED.js → AiIdentityRosterCard-BZM4aQnM.js} +1 -1
  230. package/dist/ui/assets/AiProfilesModule-BbAEWtHH.js +1 -0
  231. package/dist/ui/assets/AnnotatedAttachmentWorkspace-B68Wg-RV.js +3 -0
  232. package/dist/ui/assets/AnnotatedAttachmentWorkspace-TzYu7pkG.css +1 -0
  233. package/dist/ui/assets/{AssetTraySortControl-Kf1pelSy.js → AssetTraySortControl-C8Rztyal.js} +1 -1
  234. package/dist/ui/assets/{ContextAttachmentManager-B8boOs6Q.js → ContextAttachmentManager-Cj3c7X9y.js} +2 -2
  235. package/dist/ui/assets/{DocumentWorkspace-DXH4XUsB.js → DocumentWorkspace-B5uvzems.js} +1 -1
  236. package/dist/ui/assets/{EntityActivityTimeline-BtSTq0ON.js → EntityActivityTimeline-BVFvCB5R.js} +1 -1
  237. package/dist/ui/assets/PlanningModule-B5DWQOGz.js +1 -0
  238. package/dist/ui/assets/PlansPage-AY4cGWco.js +1 -0
  239. package/dist/ui/assets/TaskContextUpload-DZxTVesW.js +1 -0
  240. package/dist/ui/assets/TaskSettings-BeWAuyZj.js +12 -0
  241. package/dist/ui/assets/{TaskforceAgentsModule-BazdUwxZ.js → TaskforceAgentsModule-BifCYtjR.js} +6 -6
  242. package/dist/ui/assets/{WorkflowManagerModule-DWFspC-o.js → WorkflowManagerModule-DEr5di_r.js} +1 -1
  243. package/dist/ui/assets/index-I5zhdtgt.js +7 -0
  244. package/dist/ui/assets/index-VRFsx3wz.css +1 -0
  245. package/dist/ui/assets/{vendor-icons-BkFLXavV.js → vendor-icons-D9Lpw-j4.js} +1 -1
  246. package/dist/ui/index.html +3 -3
  247. package/dist/utils/taskActivity.d.ts +3 -2
  248. package/dist/utils/taskActivity.js +19 -2
  249. package/dist/utils/taskEditorReconciliation.d.ts +40 -0
  250. package/dist/utils/taskEditorReconciliation.js +217 -0
  251. package/dist/utils/taskNormalization.d.ts +1 -0
  252. package/dist/utils/taskNormalization.js +39 -0
  253. package/dist/utils/taskSearch.js +2 -2
  254. package/dist/utils/uiNotice.d.ts +4 -0
  255. package/package.json +16 -4
  256. package/dist/ui/assets/AiProfilesModule-C3i0LOxe.js +0 -1
  257. package/dist/ui/assets/AnnotatedAttachmentWorkspace-CBRZ8rca.css +0 -1
  258. package/dist/ui/assets/AnnotatedAttachmentWorkspace-DVZk5Tln.js +0 -3
  259. package/dist/ui/assets/PlanningModule-BPVyEa-f.js +0 -1
  260. package/dist/ui/assets/PlansPage-ChqgxALc.js +0 -1
  261. package/dist/ui/assets/TaskContextUpload-CB3ol0sN.js +0 -1
  262. package/dist/ui/assets/TaskSettings-g4ECKNOz.js +0 -12
  263. package/dist/ui/assets/index-BUplSsv_.js +0 -7
  264. package/dist/ui/assets/index-gz2EXuoK.css +0 -1
@@ -4,7 +4,7 @@ import { normalizeTaskFromApi } from '../../utils/taskNormalization.js';
4
4
  import { createCloudTaskHttpMutationClient, fetchTaskHttpMutation, isConclusiveTaskHttpResponse, } from './taskHttpMutation.js';
5
5
  import { canonicalWorkspaceSyncJson } from '../../sync/v3/canonicalWorkspaceSyncJson.js';
6
6
  export function useTaskFormActions(input) {
7
- const { activeCategories, activeTab, apiEndpoint, assignee, attachments, attachmentsDirty, category, checklistItems, comments, complexity, description, dueDate, createTaskDurably, durableTaskHttpRootActivationEnabled = false, workspaceId, taskHttpMutationClient, updateTask, editingTaskId, fetchTasks, formTaxonomies, getAttachmentsDraft: getAttachmentsDraftInput, getDescriptionDraft: getDescriptionDraftInput, getPreferredCategoryValue, getTitleDraft: getTitleDraftInput, awaitPendingDescriptionImageUpload: awaitPendingDescriptionImageUploadInput, mergeTaskFromServer, onTaskCreated, onTaskCreateAccepted, workstreamInput, priority, pushNotice, queueWorkspaceSyncFromAuthoritativeTaskState, relationshipTasks, supplementalTasks = [], resetForm, resolveWorkstreamIdInput, scheduledDate, setActiveTab, setAttachmentsDirty, setComments, setError, setLastUsedCategory, setLoading, setNewCommentText, status, title, taxonomies, textDraftOccupied, type } = input;
7
+ const { activeCategories, activeTab, apiEndpoint, assignee, attachments, attachmentsDirty, category, checklistItems, comments, complexity, description, dueDate, createTaskDurably, durableTaskHttpRootActivationEnabled = false, workspaceId, taskHttpMutationClient, updateTask, editingTaskId, editorBaselineDraft = null, revisionConflict = false, fetchTasks, formTaxonomies, getAttachmentsDraft: getAttachmentsDraftInput, getDescriptionDraft: getDescriptionDraftInput, getPreferredCategoryValue, getTitleDraft: getTitleDraftInput, awaitPendingDescriptionImageUpload: awaitPendingDescriptionImageUploadInput, mergeTaskFromServer, onTaskCreated, onTaskCreateAccepted, workstreamInput, priority, pushNotice, queueWorkspaceSyncFromAuthoritativeTaskState, relationshipTasks, supplementalTasks = [], resetForm, resolveWorkstreamIdInput, scheduledDate, setActiveTab, setAttachmentsDirty, setComments, setError, setLastUsedCategory, setLoading, setNewCommentText, status, title, taxonomies, textDraftOccupied, type } = input;
8
8
  const getTitleDraft = getTitleDraftInput || (() => title);
9
9
  const getDescriptionDraft = getDescriptionDraftInput || (() => description);
10
10
  const getAttachmentsDraft = getAttachmentsDraftInput || (() => attachments);
@@ -53,29 +53,46 @@ export function useTaskFormActions(input) {
53
53
  }
54
54
  createDraftOccupiedRef.current = createDraftOccupied;
55
55
  }, [createDraftOccupied, createTaskDurably, workspaceId]);
56
- const buildTaskDraftSignature = useCallback((textDraft = readTextDraft(), attachmentDraft = getAttachmentsDraftRef.current()) => JSON.stringify({
57
- title: textDraft.title.trim(),
56
+ const buildTaskDraftSignatureFromEditorDraft = useCallback((draft) => JSON.stringify({
57
+ title: draft.title.trim(),
58
+ description: draft.description,
59
+ category: draft.category,
60
+ type: draft.type,
61
+ priority: Number(draft.priority),
62
+ complexity: Number(draft.complexity) || 3,
63
+ assignee: draft.assignee || 'agent',
64
+ scheduledDate: draft.scheduledDate || '',
65
+ dueDate: draft.dueDate || '',
66
+ workstreamId: draft.workstreamId || '',
67
+ checklistItems: draft.checklistItems,
68
+ taxonomies: draft.taxonomies,
69
+ attachments: draft.attachments,
70
+ }), []);
71
+ const buildTaskDraftSignature = useCallback((textDraft = readTextDraft(), attachmentDraft = getAttachmentsDraftRef.current()) => buildTaskDraftSignatureFromEditorDraft({
72
+ title: textDraft.title,
58
73
  description: textDraft.description,
59
74
  category: typeof category === 'string'
60
75
  ? category
61
76
  : category.label || getPreferredCategoryValue(activeCategories),
62
77
  type: typeof type === 'string' ? type : type.label || DEFAULT_TYPE_VALUE,
63
- priority: Number(priority),
78
+ priority,
64
79
  complexity: Number(complexity) || 3,
80
+ status,
65
81
  assignee: assignee || 'agent',
66
- scheduledDate: scheduledDate || '',
67
- dueDate: dueDate || '',
68
- workstreamInput: workstreamInput.trim(),
82
+ scheduledDate,
83
+ dueDate,
84
+ workstreamId: workstreamInput.trim()
85
+ ? (resolveWorkstreamIdInput(workstreamInput)?.id || workstreamInput.trim())
86
+ : '',
69
87
  checklistItems,
70
- comments,
71
88
  taxonomies: formTaxonomies,
72
89
  attachments: attachmentDraft,
73
90
  }), [
74
91
  activeCategories,
75
92
  assignee,
93
+ buildTaskDraftSignatureFromEditorDraft,
76
94
  category,
77
95
  checklistItems,
78
- comments,
79
96
  complexity,
80
97
  description,
81
98
  dueDate,
@@ -83,7 +100,9 @@ export function useTaskFormActions(input) {
83
100
  getPreferredCategoryValue,
84
101
  priority,
85
102
  readTextDraft,
103
+ resolveWorkstreamIdInput,
86
104
  scheduledDate,
105
+ status,
87
106
  title,
88
107
  type,
89
108
  workstreamInput
@@ -94,6 +113,17 @@ export function useTaskFormActions(input) {
94
113
  autoSaveTimerRef.current = null;
95
114
  }
96
115
  }, []);
116
+ const hasUnsavedChangesNow = useCallback(() => Boolean(editingTaskId
117
+ && lastSavedDraftSignature !== null
118
+ && buildTaskDraftSignature() !== lastSavedDraftSignature), [buildTaskDraftSignature, editingTaskId, lastSavedDraftSignature]);
119
+ const hasUnsavedChanges = hasUnsavedChangesNow();
120
+ useEffect(() => {
121
+ if (!revisionConflict)
122
+ return;
123
+ clearAutoSaveTimer();
124
+ queuedAutoSaveRef.current = null;
125
+ setAutoSaveState('idle');
126
+ }, [clearAutoSaveTimer, revisionConflict]);
97
127
  const enqueueAutoSave = useCallback((request) => {
98
128
  if (activeAutoSaveSignatureRef.current === request.signature
99
129
  && queuedAutoSaveRef.current === null
@@ -127,6 +157,11 @@ export function useTaskFormActions(input) {
127
157
  const saveTask = useCallback(async (options) => {
128
158
  const quiet = options?.quiet ?? false;
129
159
  const source = options?.source ?? 'manual';
160
+ if (editingTaskId && revisionConflict) {
161
+ setError('');
162
+ setAutoSaveState('idle');
163
+ return false;
164
+ }
130
165
  if (!editingTaskId && source === 'manual') {
131
166
  try {
132
167
  await awaitPendingDescriptionImageUploadRef.current?.();
@@ -240,6 +275,9 @@ export function useTaskFormActions(input) {
240
275
  }
241
276
  if (editingTaskId && Object.keys(body).length > 0) {
242
277
  body.saveSource = source;
278
+ if (existingTask?.detailRevision) {
279
+ body.expectedDetailRevision = existingTask.detailRevision;
280
+ }
243
281
  }
244
282
  if (editingTaskId && updateTask) {
245
283
  const saved = await updateTask(editingTaskId, body);
@@ -413,6 +451,7 @@ export function useTaskFormActions(input) {
413
451
  taskHttpMutationClient,
414
452
  updateTask,
415
453
  editingTaskId,
454
+ revisionConflict,
416
455
  editingReadOnlyMode,
417
456
  formTaxonomies,
418
457
  getPreferredCategoryValue,
@@ -468,14 +507,23 @@ export function useTaskFormActions(input) {
468
507
  setAutoSaveState('idle');
469
508
  return;
470
509
  }
471
- setLastSavedDraftSignature(buildTaskDraftSignature());
510
+ setLastSavedDraftSignature(editorBaselineDraft
511
+ ? buildTaskDraftSignatureFromEditorDraft(editorBaselineDraft)
512
+ : buildTaskDraftSignature());
472
513
  setAutoSaveState('idle');
473
- }, [clearAutoSaveTimer, editingTaskId]);
514
+ }, [
515
+ buildTaskDraftSignatureFromEditorDraft,
516
+ clearAutoSaveTimer,
517
+ editingTaskId,
518
+ editorBaselineDraft,
519
+ ]);
474
520
  const flushAutoSave = useCallback(async () => {
475
521
  clearAutoSaveTimer();
476
522
  if (editingReadOnlyMode) {
477
523
  return true;
478
524
  }
525
+ if (revisionConflict)
526
+ return false;
479
527
  if (!editingTaskId || lastSavedDraftSignature === null) {
480
528
  return true;
481
529
  }
@@ -501,11 +549,12 @@ export function useTaskFormActions(input) {
501
549
  enqueueAutoSave,
502
550
  lastSavedDraftSignature,
503
551
  readTextDraft,
552
+ revisionConflict,
504
553
  saveTask,
505
554
  scheduledDate,
506
555
  ]);
507
556
  useEffect(() => {
508
- if (!editingTaskId || editingReadOnlyMode || lastSavedDraftSignature === null) {
557
+ if (!editingTaskId || editingReadOnlyMode || revisionConflict || lastSavedDraftSignature === null) {
509
558
  return;
510
559
  }
511
560
  const textDraft = readTextDraft();
@@ -551,6 +600,7 @@ export function useTaskFormActions(input) {
551
600
  enqueueAutoSave,
552
601
  lastSavedDraftSignature,
553
602
  readTextDraft,
603
+ revisionConflict,
554
604
  saveTask,
555
605
  scheduledDate,
556
606
  ]);
@@ -637,6 +687,9 @@ export function useTaskFormActions(input) {
637
687
  ]);
638
688
  return {
639
689
  autoSaveState,
690
+ hasUnsavedChanges,
691
+ hasUnsavedChangesNow,
692
+ hasPendingAutoSave: autoSaveState === 'saving',
640
693
  flushAutoSave,
641
694
  scheduleWarningPrompt,
642
695
  saveTask,
@@ -29,6 +29,11 @@ type UseSettingsModelInput = {
29
29
  manualComplexityEnabled: boolean;
30
30
  checklistDropdownEnabled: boolean;
31
31
  showTaskCardStatusLabel: boolean;
32
+ showTaskCardDescription: boolean;
33
+ showTaskCardAttachments: boolean;
34
+ showTaskCardChecklistProgress: boolean;
35
+ showTaskCardLatestActivity: boolean;
36
+ tintTaskCardActivityBackground: boolean;
32
37
  pathSaved: boolean;
33
38
  settingsSection: SettingsSection;
34
39
  setupState: any;
@@ -38,15 +43,21 @@ type UseSettingsModelInput = {
38
43
  setCurrentTheme: (value: any) => void;
39
44
  handleSaveTheme: (...args: any[]) => any;
40
45
  handleSaveGlobalTheme: (...args: any[]) => any;
46
+ handleUseGlobalThemeDefault: (...args: any[]) => any;
41
47
  setKeyShortcut: (value: string) => void;
42
48
  handleJsonBackupEnabledChange: (...args: any[]) => any;
43
49
  handleSaveGlobalJsonBackupEnabled: (...args: any[]) => any;
50
+ handleUseGlobalJsonBackupDefault: (...args: any[]) => any;
44
51
  handleSaveGlobalWeekStartsOn: (...args: any[]) => any;
45
52
  handleSaveLocale: (...args: any[]) => any;
46
53
  handleManualComplexityEnabledChange: (...args: any[]) => any;
47
54
  handleChecklistDropdownEnabledChange: (...args: any[]) => any;
48
55
  handleShowTaskCardStatusLabelChange: (...args: any[]) => any;
49
- handleResetProjectToGlobal: (...args: any[]) => any;
56
+ handleShowTaskCardDescriptionChange: (...args: any[]) => any;
57
+ handleShowTaskCardAttachmentsChange: (...args: any[]) => any;
58
+ handleShowTaskCardChecklistProgressChange: (...args: any[]) => any;
59
+ handleShowTaskCardLatestActivityChange: (...args: any[]) => any;
60
+ handleTintTaskCardActivityBackgroundChange: (...args: any[]) => any;
50
61
  handleSaveSettings: (...args: any[]) => any;
51
62
  initiativeTemplates: any[];
52
63
  fetchInitiativeTemplates: (...args: any[]) => any;
@@ -109,6 +120,11 @@ export declare function useSettingsModel(input: UseSettingsModelInput): {
109
120
  manualComplexityEnabled: boolean;
110
121
  checklistDropdownEnabled: boolean;
111
122
  showTaskCardStatusLabel: boolean;
123
+ showTaskCardDescription: boolean;
124
+ showTaskCardAttachments: boolean;
125
+ showTaskCardChecklistProgress: boolean;
126
+ showTaskCardLatestActivity: boolean;
127
+ tintTaskCardActivityBackground: boolean;
112
128
  pathSaved: boolean;
113
129
  initialSection: SettingsSection;
114
130
  setupState: any;
@@ -118,15 +134,21 @@ export declare function useSettingsModel(input: UseSettingsModelInput): {
118
134
  onThemeChange: (value: any) => void;
119
135
  onSaveTheme: (...args: any[]) => any;
120
136
  onSaveGlobalTheme: (...args: any[]) => any;
137
+ onUseGlobalThemeDefault: (...args: any[]) => any;
121
138
  onKeyShortcutChange: (value: string) => void;
122
139
  onJsonBackupEnabledChange: (...args: any[]) => any;
123
140
  onSaveGlobalJsonBackupEnabled: (...args: any[]) => any;
141
+ onUseGlobalJsonBackupDefault: (...args: any[]) => any;
124
142
  onSaveGlobalWeekStartsOn: (...args: any[]) => any;
125
143
  onSaveLocale: (...args: any[]) => any;
126
144
  onManualComplexityEnabledChange: (...args: any[]) => any;
127
145
  onChecklistDropdownEnabledChange: (...args: any[]) => any;
128
146
  onShowTaskCardStatusLabelChange: (...args: any[]) => any;
129
- onResetProjectToGlobal: (...args: any[]) => any;
147
+ onShowTaskCardDescriptionChange: (...args: any[]) => any;
148
+ onShowTaskCardAttachmentsChange: (...args: any[]) => any;
149
+ onShowTaskCardChecklistProgressChange: (...args: any[]) => any;
150
+ onShowTaskCardLatestActivityChange: (...args: any[]) => any;
151
+ onTintTaskCardActivityBackgroundChange: (...args: any[]) => any;
130
152
  onSaveSettings: (...args: any[]) => any;
131
153
  initiativeTemplates: any[];
132
154
  onRefreshInitiativeTemplates: (...args: any[]) => any;
@@ -1,6 +1,6 @@
1
1
  import { useCallback } from 'react';
2
2
  export function useSettingsModel(input) {
3
- const { resolveCloudAuthUrl, currentWorkspaceId, normalizedCloudAuthBaseUrl, normalizedCloudMcpBaseUrl, mergedConfig, availableWorkspaces, currentTheme, configLoaded, globalTheme, themeUseGlobalDefault, keyShortcut, jsonBackupEnabled, globalJsonBackupEnabled, globalWeekStartsOn, locale, supportedLocales, jsonBackupUseGlobalDefault, manualComplexityEnabled, checklistDropdownEnabled, showTaskCardStatusLabel, pathSaved, settingsSection, setupState, buildInfo, saveSetupMode, saveWorkspaceProfile, setCurrentTheme, handleSaveTheme, handleSaveGlobalTheme, setKeyShortcut, handleJsonBackupEnabledChange, handleSaveGlobalJsonBackupEnabled, handleSaveGlobalWeekStartsOn, handleSaveLocale, handleManualComplexityEnabledChange, handleChecklistDropdownEnabledChange, handleShowTaskCardStatusLabelChange, handleResetProjectToGlobal, handleSaveSettings, initiativeTemplates, fetchInitiativeTemplates, createInitiativeFromTemplate, setShowFolderBrowser, setBrowserTarget, fetchFolders, activeCategories, pathValidation, taxonomyDisplayLabels, handleUpdateCategory, handleRemoveCategory, handleSaveCategory, handleAddPath, handleRemovePath, handleUpdateCategoryIcon, handleUpdateCategoryColor, activeTypes, handleSaveType, handleRemoveType, handleUpdateType, taxonomies, handleUpdateTaxonomies, priorities, handleUpdatePriorities, analyzeSystemTaxonomyPack, handleApplySystemTaxonomyPack, handleUpdateTaxonomyDisplayLabels, projectRoot, projectName, mcpHostRoot, serverHostRoot, mcpScriptPath, tenantId, runtimeMode, workspaceSwitchingEnabled, deleteWorkspace, setMcpHostRoot, isAuthenticated } = input;
3
+ const { resolveCloudAuthUrl, currentWorkspaceId, normalizedCloudAuthBaseUrl, normalizedCloudMcpBaseUrl, mergedConfig, availableWorkspaces, currentTheme, configLoaded, globalTheme, themeUseGlobalDefault, keyShortcut, jsonBackupEnabled, globalJsonBackupEnabled, globalWeekStartsOn, locale, supportedLocales, jsonBackupUseGlobalDefault, manualComplexityEnabled, checklistDropdownEnabled, showTaskCardStatusLabel, showTaskCardDescription, showTaskCardAttachments, showTaskCardChecklistProgress, showTaskCardLatestActivity, tintTaskCardActivityBackground, pathSaved, settingsSection, setupState, buildInfo, saveSetupMode, saveWorkspaceProfile, setCurrentTheme, handleSaveTheme, handleSaveGlobalTheme, handleUseGlobalThemeDefault, setKeyShortcut, handleJsonBackupEnabledChange, handleSaveGlobalJsonBackupEnabled, handleUseGlobalJsonBackupDefault, handleSaveGlobalWeekStartsOn, handleSaveLocale, handleManualComplexityEnabledChange, handleChecklistDropdownEnabledChange, handleShowTaskCardStatusLabelChange, handleShowTaskCardDescriptionChange, handleShowTaskCardAttachmentsChange, handleShowTaskCardChecklistProgressChange, handleShowTaskCardLatestActivityChange, handleTintTaskCardActivityBackgroundChange, handleSaveSettings, initiativeTemplates, fetchInitiativeTemplates, createInitiativeFromTemplate, setShowFolderBrowser, setBrowserTarget, fetchFolders, activeCategories, pathValidation, taxonomyDisplayLabels, handleUpdateCategory, handleRemoveCategory, handleSaveCategory, handleAddPath, handleRemovePath, handleUpdateCategoryIcon, handleUpdateCategoryColor, activeTypes, handleSaveType, handleRemoveType, handleUpdateType, taxonomies, handleUpdateTaxonomies, priorities, handleUpdatePriorities, analyzeSystemTaxonomyPack, handleApplySystemTaxonomyPack, handleUpdateTaxonomyDisplayLabels, projectRoot, projectName, mcpHostRoot, serverHostRoot, mcpScriptPath, tenantId, runtimeMode, workspaceSwitchingEnabled, deleteWorkspace, setMcpHostRoot, isAuthenticated } = input;
4
4
  const fetchCloudAuthApi = useCallback(async (path, init) => {
5
5
  const resolvedUrl = resolveCloudAuthUrl(path);
6
6
  const headers = new Headers(init?.headers || undefined);
@@ -75,6 +75,11 @@ export function useSettingsModel(input) {
75
75
  manualComplexityEnabled,
76
76
  checklistDropdownEnabled,
77
77
  showTaskCardStatusLabel,
78
+ showTaskCardDescription,
79
+ showTaskCardAttachments,
80
+ showTaskCardChecklistProgress,
81
+ showTaskCardLatestActivity,
82
+ tintTaskCardActivityBackground,
78
83
  pathSaved,
79
84
  initialSection: settingsSection,
80
85
  setupState,
@@ -84,15 +89,21 @@ export function useSettingsModel(input) {
84
89
  onThemeChange: setCurrentTheme,
85
90
  onSaveTheme: handleSaveTheme,
86
91
  onSaveGlobalTheme: handleSaveGlobalTheme,
92
+ onUseGlobalThemeDefault: handleUseGlobalThemeDefault,
87
93
  onKeyShortcutChange: setKeyShortcut,
88
94
  onJsonBackupEnabledChange: handleJsonBackupEnabledChange,
89
95
  onSaveGlobalJsonBackupEnabled: handleSaveGlobalJsonBackupEnabled,
96
+ onUseGlobalJsonBackupDefault: handleUseGlobalJsonBackupDefault,
90
97
  onSaveGlobalWeekStartsOn: handleSaveGlobalWeekStartsOn,
91
98
  onSaveLocale: handleSaveLocale,
92
99
  onManualComplexityEnabledChange: handleManualComplexityEnabledChange,
93
100
  onChecklistDropdownEnabledChange: handleChecklistDropdownEnabledChange,
94
101
  onShowTaskCardStatusLabelChange: handleShowTaskCardStatusLabelChange,
95
- onResetProjectToGlobal: handleResetProjectToGlobal,
102
+ onShowTaskCardDescriptionChange: handleShowTaskCardDescriptionChange,
103
+ onShowTaskCardAttachmentsChange: handleShowTaskCardAttachmentsChange,
104
+ onShowTaskCardChecklistProgressChange: handleShowTaskCardChecklistProgressChange,
105
+ onShowTaskCardLatestActivityChange: handleShowTaskCardLatestActivityChange,
106
+ onTintTaskCardActivityBackgroundChange: handleTintTaskCardActivityBackgroundChange,
96
107
  onSaveSettings: handleSaveSettings,
97
108
  initiativeTemplates,
98
109
  onRefreshInitiativeTemplates: fetchInitiativeTemplates,
@@ -18,18 +18,29 @@ interface UseSettingsPersistenceInput {
18
18
  setManualComplexityEnabled: (value: boolean) => void;
19
19
  setChecklistDropdownEnabled: (value: boolean) => void;
20
20
  setShowTaskCardStatusLabel: (value: boolean) => void;
21
+ setShowTaskCardDescription: (value: boolean) => void;
22
+ setShowTaskCardAttachments: (value: boolean) => void;
23
+ setShowTaskCardChecklistProgress: (value: boolean) => void;
24
+ setShowTaskCardLatestActivity: (value: boolean) => void;
25
+ setTintTaskCardActivityBackground: (value: boolean) => void;
21
26
  }
22
27
  export declare function useSettingsPersistence(input: UseSettingsPersistenceInput): {
23
28
  handleSaveSettings: () => Promise<boolean>;
24
29
  handleSaveTheme: (theme: TaskforceTheme) => Promise<boolean>;
25
30
  handleSaveGlobalTheme: (theme: TaskforceTheme) => Promise<boolean>;
31
+ handleUseGlobalThemeDefault: () => Promise<boolean>;
26
32
  handleJsonBackupEnabledChange: (enabled: boolean) => Promise<boolean>;
27
33
  handleSaveGlobalJsonBackupEnabled: (enabled: boolean) => Promise<boolean>;
34
+ handleUseGlobalJsonBackupDefault: () => Promise<boolean>;
28
35
  handleSaveGlobalWeekStartsOn: (weekStartsOn: WeekStartsOn) => Promise<boolean>;
29
36
  handleSaveLocale: (nextLocale: string) => Promise<boolean>;
30
37
  handleManualComplexityEnabledChange: (enabled: boolean) => Promise<boolean>;
31
38
  handleChecklistDropdownEnabledChange: (enabled: boolean) => Promise<boolean>;
32
39
  handleShowTaskCardStatusLabelChange: (enabled: boolean) => Promise<boolean>;
33
- handleResetProjectToGlobal: () => Promise<boolean>;
40
+ handleShowTaskCardDescriptionChange: (enabled: boolean) => Promise<boolean>;
41
+ handleShowTaskCardAttachmentsChange: (enabled: boolean) => Promise<boolean>;
42
+ handleShowTaskCardChecklistProgressChange: (enabled: boolean) => Promise<boolean>;
43
+ handleShowTaskCardLatestActivityChange: (enabled: boolean) => Promise<boolean>;
44
+ handleTintTaskCardActivityBackgroundChange: (enabled: boolean) => Promise<boolean>;
34
45
  };
35
46
  export {};
@@ -1,7 +1,7 @@
1
1
  import { useCallback } from 'react';
2
2
  import { setLocale as setAppLocale } from '../../localization';
3
3
  export function useSettingsPersistence(input) {
4
- const { keyShortcut, themeUseGlobalDefault, runtimeMode, jsonBackupUseGlobalDefault, globalTheme, globalJsonBackupEnabled, setCurrentTheme, setThemeUseGlobalDefault, setGlobalTheme, setJsonBackupEnabled, setJsonBackupUseGlobalDefault, setGlobalJsonBackupEnabled, setGlobalWeekStartsOn, setLocale, setManualComplexityEnabled, setChecklistDropdownEnabled, setShowTaskCardStatusLabel } = input;
4
+ const { keyShortcut, themeUseGlobalDefault, runtimeMode, jsonBackupUseGlobalDefault, globalTheme, globalJsonBackupEnabled, setCurrentTheme, setThemeUseGlobalDefault, setGlobalTheme, setJsonBackupEnabled, setJsonBackupUseGlobalDefault, setGlobalJsonBackupEnabled, setGlobalWeekStartsOn, setLocale, setManualComplexityEnabled, setChecklistDropdownEnabled, setShowTaskCardStatusLabel, setShowTaskCardDescription, setShowTaskCardAttachments, setShowTaskCardChecklistProgress, setShowTaskCardLatestActivity, setTintTaskCardActivityBackground } = input;
5
5
  const handleSaveSettings = useCallback(async () => {
6
6
  try {
7
7
  const globalRes = await fetch('/api/taskforce/global-settings', {
@@ -61,6 +61,25 @@ export function useSettingsPersistence(input) {
61
61
  return false;
62
62
  }
63
63
  }, [setGlobalTheme, themeUseGlobalDefault, setCurrentTheme]);
64
+ const handleUseGlobalThemeDefault = useCallback(async () => {
65
+ try {
66
+ const configRes = await fetch('/api/taskforce/config', {
67
+ method: 'POST',
68
+ headers: { 'Content-Type': 'application/json' },
69
+ body: JSON.stringify({ theme: null }),
70
+ });
71
+ if (!configRes.ok) {
72
+ throw new Error(`Failed to save config (${configRes.status})`);
73
+ }
74
+ setThemeUseGlobalDefault(true);
75
+ setCurrentTheme(globalTheme);
76
+ return true;
77
+ }
78
+ catch {
79
+ console.error('[Taskforce] Failed to use global theme default');
80
+ return false;
81
+ }
82
+ }, [globalTheme, setCurrentTheme, setThemeUseGlobalDefault]);
64
83
  const handleJsonBackupEnabledChange = useCallback(async (enabled) => {
65
84
  if (runtimeMode === 'cloud') {
66
85
  setJsonBackupEnabled(false);
@@ -113,6 +132,25 @@ export function useSettingsPersistence(input) {
113
132
  return false;
114
133
  }
115
134
  }, [runtimeMode, setGlobalJsonBackupEnabled, jsonBackupUseGlobalDefault, setJsonBackupEnabled]);
135
+ const handleUseGlobalJsonBackupDefault = useCallback(async () => {
136
+ try {
137
+ const configRes = await fetch('/api/taskforce/config', {
138
+ method: 'POST',
139
+ headers: { 'Content-Type': 'application/json' },
140
+ body: JSON.stringify({ jsonBackupEnabled: null }),
141
+ });
142
+ if (!configRes.ok) {
143
+ throw new Error(`Failed to save config (${configRes.status})`);
144
+ }
145
+ setJsonBackupUseGlobalDefault(true);
146
+ setJsonBackupEnabled(globalJsonBackupEnabled);
147
+ return true;
148
+ }
149
+ catch {
150
+ console.error('[Taskforce] Failed to use global backup default');
151
+ return false;
152
+ }
153
+ }, [globalJsonBackupEnabled, setJsonBackupEnabled, setJsonBackupUseGlobalDefault]);
116
154
  const handleSaveGlobalWeekStartsOn = useCallback(async (weekStartsOn) => {
117
155
  setGlobalWeekStartsOn(weekStartsOn);
118
156
  try {
@@ -176,43 +214,12 @@ export function useSettingsPersistence(input) {
176
214
  return false;
177
215
  }
178
216
  }, [setChecklistDropdownEnabled]);
179
- const handleShowTaskCardStatusLabelChange = useCallback(async (enabled) => {
180
- setShowTaskCardStatusLabel(enabled);
181
- try {
182
- const configRes = await fetch('/api/taskforce/config', {
183
- method: 'POST',
184
- headers: { 'Content-Type': 'application/json' },
185
- body: JSON.stringify({ showTaskCardStatusLabel: enabled }),
186
- });
187
- if (!configRes.ok) {
188
- throw new Error(`Failed to save config (${configRes.status})`);
189
- }
190
- return true;
191
- }
192
- catch {
193
- console.error('[Taskforce] Failed to save task card status label setting');
194
- return false;
195
- }
196
- }, [setShowTaskCardStatusLabel]);
197
- const handleResetProjectToGlobal = useCallback(async () => {
198
- setThemeUseGlobalDefault(true);
199
- setJsonBackupUseGlobalDefault(true);
200
- setCurrentTheme(globalTheme);
201
- setJsonBackupEnabled(globalJsonBackupEnabled);
202
- setManualComplexityEnabled(false);
203
- setChecklistDropdownEnabled(true);
204
- setShowTaskCardStatusLabel(false);
217
+ const saveTaskCardPreference = useCallback(async (key, enabled, errorMessage) => {
205
218
  try {
206
219
  const configRes = await fetch('/api/taskforce/config', {
207
220
  method: 'POST',
208
221
  headers: { 'Content-Type': 'application/json' },
209
- body: JSON.stringify({
210
- theme: null,
211
- jsonBackupEnabled: null,
212
- manualComplexityEnabled: null,
213
- checklistDropdownEnabled: null,
214
- showTaskCardStatusLabel: null
215
- }),
222
+ body: JSON.stringify({ [key]: enabled }),
216
223
  });
217
224
  if (!configRes.ok) {
218
225
  throw new Error(`Failed to save config (${configRes.status})`);
@@ -220,31 +227,51 @@ export function useSettingsPersistence(input) {
220
227
  return true;
221
228
  }
222
229
  catch {
223
- console.error('[Taskforce] Failed to reset project settings');
230
+ console.error(`[Taskforce] ${errorMessage}`);
224
231
  return false;
225
232
  }
226
- }, [
227
- setThemeUseGlobalDefault,
228
- setJsonBackupUseGlobalDefault,
229
- setCurrentTheme,
230
- globalTheme,
231
- setJsonBackupEnabled,
232
- globalJsonBackupEnabled,
233
- setManualComplexityEnabled,
234
- setChecklistDropdownEnabled,
235
- setShowTaskCardStatusLabel
236
- ]);
233
+ }, []);
234
+ const handleShowTaskCardStatusLabelChange = useCallback(async (enabled) => {
235
+ setShowTaskCardStatusLabel(enabled);
236
+ return saveTaskCardPreference('showTaskCardStatusLabel', enabled, 'Failed to save task card status label setting');
237
+ }, [saveTaskCardPreference, setShowTaskCardStatusLabel]);
238
+ const handleShowTaskCardDescriptionChange = useCallback(async (enabled) => {
239
+ setShowTaskCardDescription(enabled);
240
+ return saveTaskCardPreference('showTaskCardDescription', enabled, 'Failed to save task card description setting');
241
+ }, [saveTaskCardPreference, setShowTaskCardDescription]);
242
+ const handleShowTaskCardAttachmentsChange = useCallback(async (enabled) => {
243
+ setShowTaskCardAttachments(enabled);
244
+ return saveTaskCardPreference('showTaskCardAttachments', enabled, 'Failed to save task card attachments setting');
245
+ }, [saveTaskCardPreference, setShowTaskCardAttachments]);
246
+ const handleShowTaskCardChecklistProgressChange = useCallback(async (enabled) => {
247
+ setShowTaskCardChecklistProgress(enabled);
248
+ return saveTaskCardPreference('showTaskCardChecklistProgress', enabled, 'Failed to save task card checklist progress setting');
249
+ }, [saveTaskCardPreference, setShowTaskCardChecklistProgress]);
250
+ const handleShowTaskCardLatestActivityChange = useCallback(async (enabled) => {
251
+ setShowTaskCardLatestActivity(enabled);
252
+ return saveTaskCardPreference('showTaskCardLatestActivity', enabled, 'Failed to save task card latest activity setting');
253
+ }, [saveTaskCardPreference, setShowTaskCardLatestActivity]);
254
+ const handleTintTaskCardActivityBackgroundChange = useCallback(async (enabled) => {
255
+ setTintTaskCardActivityBackground(enabled);
256
+ return saveTaskCardPreference('tintTaskCardActivityBackground', enabled, 'Failed to save task card activity background setting');
257
+ }, [saveTaskCardPreference, setTintTaskCardActivityBackground]);
237
258
  return {
238
259
  handleSaveSettings,
239
260
  handleSaveTheme,
240
261
  handleSaveGlobalTheme,
262
+ handleUseGlobalThemeDefault,
241
263
  handleJsonBackupEnabledChange,
242
264
  handleSaveGlobalJsonBackupEnabled,
265
+ handleUseGlobalJsonBackupDefault,
243
266
  handleSaveGlobalWeekStartsOn,
244
267
  handleSaveLocale,
245
268
  handleManualComplexityEnabledChange,
246
269
  handleChecklistDropdownEnabledChange,
247
270
  handleShowTaskCardStatusLabelChange,
248
- handleResetProjectToGlobal
271
+ handleShowTaskCardDescriptionChange,
272
+ handleShowTaskCardAttachmentsChange,
273
+ handleShowTaskCardChecklistProgressChange,
274
+ handleShowTaskCardLatestActivityChange,
275
+ handleTintTaskCardActivityBackgroundChange
249
276
  };
250
277
  }
@@ -20,8 +20,12 @@ interface UseFilterSortProps {
20
20
  referenceDataLoaded: boolean;
21
21
  assigneeOptionsLoaded: boolean;
22
22
  assigneeOptions?: TaskAssigneeOption[];
23
+ runtimeMode?: 'local' | 'cloud';
24
+ workspaceId?: string;
25
+ cloudTaskSearchEnabled?: boolean;
26
+ cloudTaskSearchScope?: 'active' | 'archive';
23
27
  }
24
- export declare function useFilterSort({ tasks, archivedTasks, activeCategories, activeTypes, priorities, taxonomies, configLoaded, referenceDataLoaded, assigneeOptionsLoaded, assigneeOptions }: UseFilterSortProps): {
28
+ export declare function useFilterSort({ tasks, archivedTasks, activeCategories, activeTypes, priorities, taxonomies, configLoaded, referenceDataLoaded, assigneeOptionsLoaded, assigneeOptions, runtimeMode, workspaceId, cloudTaskSearchEnabled, cloudTaskSearchScope, }: UseFilterSortProps): {
25
29
  searchQuery: string;
26
30
  setSearchQuery: import("react").Dispatch<import("react").SetStateAction<string>>;
27
31
  filterCategories: string[];
@@ -45,8 +49,8 @@ export declare function useFilterSort({ tasks, archivedTasks, activeCategories,
45
49
  sortOrder: "asc" | "desc";
46
50
  setSortOrder: import("react").Dispatch<import("react").SetStateAction<"asc" | "desc">>;
47
51
  toggleSortOrder: () => void;
48
- groupBy: "category" | "type" | "priority" | "status" | "complexity" | "assignee" | "schedule";
49
- setGroupBy: import("react").Dispatch<import("react").SetStateAction<"category" | "type" | "priority" | "status" | "complexity" | "assignee" | "schedule">>;
52
+ groupBy: "category" | "type" | "priority" | "complexity" | "status" | "assignee" | "schedule";
53
+ setGroupBy: import("react").Dispatch<import("react").SetStateAction<"category" | "type" | "priority" | "complexity" | "status" | "assignee" | "schedule">>;
50
54
  emptyColumnMode: "show" | "collapse" | "hide";
51
55
  setEmptyColumnMode: import("react").Dispatch<import("react").SetStateAction<"show" | "collapse" | "hide">>;
52
56
  collapsedCategories: Record<string, boolean>;
@@ -29,8 +29,9 @@ import { normalizeTaskSearchQuery, taskMatchesSearch } from '../utils/taskSearch
29
29
  import { compareTasksBySortKey } from '../utils/taxonomySorting.js';
30
30
  import { getFilterVisibleTaxonomyOptions } from '../utils/taxonomyFilterOptions.js';
31
31
  import { getVisibleTaxonomiesForTasks } from '../utils/taxonomyLifecycle.js';
32
+ import { useCloudTaskSearch } from './tasks/useCloudTaskSearch';
32
33
  // === SECTION: Filter State ===
33
- export function useFilterSort({ tasks, archivedTasks, activeCategories, activeTypes, priorities, taxonomies, configLoaded, referenceDataLoaded, assigneeOptionsLoaded, assigneeOptions }) {
34
+ export function useFilterSort({ tasks, archivedTasks, activeCategories, activeTypes, priorities, taxonomies, configLoaded, referenceDataLoaded, assigneeOptionsLoaded, assigneeOptions, runtimeMode = 'local', workspaceId = '', cloudTaskSearchEnabled = false, cloudTaskSearchScope = 'active', }) {
34
35
  const relationshipTasks = useMemo(() => [...tasks, ...archivedTasks], [archivedTasks, tasks]);
35
36
  const resolvedAssigneeOptions = useMemo(() => normalizeAssigneeOptions(assigneeOptions), [assigneeOptions]);
36
37
  const filterableTaxonomies = useMemo(() => getVisibleTaxonomiesForTasks(taxonomies, relationshipTasks)
@@ -42,6 +43,13 @@ export function useFilterSort({ tasks, archivedTasks, activeCategories, activeTy
42
43
  const filterableTaxonomyById = useMemo(() => new Map(filterableTaxonomies.map((taxonomy) => [taxonomy.id, taxonomy])), [filterableTaxonomies]);
43
44
  const assigneeFilterValues = useMemo(() => resolvedAssigneeOptions.map((opt) => opt.value), [resolvedAssigneeOptions]);
44
45
  const [searchQuery, setSearchQuery] = useState('');
46
+ const cloudSearchMatchIds = useCloudTaskSearch({
47
+ query: searchQuery,
48
+ runtimeMode,
49
+ workspaceId,
50
+ enabled: cloudTaskSearchEnabled,
51
+ scope: cloudTaskSearchScope,
52
+ });
45
53
  const [filterCategories, setFilterCategories] = useState([]);
46
54
  const [filterPriorities, setFilterPriorities] = useState([]);
47
55
  const [filterTypes, setFilterTypes] = useState([]);
@@ -229,7 +237,8 @@ export function useFilterSort({ tasks, archivedTasks, activeCategories, activeTy
229
237
  const isAllPrioritiesSelected = isAllFilterValuesSelected(Array.from(new Set(filterPriorities.map((value) => Number(value)).filter((value) => Number.isFinite(value)))), priorities.map((priority) => Number(priority.value)).filter((value) => Number.isFinite(value)));
230
238
  const isAllStatusesSelected = isAllFilterValuesSelected(filterStatus, STATUS_OPTIONS.map((status) => status.value));
231
239
  return tasks.filter(t => {
232
- const matchSearch = taskMatchesSearch(t, normalizedSearchQuery, taskSearchContext);
240
+ const matchSearch = taskMatchesSearch(t, normalizedSearchQuery, taskSearchContext)
241
+ || (Boolean(normalizedSearchQuery.raw) && cloudSearchMatchIds.has(t.id));
233
242
  const matchCategory = !hasInitedFilters || isAllCategoriesSelected || filterCategories.includes(t.category);
234
243
  const matchPriority = !hasInitedFilters || isAllPrioritiesSelected || matchesPriorityFilter(t.priority, filterPriorities);
235
244
  const matchType = !hasInitedFilters || isAllTypesSelected || filterTypes.includes(t.type || DEFAULT_TYPE_VALUE);
@@ -253,7 +262,7 @@ export function useFilterSort({ tasks, archivedTasks, activeCategories, activeTy
253
262
  });
254
263
  return matchSearch && matchCategory && matchPriority && matchType && matchStatus && matchAssignee && matchTaxonomies;
255
264
  }).sort((a, b) => compareTasksBySortKey(a, b, sortBy, sortOrder, taxonomies));
256
- }, [tasks, normalizedSearchQuery, taskSearchContext, filterCategories, filterPriorities, filterTypes, filterStatus, filterAssignees, filterAssigneesAllSelected, filterTaxonomies, sortBy, sortOrder, hasInitedFilters, filterableTaxonomies, filterableTaxonomyById, activeCategories, activeTypes, priorities, isAllFilterValuesSelected, taxonomies]);
265
+ }, [tasks, normalizedSearchQuery, taskSearchContext, cloudSearchMatchIds, filterCategories, filterPriorities, filterTypes, filterStatus, filterAssignees, filterAssigneesAllSelected, filterTaxonomies, sortBy, sortOrder, hasInitedFilters, filterableTaxonomies, filterableTaxonomyById, activeCategories, activeTypes, priorities, isAllFilterValuesSelected, taxonomies]);
257
266
  const searchAgnosticTasks = useMemo(() => {
258
267
  const isAllCategoriesSelected = isAllFilterValuesSelected(filterCategories, activeCategories.map((category) => category.value));
259
268
  const isAllTypesSelected = isAllFilterValuesSelected(filterTypes, activeTypes.map((type) => type.value));
@@ -288,7 +297,8 @@ export function useFilterSort({ tasks, archivedTasks, activeCategories, activeTy
288
297
  const isAllTypesSelected = isAllFilterValuesSelected(filterTypes, activeTypes.map((type) => type.value));
289
298
  const isAllPrioritiesSelected = isAllFilterValuesSelected(Array.from(new Set(filterPriorities.map((value) => Number(value)).filter((value) => Number.isFinite(value)))), priorities.map((priority) => Number(priority.value)).filter((value) => Number.isFinite(value)));
290
299
  return archivedTasks.filter(t => {
291
- const matchSearch = taskMatchesSearch(t, normalizedSearchQuery, taskSearchContext);
300
+ const matchSearch = taskMatchesSearch(t, normalizedSearchQuery, taskSearchContext)
301
+ || (Boolean(normalizedSearchQuery.raw) && cloudSearchMatchIds.has(t.id));
292
302
  const matchCategory = !hasInitedFilters || isAllCategoriesSelected || filterCategories.includes(t.category);
293
303
  const matchPriority = !hasInitedFilters || isAllPrioritiesSelected || matchesPriorityFilter(t.priority, filterPriorities);
294
304
  const matchType = !hasInitedFilters || isAllTypesSelected || filterTypes.includes(t.type || DEFAULT_TYPE_VALUE);
@@ -310,7 +320,7 @@ export function useFilterSort({ tasks, archivedTasks, activeCategories, activeTy
310
320
  });
311
321
  return matchSearch && matchCategory && matchPriority && matchType && matchAssignee && matchTaxonomies;
312
322
  }).sort((a, b) => compareTasksBySortKey(a, b, sortBy, sortOrder, taxonomies));
313
- }, [archivedTasks, normalizedSearchQuery, taskSearchContext, filterCategories, filterPriorities, filterTypes, filterAssignees, filterAssigneesAllSelected, filterTaxonomies, sortBy, sortOrder, hasInitedFilters, activeCategories, activeTypes, priorities, filterableTaxonomies, filterableTaxonomyById, isAllFilterValuesSelected, taxonomies]);
323
+ }, [archivedTasks, normalizedSearchQuery, taskSearchContext, cloudSearchMatchIds, filterCategories, filterPriorities, filterTypes, filterAssignees, filterAssigneesAllSelected, filterTaxonomies, sortBy, sortOrder, hasInitedFilters, activeCategories, activeTypes, priorities, filterableTaxonomies, filterableTaxonomyById, isAllFilterValuesSelected, taxonomies]);
314
324
  const groupedTasks = useMemo(() => {
315
325
  const groups = {};
316
326
  filteredTasks.forEach(task => {
@@ -12,10 +12,14 @@ interface UseTaskDataProps {
12
12
  handleUnauthorized: () => void;
13
13
  /** Only used to detect auth changes in the fetch dependency array */
14
14
  authRequiredForApi: boolean;
15
+ setError?: (value: string) => void;
16
+ setLoading?: (value: boolean) => void;
15
17
  }
16
- export declare function useTaskData({ tasks, archivedTasks, setTasks, setArchivedTasks, setLoadingTasks, getCurrentWorkspaceId, workspaceResetKey, shouldDeferProtectedApiCalls, shouldBlockProtectedApiCalls, handleUnauthorized, authRequiredForApi }: UseTaskDataProps): {
18
+ export declare function useTaskData({ tasks, archivedTasks, setTasks, setArchivedTasks, setLoadingTasks, getCurrentWorkspaceId, workspaceResetKey, shouldDeferProtectedApiCalls, shouldBlockProtectedApiCalls, handleUnauthorized, authRequiredForApi, setError, setLoading }: UseTaskDataProps): {
17
19
  tasksRef: import("react").RefObject<TaskItem[]>;
18
20
  archivedTasksRef: import("react").RefObject<TaskItem[]>;
21
+ taskDetails: TaskItem[];
22
+ staleTaskDetailRevisions: Record<string, string>;
19
23
  recentlyChangedTaskIds: string[];
20
24
  markRecentlyChangedTasks: (taskIds: string[]) => void;
21
25
  fetchTasks: (isSilent?: boolean, options?: {
@@ -26,6 +30,11 @@ export declare function useTaskData({ tasks, archivedTasks, setTasks, setArchive
26
30
  fetchArchive: (isSilent?: boolean, options?: {
27
31
  ignoreAuthGuard?: boolean;
28
32
  }) => Promise<boolean>;
33
+ loadTaskDetail: (task: TaskItem, options?: {
34
+ shouldCommit?: () => boolean;
35
+ force?: boolean;
36
+ }) => Promise<TaskItem | null>;
37
+ cancelTaskDetailLoad: () => void;
29
38
  refreshTaskCollections: (options?: {
30
39
  isSilent?: boolean;
31
40
  ignoreAuthGuard?: boolean;