@chainingintention/pi-web-cn 1.202610.0 → 1.202610.2

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 (333) hide show
  1. package/README.md +60 -106
  2. package/dist/.plugins-ready +1 -0
  3. package/dist/cli.js +161 -45
  4. package/dist/cli.js.map +1 -1
  5. package/dist/client/assets/index-CFhaGb5m.js +4357 -0
  6. package/dist/client/assets/vendor-editor-core-YWog6mAR.js +10 -0
  7. package/dist/client/assets/vendor-editor-languages-DOCw3vc9.js +28 -0
  8. package/dist/client/index.html +5 -5
  9. package/dist/config.js +30 -6
  10. package/dist/config.js.map +1 -1
  11. package/dist/docker/piWebDockerCommandPlan.js +13 -4
  12. package/dist/docker/piWebDockerCommandPlan.js.map +1 -1
  13. package/dist/nativeServices/installedServiceDefinitions.js +23 -10
  14. package/dist/nativeServices/installedServiceDefinitions.js.map +1 -1
  15. package/dist/nativeServices/serviceAction.js +78 -20
  16. package/dist/nativeServices/serviceAction.js.map +1 -1
  17. package/dist/nativeServices/serviceDoctor.js +339 -67
  18. package/dist/nativeServices/serviceDoctor.js.map +1 -1
  19. package/dist/nativeServices/servicePlan.js +2 -2
  20. package/dist/nativeServices/servicePlan.js.map +1 -1
  21. package/dist/pi-packages/captains-log/README.md +11 -0
  22. package/dist/pi-packages/captains-log/dist/browser/channelProtocol.js +29 -0
  23. package/dist/pi-packages/captains-log/dist/browser/index.js +146 -0
  24. package/dist/pi-packages/captains-log/dist/browser/markdown.js +67 -0
  25. package/dist/pi-packages/captains-log/dist/browser/panel.js +151 -0
  26. package/dist/pi-packages/captains-log/dist/browser/protocol.js +26 -0
  27. package/dist/pi-packages/captains-log/dist/companion.js +126 -0
  28. package/dist/pi-packages/captains-log/dist/roundtrip.js +62 -0
  29. package/dist/pi-packages/captains-log/dist/server.js +210 -0
  30. package/dist/pi-packages/captains-log/dist/store.js +75 -0
  31. package/dist/pi-packages/captains-log/docs/usage.md +29 -0
  32. package/dist/pi-packages/captains-log/package.json +35 -0
  33. package/dist/pi-packages/captains-log/tsconfig.json +21 -0
  34. package/dist/pi-packages/captains-log/vite.config.mjs +28 -0
  35. package/dist/pi-packages/relays/pi-web-plugin.js +9 -9
  36. package/dist/pi-packages/relays/prompts/relay.md +1 -1
  37. package/dist/pi-packages/relays/relaysPanelElement.js +8 -8
  38. package/dist/pi-packages/relays/skills/relay-runner/SKILL.md +21 -13
  39. package/dist/pi-web-plugins/files/browser/assets/files-icon-DZObYhxb.svg +3 -0
  40. package/dist/pi-web-plugins/files/browser/pi-web-plugin.js +456 -0
  41. package/dist/pi-web-plugins/files/package.json +15 -0
  42. package/dist/pi-web-plugins/git/browser/git-panel.js +55 -32
  43. package/dist/pi-web-plugins/git/browser/pi-web-plugin.js +2 -2
  44. package/dist/pi-web-plugins/git/git-backend.js +19 -26
  45. package/dist/pi-web-plugins/git/gitStatusCache.js +48 -0
  46. package/dist/pi-web-plugins/git/package.json +1 -0
  47. package/dist/pi-web-plugins/git/server-plugin.js +11 -10
  48. package/dist/pi-web-plugins/info/infoInternals.js +4 -1
  49. package/dist/pi-web-plugins/info/package.json +1 -1
  50. package/dist/pi-web-plugins/info/pi-web-plugin.js +8 -8
  51. package/dist/pi-web-plugins/mermaid/browser/mermaid-engine.js +3590 -0
  52. package/dist/pi-web-plugins/mermaid/browser/pi-web-plugin.js +6 -0
  53. package/dist/pi-web-plugins/mermaid/package.json +13 -0
  54. package/dist/pi-web-plugins/terminal/browser/pi-web-plugin.js +210 -0
  55. package/dist/pi-web-plugins/terminal/package.json +16 -0
  56. package/dist/pi-web-plugins/terminal/server-plugin.js +365 -0
  57. package/dist/pi-web-plugins/terminal/terminalService.js +435 -0
  58. package/dist/pi-web-plugins/updates/package.json +1 -1
  59. package/dist/pi-web-plugins/updates/pi-web-plugin.js +69 -68
  60. package/dist/pi-web-plugins/workspace-tasks/package.json +1 -1
  61. package/dist/pi-web-plugins/workspace-tasks/pi-web-plugin.js +3 -3
  62. package/dist/piWebVersionReport.js +47 -24
  63. package/dist/piWebVersionReport.js.map +1 -1
  64. package/dist/plugin-api.d.ts +260 -25
  65. package/dist/plugin-api.js +1 -0
  66. package/dist/pluginRecoveryCli.js +3 -2
  67. package/dist/pluginRecoveryCli.js.map +1 -1
  68. package/dist/server/activity/workspaceActivityService.js +5 -5
  69. package/dist/server/activity/workspaceActivityService.js.map +1 -1
  70. package/dist/server/app.js +71 -44
  71. package/dist/server/app.js.map +1 -1
  72. package/dist/server/audit/managementAuditStore.js +1 -1
  73. package/dist/server/browserMessageProjection.js +0 -2
  74. package/dist/server/browserMessageProjection.js.map +1 -1
  75. package/dist/server/configRoutes.js +1 -2
  76. package/dist/server/configRoutes.js.map +1 -1
  77. package/dist/server/knownPiPackages.js +12 -0
  78. package/dist/server/knownPiPackages.js.map +1 -0
  79. package/dist/server/machines/machineClient.js +2 -2
  80. package/dist/server/machines/machineClient.js.map +1 -1
  81. package/dist/server/machines/machinePluginProxyRoutes.js +120 -21
  82. package/dist/server/machines/machinePluginProxyRoutes.js.map +1 -1
  83. package/dist/server/machines/machineProxyRoutes.js +80 -22
  84. package/dist/server/machines/machineProxyRoutes.js.map +1 -1
  85. package/dist/server/machines/machineService.js +22 -0
  86. package/dist/server/machines/machineService.js.map +1 -1
  87. package/dist/server/managementEmbed.js +2 -2
  88. package/dist/server/managementEmbed.js.map +1 -1
  89. package/dist/server/managementPrivileges.js +7 -1
  90. package/dist/server/managementPrivileges.js.map +1 -1
  91. package/dist/server/managementTerminalProxyRoutes.js +124 -0
  92. package/dist/server/managementTerminalProxyRoutes.js.map +1 -0
  93. package/dist/server/normalAuth.js +5 -1
  94. package/dist/server/normalAuth.js.map +1 -1
  95. package/dist/server/notices/serverNoticeStore.js +50 -6
  96. package/dist/server/notices/serverNoticeStore.js.map +1 -1
  97. package/dist/server/piPackageService.js +5 -5
  98. package/dist/server/piPackageService.js.map +1 -1
  99. package/dist/server/piWebPluginCatalog.js +211 -34
  100. package/dist/server/piWebPluginCatalog.js.map +1 -1
  101. package/dist/server/piWebPluginLifecycle.js +29 -3
  102. package/dist/server/piWebPluginLifecycle.js.map +1 -1
  103. package/dist/server/piWebPluginService.js +176 -291
  104. package/dist/server/piWebPluginService.js.map +1 -1
  105. package/dist/server/piWebStatus.js +45 -55
  106. package/dist/server/piWebStatus.js.map +1 -1
  107. package/dist/server/pluginCallbackDrain.js +35 -0
  108. package/dist/server/pluginCallbackDrain.js.map +1 -0
  109. package/dist/server/plugins/pluginBackendChannelProxyAdmission.js +84 -0
  110. package/dist/server/plugins/pluginBackendChannelProxyAdmission.js.map +1 -0
  111. package/dist/server/plugins/pluginBackendChannelProxyCoordinator.js +234 -0
  112. package/dist/server/plugins/pluginBackendChannelProxyCoordinator.js.map +1 -0
  113. package/dist/server/plugins/pluginBackendChannelProxyRoutes.js +63 -0
  114. package/dist/server/plugins/pluginBackendChannelProxyRoutes.js.map +1 -0
  115. package/dist/server/plugins/pluginBackendProxyRoutes.js +7 -4
  116. package/dist/server/plugins/pluginBackendProxyRoutes.js.map +1 -1
  117. package/dist/server/plugins/pluginBackendRegistry.js +773 -0
  118. package/dist/server/plugins/pluginBackendRegistry.js.map +1 -0
  119. package/dist/server/plugins/serverPluginPiSessionEventsCapability.js +34 -0
  120. package/dist/server/plugins/serverPluginPiSessionEventsCapability.js.map +1 -0
  121. package/dist/server/plugins/serverPluginPiSessionsCapability.js +185 -0
  122. package/dist/server/plugins/serverPluginPiSessionsCapability.js.map +1 -0
  123. package/dist/server/plugins/serverPluginRuntime.js +1049 -107
  124. package/dist/server/plugins/serverPluginRuntime.js.map +1 -1
  125. package/dist/server/plugins/serverPluginWorkspacesCapability.js +114 -0
  126. package/dist/server/plugins/serverPluginWorkspacesCapability.js.map +1 -0
  127. package/dist/server/projectTrustRoutes.js +86 -0
  128. package/dist/server/projectTrustRoutes.js.map +1 -0
  129. package/dist/server/projects/projectLifecycleService.js +84 -0
  130. package/dist/server/projects/projectLifecycleService.js.map +1 -0
  131. package/dist/server/projects/projectService.js +2 -0
  132. package/dist/server/projects/projectService.js.map +1 -1
  133. package/dist/server/realtime/sessionEventHub.js +35 -41
  134. package/dist/server/realtime/sessionEventHub.js.map +1 -1
  135. package/dist/server/sessiond/agentHttpDispatcher.js +108 -0
  136. package/dist/server/sessiond/agentHttpDispatcher.js.map +1 -0
  137. package/dist/server/sessiond/pluginBackendChannelRoutes.js +397 -0
  138. package/dist/server/sessiond/pluginBackendChannelRoutes.js.map +1 -0
  139. package/dist/server/sessiond/pluginBackendRoutes.js +19 -9
  140. package/dist/server/sessiond/pluginBackendRoutes.js.map +1 -1
  141. package/dist/server/sessiond/projectMutationRoutes.js +38 -0
  142. package/dist/server/sessiond/projectMutationRoutes.js.map +1 -0
  143. package/dist/server/sessiond/sessionDaemonShutdown.js +36 -0
  144. package/dist/server/sessiond/sessionDaemonShutdown.js.map +1 -0
  145. package/dist/server/sessiond/sessionDaemonStartup.js +30 -4
  146. package/dist/server/sessiond/sessionDaemonStartup.js.map +1 -1
  147. package/dist/server/sessiond/sessionProxyRoutes.js +47 -15
  148. package/dist/server/sessiond/sessionProxyRoutes.js.map +1 -1
  149. package/dist/server/sessiond/sessionServiceDependencies.js +4 -0
  150. package/dist/server/sessiond/sessionServiceDependencies.js.map +1 -1
  151. package/dist/server/sessiond/sessiondStateOwnership.js +174 -39
  152. package/dist/server/sessiond/sessiondStateOwnership.js.map +1 -1
  153. package/dist/server/sessiond/workspaceRemovalRoutes.js +1 -1
  154. package/dist/server/sessiond/workspaceRemovalRoutes.js.map +1 -1
  155. package/dist/server/sessiond.js +226 -56
  156. package/dist/server/sessiond.js.map +1 -1
  157. package/dist/server/sessions/askUserTool.js +0 -3
  158. package/dist/server/sessions/askUserTool.js.map +1 -1
  159. package/dist/server/sessions/attachmentService.js +0 -1
  160. package/dist/server/sessions/attachmentService.js.map +1 -1
  161. package/dist/server/sessions/authService.js +8 -1
  162. package/dist/server/sessions/authService.js.map +1 -1
  163. package/dist/server/sessions/builtinCommands.js +2 -2
  164. package/dist/server/sessions/builtinCommands.js.map +1 -1
  165. package/dist/server/sessions/clientSessionPreview.js +14 -0
  166. package/dist/server/sessions/clientSessionPreview.js.map +1 -0
  167. package/dist/server/sessions/dockerEnvironmentFacts.js +82 -9
  168. package/dist/server/sessions/dockerEnvironmentFacts.js.map +1 -1
  169. package/dist/server/sessions/managementAgentTools.js +11 -6
  170. package/dist/server/sessions/managementAgentTools.js.map +1 -1
  171. package/dist/server/sessions/managementPermissionSystem.js +16 -4
  172. package/dist/server/sessions/managementPermissionSystem.js.map +1 -1
  173. package/dist/server/sessions/messagePaging.js +2 -2
  174. package/dist/server/sessions/messagePaging.js.map +1 -1
  175. package/dist/server/sessions/modelCatalogRefresher.js +4 -3
  176. package/dist/server/sessions/modelCatalogRefresher.js.map +1 -1
  177. package/dist/server/sessions/oauthLoginFlowService.js +2 -5
  178. package/dist/server/sessions/oauthLoginFlowService.js.map +1 -1
  179. package/dist/server/sessions/pendingAskStore.js +0 -3
  180. package/dist/server/sessions/pendingAskStore.js.map +1 -1
  181. package/dist/server/sessions/piSessionEventConnections.js +65 -0
  182. package/dist/server/sessions/piSessionEventConnections.js.map +1 -0
  183. package/dist/server/sessions/piSessionManagerGateway.js +56 -3
  184. package/dist/server/sessions/piSessionManagerGateway.js.map +1 -1
  185. package/dist/server/sessions/piSessionService.js +785 -450
  186. package/dist/server/sessions/piSessionService.js.map +1 -1
  187. package/dist/server/sessions/projectAuthService.js +29 -2
  188. package/dist/server/sessions/projectAuthService.js.map +1 -1
  189. package/dist/server/sessions/projectSettingsScope.js +12 -4
  190. package/dist/server/sessions/projectSettingsScope.js.map +1 -1
  191. package/dist/server/sessions/projectTrust.js +119 -0
  192. package/dist/server/sessions/projectTrust.js.map +1 -0
  193. package/dist/server/sessions/sessionActivityMarker.js +105 -0
  194. package/dist/server/sessions/sessionActivityMarker.js.map +1 -0
  195. package/dist/server/sessions/sessionArchiveMapping.js +2 -8
  196. package/dist/server/sessions/sessionArchiveMapping.js.map +1 -1
  197. package/dist/server/sessions/sessionCommandService.js +12 -10
  198. package/dist/server/sessions/sessionCommandService.js.map +1 -1
  199. package/dist/server/sessions/sessionEnvironmentFacts.js +5 -4
  200. package/dist/server/sessions/sessionEnvironmentFacts.js.map +1 -1
  201. package/dist/server/sessions/sessionModelScope.js +42 -5
  202. package/dist/server/sessions/sessionModelScope.js.map +1 -1
  203. package/dist/server/sessions/sessionNameGenerator.js +6 -6
  204. package/dist/server/sessions/sessionNameGenerator.js.map +1 -1
  205. package/dist/server/sessions/sessionRoutes.js +63 -15
  206. package/dist/server/sessions/sessionRoutes.js.map +1 -1
  207. package/dist/server/sessions/sessionTreeProjection.js +29 -29
  208. package/dist/server/sessions/sessionTreeProjection.js.map +1 -1
  209. package/dist/server/sessions/sessionUiEvents.js +6 -7
  210. package/dist/server/sessions/sessionUiEvents.js.map +1 -1
  211. package/dist/server/sessions/sessionUnreadStore.js +26 -1
  212. package/dist/server/sessions/sessionUnreadStore.js.map +1 -1
  213. package/dist/server/sessions/spawnSessionTool.js +3 -3
  214. package/dist/server/sessions/spawnSessionTool.js.map +1 -1
  215. package/dist/server/sessions/spawnSubsessionTool.js +5 -5
  216. package/dist/server/sessions/spawnSubsessionTool.js.map +1 -1
  217. package/dist/server/sessions/spawnTargetResolver.js +1 -1
  218. package/dist/server/sessions/transcriptMessages.js +54 -0
  219. package/dist/server/sessions/transcriptMessages.js.map +1 -0
  220. package/dist/server/status/machineStatusService.js +3 -1
  221. package/dist/server/status/machineStatusService.js.map +1 -1
  222. package/dist/server/terminals/{terminalService.js → managementCommandRunService.js} +79 -107
  223. package/dist/server/terminals/managementCommandRunService.js.map +1 -0
  224. package/dist/server/terminals/managementTerminalRoutes.js +91 -0
  225. package/dist/server/terminals/managementTerminalRoutes.js.map +1 -0
  226. package/dist/server/terminals/requiredTerminalService.js +103 -0
  227. package/dist/server/terminals/requiredTerminalService.js.map +1 -0
  228. package/dist/server/webSocketBridge.js +339 -0
  229. package/dist/server/webSocketBridge.js.map +1 -1
  230. package/dist/server/workbench/gatewayIntegration.js +4 -1
  231. package/dist/server/workbench/gatewayIntegration.js.map +1 -1
  232. package/dist/server/workspaceExplorerRoutes.js +13 -12
  233. package/dist/server/workspaceExplorerRoutes.js.map +1 -1
  234. package/dist/server/workspaces/fileContentService.js +9 -7
  235. package/dist/server/workspaces/fileContentService.js.map +1 -1
  236. package/dist/server/workspaces/fileOperationService.js +4 -3
  237. package/dist/server/workspaces/fileOperationService.js.map +1 -1
  238. package/dist/server/workspaces/projectWorkspaceCwds.js +0 -5
  239. package/dist/server/workspaces/projectWorkspaceCwds.js.map +1 -1
  240. package/dist/server/workspaces/sessionDaemonWorkspaceCatalog.js +36 -12
  241. package/dist/server/workspaces/sessionDaemonWorkspaceCatalog.js.map +1 -1
  242. package/dist/server/workspaces/workspaceCatalog.js +6 -4
  243. package/dist/server/workspaces/workspaceCatalog.js.map +1 -1
  244. package/dist/server/workspaces/workspaceDeletionRoutes.js +5 -57
  245. package/dist/server/workspaces/workspaceDeletionRoutes.js.map +1 -1
  246. package/dist/server/workspaces/workspaceProviderRegistry.js +75 -24
  247. package/dist/server/workspaces/workspaceProviderRegistry.js.map +1 -1
  248. package/dist/server/workspaces/workspaceRemovalService.js +25 -3
  249. package/dist/server/workspaces/workspaceRemovalService.js.map +1 -1
  250. package/dist/server-plugin-api.d.ts +274 -44
  251. package/dist/server-plugin-api.js +319 -2
  252. package/dist/server-plugin-api.js.map +1 -1
  253. package/dist/serverPluginRecovery.js +4 -0
  254. package/dist/serverPluginRecovery.js.map +1 -1
  255. package/dist/sessiond/activeAgentProfile.js +27 -2
  256. package/dist/sessiond/activeAgentProfile.js.map +1 -1
  257. package/dist/sessiond/config.js +4 -3
  258. package/dist/sessiond/config.js.map +1 -1
  259. package/dist/sessiond/sessionDaemonClient.js +29 -8
  260. package/dist/sessiond/sessionDaemonClient.js.map +1 -1
  261. package/dist/shared/activeAgentProfile.js +2 -34
  262. package/dist/shared/activeAgentProfile.js.map +1 -1
  263. package/dist/shared/activity.js +0 -3
  264. package/dist/shared/activity.js.map +1 -1
  265. package/dist/shared/apiTypes.js +7 -2
  266. package/dist/shared/apiTypes.js.map +1 -1
  267. package/dist/shared/capabilities.js +5 -0
  268. package/dist/shared/capabilities.js.map +1 -1
  269. package/dist/shared/federatedRoutes.js +49 -9
  270. package/dist/shared/federatedRoutes.js.map +1 -1
  271. package/dist/shared/machinePluginIds.js +30 -7
  272. package/dist/shared/machinePluginIds.js.map +1 -1
  273. package/dist/shared/machineStatus.js +35 -1
  274. package/dist/shared/machineStatus.js.map +1 -1
  275. package/dist/shared/piWebStatusParsing.js +29 -0
  276. package/dist/shared/piWebStatusParsing.js.map +1 -1
  277. package/dist/shared/pluginApiTypes.d.ts +24 -9
  278. package/dist/shared/pluginApiTypes.js +2 -0
  279. package/dist/shared/pluginApiTypes.js.map +1 -0
  280. package/dist/shared/pluginBackendProtocol.js +163 -2
  281. package/dist/shared/pluginBackendProtocol.js.map +1 -1
  282. package/dist/shared/pluginIds.js +10 -1
  283. package/dist/shared/pluginIds.js.map +1 -1
  284. package/dist/shared/requiredTerminalPlugin.js +6 -0
  285. package/dist/shared/requiredTerminalPlugin.js.map +1 -0
  286. package/dist/shared/serverNoticeContract.js +88 -0
  287. package/dist/shared/serverNoticeContract.js.map +1 -0
  288. package/dist/shared/sessionDefaults.js +41 -0
  289. package/dist/shared/sessionDefaults.js.map +1 -0
  290. package/dist/shared/workspaceFiles.js.map +1 -1
  291. package/docs/config.md +270 -323
  292. package/docs/plugins.md +171 -956
  293. package/examples/session-bridge-plugin/README.md +20 -0
  294. package/examples/session-bridge-plugin/docs/usage.md +54 -0
  295. package/examples/session-bridge-plugin/package.json +26 -0
  296. package/examples/session-bridge-plugin/src/browser/index.ts +87 -0
  297. package/examples/session-bridge-plugin/src/browser/protocol.ts +24 -0
  298. package/examples/session-bridge-plugin/src/companion.ts +65 -0
  299. package/examples/session-bridge-plugin/src/reviewRun.ts +39 -0
  300. package/examples/session-bridge-plugin/src/server.ts +85 -0
  301. package/examples/session-bridge-plugin/src/store.ts +80 -0
  302. package/examples/session-bridge-plugin/tsconfig.json +21 -0
  303. package/examples/workspace-provider-plugin/README.md +39 -0
  304. package/examples/workspace-provider-plugin/package.json +25 -0
  305. package/examples/workspace-provider-plugin/src/browser/index.ts +69 -0
  306. package/examples/workspace-provider-plugin/src/server.ts +68 -0
  307. package/examples/workspace-provider-plugin/tsconfig.json +21 -0
  308. package/package.json +22 -13
  309. package/skills/README.md +27 -2
  310. package/skills/relay-runner/SKILL.md +21 -13
  311. package/dist/client/assets/CodeViewer-CZHnKRwQ.js +0 -4
  312. package/dist/client/assets/TerminalPanel-B_EaRPHY.js +0 -187
  313. package/dist/client/assets/index-Bv1v-Jz7.js +0 -4717
  314. package/dist/client/assets/vendor-editor-core-vUi74DnD.js +0 -12
  315. package/dist/client/assets/vendor-editor-languages-DoaXUodB.js +0 -46
  316. package/dist/client/assets/vendor-editor-legacy-CYBnW6ZU.js +0 -1
  317. package/dist/client/assets/vendor-terminal-BrP-ENHg.css +0 -1
  318. package/dist/client/assets/vendor-terminal-D8k4UKM2.js +0 -35
  319. package/dist/server/git/gitService.js +0 -316
  320. package/dist/server/git/gitService.js.map +0 -1
  321. package/dist/server/gitRoutes.js +0 -23
  322. package/dist/server/gitRoutes.js.map +0 -1
  323. package/dist/server/terminalProxyRoutes.js +0 -181
  324. package/dist/server/terminalProxyRoutes.js.map +0 -1
  325. package/dist/server/terminals/terminalRoutes.js +0 -164
  326. package/dist/server/terminals/terminalRoutes.js.map +0 -1
  327. package/dist/server/terminals/terminalService.js.map +0 -1
  328. package/dist/server/terminals/terminalSize.js +0 -17
  329. package/dist/server/terminals/terminalSize.js.map +0 -1
  330. package/dist/server/workspaces/gitWorktreeDiscovery.js +0 -43
  331. package/dist/server/workspaces/gitWorktreeDiscovery.js.map +0 -1
  332. package/dist/server/workspaces/workspaceService.js +0 -52
  333. package/dist/server/workspaces/workspaceService.js.map +0 -1
