@pi-harness/pi-harness 0.1.6 → 0.1.8

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 (254) hide show
  1. package/apps/web/dist/assets/index-CCZGqUaU.css +2 -0
  2. package/apps/web/dist/assets/index-DZjjpslQ.js +20 -0
  3. package/apps/web/dist/index.html +2 -2
  4. package/apps/web/package.json +6 -6
  5. package/apps/web/src/style.css +159 -45
  6. package/node_modules/@pi-harness/api-gateway/dist/marketplace-entries/official/agent-teams.json +1 -1
  7. package/node_modules/@pi-harness/api-gateway/dist/marketplace-entries/official/anchored-standard.json +1 -1
  8. package/node_modules/@pi-harness/api-gateway/dist/marketplace-entries/official/annotation.json +1 -1
  9. package/node_modules/@pi-harness/api-gateway/dist/marketplace-entries/official/archify.json +1 -1
  10. package/node_modules/@pi-harness/api-gateway/dist/marketplace-entries/official/at-file.json +1 -1
  11. package/node_modules/@pi-harness/api-gateway/dist/marketplace-entries/official/auto-mode.json +1 -1
  12. package/node_modules/@pi-harness/api-gateway/dist/marketplace-entries/official/better-sidebar.json +1 -1
  13. package/node_modules/@pi-harness/api-gateway/dist/marketplace-entries/official/browser-fetch.json +1 -1
  14. package/node_modules/@pi-harness/api-gateway/dist/marketplace-entries/official/browser-session.json +1 -1
  15. package/node_modules/@pi-harness/api-gateway/dist/marketplace-entries/official/canvas-draw.json +1 -1
  16. package/node_modules/@pi-harness/api-gateway/dist/marketplace-entries/official/change-verifier.json +1 -1
  17. package/node_modules/@pi-harness/api-gateway/dist/marketplace-entries/official/cleaner.json +1 -1
  18. package/node_modules/@pi-harness/api-gateway/dist/marketplace-entries/official/cli-notifier.json +1 -1
  19. package/node_modules/@pi-harness/api-gateway/dist/marketplace-entries/official/code2skill.json +1 -1
  20. package/node_modules/@pi-harness/api-gateway/dist/marketplace-entries/official/colleague-skill.json +1 -1
  21. package/node_modules/@pi-harness/api-gateway/dist/marketplace-entries/official/context-doctor.json +1 -1
  22. package/node_modules/@pi-harness/api-gateway/dist/marketplace-entries/official/context.json +1 -1
  23. package/node_modules/@pi-harness/api-gateway/dist/marketplace-entries/official/cost-meter.json +1 -1
  24. package/node_modules/@pi-harness/api-gateway/dist/marketplace-entries/official/dependency-checker.json +1 -1
  25. package/node_modules/@pi-harness/api-gateway/dist/marketplace-entries/official/docker-sandbox.json +1 -1
  26. package/node_modules/@pi-harness/api-gateway/dist/marketplace-entries/official/fail-logger.json +1 -1
  27. package/node_modules/@pi-harness/api-gateway/dist/marketplace-entries/official/genui.json +1 -1
  28. package/node_modules/@pi-harness/api-gateway/dist/marketplace-entries/official/git-time-capsule.json +1 -1
  29. package/node_modules/@pi-harness/api-gateway/dist/marketplace-entries/official/graph-memory.json +1 -1
  30. package/node_modules/@pi-harness/api-gateway/dist/marketplace-entries/official/history-compressor.json +1 -1
  31. package/node_modules/@pi-harness/api-gateway/dist/marketplace-entries/official/hol-guard.json +1 -1
  32. package/node_modules/@pi-harness/api-gateway/dist/marketplace-entries/official/i18n-pair.json +1 -1
  33. package/node_modules/@pi-harness/api-gateway/dist/marketplace-entries/official/image-compressor.json +1 -1
  34. package/node_modules/@pi-harness/api-gateway/dist/marketplace-entries/official/llm-verifier.json +1 -1
  35. package/node_modules/@pi-harness/api-gateway/dist/marketplace-entries/official/mcp-client.json +1 -1
  36. package/node_modules/@pi-harness/api-gateway/dist/marketplace-entries/official/mcp-panel.json +1 -1
  37. package/node_modules/@pi-harness/api-gateway/dist/marketplace-entries/official/memory.json +1 -1
  38. package/node_modules/@pi-harness/api-gateway/dist/marketplace-entries/official/mirage-bridge.json +1 -1
  39. package/node_modules/@pi-harness/api-gateway/dist/marketplace-entries/official/mock-server.json +1 -1
  40. package/node_modules/@pi-harness/api-gateway/dist/marketplace-entries/official/modlens.json +1 -1
  41. package/node_modules/@pi-harness/api-gateway/dist/marketplace-entries/official/module-search.json +1 -1
  42. package/node_modules/@pi-harness/api-gateway/dist/marketplace-entries/official/obsidian-sync.json +1 -1
  43. package/node_modules/@pi-harness/api-gateway/dist/marketplace-entries/official/openpets.json +1 -1
  44. package/node_modules/@pi-harness/api-gateway/dist/marketplace-entries/official/plan-execute.json +1 -1
  45. package/node_modules/@pi-harness/api-gateway/dist/marketplace-entries/official/plugin-check.json +1 -1
  46. package/node_modules/@pi-harness/api-gateway/dist/marketplace-entries/official/plugin-dev.json +1 -1
  47. package/node_modules/@pi-harness/api-gateway/dist/marketplace-entries/official/plugin-finder.json +1 -1
  48. package/node_modules/@pi-harness/api-gateway/dist/marketplace-entries/official/plugin-radar.json +1 -1
  49. package/node_modules/@pi-harness/api-gateway/dist/marketplace-entries/official/plugin-stars.json +1 -1
  50. package/node_modules/@pi-harness/api-gateway/dist/marketplace-entries/official/prompt-guard.json +1 -1
  51. package/node_modules/@pi-harness/api-gateway/dist/marketplace-entries/official/prompt-library.json +1 -1
  52. package/node_modules/@pi-harness/api-gateway/dist/marketplace-entries/official/readme-gen.json +1 -1
  53. package/node_modules/@pi-harness/api-gateway/dist/marketplace-entries/official/recall-unread.json +1 -1
  54. package/node_modules/@pi-harness/api-gateway/dist/marketplace-entries/official/reverse-skill.json +1 -1
  55. package/node_modules/@pi-harness/api-gateway/dist/marketplace-entries/official/reviewer-bot.json +1 -1
  56. package/node_modules/@pi-harness/api-gateway/dist/marketplace-entries/official/runtime-doctor.json +1 -1
  57. package/node_modules/@pi-harness/api-gateway/dist/marketplace-entries/official/secure-audit.json +1 -1
  58. package/node_modules/@pi-harness/api-gateway/dist/marketplace-entries/official/session-bookmarks.json +1 -1
  59. package/node_modules/@pi-harness/api-gateway/dist/marketplace-entries/official/session-bridge.json +1 -1
  60. package/node_modules/@pi-harness/api-gateway/dist/marketplace-entries/official/session-compare.json +1 -1
  61. package/node_modules/@pi-harness/api-gateway/dist/marketplace-entries/official/session-export.json +1 -1
  62. package/node_modules/@pi-harness/api-gateway/dist/marketplace-entries/official/session-insights.json +1 -1
  63. package/node_modules/@pi-harness/api-gateway/dist/marketplace-entries/official/session-search.json +1 -1
  64. package/node_modules/@pi-harness/api-gateway/dist/marketplace-entries/official/skill-catalog.json +1 -1
  65. package/node_modules/@pi-harness/api-gateway/dist/marketplace-entries/official/skill-guard.json +1 -1
  66. package/node_modules/@pi-harness/api-gateway/dist/marketplace-entries/official/sql-lens.json +1 -1
  67. package/node_modules/@pi-harness/api-gateway/dist/marketplace-entries/official/synapse.json +1 -1
  68. package/node_modules/@pi-harness/api-gateway/dist/marketplace-entries/official/tab-manager.json +1 -1
  69. package/node_modules/@pi-harness/api-gateway/dist/marketplace-entries/official/taskboard.json +1 -1
  70. package/node_modules/@pi-harness/api-gateway/dist/marketplace-entries/official/telemetry-blocker.json +1 -1
  71. package/node_modules/@pi-harness/api-gateway/dist/marketplace-entries/official/test-harness.json +1 -1
  72. package/node_modules/@pi-harness/api-gateway/dist/marketplace-entries/official/theme-studio.json +1 -1
  73. package/node_modules/@pi-harness/api-gateway/dist/marketplace-entries/official/token-guard.json +1 -1
  74. package/node_modules/@pi-harness/api-gateway/dist/marketplace-entries/official/turn-rewind.json +1 -1
  75. package/node_modules/@pi-harness/api-gateway/dist/marketplace-entries/official/undo-savepoint.json +1 -1
  76. package/node_modules/@pi-harness/api-gateway/dist/marketplace-entries/official/vision-toolkit.json +1 -1
  77. package/node_modules/@pi-harness/api-gateway/dist/marketplace-entries/official/web-research.json +1 -1
  78. package/node_modules/@pi-harness/api-gateway/dist/marketplace-entries/official/workspace-navigator.json +1 -1
  79. package/node_modules/@pi-harness/api-gateway/dist/marketplace-entries/official/workspace-search.json +1 -1
  80. package/node_modules/@pi-harness/api-gateway/dist/marketplace-entries/official/yaml-validator.json +1 -1
  81. package/node_modules/@pi-harness/api-gateway/package.json +1 -1
  82. package/node_modules/@pi-harness/cli/package.json +1 -1
  83. package/node_modules/@pi-harness/core/package.json +1 -1
  84. package/node_modules/@pi-harness/host-webserver/package.json +1 -1
  85. package/node_modules/@pi-harness/web-app/package.json +1 -1
  86. package/package.json +6 -6
  87. package/packages/api-gateway/dist/marketplace-entries/official/agent-teams.json +1 -1
  88. package/packages/api-gateway/dist/marketplace-entries/official/anchored-standard.json +1 -1
  89. package/packages/api-gateway/dist/marketplace-entries/official/annotation.json +1 -1
  90. package/packages/api-gateway/dist/marketplace-entries/official/archify.json +1 -1
  91. package/packages/api-gateway/dist/marketplace-entries/official/at-file.json +1 -1
  92. package/packages/api-gateway/dist/marketplace-entries/official/auto-mode.json +1 -1
  93. package/packages/api-gateway/dist/marketplace-entries/official/better-sidebar.json +1 -1
  94. package/packages/api-gateway/dist/marketplace-entries/official/browser-fetch.json +1 -1
  95. package/packages/api-gateway/dist/marketplace-entries/official/browser-session.json +1 -1
  96. package/packages/api-gateway/dist/marketplace-entries/official/canvas-draw.json +1 -1
  97. package/packages/api-gateway/dist/marketplace-entries/official/change-verifier.json +1 -1
  98. package/packages/api-gateway/dist/marketplace-entries/official/cleaner.json +1 -1
  99. package/packages/api-gateway/dist/marketplace-entries/official/cli-notifier.json +1 -1
  100. package/packages/api-gateway/dist/marketplace-entries/official/code2skill.json +1 -1
  101. package/packages/api-gateway/dist/marketplace-entries/official/colleague-skill.json +1 -1
  102. package/packages/api-gateway/dist/marketplace-entries/official/context-doctor.json +1 -1
  103. package/packages/api-gateway/dist/marketplace-entries/official/context.json +1 -1
  104. package/packages/api-gateway/dist/marketplace-entries/official/cost-meter.json +1 -1
  105. package/packages/api-gateway/dist/marketplace-entries/official/dependency-checker.json +1 -1
  106. package/packages/api-gateway/dist/marketplace-entries/official/docker-sandbox.json +1 -1
  107. package/packages/api-gateway/dist/marketplace-entries/official/fail-logger.json +1 -1
  108. package/packages/api-gateway/dist/marketplace-entries/official/genui.json +1 -1
  109. package/packages/api-gateway/dist/marketplace-entries/official/git-time-capsule.json +1 -1
  110. package/packages/api-gateway/dist/marketplace-entries/official/graph-memory.json +1 -1
  111. package/packages/api-gateway/dist/marketplace-entries/official/history-compressor.json +1 -1
  112. package/packages/api-gateway/dist/marketplace-entries/official/hol-guard.json +1 -1
  113. package/packages/api-gateway/dist/marketplace-entries/official/i18n-pair.json +1 -1
  114. package/packages/api-gateway/dist/marketplace-entries/official/image-compressor.json +1 -1
  115. package/packages/api-gateway/dist/marketplace-entries/official/llm-verifier.json +1 -1
  116. package/packages/api-gateway/dist/marketplace-entries/official/mcp-client.json +1 -1
  117. package/packages/api-gateway/dist/marketplace-entries/official/mcp-panel.json +1 -1
  118. package/packages/api-gateway/dist/marketplace-entries/official/memory.json +1 -1
  119. package/packages/api-gateway/dist/marketplace-entries/official/mirage-bridge.json +1 -1
  120. package/packages/api-gateway/dist/marketplace-entries/official/mock-server.json +1 -1
  121. package/packages/api-gateway/dist/marketplace-entries/official/modlens.json +1 -1
  122. package/packages/api-gateway/dist/marketplace-entries/official/module-search.json +1 -1
  123. package/packages/api-gateway/dist/marketplace-entries/official/obsidian-sync.json +1 -1
  124. package/packages/api-gateway/dist/marketplace-entries/official/openpets.json +1 -1
  125. package/packages/api-gateway/dist/marketplace-entries/official/plan-execute.json +1 -1
  126. package/packages/api-gateway/dist/marketplace-entries/official/plugin-check.json +1 -1
  127. package/packages/api-gateway/dist/marketplace-entries/official/plugin-dev.json +1 -1
  128. package/packages/api-gateway/dist/marketplace-entries/official/plugin-finder.json +1 -1
  129. package/packages/api-gateway/dist/marketplace-entries/official/plugin-radar.json +1 -1
  130. package/packages/api-gateway/dist/marketplace-entries/official/plugin-stars.json +1 -1
  131. package/packages/api-gateway/dist/marketplace-entries/official/prompt-guard.json +1 -1
  132. package/packages/api-gateway/dist/marketplace-entries/official/prompt-library.json +1 -1
  133. package/packages/api-gateway/dist/marketplace-entries/official/readme-gen.json +1 -1
  134. package/packages/api-gateway/dist/marketplace-entries/official/recall-unread.json +1 -1
  135. package/packages/api-gateway/dist/marketplace-entries/official/reverse-skill.json +1 -1
  136. package/packages/api-gateway/dist/marketplace-entries/official/reviewer-bot.json +1 -1
  137. package/packages/api-gateway/dist/marketplace-entries/official/runtime-doctor.json +1 -1
  138. package/packages/api-gateway/dist/marketplace-entries/official/secure-audit.json +1 -1
  139. package/packages/api-gateway/dist/marketplace-entries/official/session-bookmarks.json +1 -1
  140. package/packages/api-gateway/dist/marketplace-entries/official/session-bridge.json +1 -1
  141. package/packages/api-gateway/dist/marketplace-entries/official/session-compare.json +1 -1
  142. package/packages/api-gateway/dist/marketplace-entries/official/session-export.json +1 -1
  143. package/packages/api-gateway/dist/marketplace-entries/official/session-insights.json +1 -1
  144. package/packages/api-gateway/dist/marketplace-entries/official/session-search.json +1 -1
  145. package/packages/api-gateway/dist/marketplace-entries/official/skill-catalog.json +1 -1
  146. package/packages/api-gateway/dist/marketplace-entries/official/skill-guard.json +1 -1
  147. package/packages/api-gateway/dist/marketplace-entries/official/sql-lens.json +1 -1
  148. package/packages/api-gateway/dist/marketplace-entries/official/synapse.json +1 -1
  149. package/packages/api-gateway/dist/marketplace-entries/official/tab-manager.json +1 -1
  150. package/packages/api-gateway/dist/marketplace-entries/official/taskboard.json +1 -1
  151. package/packages/api-gateway/dist/marketplace-entries/official/telemetry-blocker.json +1 -1
  152. package/packages/api-gateway/dist/marketplace-entries/official/test-harness.json +1 -1
  153. package/packages/api-gateway/dist/marketplace-entries/official/theme-studio.json +1 -1
  154. package/packages/api-gateway/dist/marketplace-entries/official/token-guard.json +1 -1
  155. package/packages/api-gateway/dist/marketplace-entries/official/turn-rewind.json +1 -1
  156. package/packages/api-gateway/dist/marketplace-entries/official/undo-savepoint.json +1 -1
  157. package/packages/api-gateway/dist/marketplace-entries/official/vision-toolkit.json +1 -1
  158. package/packages/api-gateway/dist/marketplace-entries/official/web-research.json +1 -1
  159. package/packages/api-gateway/dist/marketplace-entries/official/workspace-navigator.json +1 -1
  160. package/packages/api-gateway/dist/marketplace-entries/official/workspace-search.json +1 -1
  161. package/packages/api-gateway/dist/marketplace-entries/official/yaml-validator.json +1 -1
  162. package/packages/api-gateway/package.json +1 -1
  163. package/packages/api-gateway/src/marketplace-entries/official/agent-teams.json +1 -1
  164. package/packages/api-gateway/src/marketplace-entries/official/anchored-standard.json +1 -1
  165. package/packages/api-gateway/src/marketplace-entries/official/annotation.json +1 -1
  166. package/packages/api-gateway/src/marketplace-entries/official/archify.json +1 -1
  167. package/packages/api-gateway/src/marketplace-entries/official/at-file.json +1 -1
  168. package/packages/api-gateway/src/marketplace-entries/official/auto-mode.json +1 -1
  169. package/packages/api-gateway/src/marketplace-entries/official/better-sidebar.json +1 -1
  170. package/packages/api-gateway/src/marketplace-entries/official/browser-fetch.json +1 -1
  171. package/packages/api-gateway/src/marketplace-entries/official/browser-session.json +1 -1
  172. package/packages/api-gateway/src/marketplace-entries/official/canvas-draw.json +1 -1
  173. package/packages/api-gateway/src/marketplace-entries/official/change-verifier.json +1 -1
  174. package/packages/api-gateway/src/marketplace-entries/official/cleaner.json +1 -1
  175. package/packages/api-gateway/src/marketplace-entries/official/cli-notifier.json +1 -1
  176. package/packages/api-gateway/src/marketplace-entries/official/code2skill.json +1 -1
  177. package/packages/api-gateway/src/marketplace-entries/official/colleague-skill.json +1 -1
  178. package/packages/api-gateway/src/marketplace-entries/official/context-doctor.json +1 -1
  179. package/packages/api-gateway/src/marketplace-entries/official/context.json +1 -1
  180. package/packages/api-gateway/src/marketplace-entries/official/cost-meter.json +1 -1
  181. package/packages/api-gateway/src/marketplace-entries/official/dependency-checker.json +1 -1
  182. package/packages/api-gateway/src/marketplace-entries/official/docker-sandbox.json +1 -1
  183. package/packages/api-gateway/src/marketplace-entries/official/fail-logger.json +1 -1
  184. package/packages/api-gateway/src/marketplace-entries/official/genui.json +1 -1
  185. package/packages/api-gateway/src/marketplace-entries/official/git-time-capsule.json +1 -1
  186. package/packages/api-gateway/src/marketplace-entries/official/graph-memory.json +1 -1
  187. package/packages/api-gateway/src/marketplace-entries/official/history-compressor.json +1 -1
  188. package/packages/api-gateway/src/marketplace-entries/official/hol-guard.json +1 -1
  189. package/packages/api-gateway/src/marketplace-entries/official/i18n-pair.json +1 -1
  190. package/packages/api-gateway/src/marketplace-entries/official/image-compressor.json +1 -1
  191. package/packages/api-gateway/src/marketplace-entries/official/llm-verifier.json +1 -1
  192. package/packages/api-gateway/src/marketplace-entries/official/mcp-client.json +1 -1
  193. package/packages/api-gateway/src/marketplace-entries/official/mcp-panel.json +1 -1
  194. package/packages/api-gateway/src/marketplace-entries/official/memory.json +1 -1
  195. package/packages/api-gateway/src/marketplace-entries/official/mirage-bridge.json +1 -1
  196. package/packages/api-gateway/src/marketplace-entries/official/mock-server.json +1 -1
  197. package/packages/api-gateway/src/marketplace-entries/official/modlens.json +1 -1
  198. package/packages/api-gateway/src/marketplace-entries/official/module-search.json +1 -1
  199. package/packages/api-gateway/src/marketplace-entries/official/obsidian-sync.json +1 -1
  200. package/packages/api-gateway/src/marketplace-entries/official/openpets.json +1 -1
  201. package/packages/api-gateway/src/marketplace-entries/official/plan-execute.json +1 -1
  202. package/packages/api-gateway/src/marketplace-entries/official/plugin-check.json +1 -1
  203. package/packages/api-gateway/src/marketplace-entries/official/plugin-dev.json +1 -1
  204. package/packages/api-gateway/src/marketplace-entries/official/plugin-finder.json +1 -1
  205. package/packages/api-gateway/src/marketplace-entries/official/plugin-radar.json +1 -1
  206. package/packages/api-gateway/src/marketplace-entries/official/plugin-stars.json +1 -1
  207. package/packages/api-gateway/src/marketplace-entries/official/prompt-guard.json +1 -1
  208. package/packages/api-gateway/src/marketplace-entries/official/prompt-library.json +1 -1
  209. package/packages/api-gateway/src/marketplace-entries/official/readme-gen.json +1 -1
  210. package/packages/api-gateway/src/marketplace-entries/official/recall-unread.json +1 -1
  211. package/packages/api-gateway/src/marketplace-entries/official/reverse-skill.json +1 -1
  212. package/packages/api-gateway/src/marketplace-entries/official/reviewer-bot.json +1 -1
  213. package/packages/api-gateway/src/marketplace-entries/official/runtime-doctor.json +1 -1
  214. package/packages/api-gateway/src/marketplace-entries/official/secure-audit.json +1 -1
  215. package/packages/api-gateway/src/marketplace-entries/official/session-bookmarks.json +1 -1
  216. package/packages/api-gateway/src/marketplace-entries/official/session-bridge.json +1 -1
  217. package/packages/api-gateway/src/marketplace-entries/official/session-compare.json +1 -1
  218. package/packages/api-gateway/src/marketplace-entries/official/session-export.json +1 -1
  219. package/packages/api-gateway/src/marketplace-entries/official/session-insights.json +1 -1
  220. package/packages/api-gateway/src/marketplace-entries/official/session-search.json +1 -1
  221. package/packages/api-gateway/src/marketplace-entries/official/skill-catalog.json +1 -1
  222. package/packages/api-gateway/src/marketplace-entries/official/skill-guard.json +1 -1
  223. package/packages/api-gateway/src/marketplace-entries/official/sql-lens.json +1 -1
  224. package/packages/api-gateway/src/marketplace-entries/official/synapse.json +1 -1
  225. package/packages/api-gateway/src/marketplace-entries/official/tab-manager.json +1 -1
  226. package/packages/api-gateway/src/marketplace-entries/official/taskboard.json +1 -1
  227. package/packages/api-gateway/src/marketplace-entries/official/telemetry-blocker.json +1 -1
  228. package/packages/api-gateway/src/marketplace-entries/official/test-harness.json +1 -1
  229. package/packages/api-gateway/src/marketplace-entries/official/theme-studio.json +1 -1
  230. package/packages/api-gateway/src/marketplace-entries/official/token-guard.json +1 -1
  231. package/packages/api-gateway/src/marketplace-entries/official/turn-rewind.json +1 -1
  232. package/packages/api-gateway/src/marketplace-entries/official/undo-savepoint.json +1 -1
  233. package/packages/api-gateway/src/marketplace-entries/official/vision-toolkit.json +1 -1
  234. package/packages/api-gateway/src/marketplace-entries/official/web-research.json +1 -1
  235. package/packages/api-gateway/src/marketplace-entries/official/workspace-navigator.json +1 -1
  236. package/packages/api-gateway/src/marketplace-entries/official/workspace-search.json +1 -1
  237. package/packages/api-gateway/src/marketplace-entries/official/yaml-validator.json +1 -1
  238. package/packages/bundle-web-app/package.json +1 -1
  239. package/packages/cli/package.json +1 -1
  240. package/packages/client-web/dist/plugin-card.d.ts +8 -0
  241. package/packages/client-web/dist/plugin-card.d.ts.map +1 -0
  242. package/packages/client-web/dist/plugin-card.js +15 -0
  243. package/packages/client-web/dist/plugin-card.js.map +1 -0
  244. package/packages/client-web/dist/react-room.d.ts.map +1 -1
  245. package/packages/client-web/dist/react-room.js +134 -42
  246. package/packages/client-web/dist/react-room.js.map +1 -1
  247. package/packages/client-web/package.json +1 -1
  248. package/packages/client-web/src/plugin-card.ts +22 -0
  249. package/packages/client-web/src/react-room.tsx +254 -102
  250. package/packages/client-web/test/plugin-card.test.ts +46 -0
  251. package/packages/core/package.json +1 -1
  252. package/packages/host-webserver/package.json +1 -1
  253. package/apps/web/dist/assets/index-CihATCjR.css +0 -2
  254. package/apps/web/dist/assets/index-CxqGqqZh.js +0 -20
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pi-harness/client-web",
3
- "version": "0.1.6",
3
+ "version": "0.1.8",
4
4
  "private": true,
