@yancyyu/openhermit 1.6.29 → 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-CQm6jUR1.js → ProjectEditorOverlay-DsQt4FHy.js} +1 -1
- package/dist-renderer/assets/{TeamGraphOverlay-h0WDfifv.js → TeamGraphOverlay-BjZC53xf.js} +1 -1
- package/dist-renderer/assets/{_basePickBy-CgG_tjgX.js → _basePickBy-CrWocIjq.js} +1 -1
- package/dist-renderer/assets/{_baseUniq-DwPTU9lP.js → _baseUniq-B6d8ysWi.js} +1 -1
- package/dist-renderer/assets/{arc-7nIrGRzY.js → arc-DAIYCFP8.js} +1 -1
- package/dist-renderer/assets/{architectureDiagram-VXUJARFQ-BYhA6Ev2.js → architectureDiagram-VXUJARFQ-B3UudXJh.js} +1 -1
- package/dist-renderer/assets/{blockDiagram-VD42YOAC-BVpZUGDg.js → blockDiagram-VD42YOAC-DbptKQ4W.js} +1 -1
- package/dist-renderer/assets/{c4Diagram-YG6GDRKO-DsdreMQ9.js → c4Diagram-YG6GDRKO-C4WQuZpV.js} +1 -1
- package/dist-renderer/assets/channel-DbjZvWii.js +1 -0
- package/dist-renderer/assets/{chunk-4BX2VUAB-CcoAs7Jd.js → chunk-4BX2VUAB-Dp7fVpI_.js} +1 -1
- package/dist-renderer/assets/{chunk-55IACEB6-CGGAOoXd.js → chunk-55IACEB6-B8KGfbAy.js} +1 -1
- package/dist-renderer/assets/{chunk-B4BG7PRW-FhpTEPvD.js → chunk-B4BG7PRW-BG1oJrjA.js} +1 -1
- package/dist-renderer/assets/{chunk-DI55MBZ5-DoYySbm1.js → chunk-DI55MBZ5-DRmxNjht.js} +1 -1
- package/dist-renderer/assets/{chunk-FMBD7UC4-e9l2tGHG.js → chunk-FMBD7UC4-D6VLvy16.js} +1 -1
- package/dist-renderer/assets/{chunk-QN33PNHL-DeiXVTCy.js → chunk-QN33PNHL-DZou1667.js} +1 -1
- package/dist-renderer/assets/{chunk-QZHKN3VN-DC2UJLJM.js → chunk-QZHKN3VN-CghmasSh.js} +1 -1
- package/dist-renderer/assets/{chunk-TZMSLE5B-BHFD9eZI.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-BdybQraU.js → cose-bilkent-S5V4N54A-05e5uQDp.js} +1 -1
- package/dist-renderer/assets/{dagre-6UL2VRFP-DdF3pwM3.js → dagre-6UL2VRFP-B06bRykF.js} +1 -1
- package/dist-renderer/assets/{diagram-PSM6KHXK-B9Ldd3nh.js → diagram-PSM6KHXK-CY7VYQ7c.js} +1 -1
- package/dist-renderer/assets/{diagram-QEK2KX5R-XEqkrbpu.js → diagram-QEK2KX5R-BjKEH7dD.js} +1 -1
- package/dist-renderer/assets/{diagram-S2PKOQOG-CipwtY59.js → diagram-S2PKOQOG-Bf4ELS1_.js} +1 -1
- package/dist-renderer/assets/{erDiagram-Q2GNP2WA-BB-2ISGo.js → erDiagram-Q2GNP2WA-DJ753_L9.js} +1 -1
- package/dist-renderer/assets/{flowDiagram-NV44I4VS-B8XmJ0u2.js → flowDiagram-NV44I4VS-B71S-lC-.js} +1 -1
- package/dist-renderer/assets/{ganttDiagram-JELNMOA3-D-8XglBb.js → ganttDiagram-JELNMOA3-C_U42mSZ.js} +1 -1
- package/dist-renderer/assets/{gitGraphDiagram-V2S2FVAM-DL4ChakD.js → gitGraphDiagram-V2S2FVAM-DKUJU4Ns.js} +1 -1
- package/dist-renderer/assets/{graph-BiFNoBjP.js → graph-DY3qbzqj.js} +1 -1
- package/dist-renderer/assets/{index-BowUl0Jb.js → index-BlOrAXp3.js} +542 -532
- package/dist-renderer/assets/{index-6m1ZAymG.js → index-Bs27J5gB.js} +1 -1
- package/dist-renderer/assets/{index-Dp3kJTEe.js → index-C8B_nKOF.js} +1 -1
- package/dist-renderer/assets/index-CmZPUEhS.css +1 -0
- package/dist-renderer/assets/{index-TOpt_T7A.js → index-DLKyDr4T.js} +1 -1
- package/dist-renderer/assets/{index-qNBNjW4K.js → index-Dhsk3_DD.js} +1 -1
- package/dist-renderer/assets/{index-vAykq1H1.js → index-GpUvV2xs.js} +1 -1
- package/dist-renderer/assets/{infoDiagram-HS3SLOUP-DRIBfHDi.js → infoDiagram-HS3SLOUP-BNs0y3IG.js} +1 -1
- package/dist-renderer/assets/{journeyDiagram-XKPGCS4Q-BOMiigU4.js → journeyDiagram-XKPGCS4Q-CqPnw4UV.js} +1 -1
- package/dist-renderer/assets/{kanban-definition-3W4ZIXB7-DDxeyjod.js → kanban-definition-3W4ZIXB7-SLlzcUJ2.js} +1 -1
- package/dist-renderer/assets/{layout-DNANbrI4.js → layout-BZLlNmbr.js} +1 -1
- package/dist-renderer/assets/{linear-DxEJi1yT.js → linear-qz6v45xy.js} +1 -1
- package/dist-renderer/assets/{mindmap-definition-VGOIOE7T-nBfGriW8.js → mindmap-definition-VGOIOE7T-B1-kmEWV.js} +1 -1
- package/dist-renderer/assets/{pieDiagram-ADFJNKIX-Din5j6sV.js → pieDiagram-ADFJNKIX-B8a02iNx.js} +1 -1
- package/dist-renderer/assets/{quadrantDiagram-AYHSOK5B-DMVK2BEQ.js → quadrantDiagram-AYHSOK5B-BKv1Xfou.js} +1 -1
- package/dist-renderer/assets/{requirementDiagram-UZGBJVZJ-6SC94Gg_.js → requirementDiagram-UZGBJVZJ-B3DUpZi2.js} +1 -1
- package/dist-renderer/assets/{sankeyDiagram-TZEHDZUN-CD2gghhu.js → sankeyDiagram-TZEHDZUN-DmPzuTsy.js} +1 -1
- package/dist-renderer/assets/{sequenceDiagram-WL72ISMW-BnhkN7nZ.js → sequenceDiagram-WL72ISMW-Bo7RelRb.js} +1 -1
- package/dist-renderer/assets/{stateDiagram-FKZM4ZOC-Bn8XdYX-.js → stateDiagram-FKZM4ZOC-1epX98gV.js} +1 -1
- package/dist-renderer/assets/{stateDiagram-v2-4FDKWEC3-1b6sI1_g.js → stateDiagram-v2-4FDKWEC3-03Ym9PTr.js} +1 -1
- package/dist-renderer/assets/{timeline-definition-IT6M3QCI-CNs3RPoa.js → timeline-definition-IT6M3QCI-r6isC62H.js} +1 -1
- package/dist-renderer/assets/treemap-GDKQZRPO-CGKpOUF2.js +162 -0
- package/dist-renderer/assets/{xychartDiagram-PRI3JC2R-B8o5J2f3.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 +209 -6
- 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/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/TaskDispatchService.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/api/httpClient.ts +61 -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/settings/sections/HarnessSection.tsx +2 -6
- package/src/renderer/components/settings/sections/TaskBusSection.tsx +17 -7
- package/src/renderer/components/sidebar/SidebarSessions.tsx +23 -0
- package/src/renderer/components/sidebar/WorkspaceBrowser.tsx +1 -7
- package/src/renderer/components/team/HarnessSelect.tsx +71 -0
- package/src/renderer/components/team/TeamDetailView.tsx +35 -0
- 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/kanban/KanbanBoard.tsx +26 -64
- package/src/renderer/components/team/messages/MessagesPanel.tsx +28 -24
- package/src/renderer/components/terminal/TerminalPanel.tsx +156 -0
- package/src/renderer/hooks/useExtensionsTabState.ts +2 -2
- package/src/renderer/store/slices/extensionsSlice.ts +42 -107
- package/src/renderer/store/slices/teamSlice.ts +8 -2
- package/src/shared/types/api.ts +29 -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/utils/providerExtensionCapabilities.ts +1 -1
- package/dist-renderer/assets/channel-C0SqeFU7.js +0 -1
- package/dist-renderer/assets/classDiagram-2ON5EDUG-DWew1HpM.js +0 -1
- package/dist-renderer/assets/classDiagram-v2-WZHVMYZB-DWew1HpM.js +0 -1
- package/dist-renderer/assets/clone-Dm-k63Yr.js +0 -1
- package/dist-renderer/assets/index-BhellmRb.css +0 -1
- package/dist-renderer/assets/treemap-GDKQZRPO-DU_yr827.js +0 -162
- 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
|
@@ -23,7 +23,6 @@ import {
|
|
|
23
23
|
Info,
|
|
24
24
|
Plus,
|
|
25
25
|
Search,
|
|
26
|
-
Trash2,
|
|
27
26
|
} from 'lucide-react';
|
|
28
27
|
import { useShallow } from 'zustand/react/shallow';
|
|
29
28
|
|
|
@@ -35,12 +34,7 @@ import { SkillImportDialog } from './SkillImportDialog';
|
|
|
35
34
|
import { resolveSkillProjectPath } from './skillProjectUtils';
|
|
36
35
|
|
|
37
36
|
import type { SkillsSortState } from '@renderer/hooks/useExtensionsTabState';
|
|
38
|
-
import type {
|
|
39
|
-
SkillCatalogItem,
|
|
40
|
-
SkillDetail,
|
|
41
|
-
SkillSource,
|
|
42
|
-
SkillValidationIssue,
|
|
43
|
-
} from '@shared/types/extensions';
|
|
37
|
+
import type { SkillCatalogItem, SkillDetail, SkillValidationIssue } from '@shared/types/extensions';
|
|
44
38
|
|
|
45
39
|
const SUCCESS_BANNER_MS = 2500;
|
|
46
40
|
const NEW_SKILL_HIGHLIGHT_MS = 4000;
|
|
@@ -141,10 +135,6 @@ export const SkillsPanel = ({
|
|
|
141
135
|
const [quickFilter, setQuickFilter] = useState<SkillsQuickFilter>('all');
|
|
142
136
|
const [successMessage, setSuccessMessage] = useState<string | null>(null);
|
|
143
137
|
const [highlightedSkillId, setHighlightedSkillId] = useState<string | null>(null);
|
|
144
|
-
const [skillSources, setSkillSources] = useState<SkillSource[]>([]);
|
|
145
|
-
const [newSkillSourceUrl, setNewSkillSourceUrl] = useState('');
|
|
146
|
-
const [skillSourceLoading, setSkillSourceLoading] = useState(false);
|
|
147
|
-
const [skillSourceError, setSkillSourceError] = useState<string | null>(null);
|
|
148
138
|
const selectedSkillIdRef = useRef<string | null>(selectedSkillId);
|
|
149
139
|
const selectedSkillItemRef = useRef<SkillCatalogItem | null>(null);
|
|
150
140
|
selectedSkillIdRef.current = selectedSkillId;
|
|
@@ -221,55 +211,6 @@ export const SkillsPanel = ({
|
|
|
221
211
|
};
|
|
222
212
|
}, [fetchSkillDetail, fetchSkillsCatalog, projectPath]);
|
|
223
213
|
|
|
224
|
-
useEffect(() => {
|
|
225
|
-
if (!api.skills) return;
|
|
226
|
-
void api.skills
|
|
227
|
-
.listSources()
|
|
228
|
-
.then((snapshot) => setSkillSources(snapshot.sources))
|
|
229
|
-
.catch(() => undefined);
|
|
230
|
-
}, []);
|
|
231
|
-
|
|
232
|
-
const saveAndRefreshSkillSources = async (sources: SkillSource[]): Promise<void> => {
|
|
233
|
-
if (!api.skills) return;
|
|
234
|
-
setSkillSourceLoading(true);
|
|
235
|
-
setSkillSourceError(null);
|
|
236
|
-
try {
|
|
237
|
-
const saved = await api.skills.saveSources(sources);
|
|
238
|
-
setSkillSources(saved.sources);
|
|
239
|
-
const refreshed = await api.skills.refreshSources();
|
|
240
|
-
setSkillSources(refreshed.sources);
|
|
241
|
-
await fetchSkillsCatalog(projectPath ?? undefined);
|
|
242
|
-
setSuccessMessage('Skills 源已同步到 ~/.hermit/skills');
|
|
243
|
-
} catch (error) {
|
|
244
|
-
setSkillSourceError(error instanceof Error ? error.message : '同步 Skills 源失败');
|
|
245
|
-
} finally {
|
|
246
|
-
setSkillSourceLoading(false);
|
|
247
|
-
}
|
|
248
|
-
};
|
|
249
|
-
|
|
250
|
-
const handleAddSkillSource = async (): Promise<void> => {
|
|
251
|
-
const url = newSkillSourceUrl.trim();
|
|
252
|
-
if (!url) return;
|
|
253
|
-
const id =
|
|
254
|
-
url
|
|
255
|
-
.replace(/\.git$/, '')
|
|
256
|
-
.split(/[/:]/)
|
|
257
|
-
.filter(Boolean)
|
|
258
|
-
.slice(-2)
|
|
259
|
-
.join('-')
|
|
260
|
-
.toLowerCase()
|
|
261
|
-
.replace(/[^a-z0-9._-]+/g, '-') || `source-${Date.now().toString(36)}`;
|
|
262
|
-
await saveAndRefreshSkillSources([
|
|
263
|
-
...skillSources,
|
|
264
|
-
{ id, name: id, url, enabled: true, branch: 'main' },
|
|
265
|
-
]);
|
|
266
|
-
setNewSkillSourceUrl('');
|
|
267
|
-
};
|
|
268
|
-
|
|
269
|
-
const handleRemoveSkillSource = async (sourceId: string): Promise<void> => {
|
|
270
|
-
await saveAndRefreshSkillSources(skillSources.filter((source) => source.id !== sourceId));
|
|
271
|
-
};
|
|
272
|
-
|
|
273
214
|
const visibleSkills = useMemo(() => {
|
|
274
215
|
const q = skillsSearchQuery.trim().toLowerCase();
|
|
275
216
|
const filteredByQuery = q
|
|
@@ -307,71 +248,6 @@ export const SkillsPanel = ({
|
|
|
307
248
|
|
|
308
249
|
return (
|
|
309
250
|
<div className="flex flex-col gap-4">
|
|
310
|
-
<div className="rounded-md border border-blue-500/30 bg-blue-500/5 px-4 py-3 text-sm text-blue-300">
|
|
311
|
-
全局技能由 `~/.hermit/skills` 管理,并在团队启动前投影到各 runtime 的全局 skills
|
|
312
|
-
目录。项目技能直接安装到你选择的项目 runtime 目录。
|
|
313
|
-
</div>
|
|
314
|
-
<div className="bg-surface-raised/20 rounded-xl border border-border p-4">
|
|
315
|
-
<div className="flex flex-col gap-3 lg:flex-row lg:items-end">
|
|
316
|
-
<div className="min-w-0 flex-1 space-y-1">
|
|
317
|
-
<h3 className="text-sm font-semibold text-text">全局 Skills 源</h3>
|
|
318
|
-
<p className="text-xs text-text-muted">
|
|
319
|
-
Git 源会同步到 `~/.hermit/skills`,随后由 Hermit 投影给 Claude、Cursor、Codex
|
|
320
|
-
等运行时。
|
|
321
|
-
</p>
|
|
322
|
-
<p className="text-xs text-text-muted">
|
|
323
|
-
打开面板只读取本地缓存;只有点击“添加并同步”或“刷新源”才会访问 GitHub 更新。
|
|
324
|
-
</p>
|
|
325
|
-
<input
|
|
326
|
-
value={newSkillSourceUrl}
|
|
327
|
-
onChange={(event) => setNewSkillSourceUrl(event.target.value)}
|
|
328
|
-
placeholder="https://github.com/yancyuu/HermitSkills"
|
|
329
|
-
className="mt-2 h-8 w-full rounded-md border border-border bg-surface px-2 text-xs text-text"
|
|
330
|
-
/>
|
|
331
|
-
</div>
|
|
332
|
-
<Button
|
|
333
|
-
variant="outline"
|
|
334
|
-
size="sm"
|
|
335
|
-
disabled={skillSourceLoading || !newSkillSourceUrl.trim()}
|
|
336
|
-
onClick={() => void handleAddSkillSource()}
|
|
337
|
-
>
|
|
338
|
-
{skillSourceLoading ? '同步中...' : '添加并同步'}
|
|
339
|
-
</Button>
|
|
340
|
-
<Button
|
|
341
|
-
variant="outline"
|
|
342
|
-
size="sm"
|
|
343
|
-
disabled={skillSourceLoading || skillSources.length === 0}
|
|
344
|
-
onClick={() => void saveAndRefreshSkillSources(skillSources)}
|
|
345
|
-
>
|
|
346
|
-
刷新源
|
|
347
|
-
</Button>
|
|
348
|
-
</div>
|
|
349
|
-
{skillSourceError ? <p className="mt-2 text-xs text-red-400">{skillSourceError}</p> : null}
|
|
350
|
-
{skillSources.length > 0 ? (
|
|
351
|
-
<div className="mt-3 flex flex-wrap gap-1.5">
|
|
352
|
-
{skillSources.map((source) => (
|
|
353
|
-
<span
|
|
354
|
-
key={source.id}
|
|
355
|
-
className="inline-flex max-w-full items-center gap-1 rounded bg-surface-raised px-1.5 py-0.5 text-[10px] text-text-muted"
|
|
356
|
-
title={source.url}
|
|
357
|
-
>
|
|
358
|
-
<span className="max-w-44 truncate">
|
|
359
|
-
{source.name}
|
|
360
|
-
{source.lastError ? ' · 同步失败' : ''}
|
|
361
|
-
</span>
|
|
362
|
-
<button
|
|
363
|
-
type="button"
|
|
364
|
-
className="-mr-0.5 inline-flex size-4 shrink-0 items-center justify-center rounded hover:bg-red-500/10 hover:text-red-300"
|
|
365
|
-
onClick={() => void handleRemoveSkillSource(source.id)}
|
|
366
|
-
aria-label={`删除 Skills 源 ${source.name}`}
|
|
367
|
-
>
|
|
368
|
-
<Trash2 size={10} />
|
|
369
|
-
</button>
|
|
370
|
-
</span>
|
|
371
|
-
))}
|
|
372
|
-
</div>
|
|
373
|
-
) : null}
|
|
374
|
-
</div>
|
|
375
251
|
<div className="bg-surface-raised/20 rounded-xl border border-border p-4">
|
|
376
252
|
<div className="flex flex-col gap-4 xl:flex-row xl:items-start xl:justify-between">
|
|
377
253
|
<div className="min-w-0 flex-1 space-y-1 xl:max-w-2xl">
|
|
@@ -387,7 +263,6 @@ export const SkillsPanel = ({
|
|
|
387
263
|
</p>
|
|
388
264
|
<p className="max-w-2xl text-xs leading-5 text-text-muted">
|
|
389
265
|
需要到处生效的习惯请使用个人技能;只对当前代码库有意义的流程请使用项目技能。
|
|
390
|
-
全局技能从 Hermit 源统一投影;项目技能直接写入所选项目 runtime 目录。
|
|
391
266
|
</p>
|
|
392
267
|
</div>
|
|
393
268
|
|
|
@@ -10,6 +10,7 @@ import { useCallback, useEffect, useState } from 'react';
|
|
|
10
10
|
import { providersApi } from '@renderer/api/providers';
|
|
11
11
|
import { ALL_AGENT_TYPES, AGENT_TYPE_LABELS } from '@renderer/components/team/HarnessCards';
|
|
12
12
|
import type { CcAgentType } from '@renderer/components/team/HarnessCards';
|
|
13
|
+
import { HarnessIcon } from '@renderer/components/team/HarnessSelect';
|
|
13
14
|
import { cn } from '@renderer/lib/utils';
|
|
14
15
|
import { OPEN_HERMIT_EVENTS } from '@renderer/utils/openHermitEvents';
|
|
15
16
|
import { CheckCircle2 } from 'lucide-react';
|
|
@@ -115,12 +116,7 @@ export const HarnessSection = (): React.JSX.Element => {
|
|
|
115
116
|
background: 'var(--color-surface-raised)',
|
|
116
117
|
}}
|
|
117
118
|
>
|
|
118
|
-
<
|
|
119
|
-
className={cn(
|
|
120
|
-
'h-2 w-2 shrink-0 rounded-full',
|
|
121
|
-
covered ? 'bg-green-500' : 'bg-gray-500'
|
|
122
|
-
)}
|
|
123
|
-
/>
|
|
119
|
+
<HarnessIcon type={type} className="size-5 shrink-0" />
|
|
124
120
|
<div className="min-w-0 flex-1">
|
|
125
121
|
<p
|
|
126
122
|
className="truncate text-xs font-medium"
|
|
@@ -230,24 +230,34 @@ export function TaskBusSection(): React.JSX.Element {
|
|
|
230
230
|
.finally(() => setLoading(false));
|
|
231
231
|
|
|
232
232
|
// Restore telemetry status + Redis connection state on mount
|
|
233
|
-
fetch('/api/
|
|
233
|
+
fetch('/api/settings/task-bus')
|
|
234
234
|
.then((r) => r.json())
|
|
235
|
-
.then((
|
|
236
|
-
if (
|
|
237
|
-
|
|
235
|
+
.then((busConfig: TaskBusConfig) => {
|
|
236
|
+
if (busConfig.enabled) {
|
|
237
|
+
fetch('/api/telemetry/status')
|
|
238
|
+
.then((r) => r.json())
|
|
239
|
+
.then((s: TelemetryStatus) => {
|
|
240
|
+
if (s.connected) setConnected(true);
|
|
241
|
+
if ('sessions' in s && s.sessions > 0) setTelemetryStatus(s);
|
|
242
|
+
})
|
|
243
|
+
.catch(() => {});
|
|
244
|
+
}
|
|
238
245
|
})
|
|
239
246
|
.catch(() => {});
|
|
240
247
|
|
|
241
248
|
const poll = setInterval(() => {
|
|
242
|
-
if (collectionEnabled) {
|
|
249
|
+
if (enabled && collectionEnabled) {
|
|
243
250
|
fetch('/api/telemetry/status')
|
|
244
251
|
.then((r) => r.json())
|
|
245
|
-
.then((s: TelemetryStatus) =>
|
|
252
|
+
.then((s: TelemetryStatus) => {
|
|
253
|
+
setTelemetryStatus(s);
|
|
254
|
+
setConnected(s.connected === true);
|
|
255
|
+
})
|
|
246
256
|
.catch(() => {});
|
|
247
257
|
}
|
|
248
258
|
}, 30000);
|
|
249
259
|
return () => clearInterval(poll);
|
|
250
|
-
}, [collectionEnabled]);
|
|
260
|
+
}, [enabled, collectionEnabled]);
|
|
251
261
|
|
|
252
262
|
const buildConfig = (
|
|
253
263
|
overrides: Partial<{
|
|
@@ -402,6 +402,29 @@ const SessionRow = ({
|
|
|
402
402
|
};
|
|
403
403
|
}, [historyLimit, isExpanded, session.teamName, session.id]);
|
|
404
404
|
|
|
405
|
+
// While a live session stays expanded, keep its detail fresh with a silent
|
|
406
|
+
// refetch (no skeleton flash) so newly arrived messages show without needing
|
|
407
|
+
// to collapse and reopen.
|
|
408
|
+
useEffect(() => {
|
|
409
|
+
if (!isExpanded || !session.live) {
|
|
410
|
+
return;
|
|
411
|
+
}
|
|
412
|
+
const intervalId = window.setInterval(() => {
|
|
413
|
+
if (document.visibilityState !== 'visible') {
|
|
414
|
+
return;
|
|
415
|
+
}
|
|
416
|
+
void (async () => {
|
|
417
|
+
try {
|
|
418
|
+
const d = await api.teams.getSessionDetail(session.teamName, session.id, historyLimit);
|
|
419
|
+
setDetail(d);
|
|
420
|
+
} catch {
|
|
421
|
+
// silent — transient fetch failures are retried on the next tick
|
|
422
|
+
}
|
|
423
|
+
})();
|
|
424
|
+
}, REFRESH_INTERVAL_MS);
|
|
425
|
+
return () => window.clearInterval(intervalId);
|
|
426
|
+
}, [isExpanded, session.live, session.teamName, session.id, historyLimit]);
|
|
427
|
+
|
|
405
428
|
const handleLoadMoreHistory = useCallback(() => {
|
|
406
429
|
if (loadingDetail || loadingMoreHistory || !hasMoreHistory) {
|
|
407
430
|
return;
|
|
@@ -217,13 +217,7 @@ const TeamWorkspace = ({
|
|
|
217
217
|
<button
|
|
218
218
|
key={entry.name}
|
|
219
219
|
type="button"
|
|
220
|
-
className=
|
|
221
|
-
entry.isDirectory
|
|
222
|
-
? 'cursor-pointer hover:bg-[var(--color-surface-raised)]'
|
|
223
|
-
: onFileClick
|
|
224
|
-
? 'cursor-pointer hover:bg-[var(--color-surface-raised)]'
|
|
225
|
-
: 'hover:bg-[var(--color-surface-raised)]/50 cursor-default'
|
|
226
|
-
}`}
|
|
220
|
+
className="flex w-full cursor-pointer items-center gap-2 px-3 py-1.5 text-left text-xs transition-colors hover:bg-[var(--color-surface-raised)]"
|
|
227
221
|
onClick={() => handleEntryClick(entry)}
|
|
228
222
|
>
|
|
229
223
|
{entry.isDirectory ? (
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import type { CcAgentType } from '@shared/types/ccConnect';
|
|
2
|
+
import { ProviderBrandLogo } from '@renderer/components/common/ProviderBrandLogo';
|
|
3
|
+
import {
|
|
4
|
+
Select,
|
|
5
|
+
SelectContent,
|
|
6
|
+
SelectItem,
|
|
7
|
+
SelectTrigger,
|
|
8
|
+
SelectValue,
|
|
9
|
+
} from '@renderer/components/ui/select';
|
|
10
|
+
import { ALL_AGENT_TYPES, AGENT_TYPE_LABELS } from './HarnessCards';
|
|
11
|
+
|
|
12
|
+
interface HarnessSelectProps {
|
|
13
|
+
value: CcAgentType;
|
|
14
|
+
onChange: (value: CcAgentType) => void;
|
|
15
|
+
className?: string;
|
|
16
|
+
id?: string;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const HARNESS_PROVIDER_MAP: Partial<
|
|
20
|
+
Record<CcAgentType, 'anthropic' | 'codex' | 'gemini' | 'opencode'>
|
|
21
|
+
> = {
|
|
22
|
+
claudecode: 'anthropic',
|
|
23
|
+
codex: 'codex',
|
|
24
|
+
gemini: 'gemini',
|
|
25
|
+
opencode: 'opencode',
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
function HarnessIcon({ type, className }: { type: CcAgentType; className?: string }) {
|
|
29
|
+
const providerId = HARNESS_PROVIDER_MAP[type];
|
|
30
|
+
if (providerId) {
|
|
31
|
+
return <ProviderBrandLogo providerId={providerId} className={className} />;
|
|
32
|
+
}
|
|
33
|
+
return <span className={className}>{EMOJI_FALLBACK[type]}</span>;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const EMOJI_FALLBACK: Record<CcAgentType, string> = {
|
|
37
|
+
claudecode: '🤖',
|
|
38
|
+
codex: '🔬',
|
|
39
|
+
cursor: '💻',
|
|
40
|
+
gemini: '💎',
|
|
41
|
+
iflow: '🌊',
|
|
42
|
+
kimi: '🌙',
|
|
43
|
+
devin: '🧑💻',
|
|
44
|
+
opencode: '🔓',
|
|
45
|
+
qoder: '⚡',
|
|
46
|
+
pi: '🥧',
|
|
47
|
+
acp: '🔗',
|
|
48
|
+
tmux: '🖥️',
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
export function HarnessSelect({ value, onChange, className, id }: HarnessSelectProps) {
|
|
52
|
+
return (
|
|
53
|
+
<Select value={value} onValueChange={(v) => onChange(v as CcAgentType)}>
|
|
54
|
+
<SelectTrigger id={id} className={className}>
|
|
55
|
+
<SelectValue />
|
|
56
|
+
</SelectTrigger>
|
|
57
|
+
<SelectContent>
|
|
58
|
+
{ALL_AGENT_TYPES.map((type) => (
|
|
59
|
+
<SelectItem key={type} value={type}>
|
|
60
|
+
<div className="flex items-center gap-2">
|
|
61
|
+
<HarnessIcon type={type} className="size-4 shrink-0" />
|
|
62
|
+
<span>{AGENT_TYPE_LABELS[type]}</span>
|
|
63
|
+
</div>
|
|
64
|
+
</SelectItem>
|
|
65
|
+
))}
|
|
66
|
+
</SelectContent>
|
|
67
|
+
</Select>
|
|
68
|
+
);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
export { HarnessIcon, HARNESS_PROVIDER_MAP, EMOJI_FALLBACK };
|
|
@@ -69,6 +69,7 @@ import {
|
|
|
69
69
|
Trash2,
|
|
70
70
|
Loader2,
|
|
71
71
|
MessageSquare,
|
|
72
|
+
Shield,
|
|
72
73
|
Users,
|
|
73
74
|
} from 'lucide-react';
|
|
74
75
|
import { useShallow } from 'zustand/react/shallow';
|
|
@@ -96,6 +97,7 @@ const ProjectEditorOverlay = lazy(() =>
|
|
|
96
97
|
import { MemberList } from './members/MemberList';
|
|
97
98
|
import { MessagesPanel } from './messages/MessagesPanel';
|
|
98
99
|
import { ChangeReviewDialog } from './review/ChangeReviewDialog';
|
|
100
|
+
import { ProjectEnvPanel } from '../extensions/env/ProjectEnvPanel';
|
|
99
101
|
import {
|
|
100
102
|
getTeamPendingRepliesState,
|
|
101
103
|
setTeamPendingRepliesState,
|
|
@@ -880,6 +882,7 @@ export const TeamDetailView = ({
|
|
|
880
882
|
const [removeMemberConfirm, setRemoveMemberConfirm] = useState<string | null>(null);
|
|
881
883
|
const [updatingRoleLoading, setUpdatingRoleLoading] = useState(false);
|
|
882
884
|
const [editDialogOpen, setEditDialogOpen] = useState(false);
|
|
885
|
+
const [envDialogOpen, setEnvDialogOpen] = useState(false);
|
|
883
886
|
const [savedLaunchRequest, setSavedLaunchRequest] = useState<TeamLaunchRequest | null>(null);
|
|
884
887
|
useEffect(() => {
|
|
885
888
|
if (!editDialogOpen || !teamName) return;
|
|
@@ -2155,6 +2158,21 @@ export const TeamDetailView = ({
|
|
|
2155
2158
|
</div>
|
|
2156
2159
|
</div>
|
|
2157
2160
|
<div className="flex shrink-0 items-center gap-1.5">
|
|
2161
|
+
{data.config.projectPath && (
|
|
2162
|
+
<Tooltip>
|
|
2163
|
+
<TooltipTrigger asChild>
|
|
2164
|
+
<Button
|
|
2165
|
+
variant="ghost"
|
|
2166
|
+
size="sm"
|
|
2167
|
+
className="h-7 gap-1 px-2 text-xs text-[var(--color-text-muted)] hover:text-[var(--color-text)]"
|
|
2168
|
+
onClick={() => setEnvDialogOpen(true)}
|
|
2169
|
+
>
|
|
2170
|
+
<Shield size={12} />
|
|
2171
|
+
</Button>
|
|
2172
|
+
</TooltipTrigger>
|
|
2173
|
+
<TooltipContent side="bottom">环境变量</TooltipContent>
|
|
2174
|
+
</Tooltip>
|
|
2175
|
+
)}
|
|
2158
2176
|
<Tooltip>
|
|
2159
2177
|
<TooltipTrigger asChild>
|
|
2160
2178
|
<Button
|
|
@@ -2302,6 +2320,11 @@ export const TeamDetailView = ({
|
|
|
2302
2320
|
title="外部派单"
|
|
2303
2321
|
icon={<Columns3 size={14} />}
|
|
2304
2322
|
badge={filteredTasks.length}
|
|
2323
|
+
headerExtra={
|
|
2324
|
+
<span className="ml-1.5 rounded bg-amber-500/15 px-1.5 py-0.5 text-[10px] font-medium uppercase tracking-wide text-amber-500">
|
|
2325
|
+
Beta
|
|
2326
|
+
</span>
|
|
2327
|
+
}
|
|
2305
2328
|
defaultOpen
|
|
2306
2329
|
forceOpen={kanbanSearch.trim().length > 0}
|
|
2307
2330
|
action={
|
|
@@ -2649,6 +2672,18 @@ export const TeamDetailView = ({
|
|
|
2649
2672
|
onRestartTeam={handleRestartTeamFromEdit}
|
|
2650
2673
|
/>
|
|
2651
2674
|
|
|
2675
|
+
<Dialog open={envDialogOpen} onOpenChange={setEnvDialogOpen}>
|
|
2676
|
+
<DialogContent className="max-h-[80vh] max-w-lg overflow-y-auto">
|
|
2677
|
+
<DialogHeader>
|
|
2678
|
+
<DialogTitle>项目环境变量</DialogTitle>
|
|
2679
|
+
<DialogDescription>
|
|
2680
|
+
管理当前项目所需的环境变量,供 MCP 和 Skills 使用。
|
|
2681
|
+
</DialogDescription>
|
|
2682
|
+
</DialogHeader>
|
|
2683
|
+
<ProjectEnvPanel projectPath={data.config.projectPath ?? null} />
|
|
2684
|
+
</DialogContent>
|
|
2685
|
+
</Dialog>
|
|
2686
|
+
|
|
2652
2687
|
<Dialog
|
|
2653
2688
|
open={removeMemberConfirm !== null}
|
|
2654
2689
|
onOpenChange={(open) => {
|
|
@@ -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>
|
|
@@ -361,49 +361,26 @@ export const KanbanBoard = ({
|
|
|
361
361
|
columnTasks: TeamTask[],
|
|
362
362
|
compact?: boolean
|
|
363
363
|
): React.JSX.Element => {
|
|
364
|
-
const addHandler =
|
|
365
|
-
onAddTask && columnId === 'todo'
|
|
366
|
-
? () => onAddTask(false)
|
|
367
|
-
: onAddTask && columnId === 'in_progress'
|
|
368
|
-
? () => onAddTask(true)
|
|
369
|
-
: undefined;
|
|
370
|
-
|
|
371
|
-
const addButton = addHandler ? (
|
|
372
|
-
<button
|
|
373
|
-
type="button"
|
|
374
|
-
onClick={addHandler}
|
|
375
|
-
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)]"
|
|
376
|
-
>
|
|
377
|
-
<Plus size={13} />
|
|
378
|
-
Add task
|
|
379
|
-
</button>
|
|
380
|
-
) : null;
|
|
381
|
-
|
|
382
364
|
if (columnTasks.length === 0) {
|
|
383
365
|
return (
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
</div>
|
|
388
|
-
)
|
|
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>
|
|
389
369
|
);
|
|
390
370
|
}
|
|
391
371
|
if (enableTaskSorting) {
|
|
392
372
|
const itemIds = columnTasks.map((t) => t.id);
|
|
393
373
|
return (
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
</SortableContext>
|
|
405
|
-
{addButton}
|
|
406
|
-
</>
|
|
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>
|
|
407
384
|
);
|
|
408
385
|
}
|
|
409
386
|
return (
|
|
@@ -432,7 +409,6 @@ export const KanbanBoard = ({
|
|
|
432
409
|
onDeleteTask={onDeleteTask}
|
|
433
410
|
/>
|
|
434
411
|
))}
|
|
435
|
-
{addButton}
|
|
436
412
|
</>
|
|
437
413
|
);
|
|
438
414
|
};
|
|
@@ -614,36 +590,22 @@ export const KanbanBoard = ({
|
|
|
614
590
|
</div>
|
|
615
591
|
) : (
|
|
616
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">
|
|
617
|
-
<div className="flex
|
|
618
|
-
{visibleColumns.map((column
|
|
593
|
+
<div className="flex w-full items-start gap-3">
|
|
594
|
+
{visibleColumns.map((column) => {
|
|
619
595
|
const columnTasks = groupedOrdered.get(column.id) ?? [];
|
|
620
596
|
const accent = COLUMN_ACCENTS[column.id as 'todo' | 'in_progress' | 'done'];
|
|
621
|
-
const width = columnWidths.get(column.id) ?? 256;
|
|
622
|
-
const handleProps = getHandleProps(column.id);
|
|
623
597
|
return (
|
|
624
|
-
<div key={column.id} className="flex
|
|
625
|
-
<
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
</KanbanColumn>
|
|
636
|
-
</div>
|
|
637
|
-
{index < visibleColumns.length - 1 ? (
|
|
638
|
-
<div
|
|
639
|
-
className="group relative mx-0.5 flex items-center justify-center"
|
|
640
|
-
onPointerDown={handleProps.onPointerDown}
|
|
641
|
-
style={handleProps.style}
|
|
642
|
-
aria-label={handleProps['aria-label']}
|
|
643
|
-
>
|
|
644
|
-
<div className="h-full w-px bg-[var(--color-border)] transition-colors group-hover:bg-blue-500/50 group-active:bg-blue-500" />
|
|
645
|
-
</div>
|
|
646
|
-
) : 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>
|
|
647
609
|
</div>
|
|
648
610
|
);
|
|
649
611
|
})}
|