@yancyyu/openhermit 1.6.38 → 1.6.40

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 (243) hide show
  1. package/dist-renderer/assets/ProjectEditorOverlay-CemDOX-3.js +58 -0
  2. package/dist-renderer/assets/{TeamGraphOverlay-ZEDfZyHb.js → TeamGraphOverlay-hPY770Db.js} +1 -1
  3. package/dist-renderer/assets/{_basePickBy-CIhniz70.js → _basePickBy-BHHrJT1i.js} +1 -1
  4. package/dist-renderer/assets/{_baseUniq-cKAW4Q8I.js → _baseUniq-CWErBtke.js} +1 -1
  5. package/dist-renderer/assets/{arc-YmNsoDXW.js → arc-C_o2_Uv8.js} +1 -1
  6. package/dist-renderer/assets/{architectureDiagram-VXUJARFQ-DHEls2sX.js → architectureDiagram-VXUJARFQ-DUW0LI3t.js} +1 -1
  7. package/dist-renderer/assets/{blockDiagram-VD42YOAC-Bpwf1Sbg.js → blockDiagram-VD42YOAC-CWbCE9hQ.js} +1 -1
  8. package/dist-renderer/assets/{c4Diagram-YG6GDRKO-B0IaQ4w5.js → c4Diagram-YG6GDRKO-BjLadrfV.js} +1 -1
  9. package/dist-renderer/assets/channel-DyP9YlCF.js +1 -0
  10. package/dist-renderer/assets/{chunk-4BX2VUAB-DLk-hcFc.js → chunk-4BX2VUAB-CPnvjZl9.js} +1 -1
  11. package/dist-renderer/assets/{chunk-55IACEB6-1XRmX_Zm.js → chunk-55IACEB6-OlL47yXQ.js} +1 -1
  12. package/dist-renderer/assets/{chunk-B4BG7PRW-1waH1DAD.js → chunk-B4BG7PRW-DTasjbm8.js} +1 -1
  13. package/dist-renderer/assets/{chunk-DI55MBZ5-BqpZBtrN.js → chunk-DI55MBZ5-C5_Xaqkk.js} +1 -1
  14. package/dist-renderer/assets/{chunk-FMBD7UC4-Bly7vVym.js → chunk-FMBD7UC4-NdoM4DMR.js} +1 -1
  15. package/dist-renderer/assets/{chunk-QN33PNHL-Ci2QWBAs.js → chunk-QN33PNHL-C8Fybejy.js} +1 -1
  16. package/dist-renderer/assets/{chunk-QZHKN3VN-YCqFW7d-.js → chunk-QZHKN3VN-E98TYFXJ.js} +1 -1
  17. package/dist-renderer/assets/{chunk-TZMSLE5B-B0xGXInl.js → chunk-TZMSLE5B-h4lFgkIq.js} +1 -1
  18. package/dist-renderer/assets/classDiagram-2ON5EDUG-BqffFTae.js +1 -0
  19. package/dist-renderer/assets/classDiagram-v2-WZHVMYZB-BqffFTae.js +1 -0
  20. package/dist-renderer/assets/clone-MPcKWs2O.js +1 -0
  21. package/dist-renderer/assets/{cose-bilkent-S5V4N54A-DxcFNQKT.js → cose-bilkent-S5V4N54A-DtQ7fkrs.js} +1 -1
  22. package/dist-renderer/assets/{dagre-6UL2VRFP-DPo_RfZY.js → dagre-6UL2VRFP-CN-nL_z4.js} +1 -1
  23. package/dist-renderer/assets/{diagram-PSM6KHXK-U3hQsFe4.js → diagram-PSM6KHXK-DVJtqmm-.js} +1 -1
  24. package/dist-renderer/assets/{diagram-QEK2KX5R-OrwrAy0V.js → diagram-QEK2KX5R-DlxHxyXh.js} +1 -1
  25. package/dist-renderer/assets/{diagram-S2PKOQOG-CXATPWVw.js → diagram-S2PKOQOG-7dpzO6x6.js} +1 -1
  26. package/dist-renderer/assets/{erDiagram-Q2GNP2WA-B0e8AfMF.js → erDiagram-Q2GNP2WA-GP1TqsHi.js} +1 -1
  27. package/dist-renderer/assets/{flowDiagram-NV44I4VS-CXfzA4jJ.js → flowDiagram-NV44I4VS-C7ZLETuH.js} +1 -1
  28. package/dist-renderer/assets/{ganttDiagram-JELNMOA3-CMr08qVl.js → ganttDiagram-JELNMOA3-CvPB68dH.js} +1 -1
  29. package/dist-renderer/assets/{gitGraphDiagram-V2S2FVAM-vYFHpPmy.js → gitGraphDiagram-V2S2FVAM-B5yOm3w7.js} +1 -1
  30. package/dist-renderer/assets/{graph-DOe5j8dH.js → graph-smeyY1YZ.js} +1 -1
  31. package/dist-renderer/assets/{index-BySQS7AB.js → index-BJx8XvG1.js} +1 -1
  32. package/dist-renderer/assets/{index-C_okzZXP.js → index-CQaXUAua.js} +1 -1
  33. package/dist-renderer/assets/{index-VJ-MM9xa.js → index-CajRpxO2.js} +1 -1
  34. package/dist-renderer/assets/{index-V7dAKPqd.js → index-ChG4rE-E.js} +587 -705
  35. package/dist-renderer/assets/index-DUd0uw9C.css +32 -0
  36. package/dist-renderer/assets/{index-CzWxVCRL.js → index-IhmXZWqf.js} +1 -1
  37. package/dist-renderer/assets/{index-B2Dy7M2G.js → index-x_JkoDRH.js} +1 -1
  38. package/dist-renderer/assets/{infoDiagram-HS3SLOUP-D_WubR0B.js → infoDiagram-HS3SLOUP-D-hWRQGY.js} +1 -1
  39. package/dist-renderer/assets/{journeyDiagram-XKPGCS4Q-w9ca-1TI.js → journeyDiagram-XKPGCS4Q-Bb6W8rUG.js} +1 -1
  40. package/dist-renderer/assets/{kanban-definition-3W4ZIXB7-Jg9p6_pN.js → kanban-definition-3W4ZIXB7-CnHdUX0q.js} +1 -1
  41. package/dist-renderer/assets/{layout-B-z3y17c.js → layout-pqss_zkI.js} +1 -1
  42. package/dist-renderer/assets/{linear-D-RTX5UW.js → linear-B1mFITNh.js} +1 -1
  43. package/dist-renderer/assets/{mindmap-definition-VGOIOE7T-CDQmHOYP.js → mindmap-definition-VGOIOE7T-DTD9q7-D.js} +1 -1
  44. package/dist-renderer/assets/{pieDiagram-ADFJNKIX-D_odsQL7.js → pieDiagram-ADFJNKIX-Df3mhrn7.js} +1 -1
  45. package/dist-renderer/assets/{quadrantDiagram-AYHSOK5B-BRsmYWSA.js → quadrantDiagram-AYHSOK5B-B1FZ09vH.js} +1 -1
  46. package/dist-renderer/assets/{requirementDiagram-UZGBJVZJ-ChNE_BOV.js → requirementDiagram-UZGBJVZJ-aEO78thZ.js} +1 -1
  47. package/dist-renderer/assets/{sankeyDiagram-TZEHDZUN-C8FtpwKc.js → sankeyDiagram-TZEHDZUN-6Ui--jp-.js} +1 -1
  48. package/dist-renderer/assets/{sequenceDiagram-WL72ISMW-DmLCzNcc.js → sequenceDiagram-WL72ISMW-DF4Q1cAM.js} +1 -1
  49. package/dist-renderer/assets/splashScene-D0YB9uxm.js +17 -0
  50. package/dist-renderer/assets/{stateDiagram-FKZM4ZOC-WJBm4bhu.js → stateDiagram-FKZM4ZOC-BqA2BI8C.js} +1 -1
  51. package/dist-renderer/assets/stateDiagram-v2-4FDKWEC3-Cs2ZtUD2.js +1 -0
  52. package/dist-renderer/assets/{timeline-definition-IT6M3QCI-BXs_hOJs.js → timeline-definition-IT6M3QCI-DoOkw_A8.js} +1 -1
  53. package/dist-renderer/assets/{treemap-GDKQZRPO-o04MA0G9.js → treemap-GDKQZRPO-DUe26QdD.js} +1 -1
  54. package/dist-renderer/assets/{xychartDiagram-PRI3JC2R-Czj69XRd.js → xychartDiagram-PRI3JC2R-BKCnj5Xn.js} +1 -1
  55. package/dist-renderer/index.html +20 -53
  56. package/package.json +25 -18
  57. package/src/main/ipc/extensions.ts +2 -1
  58. package/src/main/server.ts +873 -221
  59. package/src/main/services/extensions/ExtensionFacadeService.ts +2 -5
  60. package/src/main/services/extensions/catalog/PluginCatalogService.ts +4 -2
  61. package/src/main/services/session-intelligence/ConversationTelemetryService.ts +1101 -0
  62. package/src/main/services/session-intelligence/LocalSessionScanner.ts +512 -0
  63. package/src/main/services/session-intelligence/SessionUsageParser.ts +4 -4
  64. package/src/main/services/system-manager/SystemManagerConfigService.ts +122 -0
  65. package/src/main/services/system-manager/SystemManagerPtyService.ts +233 -0
  66. package/src/main/services/system-manager/WorkflowPromptService.ts +75 -0
  67. package/src/main/services/teams-mvp/TaskDispatchService.ts +5 -6
  68. package/src/main/services/teams-mvp/TeamProvisioningService.ts +39 -2
  69. package/src/main/services/teams-mvp/TeamWorkspaceService.ts +22 -4
  70. package/src/main/utils/teamProjectResolution.ts +15 -0
  71. package/src/renderer/App.tsx +8 -4
  72. package/src/renderer/api/httpClient.ts +68 -18
  73. package/src/renderer/api/providers.ts +23 -2
  74. package/src/renderer/assets/participant-avatars/01.svg +3 -0
  75. package/src/renderer/assets/participant-avatars/02.svg +3 -0
  76. package/src/renderer/assets/participant-avatars/03.svg +3 -0
  77. package/src/renderer/assets/participant-avatars/04.svg +3 -0
  78. package/src/renderer/assets/participant-avatars/05.svg +3 -0
  79. package/src/renderer/assets/participant-avatars/06.svg +3 -0
  80. package/src/renderer/assets/participant-avatars/07.svg +3 -0
  81. package/src/renderer/assets/participant-avatars/08.svg +3 -0
  82. package/src/renderer/assets/participant-avatars/09.svg +3 -0
  83. package/src/renderer/assets/participant-avatars/10.svg +3 -0
  84. package/src/renderer/assets/participant-avatars/11.svg +3 -0
  85. package/src/renderer/assets/participant-avatars/12.svg +3 -0
  86. package/src/renderer/assets/participant-avatars/13.svg +3 -0
  87. package/src/renderer/components/chat/ChatHistoryItem.tsx +1 -1
  88. package/src/renderer/components/chat/items/SubagentItem.tsx +2 -2
  89. package/src/renderer/components/chat/viewers/MermaidDiagram.tsx +2 -2
  90. package/src/renderer/components/common/ErrorBoundary.tsx +1 -1
  91. package/src/renderer/components/common/TerminalPane.tsx +213 -0
  92. package/src/renderer/components/dashboard/CliStatusBanner.tsx +7 -7
  93. package/src/renderer/components/dashboard/DashboardView.tsx +9 -36
  94. package/src/renderer/components/extensions/ExtensionStoreView.tsx +7 -126
  95. package/src/renderer/components/extensions/ExtensionsSubTabTrigger.tsx +1 -1
  96. package/src/renderer/components/extensions/common/ExtensionToast.tsx +3 -3
  97. package/src/renderer/components/extensions/common/SourceBadge.tsx +1 -1
  98. package/src/renderer/components/extensions/mcp/McpLibraryEnableDialog.tsx +305 -0
  99. package/src/renderer/components/extensions/mcp/McpLibraryEntryDialog.tsx +418 -0
  100. package/src/renderer/components/extensions/mcp/McpLibraryPanel.tsx +404 -0
  101. package/src/renderer/components/extensions/plugins/CategoryChips.tsx +1 -1
  102. package/src/renderer/components/extensions/plugins/PluginCard.tsx +6 -6
  103. package/src/renderer/components/extensions/plugins/PluginDetailDialog.tsx +2 -2
  104. package/src/renderer/components/extensions/plugins/PluginsPanel.tsx +34 -21
  105. package/src/renderer/components/extensions/skills/SkillEditorDialog.tsx +1 -1
  106. package/src/renderer/components/extensions/skills/SkillsLibraryPanel.tsx +335 -0
  107. package/src/renderer/components/layout/PaneContent.tsx +8 -1
  108. package/src/renderer/components/layout/PaneResizeHandle.tsx +2 -2
  109. package/src/renderer/components/layout/Sidebar.tsx +13 -56
  110. package/src/renderer/components/layout/SortableTab.tsx +22 -33
  111. package/src/renderer/components/layout/TabBar.tsx +1 -1
  112. package/src/renderer/components/layout/TabContextMenu.tsx +1 -1
  113. package/src/renderer/components/report/sections/CostSection.tsx +2 -2
  114. package/src/renderer/components/report/sections/InsightsSection.tsx +1 -1
  115. package/src/renderer/components/runtime/ProviderRuntimeBackendSelector.tsx +2 -2
  116. package/src/renderer/components/runtime/ProviderRuntimeSettingsDialog.tsx +768 -157
  117. package/src/renderer/components/schedules/SchedulesView.tsx +51 -462
  118. package/src/renderer/components/schedules/calendar/CalendarDayView.tsx +173 -0
  119. package/src/renderer/components/schedules/calendar/CalendarEventBlock.tsx +113 -0
  120. package/src/renderer/components/schedules/calendar/CalendarHeader.tsx +148 -0
  121. package/src/renderer/components/schedules/calendar/CalendarMonthView.tsx +142 -0
  122. package/src/renderer/components/schedules/calendar/CalendarWeekView.tsx +219 -0
  123. package/src/renderer/components/schedules/calendar/ScheduleCalendarBoard.tsx +41 -0
  124. package/src/renderer/components/schedules/calendar/TeamGanttView.tsx +405 -0
  125. package/src/renderer/components/schedules/calendar/computeOccurrences.ts +234 -0
  126. package/src/renderer/components/schedules/calendar/index.ts +2 -0
  127. package/src/renderer/components/schedules/calendar/types.ts +44 -0
  128. package/src/renderer/components/search/CommandPalette.tsx +4 -4
  129. package/src/renderer/components/settings/SettingsTabs.tsx +50 -55
  130. package/src/renderer/components/settings/SettingsView.tsx +30 -35
  131. package/src/renderer/components/settings/components/SettingsSectionHeader.tsx +5 -1
  132. package/src/renderer/components/settings/components/SettingsSelect.tsx +5 -3
  133. package/src/renderer/components/settings/components/SettingsToggle.tsx +2 -2
  134. package/src/renderer/components/settings/sections/AdvancedSection.tsx +11 -42
  135. package/src/renderer/components/settings/sections/CliStatusSection.tsx +72 -113
  136. package/src/renderer/components/settings/sections/ConfigEditorDialog.tsx +1 -1
  137. package/src/renderer/components/settings/sections/GeneralSection.tsx +11 -3
  138. package/src/renderer/components/settings/sections/HarnessSection.tsx +18 -14
  139. package/src/renderer/components/settings/sections/PlatformsSection.tsx +3 -3
  140. package/src/renderer/components/settings/sections/TaskBusSection.tsx +33 -40
  141. package/src/renderer/components/settings/sections/index.ts +0 -1
  142. package/src/renderer/components/sidebar/SessionFiltersPopover.tsx +1 -1
  143. package/src/renderer/components/sidebar/SessionItem.tsx +3 -3
  144. package/src/renderer/components/sidebar/SidebarSessions.tsx +184 -6
  145. package/src/renderer/components/sidebar/SidebarTaskItem.tsx +4 -4
  146. package/src/renderer/components/sidebar/WorkspaceBrowser.tsx +40 -5
  147. package/src/renderer/components/splash/splashScene.ts +121 -929
  148. package/src/renderer/components/system-manager/FolderBrowser.tsx +163 -0
  149. package/src/renderer/components/system-manager/SystemManagerView.tsx +351 -0
  150. package/src/renderer/components/tasks/TasksView.tsx +112 -134
  151. package/src/renderer/components/team/CcSessionsSection.tsx +431 -89
  152. package/src/renderer/components/team/ClaudeLogsFilterPopover.tsx +1 -1
  153. package/src/renderer/components/team/ClaudeLogsPanel.tsx +1 -1
  154. package/src/renderer/components/team/CollapsibleTeamSection.tsx +17 -32
  155. package/src/renderer/components/team/ProcessesSection.tsx +2 -2
  156. package/src/renderer/components/team/TaskTooltip.tsx +2 -2
  157. package/src/renderer/components/team/TeamDetailView.tsx +319 -123
  158. package/src/renderer/components/team/TeamListFilterPopover.tsx +1 -1
  159. package/src/renderer/components/team/TeamListView.tsx +109 -124
  160. package/src/renderer/components/team/TeamSessionsSection.tsx +6 -6
  161. package/src/renderer/components/team/UnreadCommentsBadge.tsx +1 -1
  162. package/src/renderer/components/team/activity/ActivityItem.tsx +9 -9
  163. package/src/renderer/components/team/activity/ActivityTimeline.tsx +5 -5
  164. package/src/renderer/components/team/activity/LeadThoughtsGroup.tsx +3 -3
  165. package/src/renderer/components/team/activity/ReplyQuoteBlock.tsx +4 -4
  166. package/src/renderer/components/team/dialogs/CreateTaskDialog.tsx +4 -4
  167. package/src/renderer/components/team/dialogs/CreateTeamDialog.tsx +84 -306
  168. package/src/renderer/components/team/dialogs/EditTeamDialog.tsx +259 -342
  169. package/src/renderer/components/team/dialogs/GlobalTaskDetailDialog.tsx +1 -1
  170. package/src/renderer/components/team/dialogs/LaunchTeamDialog.tsx +18 -16
  171. package/src/renderer/components/team/dialogs/PlatformBindingDialog.tsx +221 -0
  172. package/src/renderer/components/team/dialogs/PlatformManualForm.tsx +8 -1
  173. package/src/renderer/components/team/dialogs/PlatformSetupQR.tsx +5 -5
  174. package/src/renderer/components/team/dialogs/RuntimeConfigDialog.tsx +361 -0
  175. package/src/renderer/components/team/dialogs/SendMessageDialog.tsx +6 -6
  176. package/src/renderer/components/team/dialogs/SkipPermissionsCheckbox.tsx +6 -6
  177. package/src/renderer/components/team/dialogs/StatusHistoryTimeline.tsx +1 -1
  178. package/src/renderer/components/team/dialogs/TaskAttachments.tsx +1 -1
  179. package/src/renderer/components/team/dialogs/TaskCommentInput.tsx +6 -6
  180. package/src/renderer/components/team/dialogs/TaskCommentsSection.tsx +4 -4
  181. package/src/renderer/components/team/dialogs/TaskDetailDialog.tsx +3 -3
  182. package/src/renderer/components/team/dialogs/platformMeta.ts +122 -11
  183. package/src/renderer/components/team/dialogs/useTeamEditForm.ts +17 -5
  184. package/src/renderer/components/team/editor/EditorFileTree.tsx +4 -4
  185. package/src/renderer/components/team/editor/EditorSearchPanel.tsx +1 -1
  186. package/src/renderer/components/team/editor/MarkdownSplitView.tsx +1 -1
  187. package/src/renderer/components/team/editor/NewFileDialog.tsx +1 -1
  188. package/src/renderer/components/team/editor/ProjectEditorOverlay.tsx +1 -1
  189. package/src/renderer/components/team/editor/SearchInFilesPanel.tsx +1 -1
  190. package/src/renderer/components/team/kanban/KanbanBoard.tsx +9 -9
  191. package/src/renderer/components/team/kanban/KanbanFilterPopover.tsx +4 -4
  192. package/src/renderer/components/team/kanban/KanbanSearchInput.tsx +1 -1
  193. package/src/renderer/components/team/kanban/KanbanSortPopover.tsx +5 -5
  194. package/src/renderer/components/team/kanban/KanbanTaskCard.tsx +4 -4
  195. package/src/renderer/components/team/members/MemberCard.tsx +14 -47
  196. package/src/renderer/components/team/members/MemberDetailDialog.tsx +3 -95
  197. package/src/renderer/components/team/members/MemberDetailStats.tsx +50 -65
  198. package/src/renderer/components/team/members/MemberDraftRow.tsx +1 -1
  199. package/src/renderer/components/team/members/MemberStatsTab.tsx +2 -2
  200. package/src/renderer/components/team/members/MemberWorkspaceTab.tsx +1 -1
  201. package/src/renderer/components/team/messages/MessageComposer.tsx +10 -112
  202. package/src/renderer/components/team/messages/MessagesFilterPopover.tsx +1 -1
  203. package/src/renderer/components/team/messages/MessagesPanel.tsx +136 -119
  204. package/src/renderer/components/team/review/ChangeReviewDialog.tsx +1 -1
  205. package/src/renderer/components/team/schedule/ScheduleStatusBadge.tsx +3 -3
  206. package/src/renderer/components/team/sidebar/TeamSidebarRail.tsx +4 -4
  207. package/src/renderer/components/team/tasks/TaskRow.tsx +1 -1
  208. package/src/renderer/components/team/tools/AddMcpInline.tsx +27 -17
  209. package/src/renderer/components/team/tools/McpChip.tsx +6 -3
  210. package/src/renderer/components/team/tools/SkillChip.tsx +3 -3
  211. package/src/renderer/components/team/tools/ToolsSection.tsx +418 -70
  212. package/src/renderer/components/ui/MemberSelect.tsx +2 -2
  213. package/src/renderer/components/ui/MentionSuggestionList.tsx +2 -2
  214. package/src/renderer/components/ui/MentionableTextarea.tsx +3 -3
  215. package/src/renderer/hooks/useExtensionsTabState.ts +3 -114
  216. package/src/renderer/index.css +56 -39
  217. package/src/renderer/index.html +17 -50
  218. package/src/renderer/store/index.ts +2 -1
  219. package/src/renderer/store/slices/scheduleSlice.ts +1 -1
  220. package/src/renderer/store/slices/teamSlice.ts +45 -168
  221. package/src/renderer/utils/claudeCodeOnlyProviders.ts +3 -10
  222. package/src/renderer/utils/memberHelpers.ts +5 -17
  223. package/src/renderer/utils/openCodeRuntimeDeliveryDiagnostics.ts +4 -2
  224. package/src/renderer/utils/providerSlashCommands.ts +0 -5
  225. package/src/renderer/utils/scheduleFormatters.ts +3 -1
  226. package/src/renderer/utils/teamMessageFiltering.ts +14 -1
  227. package/src/renderer/utils/teamModelAvailability.ts +18 -2
  228. package/src/shared/types/api.ts +121 -2
  229. package/src/shared/types/ccConnect.ts +2 -0
  230. package/src/shared/types/index.ts +3 -0
  231. package/src/shared/types/systemManager.ts +49 -0
  232. package/src/shared/types/team.ts +29 -0
  233. package/src/shared/types/terminal.ts +4 -2
  234. package/src/shared/utils/extensionNormalizers.ts +15 -8
  235. package/src/shared/utils/providerExtensionCapabilities.ts +2 -2
  236. package/dist-renderer/assets/ProjectEditorOverlay-lJZi-9Hp.js +0 -52
  237. package/dist-renderer/assets/channel-yIlSKy0e.js +0 -1
  238. package/dist-renderer/assets/classDiagram-2ON5EDUG-24fHez0s.js +0 -1
  239. package/dist-renderer/assets/classDiagram-v2-WZHVMYZB-24fHez0s.js +0 -1
  240. package/dist-renderer/assets/clone-BTNuUva-.js +0 -1
  241. package/dist-renderer/assets/index-Bi6nrZ4z.css +0 -1
  242. package/dist-renderer/assets/splashScene-C8lWNnm4.js +0 -1
  243. package/dist-renderer/assets/stateDiagram-v2-4FDKWEC3-_m6iPPUR.js +0 -1