5
5
  "description": "Browser-side Cordis client plugins for the Pi Harness web surface",
6
6
  "homepage": "https://github.com/pi-harness/pi-harness#readme",
@@ -0,0 +1,22 @@
1
+ import type { ClientMarketplacePlugin, ClientPlugin } from "./control-room.js";
2
+
3
+ export interface InstalledPluginCardContent {
4
+ readonly packageLabel: string;
5
+ readonly description: string;
6
+ readonly tags: readonly string[];
7
+ }
8
+
9
+ export function installedPluginCardContent(plugin: ClientPlugin, metadata: ClientMarketplacePlugin | undefined): InstalledPluginCardContent {
10
+ if (metadata) {
11
+ return {
12
+ packageLabel: `${plugin.name} · v${metadata.version}`,
13
+ description: metadata.description,
14
+ tags: [...metadata.capabilities, ...metadata.hooks.map((hook) => `hook:${hook}`)],
15
+ };
16
+ }
17
+ return {
18
+ packageLabel: plugin.name,
19
+ description: plugin.enabled ? "由当前运行时加载并启用,能力与 hook 已注册。" : "由当前运行时加载但已停用。",
20
+ tags: ["loader", plugin.state === "active" ? "active" : `state:${plugin.state}`],
21
+ };
22
+ }
@@ -26,7 +26,8 @@ import { loadMarketplaceCatalog } from "./marketplace-catalog.js";
26
26
  import { pluginStarsRows } from "./plugin-stars-view.js";