@@ -0,0 +1,15 @@
1
+ {
2
+ "name": "@pi-web/files-plugin",
3
+ "private": true,
4
+ "type": "module",
5
+ "piWeb": {
6
+ "plugins": [
7
+ {
8
+ "id": "files",
9
+ "browserRoot": "browser",
10
+ "module": "browser/pi-web-plugin.js",
11
+ "machineSpecific": false
12
+ }
13
+ ]
14
+ }
15
+ }
@@ -54,8 +54,8 @@ class GitUiController {
54
54
  this.synchronizeRoute(state, changedWorkspace);
55
55
  if (state.status === undefined && state.statusRequest === undefined)
56
56
  void this.refresh(context);
57
- else if (state.selectedDiffPath !== undefined && (state.selectedDiff === undefined || state.selectedStagedDiff === undefined) && !state.diffLoading) {
58
- if (state.status?.files.some((file) => file.path === state.selectedDiffPath) === true)
57
+ else if (state.status !== undefined && state.selectedDiffPath !== undefined && (state.selectedDiff === undefined || state.selectedStagedDiff === undefined) && !state.diffLoading) {
58
+ if (state.status.files.some((file) => file.path === state.selectedDiffPath))
59
59
  void this.refreshDiff(state, state.selectedDiffPath, context);
60
60
  else
61
61
  this.clearSelection(state, true);
@@ -91,13 +91,24 @@ class GitUiController {
91
91
  }
92
92
  refresh(context, force = true) {
93
93
  const state = this.stateFor(context);
94
+ const background = !force;
94
95
  if (state.statusRequest !== undefined) {
95
- state.statusRefreshPending ||= force;
96
+ if (!background && !state.statusLoading) {
97
+ state.statusLoading = true;
98
+ this.requestRender(state);
99
+ }
96
100
  return state.statusRequest;
97
101
  }
98
- state.statusLoading = true;
102
+ const previousStatus = state.status;
103
+ const previousError = state.error;
104
+ const previousStale = state.stale;
105
+ const previousPath = state.selectedDiffPath;
106
+ const showLoading = !background || state.status === undefined;
107
+ if (showLoading) {
108
+ state.statusLoading = true;
109
+ this.requestRender(state);
110
+ }
99
111
  state.lastStatusRefreshAt = Date.now();
100
- this.requestRender(state);
101
112
  const request = requestGitBackend(context, GIT_STATUS_OPERATION, force ? { refresh: true } : null)
102
113
  .then(parseGitStatusResponse)
103
114
  .then(async (status) => {
@@ -105,18 +116,20 @@ class GitUiController {
105
116
  return;
106
117
  if (state.status?.hash !== status.hash) {
107
118
  state.status = status;
108
- state.visibleFileLimit = Math.min(GIT_FILE_RENDER_BATCH, status.files.length);
119
+ state.visibleFileLimit = GIT_FILE_RENDER_BATCH;
109
120
  revealSelectedFile(state, this.view);
110
121
  }
111
122
  state.stale = false;
112
- state.error = undefined;
113
123
  const path = state.selectedDiffPath;
114
- if (path === undefined)
115
- return;
116
- if (!status.files.some((file) => file.path === path))
117
- this.clearSelection(state, true);
118
- else if (this.connectedWorkspaceKey === workspaceContextKey(context))
119
- await this.refreshDiff(state, path, context);
124
+ if (path !== undefined && status.files.some((file) => file.path === path)
125
+ && this.connectedWorkspaceKey === workspaceContextKey(context)) {
126
+ await this.refreshDiff(state, path, context, background);
127
+ }
128
+ else {
129
+ state.error = undefined;
130
+ if (path !== undefined && !status.files.some((file) => file.path === path))
131
+ this.clearSelection(state, true);
132
+ }
120
133
  })
121
134
  .catch((error) => {
122
135
  if (state.retained)
@@ -126,13 +139,11 @@ class GitUiController {
126
139
  if (state.statusRequest !== request)
127
140
  return;
128
141
  state.statusRequest = undefined;
142
+ const wasLoading = state.statusLoading;
129
143
  state.statusLoading = false;
130
- this.requestRender(state);
131
- if (state.statusRefreshPending && state.retained && this.connectedWorkspaceKey === workspaceContextKey(context)) {
132
- state.statusRefreshPending = false;
133
- return this.refresh(context);
134
- }
135
- return undefined;
144
+ if (wasLoading || previousStatus !== state.status || previousError !== state.error
145
+ || previousStale !== state.stale || previousPath !== state.selectedDiffPath)
146
+ this.requestRender(state);
136
147
  });
137
148
  state.statusRequest = request;
138
149
  return request;
@@ -217,7 +228,6 @@ class GitUiController {
217
228
  error: undefined,
218
229
  expandedDirectories: new Set(),
219
230
  statusRequest: undefined,
220
- statusRefreshPending: false,
221
231
  diffRequestSequence: 0,
222
232
  viewStateCache: undefined,
223
233
  visibleFileLimit: GIT_FILE_RENDER_BATCH,
@@ -270,6 +280,7 @@ class GitUiController {
270
280
  if (state.selectedDiffPath === path)
271
281
  return;
272
282
  state.selectedDiffPath = path;
283
+ revealSelectedFile(state, this.view);
273
284
  state.selectedDiff = undefined;
274
285
  state.selectedStagedDiff = undefined;
275
286
  state.diffLoading = false;
@@ -281,11 +292,17 @@ class GitUiController {
281
292
  this.route.write(undefined, { replace: true });
282
293
  }
283
294
  }
284
- async refreshDiff(state, path, context) {
295
+ async refreshDiff(state, path, context, background = false) {
285
296
  const sequence = state.diffRequestSequence + 1;
286
297
  state.diffRequestSequence = sequence;
287
- state.diffLoading = true;
288
- this.requestRender(state);
298
+ const previousDiff = state.selectedDiff;
299
+ const previousStagedDiff = state.selectedStagedDiff;
300
+ const previousError = state.error;
301
+ const showLoading = !background || previousDiff === undefined || previousStagedDiff === undefined;
302
+ if (showLoading) {
303
+ state.diffLoading = true;
304
+ this.requestRender(state);
305
+ }
289
306
  try {
290
307
  const [selectedDiff, selectedStagedDiff] = await Promise.all([
291
308
  requestGitBackend(context, GIT_DIFF_OPERATION, { path }).then(parseGitDiffResponse),
@@ -304,8 +321,11 @@ class GitUiController {
304
321
  }
305
322
  finally {
306
323
  if (state.retained && state.diffRequestSequence === sequence && state.selectedDiffPath === path) {
324
+ const wasLoading = state.diffLoading;
307
325
  state.diffLoading = false;
308
- this.requestRender(state);
326
+ if (wasLoading || previousDiff !== state.selectedDiff || previousStagedDiff !== state.selectedStagedDiff
327
+ || previousError !== state.error)
328
+ this.requestRender(state);
309
329
  }
310
330
  }
311
331
  }
@@ -324,7 +344,7 @@ function createGitActions(panelId, controller) {
324
344
  shortcutAliases: ["core:view.git"],
325
345
  group: "导航",
326
346
  enabled: hasGitWorkspace,
327
- run: (context) => { context.selectMainView(panelId); },
347
+ run: (context) => { context.selectWorkspaceTool(panelId); },
328
348
  },
329
349
  {
330
350
  id: "workspace.refresh-git",
@@ -333,7 +353,7 @@ function createGitActions(panelId, controller) {
333
353
  shortcutAliases: ["core:workspace.refresh-git"],
334
354
  group: "工作区",
335
355
  enabled: hasGitWorkspace,
336
- run: (context) => context.refreshWorkspacePanels?.(panelId),
356
+ run: (context) => context.refreshWorkspacePanels(panelId),
337
357
  },
338
358
  ];
339
359
  }
@@ -353,16 +373,17 @@ function createGitPanel(html, svg, controller) {
353
373
  `,
354
374
  order: 20,
355
375
  routeAliases: ["git", "core:workspace.git"],
376
+ navigationAliases: ["core:workspace.git"],
356
377
  visible: (context) => controller.isOwnedWorkspace(context.workspace),
357
378
  onInvalidate: (context) => controller.invalidate(context),
358
379
  render: (context) => renderGitPanel(html, controller, context),
359
380
  };
360
381
  }
361
382
  function requestGitBackend(context, operation, input) {
362
- if (context.backend === undefined || context.workspace.provider?.capabilities.request === false) {
383
+ if (context.peer?.request === undefined) {
363
384
  return Promise.reject(new Error("Git 工作区后端不可用。请更新并重启此机器上的 PI WEB,然后重新加载浏览器。"));
364
385
  }
365
- return context.backend.request(operation, input);
386
+ return context.peer.request(operation, input);
366
387
  }
367
388
  function renderGitPanel(html, controller, context) {
368
389
  const state = controller.state(context);
@@ -639,10 +660,12 @@ function formatLineNumber(lineNumber) {
639
660
  return lineNumber === undefined ? "" : String(lineNumber);
640
661
  }
641
662
  function createDiffView(response, previous) {
642
- return {
643
- response,
644
- lines: previous?.response.hash === response.hash ? previous.lines : undefined,
645
- };
663
+ if (previous?.response.hash !== response.hash)
664
+ return { response, lines: undefined };
665
+ if (previous.response.path === response.path && previous.response.staged === response.staged
666
+ && previous.response.truncated === response.truncated)
667
+ return previous;
668
+ return { response, lines: previous.lines };
646
669
  }
647
670
  function workspaceContextKey(context) {
648
671
  return JSON.stringify([context.machine.id, context.workspace.projectId, context.workspace.id]);
@@ -1,10 +1,10 @@
1
1
  // Generated from pi-web-plugins/git/browser/pi-web-plugin.ts. Do not edit directly.
2
2
  import { createGitBrowserContributions } from "./git-panel.js";
3
3
  const plugin = {
4
- apiVersion: 2,
4
+ apiVersion: 4,
5
5
  name: "Git",
6
6
  activate: ({ pluginId, runtimePluginId, html, svg }) => ({
7
- contributions: createGitBrowserContributions(pluginId, runtimePluginId ?? pluginId, html, svg),
7
+ contributions: createGitBrowserContributions(pluginId, runtimePluginId, html, svg),
8
8
  }),
9
9
  };
10
10
  export default plugin;
@@ -1,10 +1,12 @@
1
1
  // Generated from pi-web-plugins/git/git-backend.ts. Do not edit directly.
2
2
  import { createHash } from "node:crypto";
3
3
  import { realpath } from "node:fs/promises";
4
+ import { GitStatusCache } from "./gitStatusCache.js";
4
5
  import { isAbsolute, join, relative, sep } from "node:path";
5
6
  import { GIT_DIFF_OPERATION, GIT_STATUS_OPERATION, } from "./browser/git-contract.js";
6
7
  export { GIT_DIFF_OPERATION, GIT_STATUS_OPERATION } from "./browser/git-contract.js";
7
8
  const GIT_COMMAND_TIMEOUT_MS = 10_000;
9
+ const statusCaches = new WeakMap();
8
10
  const GIT_LOCAL_ENV_VARS = Object.freeze([
9
11
  "GIT_ALTERNATE_OBJECT_DIRECTORIES",
10
12
  "GIT_COMMON_DIR",
@@ -15,39 +17,27 @@ const GIT_LOCAL_ENV_VARS = Object.freeze([
15
17
  "GIT_QUARANTINE_PATH",
16
18
  "GIT_WORK_TREE",
17
19
  ]);
18
- /** Dispatch the Git-owned status/diff schema through the provider's public request seam. */
19
- export function createGitBackend(activationContext) {
20
- const statuses = new Map();
21
- return (request) => {
22
- if (request.operation !== GIT_STATUS_OPERATION)
23
- return requestGitBackend(activationContext, request);
24
- requireStatusInput(request.input);
25
- const key = JSON.stringify([request.project.id, request.project.path, request.workspace.key, request.workspace.path]);
26
- const cached = statuses.get(key);
27
- if (cached !== undefined && (cached.expires === Infinity || (request.input === null && cached.expires > Date.now())))
28
- return cached.promise;
29
- const entry = { expires: Infinity, promise: requestGitBackend(activationContext, { ...request, signal: new AbortController().signal }).then((result) => {
30
- entry.expires = Date.now() + 2000;
31
- for (const [oldKey, old] of statuses)
32
- if (old.expires <= Date.now())
33
- statuses.delete(oldKey);
34
- return result;
35
- }, (error) => { statuses.delete(key); throw error; }) };
36
- statuses.set(key, entry);
37
- return entry.promise;
38
- };
39
- }
20
+ /** Dispatch the Git status/diff schema through the package peer request seam. */
40
21
  export async function requestGitBackend(activationContext, request) {
41
22
  const runGit = createGitRunner(activationContext, request.signal);
42
23
  if (request.operation === GIT_STATUS_OPERATION) {
43
24
  requireStatusInput(request.input);
44
- return statusProviderResponse(await gitStatusWithRunner(runGit, request.workspace.path));
25
+ let cache = statusCaches.get(activationContext);
26
+ if (cache === undefined) {
27
+ cache = new GitStatusCache((cwd, signal) => gitStatus(activationContext, cwd, signal));
28
+ statusCaches.set(activationContext, cache);
29
+ }
30
+ const key = JSON.stringify([request.project.id, request.project.path, request.workspace.id, request.workspace.path, request.workspace.provider]);
31
+ return statusPeerResponse(await cache.get(key, request.workspace.path, request.signal, request.input !== null));
45
32
  }
46
33
  if (request.operation === GIT_DIFF_OPERATION) {
47
- return diffProviderResponse(await gitDiffWithRunner(runGit, request.workspace.path, parseDiffInput(request.input)));
34
+ return diffPeerResponse(await gitDiffWithRunner(runGit, request.workspace.path, parseDiffInput(request.input)));
48
35
  }
49
36
  throw new Error(`Unsupported Git workspace backend operation: ${request.operation}`);
50
37
  }
38
+ export async function gitStatus(context, cwd, signal) {
39
+ return gitStatusWithRunner(createGitRunner(context, signal), cwd);
40
+ }
51
41
  async function gitStatusWithRunner(runGit, cwd) {
52
42
  const result = await runGit(cwd, ["status", "--porcelain=v2", "--branch", "--untracked-files=all", "-z"]);
53
43
  if (result.code !== 0)
@@ -137,6 +127,9 @@ async function resolveSubmoduleToCommit(runGit, cwd, sub) {
137
127
  const resolved = head.stdout.trim();
138
128
  return head.code === 0 && resolved !== "" ? resolved : sub.indexOid;
139
129
  }
130
+ export async function gitDiff(context, cwd, options, signal) {
131
+ return gitDiffWithRunner(createGitRunner(context, signal), cwd, options);
132
+ }
140
133
  async function gitDiffWithRunner(runGit, cwd, options) {
141
134
  const staged = options.staged === true;
142
135
  let path;
@@ -371,7 +364,7 @@ function displayFromCommit(headOid) {
371
364
  function hash(value) {
372
365
  return createHash("sha1").update(value).digest("hex");
373
366
  }
374
- function statusProviderResponse(status) {
367
+ function statusPeerResponse(status) {
375
368
  return {
376
369
  isGitRepo: status.isGitRepo,
377
370
  hash: status.hash,
@@ -391,7 +384,7 @@ function statusProviderResponse(status) {
391
384
  ...(status.truncated === true ? { truncated: true } : {}),
392
385
  };
393
386
  }
394
- function diffProviderResponse(diff) {
387
+ function diffPeerResponse(diff) {
395
388
  return {
396
389
  ...(diff.path === undefined ? {} : { path: diff.path }),
397
390
  staged: diff.staged,
@@ -0,0 +1,48 @@
1
+ // Generated from pi-web-plugins/git/gitStatusCache.ts. Do not edit directly.
2
+ /** Completed snapshots are shared; cancellable scans belong to their exact invocation signal. */
3
+ export class GitStatusCache {
4
+ scan;
5
+ now;
6
+ entries = new Map();
7
+ constructor(scan, now = Date.now) {
8
+ this.scan = scan;
9
+ this.now = now;
10
+ }
11
+ get(key, cwd, signal, force = false) {
12
+ signal.throwIfAborted();
13
+ const entry = this.entries.get(key) ?? { expires: 0, generation: 0, pending: new Map() };
14
+ const pending = entry.pending.get(signal);
15
+ if (pending !== undefined)
16
+ return pending;
17
+ if (!force && entry.value !== undefined && entry.expires > this.now())
18
+ return Promise.resolve(entry.value);
19
+ this.entries.set(key, entry);
20
+ if (force)
21
+ delete entry.value;
22
+ const generation = ++entry.generation;
23
+ const request = Promise.resolve().then(() => this.scan(cwd, signal)).then((status) => {
24
+ signal.throwIfAborted();
25
+ if (entry.generation === generation) {
26
+ if (status.isGitRepo) {
27
+ entry.value = status;
28
+ entry.expires = this.now() + 2000;
29
+ }
30
+ else
31
+ delete entry.value;
32
+ }
33
+ return status;
34
+ }).catch((error) => {
35
+ if (entry.generation === generation)
36
+ delete entry.value;
37
+ throw error;
38
+ }).finally(() => {
39
+ entry.pending.delete(signal);
40
+ for (const [oldKey, old] of this.entries) {
41
+ if (old.pending.size === 0 && (old.value === undefined || old.expires <= this.now()))
42
+ this.entries.delete(oldKey);
43
+ }
44
+ });
45
+ entry.pending.set(signal, request);
46
+ return request;
47
+ }
48
+ }
@@ -6,6 +6,7 @@
6
6
  "plugins": [
7
7
  {
8
8
  "id": "git",
9
+ "browserRoot": "browser",
9
10
  "module": "browser/pi-web-plugin.js",
10
11
  "serverModule": "server-plugin.js",
11
12
  "machineSpecific": true
@@ -1,6 +1,6 @@
1
1
  // Generated from pi-web-plugins/git/server-plugin.ts. Do not edit directly.
2
2
  import { basename, resolve } from "node:path";
3
- import { createGitBackend } from "./git-backend.js";
3
+ import { requestGitBackend } from "./git-backend.js";
4
4
  const GIT_LOCAL_ENV_VARS = Object.freeze([
5
5
  "GIT_ALTERNATE_OBJECT_DIRECTORIES",
6
6
  "GIT_COMMON_DIR",
@@ -12,21 +12,23 @@ const GIT_LOCAL_ENV_VARS = Object.freeze([
12
12
  "GIT_WORK_TREE",
13
13
  ]);
14
14
  const plugin = {
15
- apiVersion: 1,
15
+ apiVersion: 3,
16
16
  name: "Git",
17
17
  activate(context) {
18
- return { workspaceProvider: createGitWorkspaceProvider(context) };
18
+ return {
19
+ workspaceProvider: createGitWorkspaceProvider(context),
20
+ peer: { request: (request) => requestGitBackend(context, request) },
21
+ };
19
22
  },
20
23
  };
21
24
  export default plugin;
22
25
  export function createGitWorkspaceProvider(context) {
23
- const requestBackend = createGitBackend(context);
24
26
  return Object.freeze({
25
27
  fallback: true,
26
28
  async probe(project, signal) {
27
29
  const result = await runGit(context, project.path, ["rev-parse", "--is-inside-work-tree"], signal);
28
30
  if (result.signal !== null)
29
- throw new Error(`git repository probe ended from signal ${result.signal}`);
31
+ throw new Error(`Git 仓库探测因信号 ${result.signal} 结束`);
30
32
  if (result.exitCode !== 0)
31
33
  return "pass";
32
34
  return result.stdout.trim() === "true" ? "claim" : "pass";
@@ -37,7 +39,7 @@ export function createGitWorkspaceProvider(context) {
37
39
  if (mainRootOutput === "")
38
40
  throw new Error("Git 返回了空的工作树根目录");
39
41
  const mainRoot = resolve(mainRootOutput);
40
- const commonDirectoryResult = await requireGit(runGit(context, project.path, ["rev-parse", "--git-common-dir"], signal), "resolve the Git common directory");
42
+ const commonDirectoryResult = await requireGit(runGit(context, project.path, ["rev-parse", "--git-common-dir"], signal), "解析 Git 公共目录");
41
43
  const commonDirectoryOutput = commonDirectoryResult.stdout.trim();
42
44
  if (commonDirectoryOutput === "")
43
45
  throw new Error("Git 返回了空的公共目录");
@@ -81,7 +83,6 @@ export function createGitWorkspaceProvider(context) {
81
83
  };
82
84
  });
83
85
  },
84
- request: (request) => requestBackend(request),
85
86
  async prepareRemove({ project, workspace, signal }) {
86
87
  const privatePath = gitPrivateWorktreePath(workspace);
87
88
  if (resolve(privatePath) !== workspace.path) {
@@ -163,7 +164,7 @@ async function runGit(context, cwd, args, signal) {
163
164
  signal,
164
165
  });
165
166
  if (result.stdoutTruncated || result.stderrTruncated) {
166
- throw new Error(`git ${args.join(" ")} exceeded the host output limit`);
167
+ throw new Error(`git ${args.join(" ")} 超出宿主输出限制`);
167
168
  }
168
169
  return result;
169
170
  }
@@ -172,6 +173,6 @@ async function requireGit(resultPromise, action) {
172
173
  if (result.signal === null && result.exitCode === 0)
173
174
  return result;
174
175
  const detail = result.stderr.trim();
175
- const outcome = result.signal === null ? `exit ${String(result.exitCode)}` : `signal ${result.signal}`;
176
- throw new Error(`Unable to ${action} (${outcome})${detail === "" ? "" : `: ${detail}`}`);
176
+ const outcome = result.signal === null ? `退出码 ${String(result.exitCode)}` : `信号 ${result.signal}`;
177
+ throw new Error(`无法${action}(${outcome})${detail === "" ? "" : `:${detail}`}`);
177
178
  }
@@ -84,8 +84,11 @@ export function piVersionDriftNote(web, sessiond) {
84
84
  }
85
85
  export function workspaceFlags(workspace) {
86
86
  return [
87
+ workspace.provider === undefined
88
+ ? workspace.isGitRepo === undefined && workspace.isGitWorktree !== true ? "文件夹工作区" : undefined
89
+ : `提供器 ${workspace.provider.pluginId}`,
87
90
  workspace.branch === undefined || workspace.branch === "" ? undefined : `分支 ${workspace.branch}`,
88
- workspace.isGitWorktree === true ? "Git 工作树" : workspace.isGitRepo === true ? "Git 仓库" : "非 Git 仓库",
91
+ workspace.isGitWorktree === true ? "Git 工作树" : workspace.isGitRepo === true ? "Git 仓库" : workspace.isGitRepo === false ? "非 Git 仓库" : undefined,
89
92
  workspace.isMain ? "主工作区" : undefined,
90
93
  ].filter((flag) => flag !== undefined);
91
94
  }
@@ -3,7 +3,7 @@
3
3
  "private": true,
4
4
  "piWeb": {
5
5
  "plugins": [
6
- { "id": "info", "module": "pi-web-plugin.js" }
6
+ { "id": "info", "browserRoot": ".", "module": "pi-web-plugin.js" }
7
7
  ]
8
8
  }
9
9
  }
@@ -7,7 +7,7 @@
7
7
  // the internals for your own.
8
8
  import { copyDiagnostics, renderInfoPanel } from "./infoInternals.js";
9
9
  const plugin = {
10
- apiVersion: 2,
10
+ apiVersion: 4,
11
11
  name: "信息插件",
12
12
  activate: ({ html, svg }) => ({
13
13
  contributions: {
@@ -24,19 +24,19 @@ const plugin = {
24
24
  {
25
25
  id: "workspace.kind-label",
26
26
  order: 100,
27
- items: (context) => [{ type: "text", text: context.workspace.isGitRepo === true ? "Git" : "文件夹", title: context.workspace.path }],
27
+ items: (context) => [{ type: "text", text: context.workspace.provider?.pluginId ?? (context.workspace.isGitRepo === true ? "Git" : "文件夹"), title: context.workspace.path }],
28
28
  },
29
29
  ],
30
30
  workspacePanels: [
31
31
  {
32
32
  id: "workspace.info",
33
33
  title: "信息",
34
- icon: svg `
35
- <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
36
- <circle cx="12" cy="12" r="9"></circle>
37
- <path d="M12 11v5"></path>
38
- <path d="M12 8h.01"></path>
39
- </svg>
34
+ icon: svg `
35
+ <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
36
+ <circle cx="12" cy="12" r="9"></circle>
37
+ <path d="M12 11v5"></path>
38
+ <path d="M12 8h.01"></path>
39
+ </svg>
40
40
  `,
41
41
  order: 1000,
42
42
  render: (context) => renderInfoPanel(html, context),