@yancyyu/openhermit 1.6.28 → 1.6.30
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.
- package/dist-renderer/assets/ProjectEditorOverlay-DsQt4FHy.js +52 -0
- package/dist-renderer/assets/{TeamGraphOverlay-Ba5njic5.js → TeamGraphOverlay-BjZC53xf.js} +1 -1
- package/dist-renderer/assets/{_basePickBy-BvnK-OC1.js → _basePickBy-CrWocIjq.js} +1 -1
- package/dist-renderer/assets/{_baseUniq-DmFYXx9G.js → _baseUniq-B6d8ysWi.js} +1 -1
- package/dist-renderer/assets/{arc-DX4ZQFY4.js → arc-DAIYCFP8.js} +1 -1
- package/dist-renderer/assets/{architectureDiagram-VXUJARFQ-DfYr3vEN.js → architectureDiagram-VXUJARFQ-B3UudXJh.js} +1 -1
- package/dist-renderer/assets/{blockDiagram-VD42YOAC-DuXdVeWn.js → blockDiagram-VD42YOAC-DbptKQ4W.js} +1 -1
- package/dist-renderer/assets/{c4Diagram-YG6GDRKO-Bw2nixXe.js → c4Diagram-YG6GDRKO-C4WQuZpV.js} +1 -1
- package/dist-renderer/assets/channel-DbjZvWii.js +1 -0
- package/dist-renderer/assets/{chunk-4BX2VUAB-DLiNGQoE.js → chunk-4BX2VUAB-Dp7fVpI_.js} +1 -1
- package/dist-renderer/assets/{chunk-55IACEB6-B1L_8VIF.js → chunk-55IACEB6-B8KGfbAy.js} +1 -1
- package/dist-renderer/assets/{chunk-B4BG7PRW-DaZMWKGk.js → chunk-B4BG7PRW-BG1oJrjA.js} +1 -1
- package/dist-renderer/assets/{chunk-DI55MBZ5-ku-dflJG.js → chunk-DI55MBZ5-DRmxNjht.js} +1 -1
- package/dist-renderer/assets/{chunk-FMBD7UC4-DV-mF1dP.js → chunk-FMBD7UC4-D6VLvy16.js} +1 -1
- package/dist-renderer/assets/{chunk-QN33PNHL-ByGcDFQ0.js → chunk-QN33PNHL-DZou1667.js} +1 -1
- package/dist-renderer/assets/{chunk-QZHKN3VN-7dv-Min8.js → chunk-QZHKN3VN-CghmasSh.js} +1 -1
- package/dist-renderer/assets/{chunk-TZMSLE5B-WdXL5fTu.js → chunk-TZMSLE5B-B7apcMPK.js} +1 -1
- package/dist-renderer/assets/classDiagram-2ON5EDUG-D_FGxxsl.js +1 -0
- package/dist-renderer/assets/classDiagram-v2-WZHVMYZB-D_FGxxsl.js +1 -0
- package/dist-renderer/assets/clone-CJ1kxO2J.js +1 -0
- package/dist-renderer/assets/{cose-bilkent-S5V4N54A-CNcsvqPl.js → cose-bilkent-S5V4N54A-05e5uQDp.js} +1 -1
- package/dist-renderer/assets/{dagre-6UL2VRFP-DBNx4qqx.js → dagre-6UL2VRFP-B06bRykF.js} +1 -1
- package/dist-renderer/assets/{diagram-PSM6KHXK-BfVlT6sT.js → diagram-PSM6KHXK-CY7VYQ7c.js} +1 -1
- package/dist-renderer/assets/{diagram-QEK2KX5R-HvVjs0K6.js → diagram-QEK2KX5R-BjKEH7dD.js} +1 -1
- package/dist-renderer/assets/{diagram-S2PKOQOG-DYb_KnWS.js → diagram-S2PKOQOG-Bf4ELS1_.js} +1 -1
- package/dist-renderer/assets/{erDiagram-Q2GNP2WA-Ba-IgI5G.js → erDiagram-Q2GNP2WA-DJ753_L9.js} +1 -1
- package/dist-renderer/assets/{flowDiagram-NV44I4VS-2iDN8Kpj.js → flowDiagram-NV44I4VS-B71S-lC-.js} +1 -1
- package/dist-renderer/assets/{ganttDiagram-JELNMOA3-Byjf8Fa3.js → ganttDiagram-JELNMOA3-C_U42mSZ.js} +1 -1
- package/dist-renderer/assets/{gitGraphDiagram-V2S2FVAM-DbKvfZ_j.js → gitGraphDiagram-V2S2FVAM-DKUJU4Ns.js} +1 -1
- package/dist-renderer/assets/{graph-Enirf-f8.js → graph-DY3qbzqj.js} +1 -1
- package/dist-renderer/assets/{index-DY1zqsb6.js → index-BlOrAXp3.js} +551 -537
- package/dist-renderer/assets/{index-AjxP_rE_.js → index-Bs27J5gB.js} +1 -1
- package/dist-renderer/assets/{index-CtlzGepK.js → index-C8B_nKOF.js} +1 -1
- package/dist-renderer/assets/index-CmZPUEhS.css +1 -0
- package/dist-renderer/assets/{index-COZPUWJW.js → index-DLKyDr4T.js} +1 -1
- package/dist-renderer/assets/{index-DdhqolqE.js → index-Dhsk3_DD.js} +1 -1
- package/dist-renderer/assets/{index-ChR1D6ZF.js → index-GpUvV2xs.js} +1 -1
- package/dist-renderer/assets/{infoDiagram-HS3SLOUP-D6uicwz1.js → infoDiagram-HS3SLOUP-BNs0y3IG.js} +1 -1
- package/dist-renderer/assets/{journeyDiagram-XKPGCS4Q-DqwZsXlQ.js → journeyDiagram-XKPGCS4Q-CqPnw4UV.js} +1 -1
- package/dist-renderer/assets/{kanban-definition-3W4ZIXB7-fCDVhVUm.js → kanban-definition-3W4ZIXB7-SLlzcUJ2.js} +1 -1
- package/dist-renderer/assets/{layout-CPFgj98r.js → layout-BZLlNmbr.js} +1 -1
- package/dist-renderer/assets/{linear-CYiQ7Y3M.js → linear-qz6v45xy.js} +1 -1
- package/dist-renderer/assets/{mindmap-definition-VGOIOE7T-D31dS2KE.js → mindmap-definition-VGOIOE7T-B1-kmEWV.js} +1 -1
- package/dist-renderer/assets/{pieDiagram-ADFJNKIX-BOsCJfds.js → pieDiagram-ADFJNKIX-B8a02iNx.js} +1 -1
- package/dist-renderer/assets/{quadrantDiagram-AYHSOK5B-CYTVQCfr.js → quadrantDiagram-AYHSOK5B-BKv1Xfou.js} +1 -1
- package/dist-renderer/assets/{requirementDiagram-UZGBJVZJ-CODCFpkt.js → requirementDiagram-UZGBJVZJ-B3DUpZi2.js} +1 -1
- package/dist-renderer/assets/{sankeyDiagram-TZEHDZUN-Z4ce9ZtZ.js → sankeyDiagram-TZEHDZUN-DmPzuTsy.js} +1 -1
- package/dist-renderer/assets/{sequenceDiagram-WL72ISMW-CmS9TxhW.js → sequenceDiagram-WL72ISMW-Bo7RelRb.js} +1 -1
- package/dist-renderer/assets/{stateDiagram-FKZM4ZOC-o9k-ns3q.js → stateDiagram-FKZM4ZOC-1epX98gV.js} +1 -1
- package/dist-renderer/assets/{stateDiagram-v2-4FDKWEC3-CxHMyEt1.js → stateDiagram-v2-4FDKWEC3-03Ym9PTr.js} +1 -1
- package/dist-renderer/assets/{timeline-definition-IT6M3QCI-B6T3zrde.js → timeline-definition-IT6M3QCI-r6isC62H.js} +1 -1
- package/dist-renderer/assets/{treemap-GDKQZRPO-CVd5GNDw.js → treemap-GDKQZRPO-CGKpOUF2.js} +1 -1
- package/dist-renderer/assets/{xychartDiagram-PRI3JC2R-CleBrdqc.js → xychartDiagram-PRI3JC2R-t4-rwdAw.js} +1 -1
- package/dist-renderer/index.html +2 -2
- package/package.json +4 -1
- package/src/main/ipc/extensions.ts +353 -0
- package/src/main/server.ts +907 -184
- package/src/main/services/extensions/ExtensionFacadeService.ts +135 -0
- package/src/main/services/extensions/catalog/GlamaMcpEnrichmentService.ts +190 -0
- package/src/main/services/extensions/catalog/McpCatalogAggregator.ts +150 -0
- package/src/main/services/extensions/catalog/OfficialMcpRegistryService.ts +381 -0
- package/src/main/services/extensions/catalog/PluginCatalogService.ts +392 -0
- package/src/main/services/extensions/credentials/CredentialService.ts +343 -0
- package/src/main/services/extensions/install/McpInstallService.ts +407 -0
- package/src/main/services/extensions/install/PluginInstallService.ts +198 -0
- package/src/main/services/extensions/runtime/ClaudeCodeAdapter.ts +199 -0
- package/src/main/services/extensions/runtime/CodexAdapter.ts +100 -0
- package/src/main/services/extensions/runtime/CursorAdapter.ts +154 -0
- package/src/main/services/extensions/runtime/ExtensionsRuntimeAdapter.ts +172 -0
- package/src/main/services/extensions/runtime/GeminiAdapter.ts +91 -0
- package/src/main/services/extensions/runtime/HarnessInstallAdapter.ts +49 -0
- package/src/main/services/extensions/runtime/McpConfigStateReader.ts +209 -0
- package/src/main/services/extensions/runtime/OpenCodeAdapter.ts +91 -0
- package/src/main/services/extensions/runtime/adapterRegistry.ts +54 -0
- package/src/main/services/extensions/runtime/mcpDiagnosticsParser.ts +214 -0
- package/src/main/services/extensions/runtime/mcpRuntimeJson.ts +45 -0
- package/src/main/services/extensions/skills/SkillImportService.ts +155 -0
- package/src/main/services/extensions/skills/SkillMetadataParser.ts +323 -0
- package/src/main/services/extensions/skills/SkillPlanService.ts +411 -0
- package/src/main/services/extensions/skills/SkillReviewService.ts +73 -0
- package/src/main/services/extensions/skills/SkillRootsResolver.ts +49 -0
- package/src/main/services/extensions/skills/SkillScaffoldService.ts +89 -0
- package/src/main/services/extensions/skills/SkillScanner.ts +117 -0
- package/src/main/services/extensions/skills/SkillValidator.ts +69 -0
- package/src/main/services/extensions/skills/SkillsCatalogService.ts +92 -0
- package/src/main/services/extensions/skills/SkillsMutationService.ts +146 -0
- package/src/main/services/extensions/skills/SkillsWatcherService.ts +134 -0
- package/src/main/services/extensions/state/McpInstallationStateService.ts +42 -0
- package/src/main/services/extensions/state/PluginInstallationStateService.ts +281 -0
- package/src/main/services/identity/AgentTeamsIdentityStore.ts +218 -0
- package/src/main/services/runtime/providerAwareCliEnv.ts +60 -0
- package/src/main/services/session-intelligence/UsageTelemetryService.ts +33 -18
- package/src/main/services/team/ClaudeBinaryResolver.ts +469 -0
- package/src/main/services/team/ClaudeDoctorProbe.ts +0 -0
- package/src/main/services/team/cliFlavor.ts +54 -0
- package/src/main/services/teams-mvp/CollaborationBoardService.ts +310 -0
- package/src/main/services/teams-mvp/TaskDispatchService.ts +883 -95
- package/src/main/services/teams-mvp/TeamProvisioningService.ts +58 -19
- package/src/main/services/teams-mvp/TeamWorkspaceService.ts +25 -2
- package/src/main/services/teams-mvp/index.ts +3 -0
- package/src/main/utils/atomicWrite.ts +72 -0
- package/src/main/utils/childProcess.ts +554 -0
- package/src/main/utils/cliEnv.ts +54 -0
- package/src/main/utils/cliPathMerge.ts +97 -0
- package/src/main/utils/pathDecoder.ts +664 -0
- package/src/main/utils/pathValidation.ts +432 -0
- package/src/main/utils/shellEnv.ts +331 -0
- package/src/renderer/App.tsx +5 -0
- package/src/renderer/api/httpClient.ts +128 -0
- package/src/renderer/components/extensions/ExtensionStoreView.tsx +59 -34
- package/src/renderer/components/extensions/ExtensionsSubTabTrigger.tsx +1 -1
- package/src/renderer/components/extensions/common/ExtensionToast.tsx +141 -0
- package/src/renderer/components/extensions/common/HarnessSelector.tsx +71 -0
- package/src/renderer/components/extensions/env/EnvVarPanel.tsx +335 -0
- package/src/renderer/components/extensions/env/ProjectEnvPanel.tsx +239 -0
- package/src/renderer/components/extensions/mcp/CustomMcpServerDialog.tsx +14 -223
- package/src/renderer/components/extensions/mcp/McpServerDetailDialog.tsx +11 -0
- package/src/renderer/components/extensions/mcp/McpServersPanel.tsx +51 -1
- package/src/renderer/components/extensions/skills/SkillsPanel.tsx +1 -126
- package/src/renderer/components/layout/PaneContent.tsx +2 -0
- package/src/renderer/components/layout/SortableTab.tsx +1 -0
- package/src/renderer/components/layout/TabBarActions.tsx +12 -12
- package/src/renderer/components/schedules/SchedulesView.tsx +54 -22
- package/src/renderer/components/settings/sections/AdvancedSection.tsx +1 -1
- package/src/renderer/components/settings/sections/HarnessSection.tsx +2 -6
- package/src/renderer/components/settings/sections/TaskBusSection.tsx +144 -84
- package/src/renderer/components/sidebar/SidebarSessions.tsx +23 -0
- package/src/renderer/components/sidebar/WorkspaceBrowser.tsx +1 -7
- package/src/renderer/components/tasks/TasksView.tsx +343 -0
- package/src/renderer/components/team/HarnessSelect.tsx +71 -0
- package/src/renderer/components/team/TeamDetailView.tsx +55 -98
- package/src/renderer/components/team/dialogs/CreateTeamDialog.tsx +21 -12
- package/src/renderer/components/team/dialogs/EditTeamDialog.tsx +8 -13
- package/src/renderer/components/team/dialogs/LaunchTeamDialog.tsx +1 -1
- package/src/renderer/components/team/editor/EditorContextMenu.tsx +8 -23
- package/src/renderer/components/team/editor/EditorFileTree.tsx +0 -4
- package/src/renderer/components/team/editor/EditorSelectionMenu.tsx +1 -8
- package/src/renderer/components/team/editor/ProjectEditorOverlay.tsx +0 -10
- package/src/renderer/components/team/kanban/KanbanBoard.tsx +31 -65
- package/src/renderer/components/team/members/MemberDetailDialog.tsx +8 -33
- package/src/renderer/components/team/messages/MessageComposer.tsx +39 -3
- package/src/renderer/components/team/messages/MessagesPanel.tsx +100 -26
- package/src/renderer/components/team/messages/StatusBlock.tsx +2 -24
- package/src/renderer/components/team/schedule/ScheduleEmptyState.tsx +1 -1
- package/src/renderer/components/terminal/TerminalPanel.tsx +156 -0
- package/src/renderer/components/ui/MentionableTextarea.tsx +0 -1
- package/src/renderer/hooks/useExtensionsTabState.ts +2 -2
- package/src/renderer/store/slices/extensionsSlice.ts +42 -107
- package/src/renderer/store/slices/scheduleSlice.ts +21 -0
- package/src/renderer/store/slices/teamSlice.ts +67 -25
- package/src/renderer/types/tabs.ts +1 -0
- package/src/shared/types/api.ts +58 -0
- package/src/shared/types/extensions/index.ts +1 -0
- package/src/shared/types/extensions/mcp.ts +2 -0
- package/src/shared/types/extensions/plugin.ts +2 -1
- package/src/shared/types/extensions/skill.ts +7 -0
- package/src/shared/types/team.ts +104 -1
- package/src/shared/utils/providerExtensionCapabilities.ts +1 -1
- package/dist-renderer/assets/ProjectEditorOverlay-A4DZTvSy.js +0 -57
- package/dist-renderer/assets/channel-Pre42N5O.js +0 -1
- package/dist-renderer/assets/classDiagram-2ON5EDUG-CdJsTJsj.js +0 -1
- package/dist-renderer/assets/classDiagram-v2-WZHVMYZB-CdJsTJsj.js +0 -1
- package/dist-renderer/assets/clone-BjQBiNfj.js +0 -1
- package/dist-renderer/assets/index-BIOJremZ.css +0 -1
- package/src/features/recent-projects/main/adapters/input/http/registerRecentProjectsHttp.ts +0 -30
- package/src/features/recent-projects/main/adapters/output/presenters/DashboardRecentProjectsPresenter.ts +0 -27
- package/src/features/recent-projects/main/adapters/output/sources/ClaudeRecentProjectsSourceAdapter.ts +0 -91
- package/src/features/recent-projects/main/adapters/output/sources/CodexRecentProjectsSourceAdapter.ts +0 -326
- package/src/features/recent-projects/main/composition/createRecentProjectsFeature.ts +0 -43
- package/src/features/recent-projects/main/index.ts +0 -3
- package/src/features/recent-projects/main/infrastructure/cache/InMemoryRecentProjectsCache.ts +0 -34
- package/src/features/recent-projects/main/infrastructure/codex/CodexAppServerClient.ts +0 -116
- package/src/features/recent-projects/main/infrastructure/identity/RecentProjectIdentityResolver.ts +0 -20
- package/src/features/recent-projects/main/infrastructure/identity/normalizeIdentityPath.ts +0 -10
- package/src/renderer/components/extensions/apikeys/ApiKeyCard.tsx +0 -143
- package/src/renderer/components/extensions/apikeys/ApiKeyFormDialog.tsx +0 -282
- package/src/renderer/components/extensions/apikeys/ApiKeysPanel.tsx +0 -280
|
@@ -44,7 +44,8 @@ import {
|
|
|
44
44
|
X,
|
|
45
45
|
} from 'lucide-react';
|
|
46
46
|
|
|
47
|
-
import {
|
|
47
|
+
import { AGENT_TYPE_LABELS } from '../HarnessCards';
|
|
48
|
+
import { HarnessSelect } from '../HarnessSelect';
|
|
48
49
|
import { ProjectPathSelector } from './ProjectPathSelector';
|
|
49
50
|
import { OptionalSettingsSection } from './OptionalSettingsSection';
|
|
50
51
|
import { AutoResizeTextarea } from '@renderer/components/ui/auto-resize-textarea';
|
|
@@ -52,7 +53,13 @@ import { platformMeta, isQRPlatform } from './platformMeta';
|
|
|
52
53
|
import PlatformSetupQR from './PlatformSetupQR';
|
|
53
54
|
import PlatformManualForm from './PlatformManualForm';
|
|
54
55
|
|
|
55
|
-
import type {
|
|
56
|
+
import type {
|
|
57
|
+
EffortLevel,
|
|
58
|
+
Project,
|
|
59
|
+
TeamCreateRequest,
|
|
60
|
+
TeamFastMode,
|
|
61
|
+
TeamProviderId,
|
|
62
|
+
} from '@shared/types';
|
|
56
63
|
import type { CcAgentType } from '@shared/types/ccConnect';
|
|
57
64
|
import type { GlobalProvider } from '@shared/types/providers';
|
|
58
65
|
|
|
@@ -69,6 +76,14 @@ export interface TeamCopyData {
|
|
|
69
76
|
teamName: string;
|
|
70
77
|
description?: string;
|
|
71
78
|
color?: string;
|
|
79
|
+
providerId?: TeamProviderId;
|
|
80
|
+
model?: string;
|
|
81
|
+
effort?: EffortLevel;
|
|
82
|
+
fastMode?: TeamFastMode;
|
|
83
|
+
limitContext?: boolean;
|
|
84
|
+
skipPermissions?: boolean;
|
|
85
|
+
templateSourceId?: string;
|
|
86
|
+
templateDirectoryId?: string;
|
|
72
87
|
}
|
|
73
88
|
|
|
74
89
|
// ---------------------------------------------------------------------------
|
|
@@ -549,18 +564,12 @@ export const CreateTeamDialog = ({
|
|
|
549
564
|
|
|
550
565
|
<div className="space-y-1.5">
|
|
551
566
|
<Label htmlFor="team-harness">Agent 类型</Label>
|
|
552
|
-
<
|
|
567
|
+
<HarnessSelect
|
|
553
568
|
id="team-harness"
|
|
554
|
-
className="flex w-full rounded-md border border-[var(--color-border)] bg-transparent px-3 py-2 text-sm"
|
|
555
569
|
value={selectedHarness}
|
|
556
|
-
onChange={
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
<option key={t} value={t}>
|
|
560
|
-
{AGENT_TYPE_LABELS[t]}
|
|
561
|
-
</option>
|
|
562
|
-
))}
|
|
563
|
-
</select>
|
|
570
|
+
onChange={setSelectedHarness}
|
|
571
|
+
className="w-full"
|
|
572
|
+
/>
|
|
564
573
|
</div>
|
|
565
574
|
|
|
566
575
|
<ProjectPathSelector
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { useEffect, useState } from 'react';
|
|
2
2
|
|
|
3
3
|
import { api } from '@renderer/api';
|
|
4
|
-
import {
|
|
4
|
+
import { AGENT_TYPE_LABELS } from '@renderer/components/team/HarnessCards';
|
|
5
|
+
import { HarnessSelect } from '@renderer/components/team/HarnessSelect';
|
|
5
6
|
import { Button } from '@renderer/components/ui/button';
|
|
6
7
|
import { Checkbox } from '@renderer/components/ui/checkbox';
|
|
7
8
|
import {
|
|
@@ -259,20 +260,14 @@ export const EditTeamDialog = ({
|
|
|
259
260
|
<label className="mb-1 block text-xs font-medium text-[var(--color-text-secondary)]">
|
|
260
261
|
Agent 类型
|
|
261
262
|
</label>
|
|
262
|
-
<
|
|
263
|
-
value={agentType}
|
|
264
|
-
onChange={(
|
|
263
|
+
<HarnessSelect
|
|
264
|
+
value={agentType as CcAgentType}
|
|
265
|
+
onChange={(v) => {
|
|
265
266
|
clearError();
|
|
266
|
-
setAgentType(
|
|
267
|
+
setAgentType(v);
|
|
267
268
|
}}
|
|
268
|
-
className="w-full
|
|
269
|
-
|
|
270
|
-
{ALL_AGENT_TYPES.map((type) => (
|
|
271
|
-
<option key={type} value={type}>
|
|
272
|
-
{AGENT_TYPE_LABELS[type]}
|
|
273
|
-
</option>
|
|
274
|
-
))}
|
|
275
|
-
</select>
|
|
269
|
+
className="w-full"
|
|
270
|
+
/>
|
|
276
271
|
</div>
|
|
277
272
|
|
|
278
273
|
<div>
|
|
@@ -2058,7 +2058,7 @@ export const LaunchTeamDialog = (props: LaunchTeamDialogProps): React.JSX.Elemen
|
|
|
2058
2058
|
) : effectiveTeamName ? (
|
|
2059
2059
|
`为团队“${effectiveTeamName}”创建自动运行计划`
|
|
2060
2060
|
) : (
|
|
2061
|
-
'
|
|
2061
|
+
'创建团队自动运行计划'
|
|
2062
2062
|
);
|
|
2063
2063
|
|
|
2064
2064
|
const submitLabel = isLaunchMode
|
|
@@ -16,7 +16,6 @@ import {
|
|
|
16
16
|
FilePlus,
|
|
17
17
|
FolderOpen,
|
|
18
18
|
FolderPlus,
|
|
19
|
-
ListTodo,
|
|
20
19
|
MessageSquare,
|
|
21
20
|
Pencil,
|
|
22
21
|
Trash2,
|
|
@@ -39,8 +38,6 @@ interface EditorContextMenuProps {
|
|
|
39
38
|
onNewFolder: (parentDir: string) => void;
|
|
40
39
|
onDelete: (path: string) => void;
|
|
41
40
|
onRename: (path: string) => void;
|
|
42
|
-
/** Trigger "Create Task" with a file mention (files only, not directories) */
|
|
43
|
-
onCreateTask?: (filePath: string) => void;
|
|
44
41
|
/** Trigger "Write Teammate" with a file mention (files only, not directories) */
|
|
45
42
|
onSendMessage?: (filePath: string) => void;
|
|
46
43
|
}
|
|
@@ -56,7 +53,6 @@ export const EditorContextMenu = ({
|
|
|
56
53
|
onNewFolder,
|
|
57
54
|
onDelete,
|
|
58
55
|
onRename,
|
|
59
|
-
onCreateTask,
|
|
60
56
|
onSendMessage,
|
|
61
57
|
}: EditorContextMenuProps): React.ReactElement => {
|
|
62
58
|
const [target, setTarget] = useState<TargetEntry | null>(null);
|
|
@@ -183,27 +179,16 @@ export const EditorContextMenu = ({
|
|
|
183
179
|
)}
|
|
184
180
|
|
|
185
181
|
{/* Team actions — file only */}
|
|
186
|
-
{target && !target.isDir &&
|
|
182
|
+
{target && !target.isDir && onSendMessage && (
|
|
187
183
|
<>
|
|
188
184
|
<ContextMenu.Separator className="my-1 h-px bg-border" />
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
</ContextMenu.Item>
|
|
197
|
-
)}
|
|
198
|
-
{onCreateTask && (
|
|
199
|
-
<ContextMenu.Item
|
|
200
|
-
className="flex cursor-pointer items-center gap-2 rounded px-2 py-1.5 text-xs text-text outline-none hover:bg-surface-raised focus:bg-surface-raised"
|
|
201
|
-
onSelect={() => onCreateTask(target.path)}
|
|
202
|
-
>
|
|
203
|
-
<ListTodo className="size-3.5 text-text-muted" />
|
|
204
|
-
Create Task
|
|
205
|
-
</ContextMenu.Item>
|
|
206
|
-
)}
|
|
185
|
+
<ContextMenu.Item
|
|
186
|
+
className="flex cursor-pointer items-center gap-2 rounded px-2 py-1.5 text-xs text-text outline-none hover:bg-surface-raised focus:bg-surface-raised"
|
|
187
|
+
onSelect={() => onSendMessage(target.path)}
|
|
188
|
+
>
|
|
189
|
+
<MessageSquare className="size-3.5 text-text-muted" />
|
|
190
|
+
Write Teammate
|
|
191
|
+
</ContextMenu.Item>
|
|
207
192
|
</>
|
|
208
193
|
)}
|
|
209
194
|
</ContextMenu.Content>
|
|
@@ -54,8 +54,6 @@ import type { FileTreeEntry, GitFileStatusType } from '@shared/types/editor';
|
|
|
54
54
|
interface EditorFileTreeProps {
|
|
55
55
|
selectedFilePath: string | null;
|
|
56
56
|
onFileSelect: (filePath: string) => void;
|
|
57
|
-
/** Trigger "Create Task" with a file mention from context menu */
|
|
58
|
-
onCreateTask?: (filePath: string) => void;
|
|
59
57
|
/** Trigger "Write Teammate" with a file mention from context menu */
|
|
60
58
|
onSendMessage?: (filePath: string) => void;
|
|
61
59
|
}
|
|
@@ -91,7 +89,6 @@ let fileTreeRenderCount = 0;
|
|
|
91
89
|
export const EditorFileTree = ({
|
|
92
90
|
selectedFilePath,
|
|
93
91
|
onFileSelect,
|
|
94
|
-
onCreateTask,
|
|
95
92
|
onSendMessage,
|
|
96
93
|
}: EditorFileTreeProps): React.ReactElement => {
|
|
97
94
|
fileTreeRenderCount++;
|
|
@@ -452,7 +449,6 @@ export const EditorFileTree = ({
|
|
|
452
449
|
onNewFolder={handleNewFolder}
|
|
453
450
|
onDelete={handleDelete}
|
|
454
451
|
onRename={handleRename}
|
|
455
|
-
onCreateTask={onCreateTask}
|
|
456
452
|
onSendMessage={onSendMessage}
|
|
457
453
|
>
|
|
458
454
|
<DndContext
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
|
|
8
8
|
import { Button } from '@renderer/components/ui/button';
|
|
9
9
|
import { Tooltip, TooltipContent, TooltipTrigger } from '@renderer/components/ui/tooltip';
|
|
10
|
-
import {
|
|
10
|
+
import { MessageSquare } from 'lucide-react';
|
|
11
11
|
|
|
12
12
|
import type { EditorSelectionInfo } from '@shared/types/editor';
|
|
13
13
|
|
|
@@ -20,7 +20,6 @@ interface EditorSelectionMenuProps {
|
|
|
20
20
|
/** Bounding rect of the editor content container (for viewport → container conversion) */
|
|
21
21
|
containerRect: DOMRect;
|
|
22
22
|
onSendMessage: () => void;
|
|
23
|
-
onCreateTask: () => void;
|
|
24
23
|
}
|
|
25
24
|
|
|
26
25
|
// =============================================================================
|
|
@@ -39,7 +38,6 @@ export const EditorSelectionMenu = ({
|
|
|
39
38
|
info,
|
|
40
39
|
containerRect,
|
|
41
40
|
onSendMessage,
|
|
42
|
-
onCreateTask,
|
|
43
41
|
}: EditorSelectionMenuProps): React.ReactElement | null => {
|
|
44
42
|
if (!info.text.trim()) return null;
|
|
45
43
|
|
|
@@ -71,11 +69,6 @@ export const EditorSelectionMenu = ({
|
|
|
71
69
|
label="Write Teammate"
|
|
72
70
|
onClick={onSendMessage}
|
|
73
71
|
/>
|
|
74
|
-
<MenuButton
|
|
75
|
-
icon={<ListTodo className="size-3.5" />}
|
|
76
|
-
label="Create Task"
|
|
77
|
-
onClick={onCreateTask}
|
|
78
|
-
/>
|
|
79
72
|
</div>
|
|
80
73
|
);
|
|
81
74
|
};
|
|
@@ -628,12 +628,6 @@ export const ProjectEditorOverlay = ({
|
|
|
628
628
|
<EditorFileTree
|
|
629
629
|
selectedFilePath={activeTabId}
|
|
630
630
|
onFileSelect={handleFileSelect}
|
|
631
|
-
onCreateTask={
|
|
632
|
-
onEditorAction
|
|
633
|
-
? (filePath: string) =>
|
|
634
|
-
onEditorAction(buildFileAction('createTask', filePath, projectPath))
|
|
635
|
-
: undefined
|
|
636
|
-
}
|
|
637
631
|
onSendMessage={
|
|
638
632
|
onEditorAction
|
|
639
633
|
? (filePath: string) =>
|
|
@@ -829,10 +823,6 @@ export const ProjectEditorOverlay = ({
|
|
|
829
823
|
onEditorAction(buildSelectionAction('sendMessage', selectionInfo));
|
|
830
824
|
setSelectionInfo(null);
|
|
831
825
|
}}
|
|
832
|
-
onCreateTask={() => {
|
|
833
|
-
onEditorAction(buildSelectionAction('createTask', selectionInfo));
|
|
834
|
-
setSelectionInfo(null);
|
|
835
|
-
}}
|
|
836
826
|
/>
|
|
837
827
|
)}
|
|
838
828
|
</div>
|
|
@@ -274,7 +274,11 @@ export const KanbanBoard = ({
|
|
|
274
274
|
}: KanbanBoardProps): React.JSX.Element => {
|
|
275
275
|
const boardRef = useRef<HTMLDivElement>(null);
|
|
276
276
|
const scrollRestoreTimeoutsRef = useRef<number[]>([]);
|
|
277
|
-
const [viewMode, setViewMode] = useState<KanbanViewMode>('
|
|
277
|
+
const [viewMode, setViewMode] = useState<KanbanViewMode>('columns');
|
|
278
|
+
|
|
279
|
+
useEffect(() => {
|
|
280
|
+
setViewMode('columns');
|
|
281
|
+
}, []);
|
|
278
282
|
const enableTaskSorting =
|
|
279
283
|
viewMode === 'columns' && !!onColumnOrderChange && sort.field === 'manual';
|
|
280
284
|
|
|
@@ -357,49 +361,26 @@ export const KanbanBoard = ({
|
|
|
357
361
|
columnTasks: TeamTask[],
|
|
358
362
|
compact?: boolean
|
|
359
363
|
): React.JSX.Element => {
|
|
360
|
-
const addHandler =
|
|
361
|
-
onAddTask && columnId === 'todo'
|
|
362
|
-
? () => onAddTask(false)
|
|
363
|
-
: onAddTask && columnId === 'in_progress'
|
|
364
|
-
? () => onAddTask(true)
|
|
365
|
-
: undefined;
|
|
366
|
-
|
|
367
|
-
const addButton = addHandler ? (
|
|
368
|
-
<button
|
|
369
|
-
type="button"
|
|
370
|
-
onClick={addHandler}
|
|
371
|
-
className="flex w-full items-center justify-center gap-1.5 rounded-md border border-dashed border-[var(--color-border)] p-3 text-xs text-[var(--color-text-muted)] transition-colors hover:border-[var(--color-border-emphasis)] hover:text-[var(--color-text-secondary)]"
|
|
372
|
-
>
|
|
373
|
-
<Plus size={13} />
|
|
374
|
-
Add task
|
|
375
|
-
</button>
|
|
376
|
-
) : null;
|
|
377
|
-
|
|
378
364
|
if (columnTasks.length === 0) {
|
|
379
365
|
return (
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
</div>
|
|
384
|
-
)
|
|
366
|
+
<div className="rounded-md border border-dashed border-[var(--color-border)] p-3 text-xs text-[var(--color-text-muted)]">
|
|
367
|
+
No tasks
|
|
368
|
+
</div>
|
|
385
369
|
);
|
|
386
370
|
}
|
|
387
371
|
if (enableTaskSorting) {
|
|
388
372
|
const itemIds = columnTasks.map((t) => t.id);
|
|
389
373
|
return (
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
</SortableContext>
|
|
401
|
-
{addButton}
|
|
402
|
-
</>
|
|
374
|
+
<SortableContext items={itemIds} strategy={verticalListSortingStrategy}>
|
|
375
|
+
{columnTasks.map((task) => (
|
|
376
|
+
<SortableKanbanTaskCard
|
|
377
|
+
key={task.id}
|
|
378
|
+
task={task}
|
|
379
|
+
columnId={columnId}
|
|
380
|
+
memberColorMap={memberColorMap}
|
|
381
|
+
/>
|
|
382
|
+
))}
|
|
383
|
+
</SortableContext>
|
|
403
384
|
);
|
|
404
385
|
}
|
|
405
386
|
return (
|
|
@@ -428,7 +409,6 @@ export const KanbanBoard = ({
|
|
|
428
409
|
onDeleteTask={onDeleteTask}
|
|
429
410
|
/>
|
|
430
411
|
))}
|
|
431
|
-
{addButton}
|
|
432
412
|
</>
|
|
433
413
|
);
|
|
434
414
|
};
|
|
@@ -610,36 +590,22 @@ export const KanbanBoard = ({
|
|
|
610
590
|
</div>
|
|
611
591
|
) : (
|
|
612
592
|
<div className="w-full min-w-0 max-w-full overflow-x-auto overflow-y-hidden px-1 pb-6 pr-4 pt-2">
|
|
613
|
-
<div className="flex
|
|
614
|
-
{visibleColumns.map((column
|
|
593
|
+
<div className="flex w-full items-start gap-3">
|
|
594
|
+
{visibleColumns.map((column) => {
|
|
615
595
|
const columnTasks = groupedOrdered.get(column.id) ?? [];
|
|
616
596
|
const accent = COLUMN_ACCENTS[column.id as 'todo' | 'in_progress' | 'done'];
|
|
617
|
-
const width = columnWidths.get(column.id) ?? 256;
|
|
618
|
-
const handleProps = getHandleProps(column.id);
|
|
619
597
|
return (
|
|
620
|
-
<div key={column.id} className="flex
|
|
621
|
-
<
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
</KanbanColumn>
|
|
632
|
-
</div>
|
|
633
|
-
{index < visibleColumns.length - 1 ? (
|
|
634
|
-
<div
|
|
635
|
-
className="group relative mx-0.5 flex items-center justify-center"
|
|
636
|
-
onPointerDown={handleProps.onPointerDown}
|
|
637
|
-
style={handleProps.style}
|
|
638
|
-
aria-label={handleProps['aria-label']}
|
|
639
|
-
>
|
|
640
|
-
<div className="h-full w-px bg-[var(--color-border)] transition-colors group-hover:bg-blue-500/50 group-active:bg-blue-500" />
|
|
641
|
-
</div>
|
|
642
|
-
) : null}
|
|
598
|
+
<div key={column.id} className="min-w-0 flex-1">
|
|
599
|
+
<KanbanColumn
|
|
600
|
+
title={column.title}
|
|
601
|
+
count={columnTasks.length}
|
|
602
|
+
icon={accent.icon}
|
|
603
|
+
headerBg={accent.headerBg}
|
|
604
|
+
bodyBg={accent.bodyBg}
|
|
605
|
+
bodyClassName="max-h-none overflow-visible"
|
|
606
|
+
>
|
|
607
|
+
{renderCards(column.id, columnTasks, true)}
|
|
608
|
+
</KanbanColumn>
|
|
643
609
|
</div>
|
|
644
610
|
);
|
|
645
611
|
})}
|
|
@@ -26,7 +26,6 @@ import { type MemberActivityFilter, type MemberDetailTab } from './memberDetailT
|
|
|
26
26
|
import { MemberLaunchDiagnosticsButton } from './MemberLaunchDiagnosticsButton';
|
|
27
27
|
import { MemberMessagesTab } from './MemberMessagesTab';
|
|
28
28
|
import { MemberStatsTab } from './MemberStatsTab';
|
|
29
|
-
import { MemberTasksTab } from './MemberTasksTab';
|
|
30
29
|
import { MemberWorkspaceTab } from './MemberWorkspaceTab';
|
|
31
30
|
|
|
32
31
|
import type { TeamLaunchParams } from '@renderer/store/slices/teamSlice';
|
|
@@ -56,8 +55,10 @@ interface MemberDetailDialogProps {
|
|
|
56
55
|
launchParams?: TeamLaunchParams;
|
|
57
56
|
onClose: () => void;
|
|
58
57
|
onSendMessage: () => void;
|
|
59
|
-
|
|
60
|
-
|
|
58
|
+
/** Deprecated: team tasks UI has been removed, kept for compatibility with older callers/tests. */
|
|
59
|
+
onAssignTask?: () => void;
|
|
60
|
+
/** Deprecated: team tasks UI has been removed, kept for compatibility with older callers/tests. */
|
|
61
|
+
onTaskClick?: (task: TeamTaskWithKanban) => void;
|
|
61
62
|
onRemoveMember?: () => void;
|
|
62
63
|
onRestartMember?: (memberName: string) => Promise<void> | void;
|
|
63
64
|
onUpdateRole?: (memberName: string, role: string | undefined) => Promise<void> | void;
|
|
@@ -83,18 +84,12 @@ export const MemberDetailDialog = ({
|
|
|
83
84
|
launchParams,
|
|
84
85
|
onClose,
|
|
85
86
|
onSendMessage,
|
|
86
|
-
onAssignTask,
|
|
87
|
-
onTaskClick,
|
|
88
87
|
onRemoveMember,
|
|
89
88
|
onRestartMember,
|
|
90
89
|
onUpdateRole,
|
|
91
90
|
updatingRole,
|
|
92
91
|
onViewMemberChanges,
|
|
93
92
|
}: MemberDetailDialogProps): React.JSX.Element | null => {
|
|
94
|
-
const memberTasks = useMemo(
|
|
95
|
-
() => (member ? tasks.filter((t) => t.owner === member.name) : []),
|
|
96
|
-
[tasks, member]
|
|
97
|
-
);
|
|
98
93
|
const memberMessages = useStore((state) =>
|
|
99
94
|
selectMemberMessagesForTeamMember(state, teamName, member?.name ?? null)
|
|
100
95
|
);
|
|
@@ -111,17 +106,9 @@ export const MemberDetailDialog = ({
|
|
|
111
106
|
}).length;
|
|
112
107
|
}, [member, memberMessages, members, tasks, teamName]);
|
|
113
108
|
|
|
114
|
-
const
|
|
115
|
-
|
|
116
|
-
[memberTasks]
|
|
117
|
-
);
|
|
118
|
-
|
|
119
|
-
const completedTasks = useMemo(
|
|
120
|
-
() => memberTasks.filter((t) => t.status === 'completed').length,
|
|
121
|
-
[memberTasks]
|
|
109
|
+
const [activeTab, setActiveTab] = useState<MemberDetailTab>(
|
|
110
|
+
initialTab === 'tasks' ? 'workspace' : initialTab
|
|
122
111
|
);
|
|
123
|
-
|
|
124
|
-
const [activeTab, setActiveTab] = useState<MemberDetailTab>(initialTab);
|
|
125
112
|
const [restarting, setRestarting] = useState(false);
|
|
126
113
|
const [restartError, setRestartError] = useState<string | null>(null);
|
|
127
114
|
|
|
@@ -202,8 +189,8 @@ export const MemberDetailDialog = ({
|
|
|
202
189
|
</DialogHeader>
|
|
203
190
|
|
|
204
191
|
<MemberDetailStats
|
|
205
|
-
totalTasks={
|
|
206
|
-
inProgressTasks={
|
|
192
|
+
totalTasks={0}
|
|
193
|
+
inProgressTasks={0}
|
|
207
194
|
totalTokens={totalTokens}
|
|
208
195
|
statsLoading={statsLoading}
|
|
209
196
|
statsComputedAt={memberStats?.computedAt}
|
|
@@ -217,14 +204,6 @@ export const MemberDetailDialog = ({
|
|
|
217
204
|
className="min-w-0 overflow-hidden"
|
|
218
205
|
>
|
|
219
206
|
<TabsList className="w-full">
|
|
220
|
-
<TabsTrigger value="tasks" className="flex-1 gap-1.5">
|
|
221
|
-
Tasks
|
|
222
|
-
{memberTasks.length > 0 && (
|
|
223
|
-
<span className="rounded-full bg-[var(--color-surface)] px-1.5 text-[10px]">
|
|
224
|
-
{memberTasks.length}
|
|
225
|
-
</span>
|
|
226
|
-
)}
|
|
227
|
-
</TabsTrigger>
|
|
228
207
|
<TabsTrigger value="workspace" className="flex-1 gap-1.5">
|
|
229
208
|
<FolderOpen size={12} />
|
|
230
209
|
Workspace
|
|
@@ -242,9 +221,6 @@ export const MemberDetailDialog = ({
|
|
|
242
221
|
Stats
|
|
243
222
|
</TabsTrigger>
|
|
244
223
|
</TabsList>
|
|
245
|
-
<TabsContent value="tasks">
|
|
246
|
-
<MemberTasksTab tasks={memberTasks} onTaskClick={onTaskClick} />
|
|
247
|
-
</TabsContent>
|
|
248
224
|
<TabsContent value="workspace">
|
|
249
225
|
<MemberWorkspaceTab
|
|
250
226
|
teamName={teamName}
|
|
@@ -260,7 +236,6 @@ export const MemberDetailDialog = ({
|
|
|
260
236
|
members={members}
|
|
261
237
|
tasks={tasks}
|
|
262
238
|
initialFilter={initialActivityFilter}
|
|
263
|
-
onTaskClick={onTaskClick}
|
|
264
239
|
/>
|
|
265
240
|
</TabsContent>
|
|
266
241
|
<TabsContent value="stats">
|
|
@@ -69,6 +69,11 @@ interface MessageComposerProps {
|
|
|
69
69
|
actionMode?: AgentActionMode,
|
|
70
70
|
taskRefs?: TaskRef[]
|
|
71
71
|
) => void;
|
|
72
|
+
onDispatchTask?: (
|
|
73
|
+
toTeam: string,
|
|
74
|
+
subject: string,
|
|
75
|
+
description: string
|
|
76
|
+
) => Promise<boolean | void> | boolean | void;
|
|
72
77
|
}
|
|
73
78
|
|
|
74
79
|
export const MessageComposer = ({
|
|
@@ -86,6 +91,7 @@ export const MessageComposer = ({
|
|
|
86
91
|
onSessionChange,
|
|
87
92
|
textareaRef: externalTextareaRef,
|
|
88
93
|
onSend,
|
|
94
|
+
onDispatchTask,
|
|
89
95
|
}: MessageComposerProps): React.JSX.Element => {
|
|
90
96
|
const internalTextareaRef = useRef<HTMLTextAreaElement>(null);
|
|
91
97
|
const textareaRef = useMemo(() => {
|
|
@@ -223,7 +229,26 @@ export const MessageComposer = ({
|
|
|
223
229
|
? '斜杠命令需要团队负责人在线'
|
|
224
230
|
: null
|
|
225
231
|
: null;
|
|
226
|
-
const
|
|
232
|
+
const teamDispatch = useMemo(() => {
|
|
233
|
+
const match = trimmed.match(/^@([^\s]+)\s+([\s\S]+)$/);
|
|
234
|
+
if (!match || !onDispatchTask) return null;
|
|
235
|
+
const mentioned = match[1];
|
|
236
|
+
const subject = match[2]?.trim();
|
|
237
|
+
if (!mentioned || !subject) return null;
|
|
238
|
+
const targetTeam = teamMentionSuggestions.find((team) => {
|
|
239
|
+
const slug = team.id.startsWith('team:') ? team.id.slice('team:'.length) : team.id;
|
|
240
|
+
return slug === mentioned || team.name === mentioned;
|
|
241
|
+
});
|
|
242
|
+
const slug = targetTeam
|
|
243
|
+
? targetTeam.id.startsWith('team:')
|
|
244
|
+
? targetTeam.id.slice('team:'.length)
|
|
245
|
+
: targetTeam.id
|
|
246
|
+
: mentioned;
|
|
247
|
+
return { slug, subject };
|
|
248
|
+
}, [onDispatchTask, teamMentionSuggestions, trimmed]);
|
|
249
|
+
const canDispatchToTeam =
|
|
250
|
+
teamDispatch !== null && trimmed.length > 0 && trimmed.length <= MAX_TEXT_LENGTH && !sending;
|
|
251
|
+
const canSendRegularMessage =
|
|
227
252
|
recipient.length > 0 &&
|
|
228
253
|
trimmed.length > 0 &&
|
|
229
254
|
trimmed.length <= MAX_TEXT_LENGTH &&
|
|
@@ -231,6 +256,7 @@ export const MessageComposer = ({
|
|
|
231
256
|
!isProvisioning &&
|
|
232
257
|
!attachmentsBlocked &&
|
|
233
258
|
!slashCommandRestrictionReason;
|
|
259
|
+
const canSend = canDispatchToTeam || canSendRegularMessage;
|
|
234
260
|
|
|
235
261
|
// Track whether we initiated a send — clear draft only on confirmed success
|
|
236
262
|
const pendingSendRef = useRef(false);
|
|
@@ -238,9 +264,19 @@ export const MessageComposer = ({
|
|
|
238
264
|
const handleSend = useCallback(() => {
|
|
239
265
|
if (!canSend) return;
|
|
240
266
|
dismissMentionsRef.current?.();
|
|
241
|
-
pendingSendRef.current = true;
|
|
242
267
|
const taskRefs = extractTaskRefsFromText(draft.text, taskSuggestions);
|
|
243
268
|
const serialized = serializeChipsWithText(trimmed, draft.chips);
|
|
269
|
+
|
|
270
|
+
if (teamDispatch && onDispatchTask) {
|
|
271
|
+
void Promise.resolve(
|
|
272
|
+
onDispatchTask(teamDispatch.slug, teamDispatch.subject, serialized)
|
|
273
|
+
).then((dispatched) => {
|
|
274
|
+
if (dispatched !== false) draft.clearDraft();
|
|
275
|
+
});
|
|
276
|
+
return;
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
pendingSendRef.current = true;
|
|
244
280
|
onSend(
|
|
245
281
|
recipient,
|
|
246
282
|
serialized,
|
|
@@ -249,7 +285,7 @@ export const MessageComposer = ({
|
|
|
249
285
|
undefined,
|
|
250
286
|
taskRefs
|
|
251
287
|
);
|
|
252
|
-
}, [canSend, recipient, trimmed, onSend, draft
|
|
288
|
+
}, [canSend, recipient, trimmed, onSend, draft, taskSuggestions, teamDispatch, onDispatchTask]);
|
|
253
289
|
|
|
254
290
|
// Clear draft only after send completes successfully (sending: true → false, no error)
|
|
255
291
|
useEffect(() => {
|