27
27
  import { browserSessionTabs } from "./browser-session-view.js";
28
28
  import { matchesPluginQuery } from "./plugin-search.js";
29
- import { installedPluginDetailPath, readInstalledPluginDetailId } from "./plugin-navigation.js";
29
+ import { readInstalledPluginDetailId } from "./plugin-navigation.js";
30
+ import { installedPluginCardContent } from "./plugin-card.js";
30
31
 
31
32
  export type { ClientApi } from "./control-room.js";
32
33
 
@@ -740,12 +741,12 @@ function Details({ event, onClose, onCopy }: { event: Record<string, unknown> |
740
741
  <aside className="details-panel">
741
742
  <header>
742
743
  <strong>{eventLabel(event)}</strong>
743
- <button aria-label="关闭事件详情" onClick={onClose} type="button">
744
+ <button aria-label={fileDetail ? "关闭文件差异" : "关闭事件详情"} onClick={onClose} type="button">
744
745
  ×
745
746
  </button>
746
747
  </header>
747
748
  <div className="details-body">
748
- <div className="detail-stats">
749
+ <div className={`detail-stats ${fileDetail ? "is-file" : ""}`}>
749
750
  {stats.map(([key, item]) => (
750
751
  <div key={key}>
751
752
  <small>{key}</small>
@@ -844,11 +845,22 @@ function Trajectory({ events, onSelect }: { events: readonly Record<string, unkn
844
845
  );
845
846
  }
846
847
 
847
- function Files({ files, api, onDiff, onRefresh }: { files: readonly ClientFile[]; api: ClientApi; onDiff: (path: string) => void; onRefresh: () => void }) {
848
+ function Files({
849
+ files,
850
+ api,
851
+ onDiff,
852
+ onRefresh,
853
+ }: {
854
+ files: readonly ClientFile[];
855
+ api: ClientApi;
856
+ onDiff: (path: string) => Promise<void>;
857
+ onRefresh: () => void;
858
+ }) {
848
859
  const additions = files.filter((file) => file.status.includes("A") || file.status === "??").length;
849
860
  const deletions = files.filter((file) => file.status.includes("D")).length;
850
861
  const [message, setMessage] = useState("");
851
862
  const [busy, setBusy] = useState(false);
863
+ const [diffPending, setDiffPending] = useState<string>();
852
864
  const [error, setError] = useState("");
853
865
  const [revertConfirmOpen, setRevertConfirmOpen] = useState(false);
854
866
  const commit = () => {
@@ -879,6 +891,14 @@ function Files({ files, api, onDiff, onRefresh }: { files: readonly ClientFile[]
879
891
  .catch((cause: unknown) => setError(cause instanceof Error ? cause.message : String(cause)))
880
892
  .finally(() => setBusy(false));
881
893
  };
894
+ const openDiff = (path: string) => {
895
+ if (busy || diffPending) return;
896
+ setDiffPending(path);
897
+ setError("");
898
+ void onDiff(path)
899
+ .catch((cause: unknown) => setError(cause instanceof Error ? cause.message : String(cause)))
900
+ .finally(() => setDiffPending(undefined));
901
+ };
882
902
  return (
883
903
  <section className="view-panel files-view">
884
904
  <div className="files-content">
@@ -894,8 +914,14 @@ function Files({ files, api, onDiff, onRefresh }: { files: readonly ClientFile[]
894
914
  <b className={`file-kind ${file.label === "untracked" ? "new" : ""}`}>{file.label}</b>
895
915
  <code>{file.path}</code>
896
916
  <span className={file.status.includes("D") ? "del" : "add"}>{file.status}</span>
897
- <button className="diff-button" onClick={() => onDiff(file.path)} type="button">
898
- 查看差异
917
+ <button
918
+ aria-label={`${diffPending === file.path ? "正在读取" : "查看"}${file.path}的差异`}
919
+ className="diff-button"
920
+ disabled={busy || diffPending !== undefined}
921
+ onClick={() => openDiff(file.path)}
922
+ type="button"
923
+ >
924
+ {diffPending === file.path ? "读取中…" : "查看差异"}
899
925
  </button>
900
926
  </div>
901
927
  ))
@@ -4070,6 +4096,77 @@ function PluginUninstallDialog({
4070
4096
  );
4071
4097
  }
4072
4098
 
4099
+ function PluginCategoryNav({
4100
+ label,
4101
+ categories,
4102
+ activeCategory,
4103
+ onChange,
4104
+ }: {
4105
+ label: string;
4106
+ categories: readonly ClientMarketplaceCategory[];
4107
+ activeCategory: string;
4108
+ onChange: (category: string) => void;
4109
+ }) {
4110
+ const navRef = useRef<HTMLElement>(null);
4111
+ const [canScrollLeft, setCanScrollLeft] = useState(false);
4112
+ const [canScrollRight, setCanScrollRight] = useState(false);
4113
+ const updateScrollState = useCallback(() => {
4114
+ const nav = navRef.current;
4115
+ if (!nav) return;
4116
+ const maximum = Math.max(0, nav.scrollWidth - nav.clientWidth);
4117
+ setCanScrollLeft(nav.scrollLeft > 1);
4118
+ setCanScrollRight(nav.scrollLeft < maximum - 1);
4119
+ }, []);
4120
+ useEffect(() => {
4121
+ const nav = navRef.current;
4122
+ if (!nav) return;
4123
+ updateScrollState();
4124
+ const observer = typeof ResizeObserver === "undefined" ? undefined : new ResizeObserver(updateScrollState);
4125
+ observer?.observe(nav);
4126
+ window.addEventListener("resize", updateScrollState);
4127
+ return () => {
4128
+ observer?.disconnect();
4129
+ window.removeEventListener("resize", updateScrollState);
4130
+ };
4131
+ }, [categories, updateScrollState]);
4132
+ const scroll = (direction: -1 | 1) => {
4133
+ const nav = navRef.current;
4134
+ if (!nav) return;
4135
+ nav.scrollBy({ behavior: "smooth", left: direction * Math.max(180, nav.clientWidth * 0.72) });
4136
+ };
4137
+ return (
4138
+ <div className={`marketplace-categories-shell ${canScrollLeft ? "can-scroll-left" : ""} ${canScrollRight ? "can-scroll-right" : ""}`}>
4139
+ {canScrollLeft && (
4140
+ <button aria-label="向左查看更多分类" className="marketplace-category-scroll previous" onClick={() => scroll(-1)} type="button">
4141
+
4142
+ </button>
4143
+ )}
4144
+ <nav aria-label={label} className="marketplace-categories" onScroll={updateScrollState} ref={navRef}>
4145
+ {categories.map((category) => {
4146
+ const active = activeCategory === category.id;
4147
+ return (
4148
+ <button
4149
+ aria-pressed={active}
4150
+ className={`marketplace-category ${active ? "active" : ""}`}
4151
+ key={category.id || "all"}
4152
+ onClick={() => onChange(category.id)}
4153
+ type="button"
4154
+ >
4155
+ <span>{category.label}</span>
4156
+ <span className={active ? "font-mono text-[10px] text-[#3565c5]" : "font-mono text-[10px] text-[#687381]"}>{category.count}</span>
4157
+ </button>
4158
+ );
4159
+ })}
4160
+ </nav>
4161
+ {canScrollRight && (
4162
+ <button aria-label="向右查看更多分类" className="marketplace-category-scroll next" onClick={() => scroll(1)} type="button">
4163
+
4164
+ </button>
4165
+ )}
4166
+ </div>
4167
+ );
4168
+ }
4169
+
4073
4170
  function Plugins({
4074
4171
  plugins,
4075
4172
  panels,
@@ -4178,23 +4275,7 @@ function Plugins({
4178
4275
  {query.trim() || categoryFilter ? `${visiblePlugins.length} / ${installedPlugins.length}` : `${installedPlugins.length}`} 个插件
4179
4276
  </span>
4180
4277
  </div>
4181
- <nav aria-label="已安装插件分类" className="marketplace-categories">
4182
- {installedCategories.map((category) => {
4183
- const active = categoryFilter === category.id;
4184
- return (
4185
- <button
4186
- aria-pressed={active}
4187
- className={`marketplace-category ${active ? "active" : ""}`}
4188
- key={category.id || "all"}
4189
- onClick={() => setCategoryFilter(category.id)}
4190
- type="button"
4191
- >
4192
- <span>{category.label}</span>
4193
- <span className={active ? "font-mono text-[10px] text-[#3565c5]" : "font-mono text-[10px] text-[#687381]"}>{category.count}</span>
4194
- </button>
4195
- );
4196
- })}
4197
- </nav>
4278
+ <PluginCategoryNav activeCategory={categoryFilter} categories={installedCategories} label="已安装插件分类" onChange={setCategoryFilter} />
4198
4279
  <div className="plugins-scroll" ref={scrollRef}>
4199
4280
  <div className="plugins-list">
4200
4281
  {visiblePlugins.map((plugin) => {
@@ -4202,6 +4283,7 @@ function Plugins({
4202
4283
  const categoryLabel = metadata?.category.label ?? plugin.category?.label;
4203
4284
  const capabilityLabel = capability(plugin.name);
4204
4285
  const pluginTitle = metadata?.name ?? displayPluginName(plugin.name);
4286
+ const cardContent = installedPluginCardContent(plugin, metadata);
4205
4287
  return (
4206
4288
  <article className="catalog-card plugin-card" key={plugin.id}>
4207
4289
  <div className="plugin-card-head">
@@ -4251,24 +4333,18 @@ function Plugins({
4251
4333
  </div>
4252
4334
  </div>
4253
4335
  <div className="plugin-card-body">
4254
- <code className="plugin-package">{plugin.name}</code>
4255
- <p className="plugin-description">{plugin.enabled ? "由当前运行时加载并启用,能力与 hook 已注册。" : "由当前运行时加载但已停用。"}</p>
4336
+ <code className="plugin-package">{cardContent.packageLabel}</code>
4337
+ <p className="plugin-description">{cardContent.description}</p>
4256
4338
  <div className="hook-list">
4257
- <span>loader</span>
4258
- <span>{plugin.state === "active" ? "active" : `state:${plugin.state}`}</span>
4339
+ {cardContent.tags.map((tag, index) => (
4340
+ <span key={`${tag}-${index}`}>{tag}</span>
4341
+ ))}
4259
4342
  </div>
4260
4343
  </div>
4261
4344
  <footer className="plugin-card-footer">
4262
- <a
4263
- className="plugin-detail-link"
4264
- href={installedPluginDetailPath(plugin.name)}
4265
- onClick={(event) => {
4266
- event.preventDefault();
4267
- onOpenDetail(plugin);
4268
- }}
4269
- >
4345
+ <button aria-label={`查看 ${pluginTitle} 详情`} className="plugin-detail-link" onClick={() => onOpenDetail(plugin)} type="button">
4270
4346
  查看详情 →
4271
- </a>
4347
+ </button>
4272
4348
  {panelPluginIds.has(plugin.name) ? <span>实时面板</span> : null}
4273
4349
  </footer>
4274
4350
  </article>
@@ -4549,7 +4625,7 @@ function Marketplace({
4549
4625
  const [installing, setInstalling] = useState<string>();
4550
4626
  const [installError, setInstallError] = useState("");
4551
4627
  const [installNotice, setInstallNotice] = useState("");
4552
- const categoryTabs = marketplaceCategoryTabs(categories);
4628
+ const categoryTabs = useMemo(() => marketplaceCategoryTabs(categories), [categories]);
4553
4629
  const install = async (plugin: ClientMarketplacePlugin) => {
4554
4630
  setInstallError("");
4555
4631
  setInstallNotice("");
@@ -4604,28 +4680,12 @@ function Marketplace({
4604
4680
  </select>
4605
4681
  <span className="marketplace-count">{total} 个已审核条目 · 推荐排序</span>
4606
4682
  </div>
4607
- <nav aria-label="插件分类" className="marketplace-categories">
4608
- {categoryTabs.map((category) => {
4609
- const active = categoryFilter === category.id;
4610
- return (
4611
- <button
4612
- aria-pressed={active}
4613
- className={`marketplace-category ${active ? "active" : ""}`}
4614
- key={category.id || "all"}
4615
- onClick={() => onCategoryChange(category.id)}
4616
- type="button"
4617
- >
4618
- <span>{category.label}</span>
4619
- <span className={active ? "font-mono text-[10px] text-[#3565c5]" : "font-mono text-[10px] text-[#687381]"}>{category.count}</span>
4620
- </button>
4621
- );
4622
- })}
4623
- </nav>
4683
+ <PluginCategoryNav activeCategory={categoryFilter} categories={categoryTabs} label="插件分类" onChange={onCategoryChange} />
4624
4684
  <div className="marketplace-scroll">
4625
4685
  <div className="marketplace-grid">
4626
4686
  {plugins.map((plugin) => (
4627
4687
  <article className="catalog-card marketplace-card" key={plugin.id}>
4628
- <div className="flex items-start gap-2.5">
4688
+ <header className="marketplace-card-head">
4629
4689
  <div className="marketplace-card-mark">◈</div>
4630
4690
  <div className="min-w-0 flex-1">
4631
4691
  <div className="flex flex-wrap items-center gap-1.5">
@@ -4650,33 +4710,35 @@ function Marketplace({
4650
4710
  </span>
4651
4711
  <span className="rounded bg-[#f2edff] px-1.5 py-px text-[10px] text-[#6d4bc3]">{plugin.category.label}</span>
4652
4712
  </div>
4653
- <code className="marketplace-package">
4654
- {displayPluginName(plugin.packageName)} · v{plugin.version}
4655
- </code>
4656
- <p className="marketplace-description">{plugin.description}</p>
4657
- <div className="marketplace-tags">
4658
- {plugin.capabilities.map((item) => (
4659
- <span className="rounded bg-[#f1f4f9] px-1.5 py-px font-mono text-[10px] text-[#61666b]" key={item}>
4660
- {item}
4661
- </span>
4662
- ))}
4663
- {plugin.hooks.map((item) => (
4664
- <span className="rounded bg-[#f1f4f9] px-1.5 py-px font-mono text-[10px] text-[#61666b]" key={item}>
4665
- hook:{item}
4713
+ </div>
4714
+ </header>
4715
+ <div className="marketplace-card-body">
4716
+ <code className="marketplace-package">
4717
+ {displayPluginName(plugin.packageName)} · v{plugin.version}
4718
+ </code>
4719
+ <p className="marketplace-description">{plugin.description}</p>
4720
+ <div className="marketplace-tags">
4721
+ {plugin.capabilities.map((item) => (
4722
+ <span className="rounded bg-[#f1f4f9] px-1.5 py-px font-mono text-[10px] text-[#61666b]" key={item}>
4723
+ {item}
4724
+ </span>
4725
+ ))}
4726
+ {plugin.hooks.map((item) => (
4727
+ <span className="rounded bg-[#f1f4f9] px-1.5 py-px font-mono text-[10px] text-[#61666b]" key={`hook:${item}`}>
4728
+ hook:{item}
4729
+ </span>
4730
+ ))}
4731
+ </div>
4732
+ {plugin.statistics && (
4733
+ <div className="marketplace-statistics mt-3 grid grid-cols-3 divide-x divide-[#e3e7ee] rounded-md border border-[#e3e7ee] bg-[#f8f9fb]">
4734
+ {marketplaceStatisticItems(plugin.statistics).map((item) => (
4735
+ <span className="min-w-0 px-2 py-1.5" key={item.label} title={`${item.label} ${item.value}`}>
4736
+ <small className="block truncate text-[9px] text-[#687381]">{item.label}</small>
4737
+ <strong className="mt-0.5 block truncate font-mono text-[10px] font-medium text-[#3b424b]">{item.value}</strong>
4666
4738
  </span>
4667
4739
  ))}
4668
4740
  </div>
4669
- {plugin.statistics && (
4670
- <div className="mt-3 grid grid-cols-3 divide-x divide-[#e3e7ee] rounded-md border border-[#e3e7ee] bg-[#f8f9fb]">
4671
- {marketplaceStatisticItems(plugin.statistics).map((item) => (
4672
- <span className="min-w-0 px-2 py-1.5" key={item.label} title={`${item.label} ${item.value}`}>
4673
- <small className="block truncate text-[9px] text-[#687381]">{item.label}</small>
4674
- <strong className="mt-0.5 block truncate font-mono text-[10px] font-medium text-[#3b424b]">{item.value}</strong>
4675
- </span>
4676
- ))}
4677
- </div>
4678
- )}
4679
- </div>
4741
+ )}
4680
4742
  </div>
4681
4743
  <footer className="marketplace-card-footer">
4682
4744
  <span>
@@ -4700,7 +4762,7 @@ function Marketplace({
4700
4762
  </footer>
4701
4763
  </article>
4702
4764
  ))}
4703
- {!plugins.length && <div className="p-7 text-center text-[12px] text-[#687381]">没有匹配的插件。</div>}
4765
+ {!plugins.length && <div className="empty-state">没有匹配的插件。</div>}
4704
4766
  </div>
4705
4767
  {installNotice && <p className="mt-2 text-[11px] text-[#3565c5]">{installNotice}</p>}
4706
4768
  {installError && <p className="mt-2 text-[11px] text-[#b42318]">安装失败:{installError}</p>}
@@ -5617,7 +5679,7 @@ function PromptCompletionPopover({
5617
5679
  ? filterCommands(commands, query).slice(0, 12)
5618
5680
  : files.filter((file) => `${file.path} ${file.label} ${file.status}`.toLowerCase().includes(query.trim().toLowerCase())).slice(0, 12);
5619
5681
  return (
5620
- <div aria-label={kind === "command" ? "命令补全" : "文件补全"} className="prompt-completion" role="listbox">
5682
+ <div aria-label={kind === "command" ? "命令补全" : "文件补全"} className="prompt-completion" id="prompt-completion-list" role="listbox">
5621
5683
  <small>{kind === "command" ? "命令" : "文件"}</small>
5622
5684
  {items.length ? (
5623
5685
  items.slice(0, 12).map((item, index) => {
@@ -5627,6 +5689,7 @@ function PromptCompletionPopover({
5627
5689
  return (
5628
5690
  <button
5629
5691
  aria-selected={index === activeIndex}
5692
+ id={`prompt-completion-option-${index}`}
5630
5693
  key={`${kind}:${label}`}
5631
5694
  onClick={() => onUse(label)}
5632
5695
  onMouseEnter={() => onActiveIndexChange(index)}
@@ -5681,6 +5744,8 @@ function GlobalSearch({
5681
5744
  .map((session) => ({ kind: "session" as const, session })),
5682
5745
  ...files.filter((file) => matches(`${file.path} ${file.label} ${file.status}`)).map((file) => ({ kind: "file" as const, file })),
5683
5746
  ];
5747
+ const selectedIndex = items.length ? Math.min(activeIndex, items.length - 1) : -1;
5748
+ const activeItemId = selectedIndex >= 0 ? `global-search-option-${selectedIndex}` : undefined;
5684
5749
  useEffect(() => setActiveIndex(0), [query]);
5685
5750
  const execute = (item: GlobalSearchItem | undefined) => {
5686
5751
  if (!item) return;
@@ -5700,8 +5765,15 @@ function GlobalSearch({
5700
5765
  <div className="global-search-heading">
5701
5766
  <strong>全局搜索</strong>
5702
5767
  <small>命令 · 会话 · 文件</small>
5768
+ <button aria-label="关闭全局搜索" onClick={onClose} type="button">
5769
+ ×
5770
+ </button>
5703
5771
  </div>
5704
5772
  <input
5773
+ aria-activedescendant={activeItemId}
5774
+ aria-autocomplete="list"
5775
+ aria-controls="global-search-results"
5776
+ aria-expanded="true"
5705
5777
  aria-label="全局搜索"
5706
5778
  data-dialog-initial-focus
5707
5779
  onChange={(event) => setQuery(event.target.value)}
@@ -5717,21 +5789,22 @@ function GlobalSearch({
5717
5789
  setActiveIndex((index) => (index - 1 + items.length) % items.length);
5718
5790
  } else if (event.key === "Enter") {
5719
5791
  event.preventDefault();
5720
- execute(items[activeIndex]);
5792
+ execute(items[selectedIndex]);
5721
5793
  }
5722
5794
  }}
5723
5795
  placeholder="搜索命令、会话或文件"
5796
+ role="combobox"
5724
5797
  value={query}
5725
5798
  />
5726
- <div className="global-search-results" role="listbox">
5799
+ <div className="global-search-results" id="global-search-results" role="listbox">
5727
5800
  {items.length ? (
5728
5801
  (["command", "session", "file"] as const).map((kind) => {
5729
5802
  const group = items.filter((item) => item.kind === kind);
5730
5803
  if (!group.length) return null;
5731
5804
  const label = kind === "command" ? "命令" : kind === "session" ? "会话" : "文件";
5732
5805
  return (
5733
- <section className="global-search-group" key={kind}>
5734
- <small>{label}</small>
5806
+ <section aria-label={label} className="global-search-group" key={kind} role="group">
5807
+ <small aria-hidden="true">{label}</small>
5735
5808
  {group.map((item) => {
5736
5809
  const index = items.indexOf(item);
5737
5810
  const title =
@@ -5748,7 +5821,8 @@ function GlobalSearch({
5748
5821
  : `${item.file.path} · ${item.file.status}`;
5749
5822
  return (
5750
5823
  <button
5751
- aria-selected={index === activeIndex}
5824
+ aria-selected={index === selectedIndex}
5825
+ id={`global-search-option-${index}`}
5752
5826
  key={`${item.kind}:${title}:${index}`}
5753
5827
  onClick={() => execute(item)}
5754
5828
  onMouseEnter={() => setActiveIndex(index)}
@@ -5876,6 +5950,7 @@ export function ControlRoomView({ api = createClientApi(), appVersion }: { api?:
5876
5950
  .slice(0, 12);
5877
5951
  }, [data.commands, data.files, promptCompletion]);
5878
5952
  const promptCompletionOpen = Boolean(promptCompletion && !promptCompletionSuppressed && promptCompletionItems.length);
5953
+ const promptCompletionActiveIndex = Math.min(promptCompletionIndex, Math.max(promptCompletionItems.length - 1, 0));
5879
5954
  const installedPackages = useMemo(() => new Set(data.plugins.filter((plugin) => plugin.removable).map((plugin) => plugin.name)), [data.plugins]);
5880
5955
  const workspaceReady =
5881
5956
  !workspaceChooserOpen && Boolean(selectedWorkspacePath || data.status?.cwd || data.workspaces.find((workspace) => workspace.current)?.path || data.session);
@@ -6250,6 +6325,26 @@ export function ControlRoomView({ api = createClientApi(), appVersion }: { api?:
6250
6325
  setAnnotationSelection("");
6251
6326
  setAnnotationNote("");
6252
6327
  };
6328
+ const openCommandCompletion = () => {
6329
+ const input = promptInputRef.current;
6330
+ const caret = input?.selectionStart ?? draft.length;
6331
+ const existing = getPromptCompletion(draft, caret);
6332
+ let nextCaret = caret;
6333
+ if (existing?.kind !== "command") {
6334
+ const separator = caret > 0 && !/\s/.test(draft[caret - 1] ?? "") ? " " : "";
6335
+ const insertion = `${separator}/`;
6336
+ const nextDraft = `${draft.slice(0, caret)}${insertion}${draft.slice(caret)}`;
6337
+ nextCaret = caret + insertion.length;
6338
+ setDraft(nextDraft);
6339
+ }
6340
+ setPromptCaret(nextCaret);
6341
+ setPromptCompletionSuppressed(false);
6342
+ setPromptCompletionIndex(0);
6343
+ window.requestAnimationFrame(() => {
6344
+ input?.focus();
6345
+ input?.setSelectionRange(nextCaret, nextCaret);
6346
+ });
6347
+ };
6253
6348
  const openSession = (session: Record<string, unknown>) => {
6254
6349
  const path = typeof session.path === "string" ? session.path : "";
6255
6350
  if (!path) return;
@@ -6294,6 +6389,7 @@ export function ControlRoomView({ api = createClientApi(), appVersion }: { api?:
6294
6389
  });
6295
6390
  };
6296
6391
  const openSessionMenu = (path: string, name?: string) => {
6392
+ setSessionToolsOpen(false);
6297
6393
  setSelectedSessionPath(path);
6298
6394
  setSessionNameDraft(name ?? "");
6299
6395
  setSessionMenuPath(path);
@@ -6618,6 +6714,11 @@ export function ControlRoomView({ api = createClientApi(), appVersion }: { api?:
6618
6714
  ) : null}
6619
6715
  <form className="composer" onSubmit={submit}>
6620
6716
  <textarea
6717
+ aria-activedescendant={promptCompletionOpen ? `prompt-completion-option-${promptCompletionActiveIndex}` : undefined}
6718
+ aria-autocomplete="list"
6719
+ aria-controls={promptCompletionOpen ? "prompt-completion-list" : undefined}
6720
+ aria-expanded={promptCompletionOpen}
6721
+ aria-haspopup="listbox"
6621
6722
  aria-label="Prompt"
6622
6723
  onChange={(event) => {
6623
6724
  setDraft(event.target.value);
@@ -6644,7 +6745,7 @@ export function ControlRoomView({ api = createClientApi(), appVersion }: { api?:
6644
6745
  }
6645
6746
  if (popupOpen && (event.key === "Enter" || event.key === "Tab")) {
6646
6747
  event.preventDefault();
6647
- const item = items[promptCompletionIndex];
6748
+ const item = items[Math.min(promptCompletionIndex, items.length - 1)];
6648
6749
  if (item && completion) {
6649
6750
  const value = completion.kind === "command" ? `/${(item as ClientCommand).invocationName}` : `@${(item as ClientFile).path}`;
6650
6751
  const replacement = replacePromptCompletion(event.currentTarget.value, completion, `${value} `);
@@ -6686,7 +6787,7 @@ export function ControlRoomView({ api = createClientApi(), appVersion }: { api?:
6686
6787
  ></textarea>
6687
6788
  {promptCompletionOpen && promptCompletion && (
6688
6789
  <PromptCompletionPopover
6689
- activeIndex={promptCompletionIndex}
6790
+ activeIndex={promptCompletionActiveIndex}
6690
6791
  commands={data.commands}
6691
6792
  files={data.files}
6692
6793
  kind={promptCompletion.kind}
@@ -6711,8 +6812,14 @@ export function ControlRoomView({ api = createClientApi(), appVersion }: { api?:
6711
6812
  disabled={!data.models.length || promptBusy}
6712
6813
  value={data.status?.model ?? ""}
6713
6814
  onChange={(event) => {
6714
- const [provider, model] = event.target.value.split("/");
6715
- if (provider && model) void api.selectModel(provider, model);
6815
+ const [provider, ...modelParts] = event.target.value.split("/");
6816
+ const model = modelParts.join("/");
6817
+ if (!provider || !model) return;
6818
+ setPromptError("");
6819
+ void api
6820
+ .selectModel(provider, model)
6821
+ .then(refresh)
6822
+ .catch((cause: unknown) => setPromptError(cause instanceof Error ? cause.message : String(cause)));
6716
6823
  }}
6717
6824
  >
6718
6825
  {data.models.length ? (
@@ -6728,7 +6835,7 @@ export function ControlRoomView({ api = createClientApi(), appVersion }: { api?:
6728
6835
  <button className="tool-chip" onClick={() => setPermission((current) => !current)} type="button">
6729
6836
  ● {permission ? "改动前询问" : "自动允许"}
6730
6837
  </button>
6731
- <button className="tool-chip" onClick={() => setCommandOpen(true)} type="button">
6838
+ <button className="tool-chip" onClick={openCommandCompletion} type="button">
6732
6839
  / 命令
6733
6840
  </button>
6734
6841
  <span className="composer-hint">⌘↵ 发送 · ⌘K 命令 · ⌃C 中断</span>
@@ -6752,7 +6859,14 @@ export function ControlRoomView({ api = createClientApi(), appVersion }: { api?:
6752
6859
  <Files
6753
6860
  api={api}
6754
6861
  files={data.files}
6755
- onDiff={(file) => void api.getFileDiff(file).then((diff) => setDetails({ type: "file_diff", path: diff.path, output: diff.diff }))}
6862
+ onDiff={async (file) => {
6863
+ const diff = await api.getFileDiff(file);
6864
+ setDetails({
6865
+ type: "file_diff",
6866
+ path: diff.path,
6867
+ output: diff.diff || "没有可显示的差异(工作区可能已更新)。",
6868
+ });
6869
+ }}
6756
6870
  onRefresh={() => void refresh()}
6757
6871
  />
6758
6872
  );
@@ -6866,8 +6980,10 @@ export function ControlRoomView({ api = createClientApi(), appVersion }: { api?:
6866
6980
  aria-pressed={sessionSelectionMode}
6867
6981
  className={`session-tool-button ${sessionSelectionMode ? "active" : ""}`}
6868
6982
  onClick={() => {
6869
- setSessionSelectionMode((current) => !current);
6983
+ setSessionSelectionMode(!sessionSelectionMode);
6870
6984
  setSelectedSessionPaths(new Set());
6985
+ setSessionToolsOpen(false);
6986
+ closeSessionMenu();
6871
6987
  }}
6872
6988
  type="button"
6873
6989
  >
@@ -6878,7 +6994,10 @@ export function ControlRoomView({ api = createClientApi(), appVersion }: { api?:
6878
6994
  aria-expanded={sessionToolsOpen}
6879
6995
  aria-label="会话工具"
6880
6996
  className="session-tool-button icon"
6881
- onClick={() => setSessionToolsOpen((current) => !current)}
6997
+ onClick={() => {
6998
+ closeSessionMenu();
6999
+ setSessionToolsOpen((current) => !current);
7000
+ }}
6882
7001
  type="button"
6883
7002
  >
6884
7003
 
@@ -7143,6 +7262,20 @@ export function ControlRoomView({ api = createClientApi(), appVersion }: { api?:
7143
7262
  status <b>{value(data.status?.status, "connecting")}</b>
7144
7263
  </span>
7145
7264
  </div>
7265
+ <button
7266
+ aria-label="搜索会话、文件和命令"
7267
+ className="sidebar-link compact-session-search"
7268
+ onClick={() => {
7269
+ setCommandOpen(false);
7270
+ setSessionToolsOpen(false);
7271
+ closeSessionMenu();
7272
+ setGlobalSearchOpen(true);
7273
+ }}
7274
+ title="搜索会话、文件和命令"
7275
+ type="button"
7276
+ >
7277
+
7278
+ </button>
7146
7279
  <button aria-label="新建会话" className="sidebar-link compact-new-session" onClick={beginNewSession} title="新建会话" type="button">
7147
7280
 
7148
7281
  </button>
@@ -7211,7 +7344,17 @@ export function ControlRoomView({ api = createClientApi(), appVersion }: { api?:
7211
7344
  <div className="run-indicator running">
7212
7345
  <span className="run-dot"></span>
7213
7346
  <span>运行中 · Pi agent</span>
7214
- <button className="stop-button" onClick={() => void api.abort().then(refresh)} type="button">
7347
+ <button
7348
+ className="stop-button"
7349
+ onClick={() => {
7350
+ setPromptError("");
7351
+ void api
7352
+ .abort()
7353
+ .then(refresh)
7354
+ .catch((cause: unknown) => setPromptError(cause instanceof Error ? cause.message : String(cause)));
7355
+ }}
7356
+ type="button"
7357
+ >
7215
7358
  停止
7216
7359
  </button>
7217
7360
  </div>
@@ -7248,8 +7391,14 @@ export function ControlRoomView({ api = createClientApi(), appVersion }: { api?:
7248
7391
  >
7249
7392
 
7250
7393
  </button>
7251
- <button aria-pressed={details !== undefined} className="details-toggle" onClick={() => setDetails(details ? undefined : {})} type="button">
7252
- 详情
7394
+ <button
7395
+ aria-label={details !== undefined ? "关闭详情" : "打开详情"}
7396
+ aria-pressed={details !== undefined}
7397
+ className="details-toggle"
7398
+ onClick={() => setDetails(details ? undefined : {})}
7399
+ type="button"
7400
+ >
7401
+ <span aria-hidden="true">◨</span> <span className="details-toggle-label">详情</span>
7253
7402
  </button>
7254
7403
  </>
7255
7404
  )}
@@ -7335,11 +7484,14 @@ export function ControlRoomView({ api = createClientApi(), appVersion }: { api?:
7335
7484
  </div>
7336
7485
  </section>
7337
7486
  {!settings && page === "session" && details !== undefined && (
7338
- <Details
7339
- event={Object.keys(details).length ? details : undefined}
7340
- onClose={() => setDetails(undefined)}
7341
- onCopy={() => void navigator.clipboard?.writeText(JSON.stringify(details, null, 2))}
7342
- />
7487
+ <>
7488
+ <button aria-label="关闭详情" className="details-backdrop" onClick={() => setDetails(undefined)} type="button" />
7489
+ <Details
7490
+ event={Object.keys(details).length ? details : undefined}
7491
+ onClose={() => setDetails(undefined)}
7492
+ onCopy={() => void navigator.clipboard?.writeText(JSON.stringify(details, null, 2))}
7493
+ />
7494
+ </>
7343
7495
  )}
7344
7496
  {globalSearchOpen && (
7345
7497
  <GlobalSearch