@@ -0,0 +1,163 @@
1
+ /**
2
+ * FolderBrowser — directory browser dialog for the console path input.
3
+ * Extracted from ProjectPathSelector so the SystemManagerView can reuse it.
4
+ */
5
+ import React, { useState } from 'react';
6
+
7
+ import { api } from '@renderer/api';
8
+ import { Button } from '@renderer/components/ui/button';
9
+ import {
10
+ Dialog,
11
+ DialogContent,
12
+ DialogFooter,
13
+ DialogHeader,
14
+ DialogTitle,
15
+ } from '@renderer/components/ui/dialog';
16
+ import { Input } from '@renderer/components/ui/input';
17
+ import { Check, ChevronLeft, ChevronRight, Folder, FolderOpen, Loader2 } from 'lucide-react';
18
+
19
+ interface FolderBrowserProps {
20
+ value: string;
21
+ onChange: (path: string) => void;
22
+ }
23
+
24
+ export const FolderBrowser = ({ value, onChange }: FolderBrowserProps): React.JSX.Element => {
25
+ const [open, setOpen] = useState(false);
26
+ const [currentPath, setCurrentPath] = useState(value || '');
27
+ const [dirs, setDirs] = useState<string[]>([]);
28
+ const [loading, setLoading] = useState(false);
29
+ const [error, setError] = useState<string | null>(null);
30
+
31
+ const browse = async (dirPath: string) => {
32
+ setLoading(true);
33
+ setError(null);
34
+ try {
35
+ const result = await api.config.browseFolders(dirPath || undefined);
36
+ setCurrentPath(result.path);
37
+ setDirs(result.dirs);
38
+ } catch (err) {
39
+ setError(err instanceof Error ? err.message : '无法访问目录');
40
+ setDirs([]);
41
+ } finally {
42
+ setLoading(false);
43
+ }
44
+ };
45
+
46
+ const handleOpen = () => {
47
+ setOpen(true);
48
+ void browse(value || '');
49
+ };
50
+
51
+ const handleSelect = (dir: string) => {
52
+ onChange(dir);
53
+ setOpen(false);
54
+ };
55
+
56
+ const handleConfirm = () => {
57
+ if (currentPath) {
58
+ onChange(currentPath);
59
+ }
60
+ setOpen(false);
61
+ };
62
+
63
+ const handleNavigateUp = () => {
64
+ if (currentPath) {
65
+ const parent = currentPath.split('/').slice(0, -1).join('/') || '/';
66
+ void browse(parent);
67
+ }
68
+ };
69
+
70
+ return (
71
+ <>
72
+ <Button
73
+ size="sm"
74
+ variant="outline"
75
+ className="h-8 shrink-0 border-[var(--color-border)]"
76
+ onClick={handleOpen}
77
+ title="浏览目录"
78
+ >
79
+ <FolderOpen size={13} />
80
+ </Button>
81
+
82
+ <Dialog
83
+ open={open}
84
+ onOpenChange={(o) => {
85
+ if (!o) setOpen(false);
86
+ }}
87
+ >
88
+ <DialogContent className="max-w-md">
89
+ <DialogHeader>
90
+ <DialogTitle>选择工作目录</DialogTitle>
91
+ </DialogHeader>
92
+
93
+ {/* Path breadcrumb + manual input */}
94
+ <div className="flex items-center gap-1 text-xs text-[var(--color-text-muted)]">
95
+ <button
96
+ type="button"
97
+ className="shrink-0 hover:text-[var(--color-text)]"
98
+ onClick={handleNavigateUp}
99
+ disabled={!currentPath || currentPath === '/'}
100
+ >
101
+ <ChevronLeft size={14} />
102
+ </button>
103
+ <Input
104
+ className="h-7 flex-1 font-mono text-xs"
105
+ value={currentPath}
106
+ onChange={(e) => setCurrentPath(e.target.value)}
107
+ onKeyDown={(e) => {
108
+ if (e.key === 'Enter') void browse(currentPath);
109
+ }}
110
+ placeholder="/path/to/directory"
111
+ />
112
+ </div>
113
+
114
+ {/* Directory list */}
115
+ <div className="max-h-64 overflow-auto rounded-md border border-[var(--color-border)]">
116
+ {loading && (
117
+ <div className="flex items-center justify-center gap-2 py-6 text-xs text-[var(--color-text-muted)]">
118
+ <Loader2 className="size-4 animate-spin" />
119
+ 加载中…
120
+ </div>
121
+ )}
122
+ {error && <div className="px-3 py-4 text-xs text-red-400">{error}</div>}
123
+ {!loading && !error && dirs.length === 0 && (
124
+ <div className="px-3 py-4 text-xs text-[var(--color-text-muted)]">
125
+ 此目录下没有子目录。可手动输入路径后按 Enter。
126
+ </div>
127
+ )}
128
+ {!loading && !error && dirs.length > 0 && (
129
+ <ul className="divide-y divide-[var(--color-border)]">
130
+ {dirs.map((dir) => (
131
+ <li key={dir}>
132
+ <button
133
+ type="button"
134
+ className="flex w-full items-center gap-2 px-3 py-2 text-left text-xs hover:bg-[var(--color-surface-raised)]"
135
+ onClick={() => void browse(dir)}
136
+ onDoubleClick={() => handleSelect(dir)}
137
+ >
138
+ <Folder size={14} className="shrink-0 text-[var(--color-text-muted)]" />
139
+ <span className="truncate">{dir}</span>
140
+ <ChevronRight
141
+ size={14}
142
+ className="ml-auto shrink-0 text-[var(--color-text-muted)]"
143
+ />
144
+ </button>
145
+ </li>
146
+ ))}
147
+ </ul>
148
+ )}
149
+ </div>
150
+
151
+ <DialogFooter>
152
+ <Button variant="outline" onClick={() => setOpen(false)}>
153
+ 取消
154
+ </Button>
155
+ <Button onClick={handleConfirm} disabled={!currentPath}>
156
+ 选择此目录
157
+ </Button>
158
+ </DialogFooter>
159
+ </DialogContent>
160
+ </Dialog>
161
+ </>
162
+ );
163
+ };
@@ -0,0 +1,351 @@
1
+ import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
2
+
3
+ import '@xterm/xterm/css/xterm.css';
4
+
5
+ import { FitAddon } from '@xterm/addon-fit';
6
+ import { WebLinksAddon } from '@xterm/addon-web-links';
7
+ import { Terminal } from '@xterm/xterm';
8
+ import { api } from '@renderer/api';
9
+ import { useStore } from '@renderer/store';
10
+ import { Button } from '@renderer/components/ui/button';
11
+ import { Input } from '@renderer/components/ui/input';
12
+ import { SYSTEM_MANAGER_DISPLAY_NAME } from '@shared/types/team';
13
+ import type {
14
+ SystemManagerConfig,
15
+ SystemManagerStatus,
16
+ WorkflowPromptSummary,
17
+ } from '@shared/types/systemManager';
18
+ import { Loader2, RefreshCw, TerminalSquare } from 'lucide-react';
19
+
20
+ import { FolderBrowser } from './FolderBrowser';
21
+
22
+ interface SystemManagerViewProps {
23
+ isPaneFocused?: boolean;
24
+ }
25
+
26
+ function formatPathForTitle(pathValue: string): string {
27
+ const home = typeof process !== 'undefined' ? process.env.HOME : undefined;
28
+ if (home && pathValue.startsWith(home)) return `~${pathValue.slice(home.length)}`;
29
+ return pathValue;
30
+ }
31
+
32
+ function joinPath(basePath: string, childPath: string): string {
33
+ const trimmedBase = basePath.replace(/[\\/]+$/, '');
34
+ return `${trimmedBase}/${childPath}`;
35
+ }
36
+
37
+ export const SystemManagerView = ({
38
+ isPaneFocused: _isPaneFocused = false,
39
+ }: SystemManagerViewProps): React.JSX.Element => {
40
+ const terminalHostRef = useRef<HTMLDivElement | null>(null);
41
+ const terminalRef = useRef<Terminal | null>(null);
42
+ const fitAddonRef = useRef<FitAddon | null>(null);
43
+ const ptyIdRef = useRef<string | null>(null);
44
+ const autoStartedRef = useRef(false);
45
+ const startClaudeRef = useRef<((workDirOverride?: string) => Promise<void>) | null>(null);
46
+
47
+ const [status, setStatus] = useState<SystemManagerStatus | null>(null);
48
+ const [config, setConfig] = useState<SystemManagerConfig | null>(null);
49
+ const [workDirInput, setWorkDirInput] = useState('');
50
+ const [workflowPrompts, setWorkflowPrompts] = useState<WorkflowPromptSummary[]>([]);
51
+ const [warnings, setWarnings] = useState<string[]>([]);
52
+ const [loading, setLoading] = useState(true);
53
+ const [starting, setStarting] = useState(false);
54
+ const [running, setRunning] = useState(false);
55
+ const [error, setError] = useState<string | null>(null);
56
+ const fetchTeams = useStore((state) => state.fetchTeams);
57
+
58
+ const writeTerminalLine = useCallback((line: string) => {
59
+ terminalRef.current?.writeln(`\x1b[90m${line}\x1b[0m`);
60
+ }, []);
61
+
62
+ const fitTerminal = useCallback(() => {
63
+ try {
64
+ fitAddonRef.current?.fit();
65
+ if (ptyIdRef.current && terminalRef.current) {
66
+ api.terminal.resize(ptyIdRef.current, terminalRef.current.cols, terminalRef.current.rows);
67
+ }
68
+ } catch {
69
+ // xterm fit can throw when the element is not measurable yet.
70
+ }
71
+ }, []);
72
+
73
+ useEffect(() => {
74
+ const host = terminalHostRef.current;
75
+ if (!host) return;
76
+
77
+ const term = new Terminal({
78
+ cursorBlink: true,
79
+ convertEol: true,
80
+ fontFamily: 'JetBrains Mono, SFMono-Regular, Menlo, Monaco, Consolas, monospace',
81
+ fontSize: 13,
82
+ lineHeight: 1.28,
83
+ theme: {
84
+ background: 'var(--color-surface)',
85
+ foreground: 'var(--color-text)',
86
+ cursor: 'var(--color-text)',
87
+ selectionBackground: 'var(--color-border-emphasis)',
88
+ black: 'var(--color-surface-sidebar)',
89
+ red: '#f87171',
90
+ green: '#86efac',
91
+ yellow: '#fde68a',
92
+ blue: 'var(--color-accent)',
93
+ magenta: '#d8b4fe',
94
+ cyan: '#67e8f9',
95
+ white: 'var(--color-text)',
96
+ },
97
+ });
98
+ const fitAddon = new FitAddon();
99
+ term.loadAddon(fitAddon);
100
+ term.loadAddon(new WebLinksAddon());
101
+ term.open(host);
102
+ terminalRef.current = term;
103
+ fitAddonRef.current = fitAddon;
104
+ fitTerminal();
105
+ term.writeln('\x1b[90m# Hermit 控制台 · 本地 Claude Code PTY\x1b[0m');
106
+ term.writeln('\x1b[90m# 点击 Start Claude 等价于在当前目录运行 claude\x1b[0m');
107
+ term.writeln('');
108
+
109
+ const dataDispose = api.terminal.onData((_event, ptyId, data) => {
110
+ if (ptyId === ptyIdRef.current) term.write(data);
111
+ });
112
+ const exitDispose = api.terminal.onExit((_event, ptyId, exitCode) => {
113
+ if (ptyId === ptyIdRef.current) {
114
+ setRunning(false);
115
+ ptyIdRef.current = null;
116
+ term.writeln(`\r\n\x1b[90m[claude exited with code ${exitCode}]\x1b[0m`);
117
+ }
118
+ });
119
+ const inputDispose = term.onData((data) => {
120
+ if (ptyIdRef.current) api.terminal.write(ptyIdRef.current, data);
121
+ });
122
+ const resizeObserver = new ResizeObserver(() => fitTerminal());
123
+ resizeObserver.observe(host);
124
+
125
+ return () => {
126
+ dataDispose();
127
+ exitDispose();
128
+ inputDispose.dispose();
129
+ resizeObserver.disconnect();
130
+ if (ptyIdRef.current) {
131
+ void api.terminal.kill(ptyIdRef.current).catch(() => {
132
+ // Component is unmounting; there is no safe UI surface for this lifecycle error.
133
+ });
134
+ }
135
+ term.dispose();
136
+ terminalRef.current = null;
137
+ fitAddonRef.current = null;
138
+ ptyIdRef.current = null;
139
+ };
140
+ }, [fitTerminal]);
141
+
142
+ const load = useCallback(async (): Promise<SystemManagerConfig | null> => {
143
+ setLoading(true);
144
+ setError(null);
145
+ try {
146
+ const [nextStatus, nextConfig] = await Promise.all([
147
+ api.systemManager.getStatus(),
148
+ api.systemManager.getConfig(),
149
+ ]);
150
+ setStatus(nextStatus);
151
+ setConfig(nextConfig);
152
+ setWorkDirInput(nextConfig.selectedWorkDir);
153
+ const candidateFolders = [
154
+ nextConfig.workflowFolder,
155
+ joinPath(nextConfig.selectedWorkDir, 'workflows'),
156
+ ].filter((folder): folder is string => Boolean(folder));
157
+ let loadedWorkflow = false;
158
+ for (const folder of candidateFolders) {
159
+ try {
160
+ const workflowResult = await api.systemManager.listWorkflowPrompts(folder);
161
+ setConfig((current) =>
162
+ current ? { ...current, workflowFolder: workflowResult.folder } : current
163
+ );
164
+ setWorkflowPrompts(workflowResult.prompts);
165
+ setWarnings(workflowResult.warnings);
166
+ loadedWorkflow = true;
167
+ break;
168
+ } catch {
169
+ // Common commands are optional; missing folders should not interrupt opening the console.
170
+ }
171
+ }
172
+ if (!loadedWorkflow) {
173
+ setWorkflowPrompts([]);
174
+ setWarnings([]);
175
+ }
176
+ return nextConfig;
177
+ } catch (err) {
178
+ setError(err instanceof Error ? err.message : String(err));
179
+ return null;
180
+ } finally {
181
+ setLoading(false);
182
+ }
183
+ }, []);
184
+
185
+ const stopClaude = useCallback(async (): Promise<boolean> => {
186
+ const ptyId = ptyIdRef.current;
187
+ if (!ptyId) {
188
+ setRunning(false);
189
+ return true;
190
+ }
191
+
192
+ try {
193
+ await api.terminal.kill(ptyId);
194
+ if (ptyIdRef.current === ptyId) {
195
+ ptyIdRef.current = null;
196
+ setRunning(false);
197
+ writeTerminalLine('[stopped]');
198
+ }
199
+ return true;
200
+ } catch (err) {
201
+ const message = err instanceof Error ? err.message : String(err);
202
+ setError(message);
203
+ writeTerminalLine(`[failed to stop claude] ${message}`);
204
+ return false;
205
+ }
206
+ }, [writeTerminalLine]);
207
+
208
+ const startClaude = useCallback(async (workDirOverride?: string) => {
209
+ if (starting) return;
210
+ setStarting(true);
211
+ setError(null);
212
+ try {
213
+ const stopped = await stopClaude();
214
+ if (!stopped) return;
215
+ const nextConfig = await api.systemManager.updateConfig({
216
+ selectedWorkDir: workDirOverride ?? workDirInput,
217
+ });
218
+ setConfig(nextConfig);
219
+ void fetchTeams();
220
+ terminalRef.current?.clear();
221
+ writeTerminalLine(`# cd ${nextConfig.selectedWorkDir}`);
222
+ writeTerminalLine('$ claude');
223
+ const ptyId = await api.terminal.spawn({
224
+ cwd: nextConfig.selectedWorkDir,
225
+ cols: terminalRef.current?.cols ?? 120,
226
+ rows: terminalRef.current?.rows ?? 34,
227
+ });
228
+ ptyIdRef.current = ptyId;
229
+ setRunning(true);
230
+ terminalRef.current?.focus();
231
+ } catch (err) {
232
+ const message = err instanceof Error ? err.message : String(err);
233
+ setError(message);
234
+ writeTerminalLine(`[failed to start claude] ${message}`);
235
+ } finally {
236
+ setStarting(false);
237
+ }
238
+ }, [fetchTeams, starting, stopClaude, workDirInput, writeTerminalLine]);
239
+
240
+ useEffect(() => {
241
+ startClaudeRef.current = startClaude;
242
+ }, [startClaude]);
243
+
244
+ useEffect(() => {
245
+ void load().then((nextConfig) => {
246
+ if (nextConfig && !autoStartedRef.current) {
247
+ autoStartedRef.current = true;
248
+ void startClaudeRef.current?.(nextConfig.selectedWorkDir);
249
+ }
250
+ });
251
+ }, [load]);
252
+
253
+ const refreshConsole = useCallback(async () => {
254
+ await load();
255
+ await startClaude(workDirInput || undefined);
256
+ }, [load, startClaude, workDirInput]);
257
+
258
+ const runWorkflowPrompt = useCallback(
259
+ async (prompt: WorkflowPromptSummary) => {
260
+ if (!config?.workflowFolder) return;
261
+ if (!ptyIdRef.current) {
262
+ await startClaude();
263
+ }
264
+ const ptyId = ptyIdRef.current;
265
+ if (!ptyId) return;
266
+ const result = await api.systemManager.readWorkflowPrompt(config.workflowFolder, prompt.id);
267
+ writeTerminalLine(`$ # workflow: ${prompt.label}`);
268
+ api.terminal.write(ptyId, `${result.content}\r`);
269
+ },
270
+ [config?.workflowFolder, startClaude, writeTerminalLine]
271
+ );
272
+
273
+ const titlePath = useMemo(
274
+ () =>
275
+ formatPathForTitle(config?.selectedWorkDir ?? (workDirInput || status?.defaultWorkDir || '')),
276
+ [config?.selectedWorkDir, status?.defaultWorkDir, workDirInput]
277
+ );
278
+
279
+ return (
280
+ <div className="flex size-full flex-col bg-[var(--color-surface)] p-4 text-[var(--color-text)]">
281
+ <div className="flex min-h-0 flex-1 flex-col overflow-hidden rounded-2xl border border-[var(--color-border)] bg-[var(--color-surface)] shadow-2xl shadow-black/20">
282
+ <div className="flex min-h-12 items-center gap-3 border-b border-[var(--color-border)] bg-[var(--color-surface-raised)] px-4 py-2">
283
+ <div className="flex shrink-0 items-center gap-2">
284
+ <span className="size-3 rounded-full bg-[#ff5f57]" />
285
+ <span className="size-3 rounded-full bg-[#febc2e]" />
286
+ <span className="size-3 rounded-full bg-[#28c840]" />
287
+ </div>
288
+ <div className="flex shrink-0 items-center gap-2 font-mono text-xs text-[var(--color-text-secondary)]">
289
+ <TerminalSquare size={14} className="text-[var(--color-text-muted)]" />
290
+ {SYSTEM_MANAGER_DISPLAY_NAME}
291
+ </div>
292
+ <Input
293
+ value={workDirInput}
294
+ onChange={(event) => setWorkDirInput(event.target.value)}
295
+ onKeyDown={(e) => { if (e.key === 'Enter') void refreshConsole(); }}
296
+ className="h-8 min-w-[220px] flex-1 border-[var(--color-border)] bg-[var(--color-surface)] font-mono text-xs text-[var(--color-text)]"
297
+ placeholder={titlePath || '工作目录'}
298
+ />
299
+ <FolderBrowser value={workDirInput} onChange={setWorkDirInput} />
300
+ <div className="shrink-0 text-[11px] text-[var(--color-text-muted)]">
301
+ {running ? 'claude running' : (status?.localStatus ?? 'starting')}
302
+ </div>
303
+ <Button
304
+ size="sm"
305
+ variant="outline"
306
+ className="h-8 shrink-0 border-[var(--color-border)]"
307
+ disabled={starting}
308
+ onClick={() => void refreshConsole()}
309
+ >
310
+ {starting ? <Loader2 size={13} className="animate-spin" /> : <RefreshCw size={13} />}
311
+ 刷新
312
+ </Button>
313
+ </div>
314
+
315
+ <div className="min-h-0 flex-1 bg-[var(--color-surface)] p-2">
316
+ <div
317
+ ref={terminalHostRef}
318
+ className="size-full overflow-hidden rounded-lg bg-[var(--color-surface)]"
319
+ />
320
+ </div>
321
+
322
+ {(workflowPrompts.length || warnings.length || error || loading) && (
323
+ <div className="border-t border-[var(--color-border)] bg-[var(--color-surface)] px-4 py-3">
324
+ {workflowPrompts.length ? (
325
+ <div className="flex flex-wrap gap-2">
326
+ {workflowPrompts.map((prompt) => (
327
+ <button
328
+ key={prompt.id}
329
+ type="button"
330
+ className="rounded-full border border-[var(--color-border)] bg-[var(--color-surface-raised)] px-3 py-1 font-mono text-[11px] text-[var(--color-text-secondary)] hover:border-[var(--color-border-emphasis)] hover:text-[var(--color-text)]"
331
+ disabled={starting}
332
+ onClick={() => void runWorkflowPrompt(prompt)}
333
+ >
334
+ {prompt.label}
335
+ </button>
336
+ ))}
337
+ </div>
338
+ ) : null}
339
+ {warnings.length ? (
340
+ <div className="mt-2 text-xs text-amber-300">{warnings.join(';')}</div>
341
+ ) : null}
342
+ {error ? <div className="mt-2 text-xs text-red-300">{error}</div> : null}
343
+ {loading ? (
344
+ <div className="mt-2 text-xs text-[var(--color-text-muted)]">加载控制台配置中...</div>
345
+ ) : null}
346
+ </div>
347
+ )}
348
+ </div>
349
+ </div>
350
+ );
351
+ };