@pi-harness/pi-harness 0.1.7 → 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 (248) 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 +148 -41
  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/react-room.d.ts.map +1 -1
  241. package/packages/client-web/dist/react-room.js +128 -38
  242. package/packages/client-web/dist/react-room.js.map +1 -1
  243. package/packages/client-web/package.json +1 -1
  244. package/packages/client-web/src/react-room.tsx +220 -73
  245. package/packages/core/package.json +1 -1
  246. package/packages/host-webserver/package.json +1 -1
  247. package/apps/web/dist/assets/index-CkGv0zrr.css +0 -2
  248. package/apps/web/dist/assets/index-pMtwuwHh.js +0 -20
@@ -11,7 +11,7 @@ import { loadMarketplaceCatalog } from "./marketplace-catalog.js";
11
11
  import { pluginStarsRows } from "./plugin-stars-view.js";
12
12
  import { browserSessionTabs } from "./browser-session-view.js";
13
13
  import { matchesPluginQuery } from "./plugin-search.js";
14
- import { installedPluginDetailPath, readInstalledPluginDetailId } from "./plugin-navigation.js";
14
+ import { readInstalledPluginDetailId } from "./plugin-navigation.js";
15
15
  import { installedPluginCardContent } from "./plugin-card.js";
16
16
  const value = (input, fallback = "—") => {
17
17
  if (input === undefined || input === null || input === "")
@@ -388,7 +388,7 @@ function Details({ event, onClose, onCopy }) {
388
388
  ["耗时", value(event.duration ?? event.dur)],
389
389
  ["时间", value(event.timestamp ?? event.ts ?? event.time)],
390
390
  ];
391
- return (_jsxs("aside", { className: "details-panel", children: [_jsxs("header", { children: [_jsx("strong", { children: eventLabel(event) }), _jsx("button", { "aria-label": "\u5173\u95ED\u4E8B\u4EF6\u8BE6\u60C5", onClick: onClose, type: "button", children: "\u00D7" })] }), _jsxs("div", { className: "details-body", children: [_jsx("div", { className: "detail-stats", children: stats.map(([key, item]) => (_jsxs("div", { children: [_jsx("small", { children: key }), _jsx("b", { children: item })] }, key))) }), output !== undefined && (_jsxs("div", { className: "detail-section", children: [_jsx("small", { children: "\u8F93\u51FA" }), _jsx("pre", { className: "tool-output", children: typeof output === "string" ? output : JSON.stringify(output, null, 2) })] })), _jsxs("div", { className: "detail-section", children: [_jsx("small", { children: fileDetail ? "数据来源" : "经过的插件" }), _jsx("div", { className: "detail-plugin", children: fileDetail ? "Git workspace · /api/files" : "Runtime loader · event" })] }), _jsxs("div", { className: "detail-actions", children: [_jsx("button", { onClick: onCopy, type: "button", children: "\u590D\u5236 JSON" }), _jsx("button", { disabled: true, title: "\u5F53\u524D API \u672A\u63D0\u4F9B\u91CD\u653E\u63A5\u53E3", type: "button", children: "\u91CD\u653E" })] }), _jsxs("details", { className: "raw-json", children: [_jsx("summary", { children: "\u539F\u59CB JSON" }), _jsx("pre", { className: "raw-json-body", children: JSON.stringify(event, null, 2) })] })] })] }));
391
+ return (_jsxs("aside", { className: "details-panel", children: [_jsxs("header", { children: [_jsx("strong", { children: eventLabel(event) }), _jsx("button", { "aria-label": fileDetail ? "关闭文件差异" : "关闭事件详情", onClick: onClose, type: "button", children: "\u00D7" })] }), _jsxs("div", { className: "details-body", children: [_jsx("div", { className: `detail-stats ${fileDetail ? "is-file" : ""}`, children: stats.map(([key, item]) => (_jsxs("div", { children: [_jsx("small", { children: key }), _jsx("b", { children: item })] }, key))) }), output !== undefined && (_jsxs("div", { className: "detail-section", children: [_jsx("small", { children: "\u8F93\u51FA" }), _jsx("pre", { className: "tool-output", children: typeof output === "string" ? output : JSON.stringify(output, null, 2) })] })), _jsxs("div", { className: "detail-section", children: [_jsx("small", { children: fileDetail ? "数据来源" : "经过的插件" }), _jsx("div", { className: "detail-plugin", children: fileDetail ? "Git workspace · /api/files" : "Runtime loader · event" })] }), _jsxs("div", { className: "detail-actions", children: [_jsx("button", { onClick: onCopy, type: "button", children: "\u590D\u5236 JSON" }), _jsx("button", { disabled: true, title: "\u5F53\u524D API \u672A\u63D0\u4F9B\u91CD\u653E\u63A5\u53E3", type: "button", children: "\u91CD\u653E" })] }), _jsxs("details", { className: "raw-json", children: [_jsx("summary", { children: "\u539F\u59CB JSON" }), _jsx("pre", { className: "raw-json-body", children: JSON.stringify(event, null, 2) })] })] })] }));
392
392
  }
393
393
  function Trajectory({ events, onSelect }) {
394
394
  const [filter, setFilter] = useState("all");
@@ -403,11 +403,12 @@ function Trajectory({ events, onSelect }) {
403
403
  const visible = events.filter((event) => filter === "all" || value(event.type, "event") === filter);
404
404
  return (_jsxs("section", { className: "view-panel trajectory-view", children: [_jsxs("div", { className: "trajectory-summary", children: [_jsx("span", { children: "\u6309\u8F6E\u6B21" }), _jsxs("b", { children: [events.length, " \u4E2A\u4E8B\u4EF6"] }), _jsx("div", { className: "timeline", children: events.length ? (events.map((event, index) => (_jsx("span", { className: "timeline-turn", children: _jsx("i", { className: "timeline-strip", title: eventLabel(event) }) }, index)))) : (_jsx("span", { className: "timeline-empty", children: "\u7B49\u5F85\u771F\u5B9E\u4E8B\u4EF6\u2026" })) })] }), _jsxs("div", { className: "source-filters", children: [_jsxs("button", { className: `filter ${filter === "all" ? "active" : ""}`, onClick: () => setFilter("all"), type: "button", children: ["\u5168\u90E8 ", events.length] }), [...counts].map(([type, count]) => (_jsxs("button", { className: `filter ${filter === type ? "active" : ""}`, onClick: () => setFilter(type), type: "button", children: [type, " ", count] }, type)))] }), _jsxs("div", { className: "event-table", children: [_jsxs("div", { className: "event-head", children: [_jsx("span", { children: "\u65F6\u95F4" }), _jsx("span", { children: "\u6765\u6E90" }), _jsx("span", { children: "\u4E8B\u4EF6" }), _jsx("span", { children: "\u4EA7\u751F\u8005" }), _jsx("span", { children: "\u8017\u65F6" })] }), visible.map((event, index) => (_jsxs("button", { className: "event-row", onClick: () => onSelect(event), type: "button", children: [_jsx("span", { children: value(event.timestamp ?? event.ts ?? event.time) }), _jsxs("span", { children: [_jsx("i", { className: "event-dot" }), value(event.type, "event")] }), _jsx("strong", { children: eventLabel(event) }), _jsx("span", { children: value(event.by ?? event.source) }), _jsx("span", { children: value(event.duration ?? event.dur) })] }, index))), !visible.length && _jsx("div", { className: "empty-state", children: "\u6682\u65E0\u8F68\u8FF9\u4E8B\u4EF6\u3002" })] })] }));
405
405
  }
406
- function Files({ files, api, onDiff, onRefresh }) {
406
+ function Files({ files, api, onDiff, onRefresh, }) {
407
407
  const additions = files.filter((file) => file.status.includes("A") || file.status === "??").length;
408
408
  const deletions = files.filter((file) => file.status.includes("D")).length;
409
409
  const [message, setMessage] = useState("");
410
410
  const [busy, setBusy] = useState(false);
411
+ const [diffPending, setDiffPending] = useState();
411
412
  const [error, setError] = useState("");
412
413
  const [revertConfirmOpen, setRevertConfirmOpen] = useState(false);
413
414
  const commit = () => {
@@ -437,7 +438,16 @@ function Files({ files, api, onDiff, onRefresh }) {
437
438
  .catch((cause) => setError(cause instanceof Error ? cause.message : String(cause)))
438
439
  .finally(() => setBusy(false));
439
440
  };
440
- return (_jsxs("section", { className: "view-panel files-view", children: [_jsxs("div", { className: "files-content", children: [_jsxs("div", { className: "files-title", children: [_jsx("strong", { children: "\u672C\u6B21\u4F1A\u8BDD\u6539\u52A8" }), _jsx("span", { children: "\u7531 /api/files \u63D0\u4F9B" })] }), _jsx("div", { className: "file-summary", children: `${files.length} 个文件 · ${additions} 个新增 · ${deletions} 个删除` }), _jsx("div", { className: "file-list", children: files.length ? (files.map((file) => (_jsxs("div", { className: "file-row", children: [_jsx("b", { className: `file-kind ${file.label === "untracked" ? "new" : ""}`, children: file.label }), _jsx("code", { children: file.path }), _jsx("span", { className: file.status.includes("D") ? "del" : "add", children: file.status }), _jsx("button", { className: "diff-button", onClick: () => onDiff(file.path), type: "button", children: "\u67E5\u770B\u5DEE\u5F02" })] }, file.path)))) : (_jsx("div", { className: "empty-state", children: "\u5DE5\u4F5C\u533A\u6CA1\u6709\u672A\u63D0\u4EA4\u6539\u52A8\u3002" })) }), files.length > 0 && (_jsxs("div", { className: "file-actions", children: [_jsx("input", { "aria-label": "\u63D0\u4EA4\u8BF4\u660E", onChange: (event) => setMessage(event.target.value), placeholder: "\u63D0\u4EA4\u8BF4\u660E", value: message }), _jsx("button", { className: "primary", disabled: busy || !message.trim(), onClick: commit, type: "button", children: busy ? "处理中…" : "提交这些改动" }), _jsx("button", { disabled: busy, onClick: () => setRevertConfirmOpen(true), type: "button", children: "\u5168\u90E8\u64A4\u9500" })] })), error && _jsx("p", { className: "files-error", children: error })] }), revertConfirmOpen && (_jsx(ConfirmDialog, { busy: busy, confirmLabel: "\u786E\u8BA4\u64A4\u9500", description: "\u8FD9\u4F1A\u4E22\u5F03\u5F53\u524D\u5DE5\u4F5C\u533A\u7684\u5168\u90E8\u672A\u63D0\u4EA4\u6539\u52A8\uFF0C\u6B64\u64CD\u4F5C\u4E0D\u53EF\u6062\u590D\u3002", onClose: () => setRevertConfirmOpen(false), onConfirm: revert, target: `${files.length} 个文件`, title: "\u64A4\u9500\u5168\u90E8\u6539\u52A8" }))] }));
441
+ const openDiff = (path) => {
442
+ if (busy || diffPending)
443
+ return;
444
+ setDiffPending(path);
445
+ setError("");
446
+ void onDiff(path)
447
+ .catch((cause) => setError(cause instanceof Error ? cause.message : String(cause)))
448
+ .finally(() => setDiffPending(undefined));
449
+ };
450
+ return (_jsxs("section", { className: "view-panel files-view", children: [_jsxs("div", { className: "files-content", children: [_jsxs("div", { className: "files-title", children: [_jsx("strong", { children: "\u672C\u6B21\u4F1A\u8BDD\u6539\u52A8" }), _jsx("span", { children: "\u7531 /api/files \u63D0\u4F9B" })] }), _jsx("div", { className: "file-summary", children: `${files.length} 个文件 · ${additions} 个新增 · ${deletions} 个删除` }), _jsx("div", { className: "file-list", children: files.length ? (files.map((file) => (_jsxs("div", { className: "file-row", children: [_jsx("b", { className: `file-kind ${file.label === "untracked" ? "new" : ""}`, children: file.label }), _jsx("code", { children: file.path }), _jsx("span", { className: file.status.includes("D") ? "del" : "add", children: file.status }), _jsx("button", { "aria-label": `${diffPending === file.path ? "正在读取" : "查看"}${file.path}的差异`, className: "diff-button", disabled: busy || diffPending !== undefined, onClick: () => openDiff(file.path), type: "button", children: diffPending === file.path ? "读取中…" : "查看差异" })] }, file.path)))) : (_jsx("div", { className: "empty-state", children: "\u5DE5\u4F5C\u533A\u6CA1\u6709\u672A\u63D0\u4EA4\u6539\u52A8\u3002" })) }), files.length > 0 && (_jsxs("div", { className: "file-actions", children: [_jsx("input", { "aria-label": "\u63D0\u4EA4\u8BF4\u660E", onChange: (event) => setMessage(event.target.value), placeholder: "\u63D0\u4EA4\u8BF4\u660E", value: message }), _jsx("button", { className: "primary", disabled: busy || !message.trim(), onClick: commit, type: "button", children: busy ? "处理中…" : "提交这些改动" }), _jsx("button", { disabled: busy, onClick: () => setRevertConfirmOpen(true), type: "button", children: "\u5168\u90E8\u64A4\u9500" })] })), error && _jsx("p", { className: "files-error", children: error })] }), revertConfirmOpen && (_jsx(ConfirmDialog, { busy: busy, confirmLabel: "\u786E\u8BA4\u64A4\u9500", description: "\u8FD9\u4F1A\u4E22\u5F03\u5F53\u524D\u5DE5\u4F5C\u533A\u7684\u5168\u90E8\u672A\u63D0\u4EA4\u6539\u52A8\uFF0C\u6B64\u64CD\u4F5C\u4E0D\u53EF\u6062\u590D\u3002", onClose: () => setRevertConfirmOpen(false), onConfirm: revert, target: `${files.length} 个文件`, title: "\u64A4\u9500\u5168\u90E8\u6539\u52A8" }))] }));
441
451
  }
442
452
  function pluginPanelValue(input) {
443
453
  if (typeof input === "string")
@@ -1034,6 +1044,42 @@ function PluginUninstallDialog({ pluginName, busy, error, onCancel, onConfirm, }
1034
1044
  onCancel();
1035
1045
  }, children: _jsxs("div", { "aria-label": "\u786E\u8BA4\u5378\u8F7D\u63D2\u4EF6", "aria-modal": "true", className: "plugin-confirm-dialog", onClick: (event) => event.stopPropagation(), ref: dialogRef, role: "dialog", tabIndex: -1, children: [_jsx("header", { children: _jsxs("div", { children: [_jsx("strong", { children: "\u5378\u8F7D\u63D2\u4EF6\uFF1F" }), _jsx("small", { children: "\u5C06\u4ECE\u8FD0\u884C\u914D\u7F6E\u4E2D\u79FB\u9664\uFF0C\u4E4B\u540E\u53EF\u4EE5\u4ECE\u63D2\u4EF6\u5E02\u573A\u91CD\u65B0\u5B89\u88C5\u3002" })] }) }), _jsx("code", { children: pluginName }), error && (_jsx("p", { className: "plugin-confirm-error", role: "alert", children: error })), _jsxs("footer", { children: [_jsx("button", { "data-dialog-initial-focus": true, disabled: busy, onClick: onCancel, type: "button", children: "\u53D6\u6D88" }), _jsx("button", { className: "danger", disabled: busy, onClick: onConfirm, type: "button", children: busy ? "卸载中…" : "确认卸载" })] })] }) }));
1036
1046
  }
1047
+ function PluginCategoryNav({ label, categories, activeCategory, onChange, }) {
1048
+ const navRef = useRef(null);
1049
+ const [canScrollLeft, setCanScrollLeft] = useState(false);
1050
+ const [canScrollRight, setCanScrollRight] = useState(false);
1051
+ const updateScrollState = useCallback(() => {
1052
+ const nav = navRef.current;
1053
+ if (!nav)
1054
+ return;
1055
+ const maximum = Math.max(0, nav.scrollWidth - nav.clientWidth);
1056
+ setCanScrollLeft(nav.scrollLeft > 1);
1057
+ setCanScrollRight(nav.scrollLeft < maximum - 1);
1058
+ }, []);
1059
+ useEffect(() => {
1060
+ const nav = navRef.current;
1061
+ if (!nav)
1062
+ return;
1063
+ updateScrollState();
1064
+ const observer = typeof ResizeObserver === "undefined" ? undefined : new ResizeObserver(updateScrollState);
1065
+ observer?.observe(nav);
1066
+ window.addEventListener("resize", updateScrollState);
1067
+ return () => {
1068
+ observer?.disconnect();
1069
+ window.removeEventListener("resize", updateScrollState);
1070
+ };
1071
+ }, [categories, updateScrollState]);
1072
+ const scroll = (direction) => {
1073
+ const nav = navRef.current;
1074
+ if (!nav)
1075
+ return;
1076
+ nav.scrollBy({ behavior: "smooth", left: direction * Math.max(180, nav.clientWidth * 0.72) });
1077
+ };
1078
+ return (_jsxs("div", { className: `marketplace-categories-shell ${canScrollLeft ? "can-scroll-left" : ""} ${canScrollRight ? "can-scroll-right" : ""}`, children: [canScrollLeft && (_jsx("button", { "aria-label": "\u5411\u5DE6\u67E5\u770B\u66F4\u591A\u5206\u7C7B", className: "marketplace-category-scroll previous", onClick: () => scroll(-1), type: "button", children: "\u2039" })), _jsx("nav", { "aria-label": label, className: "marketplace-categories", onScroll: updateScrollState, ref: navRef, children: categories.map((category) => {
1079
+ const active = activeCategory === category.id;
1080
+ return (_jsxs("button", { "aria-pressed": active, className: `marketplace-category ${active ? "active" : ""}`, onClick: () => onChange(category.id), type: "button", children: [_jsx("span", { children: category.label }), _jsx("span", { className: active ? "font-mono text-[10px] text-[#3565c5]" : "font-mono text-[10px] text-[#687381]", children: category.count })] }, category.id || "all"));
1081
+ }) }), canScrollRight && (_jsx("button", { "aria-label": "\u5411\u53F3\u67E5\u770B\u66F4\u591A\u5206\u7C7B", className: "marketplace-category-scroll next", onClick: () => scroll(1), type: "button", children: "\u203A" }))] }));
1082
+ }
1037
1083
  function Plugins({ plugins, panels, catalog, onMarketplace, onOpenDetail, onToml, onToggle, onUninstall, }) {
1038
1084
  const catalogByPackage = useMemo(() => new Map(catalog.map((plugin) => [plugin.packageName, plugin])), [catalog]);
1039
1085
  const panelPluginIds = useMemo(() => new Set(panels.map((panel) => panel.pluginId)), [panels]);
@@ -1096,10 +1142,7 @@ function Plugins({ plugins, panels, catalog, onMarketplace, onOpenDetail, onToml
1096
1142
  return (_jsx("section", { className: "view-panel plugins-view", children: _jsxs("div", { className: "plugins-page", children: [_jsxs("div", { className: "subnav", children: [_jsxs("div", { "aria-label": "\u63D2\u4EF6\u76EE\u5F55", className: "segmented", children: [_jsx("button", { "aria-pressed": "true", className: "active", type: "button", children: "\u5DF2\u5B89\u88C5" }), _jsx("button", { "aria-pressed": "false", onClick: onMarketplace, type: "button", children: "\u63D2\u4EF6\u5E02\u573A" })] }), _jsx("span", { children: "\u5DF2\u5B89\u88C5\u63D2\u4EF6" }), _jsx("a", { href: "#", onClick: (event) => {
1097
1143
  event.preventDefault();
1098
1144
  onToml();
1099
- }, children: "\u67E5\u770B\u8FD0\u884C\u914D\u7F6E" })] }), _jsxs("div", { className: "plugins-toolbar", children: [_jsx("input", { "aria-label": "\u641C\u7D22\u5DF2\u5B89\u88C5\u63D2\u4EF6", onChange: (event) => setQuery(event.target.value), placeholder: "\u641C\u7D22\u540D\u79F0\u3001\u5305\u540D\u6216\u80FD\u529B\u2026", type: "search", value: query }), _jsxs("span", { "aria-live": "polite", children: [query.trim() || categoryFilter ? `${visiblePlugins.length} / ${installedPlugins.length}` : `${installedPlugins.length}`, " \u4E2A\u63D2\u4EF6"] })] }), _jsx("nav", { "aria-label": "\u5DF2\u5B89\u88C5\u63D2\u4EF6\u5206\u7C7B", className: "marketplace-categories", children: installedCategories.map((category) => {
1100
- const active = categoryFilter === category.id;
1101
- return (_jsxs("button", { "aria-pressed": active, className: `marketplace-category ${active ? "active" : ""}`, onClick: () => setCategoryFilter(category.id), type: "button", children: [_jsx("span", { children: category.label }), _jsx("span", { className: active ? "font-mono text-[10px] text-[#3565c5]" : "font-mono text-[10px] text-[#687381]", children: category.count })] }, category.id || "all"));
1102
- }) }), _jsxs("div", { className: "plugins-scroll", ref: scrollRef, children: [_jsxs("div", { className: "plugins-list", children: [visiblePlugins.map((plugin) => {
1145
+ }, children: "\u67E5\u770B\u8FD0\u884C\u914D\u7F6E" })] }), _jsxs("div", { className: "plugins-toolbar", children: [_jsx("input", { "aria-label": "\u641C\u7D22\u5DF2\u5B89\u88C5\u63D2\u4EF6", onChange: (event) => setQuery(event.target.value), placeholder: "\u641C\u7D22\u540D\u79F0\u3001\u5305\u540D\u6216\u80FD\u529B\u2026", type: "search", value: query }), _jsxs("span", { "aria-live": "polite", children: [query.trim() || categoryFilter ? `${visiblePlugins.length} / ${installedPlugins.length}` : `${installedPlugins.length}`, " \u4E2A\u63D2\u4EF6"] })] }), _jsx(PluginCategoryNav, { activeCategory: categoryFilter, categories: installedCategories, label: "\u5DF2\u5B89\u88C5\u63D2\u4EF6\u5206\u7C7B", onChange: setCategoryFilter }), _jsxs("div", { className: "plugins-scroll", ref: scrollRef, children: [_jsxs("div", { className: "plugins-list", children: [visiblePlugins.map((plugin) => {
1103
1146
  const metadata = catalogByPackage.get(plugin.name);
1104
1147
  const categoryLabel = metadata?.category.label ?? plugin.category?.label;
1105
1148
  const capabilityLabel = capability(plugin.name);
@@ -1108,10 +1151,7 @@ function Plugins({ plugins, panels, catalog, onMarketplace, onOpenDetail, onToml
1108
1151
  return (_jsxs("article", { className: "catalog-card plugin-card", children: [_jsxs("div", { className: "plugin-card-head", children: [_jsx("span", { className: "plugin-icon", children: "\u25C8" }), _jsx("div", { className: "plugin-copy", children: _jsxs("div", { className: "plugin-heading", children: [_jsxs("div", { className: "plugin-title", children: [_jsx("strong", { children: pluginTitle }), _jsx("span", { className: `plugin-state ${plugin.enabled ? "active" : ""}`, children: plugin.enabled ? "运行中" : "已停用" }), categoryLabel && _jsx("span", { className: "capability", children: categoryLabel }), categoryLabel !== capabilityLabel && _jsx("span", { className: "capability", children: capabilityLabel })] }), _jsx("div", { className: "plugin-actions", children: plugin.removable ? (_jsxs(_Fragment, { children: [_jsx("button", { "aria-label": `${plugin.enabled ? "停用" : "启用"} ${pluginTitle}`, "aria-pressed": plugin.enabled, className: "plugin-switch-button", disabled: busyPlugin !== undefined, onClick: () => void runPluginAction(plugin, (item) => onToggle(item)), type: "button", children: _jsx("span", { "aria-hidden": "true", className: `switch ${plugin.enabled ? "on" : ""}`, children: _jsx("i", {}) }) }), _jsx("button", { className: "plugin-uninstall", disabled: busyPlugin !== undefined, onClick: () => {
1109
1152
  setPluginError("");
1110
1153
  setPendingUninstall(plugin);
1111
- }, type: "button", children: "\u5378\u8F7D" })] })) : (_jsx("span", { className: `switch ${plugin.enabled ? "on" : ""}`, children: _jsx("i", {}) })) })] }) })] }), _jsxs("div", { className: "plugin-card-body", children: [_jsx("code", { className: "plugin-package", children: cardContent.packageLabel }), _jsx("p", { className: "plugin-description", children: cardContent.description }), _jsx("div", { className: "hook-list", children: cardContent.tags.map((tag, index) => (_jsx("span", { children: tag }, `${tag}-${index}`))) })] }), _jsxs("footer", { className: "plugin-card-footer", children: [_jsx("a", { className: "plugin-detail-link", href: installedPluginDetailPath(plugin.name), onClick: (event) => {
1112
- event.preventDefault();
1113
- onOpenDetail(plugin);
1114
- }, children: "\u67E5\u770B\u8BE6\u60C5 \u2192" }), panelPluginIds.has(plugin.name) ? _jsx("span", { children: "\u5B9E\u65F6\u9762\u677F" }) : null] })] }, plugin.id));
1154
+ }, type: "button", children: "\u5378\u8F7D" })] })) : (_jsx("span", { className: `switch ${plugin.enabled ? "on" : ""}`, children: _jsx("i", {}) })) })] }) })] }), _jsxs("div", { className: "plugin-card-body", children: [_jsx("code", { className: "plugin-package", children: cardContent.packageLabel }), _jsx("p", { className: "plugin-description", children: cardContent.description }), _jsx("div", { className: "hook-list", children: cardContent.tags.map((tag, index) => (_jsx("span", { children: tag }, `${tag}-${index}`))) })] }), _jsxs("footer", { className: "plugin-card-footer", children: [_jsx("button", { "aria-label": `查看 ${pluginTitle} 详情`, className: "plugin-detail-link", onClick: () => onOpenDetail(plugin), type: "button", children: "\u67E5\u770B\u8BE6\u60C5 \u2192" }), panelPluginIds.has(plugin.name) ? _jsx("span", { children: "\u5B9E\u65F6\u9762\u677F" }) : null] })] }, plugin.id));
1115
1155
  }), !installedPlugins.length ? (_jsx("div", { className: "empty-state", children: "\u8FD8\u6CA1\u6709\u5B89\u88C5\u53EF\u7BA1\u7406\u7684\u63D2\u4EF6\u3002\u53BB\u63D2\u4EF6\u5E02\u573A\u5B89\u88C5\u4E00\u4E2A\u5427\u3002" })) : !visiblePlugins.length ? (_jsx("div", { className: "empty-state", children: "\u6CA1\u6709\u5339\u914D\u5F53\u524D\u641C\u7D22\u4E0E\u5206\u7C7B\u6761\u4EF6\u7684\u5DF2\u5B89\u88C5\u63D2\u4EF6\u3002" })) : null] }), pluginError && _jsx("p", { className: "plugin-action-error", children: pluginError }), panels.length > installedPlugins.length && (_jsxs("section", { className: "mt-4 border-t border-[#e3e7ee] pt-4", children: [_jsxs("div", { className: "mb-3 flex items-baseline justify-between gap-3", children: [_jsxs("div", { children: [_jsx("strong", { className: "text-[13px] font-semibold text-[#20252b]", children: "\u5176\u4ED6\u63D2\u4EF6\u9762\u677F" }), _jsx("p", { className: "mt-1 text-[12px] text-[#687381]", children: "\u7531\u5DF2\u542F\u7528\u63D2\u4EF6\u63D0\u4F9B\u7684\u5B9E\u65F6\u72B6\u6001\u3002" })] }), _jsx("span", { className: "font-mono text-[10px] uppercase tracking-[0.08em] text-[#687381]", children: "LIVE" })] }), _jsx("div", { className: "grid gap-3 xl:grid-cols-2", children: panels
1116
1156
  .filter((panel) => !installedPlugins.some((plugin) => plugin.name === panel.pluginId))
1117
1157
  .map((panel) => (_jsx(PluginPanelCard, { panel: panel }, panel.id))) })] }))] }), pendingUninstall && (_jsx(PluginUninstallDialog, { busy: busyPlugin !== undefined, error: pluginError, onCancel: () => setPendingUninstall(undefined), onConfirm: () => void runPluginAction(pendingUninstall, onUninstall).then((completed) => {
@@ -1154,7 +1194,7 @@ function Marketplace({ plugins, capabilities, categories, total, page, hasNext,
1154
1194
  const [installing, setInstalling] = useState();
1155
1195
  const [installError, setInstallError] = useState("");
1156
1196
  const [installNotice, setInstallNotice] = useState("");
1157
- const categoryTabs = marketplaceCategoryTabs(categories);
1197
+ const categoryTabs = useMemo(() => marketplaceCategoryTabs(categories), [categories]);
1158
1198
  const install = async (plugin) => {
1159
1199
  setInstallError("");
1160
1200
  setInstallNotice("");
@@ -1174,14 +1214,11 @@ function Marketplace({ plugins, capabilities, categories, total, page, hasNext,
1174
1214
  return (_jsx("section", { className: "marketplace-page flex min-w-0 flex-1 flex-col", children: _jsxs("div", { className: "flex min-h-0 flex-1 flex-col", children: [_jsxs("div", { className: "subnav", children: [_jsxs("div", { "aria-label": "\u63D2\u4EF6\u76EE\u5F55", className: "segmented", children: [_jsx("button", { "aria-pressed": "false", onClick: onBack, type: "button", children: "\u5DF2\u5B89\u88C5" }), _jsx("button", { "aria-pressed": "true", className: "active", type: "button", children: "\u63D2\u4EF6\u5E02\u573A" })] }), _jsx("span", { children: "\u5DF2\u5BA1\u6838\u63D2\u4EF6\u76EE\u5F55" }), _jsx("a", { href: "#", onClick: (event) => {
1175
1215
  event.preventDefault();
1176
1216
  onToml();
1177
- }, children: "\u67E5\u770B\u8FD0\u884C\u914D\u7F6E" })] }), _jsxs("div", { className: "marketplace-toolbar", children: [_jsx("input", { className: "marketplace-search", "aria-label": "\u641C\u7D22\u63D2\u4EF6", onChange: (event) => onQueryChange(event.target.value), placeholder: "\u641C\u7D22\u540D\u79F0\u3001\u5305\u540D\u3001\u80FD\u529B\u2026", value: query }), _jsxs("select", { className: "marketplace-filter", "aria-label": "\u6309\u80FD\u529B\u7B5B\u9009", onChange: (event) => onCapabilityChange(event.target.value), value: capabilityFilter, children: [_jsx("option", { value: "", children: "\u5168\u90E8\u80FD\u529B" }), capabilities.map((item) => (_jsx("option", { value: item, children: item }, item)))] }), _jsxs("span", { className: "marketplace-count", children: [total, " \u4E2A\u5DF2\u5BA1\u6838\u6761\u76EE \u00B7 \u63A8\u8350\u6392\u5E8F"] })] }), _jsx("nav", { "aria-label": "\u63D2\u4EF6\u5206\u7C7B", className: "marketplace-categories", children: categoryTabs.map((category) => {
1178
- const active = categoryFilter === category.id;
1179
- return (_jsxs("button", { "aria-pressed": active, className: `marketplace-category ${active ? "active" : ""}`, onClick: () => onCategoryChange(category.id), type: "button", children: [_jsx("span", { children: category.label }), _jsx("span", { className: active ? "font-mono text-[10px] text-[#3565c5]" : "font-mono text-[10px] text-[#687381]", children: category.count })] }, category.id || "all"));
1180
- }) }), _jsxs("div", { className: "marketplace-scroll", children: [_jsxs("div", { className: "marketplace-grid", children: [plugins.map((plugin) => (_jsxs("article", { className: "catalog-card marketplace-card", children: [_jsxs("header", { className: "marketplace-card-head", children: [_jsx("div", { className: "marketplace-card-mark", children: "\u25C8" }), _jsx("div", { className: "min-w-0 flex-1", children: _jsxs("div", { className: "flex flex-wrap items-center gap-1.5", children: [_jsx("strong", { className: "marketplace-title", children: plugin.name }), _jsx("span", { className: plugin.status === "verified"
1217
+ }, children: "\u67E5\u770B\u8FD0\u884C\u914D\u7F6E" })] }), _jsxs("div", { className: "marketplace-toolbar", children: [_jsx("input", { className: "marketplace-search", "aria-label": "\u641C\u7D22\u63D2\u4EF6", onChange: (event) => onQueryChange(event.target.value), placeholder: "\u641C\u7D22\u540D\u79F0\u3001\u5305\u540D\u3001\u80FD\u529B\u2026", value: query }), _jsxs("select", { className: "marketplace-filter", "aria-label": "\u6309\u80FD\u529B\u7B5B\u9009", onChange: (event) => onCapabilityChange(event.target.value), value: capabilityFilter, children: [_jsx("option", { value: "", children: "\u5168\u90E8\u80FD\u529B" }), capabilities.map((item) => (_jsx("option", { value: item, children: item }, item)))] }), _jsxs("span", { className: "marketplace-count", children: [total, " \u4E2A\u5DF2\u5BA1\u6838\u6761\u76EE \u00B7 \u63A8\u8350\u6392\u5E8F"] })] }), _jsx(PluginCategoryNav, { activeCategory: categoryFilter, categories: categoryTabs, label: "\u63D2\u4EF6\u5206\u7C7B", onChange: onCategoryChange }), _jsxs("div", { className: "marketplace-scroll", children: [_jsxs("div", { className: "marketplace-grid", children: [plugins.map((plugin) => (_jsxs("article", { className: "catalog-card marketplace-card", children: [_jsxs("header", { className: "marketplace-card-head", children: [_jsx("div", { className: "marketplace-card-mark", children: "\u25C8" }), _jsx("div", { className: "min-w-0 flex-1", children: _jsxs("div", { className: "flex flex-wrap items-center gap-1.5", children: [_jsx("strong", { className: "marketplace-title", children: plugin.name }), _jsx("span", { className: plugin.status === "verified"
1181
1218
  ? "rounded bg-[#e6faed] px-1.5 py-px font-mono text-[10px] text-[#14733f]"
1182
1219
  : "rounded bg-[#fff5e7] px-1.5 py-px font-mono text-[10px] text-[#9a6700]", children: plugin.status === "verified" ? "已验证" : "实验性" }), _jsx("span", { className: plugin.source === "official"
1183
1220
  ? "rounded bg-[#e4edfd] px-1.5 py-px font-mono text-[10px] text-[#3565c5]"
1184
- : "rounded bg-[#fef5e7] px-1.5 py-px font-mono text-[10px] text-[#9a6700]", children: plugin.source === "official" ? "官方" : "社区" }), _jsx("span", { className: "rounded bg-[#f2edff] px-1.5 py-px text-[10px] text-[#6d4bc3]", children: plugin.category.label })] }) })] }), _jsxs("div", { className: "marketplace-card-body", children: [_jsxs("code", { className: "marketplace-package", children: [displayPluginName(plugin.packageName), " \u00B7 v", plugin.version] }), _jsx("p", { className: "marketplace-description", children: plugin.description }), _jsxs("div", { className: "marketplace-tags", children: [plugin.capabilities.map((item) => (_jsx("span", { className: "rounded bg-[#f1f4f9] px-1.5 py-px font-mono text-[10px] text-[#61666b]", children: item }, item))), plugin.hooks.map((item) => (_jsxs("span", { className: "rounded bg-[#f1f4f9] px-1.5 py-px font-mono text-[10px] text-[#61666b]", children: ["hook:", item] }, `hook:${item}`)))] }), plugin.statistics && (_jsx("div", { className: "mt-3 grid grid-cols-3 divide-x divide-[#e3e7ee] rounded-md border border-[#e3e7ee] bg-[#f8f9fb]", children: marketplaceStatisticItems(plugin.statistics).map((item) => (_jsxs("span", { className: "min-w-0 px-2 py-1.5", title: `${item.label} ${item.value}`, children: [_jsx("small", { className: "block truncate text-[9px] text-[#687381]", children: item.label }), _jsx("strong", { className: "mt-0.5 block truncate font-mono text-[10px] font-medium text-[#3b424b]", children: item.value })] }, item.label))) }))] }), _jsxs("footer", { className: "marketplace-card-footer", children: [_jsxs("span", { children: [plugin.author, " \u00B7 ", plugin.license] }), _jsx("a", { href: marketplaceDetailPath(plugin.id), onClick: (event) => {
1221
+ : "rounded bg-[#fef5e7] px-1.5 py-px font-mono text-[10px] text-[#9a6700]", children: plugin.source === "official" ? "官方" : "社区" }), _jsx("span", { className: "rounded bg-[#f2edff] px-1.5 py-px text-[10px] text-[#6d4bc3]", children: plugin.category.label })] }) })] }), _jsxs("div", { className: "marketplace-card-body", children: [_jsxs("code", { className: "marketplace-package", children: [displayPluginName(plugin.packageName), " \u00B7 v", plugin.version] }), _jsx("p", { className: "marketplace-description", children: plugin.description }), _jsxs("div", { className: "marketplace-tags", children: [plugin.capabilities.map((item) => (_jsx("span", { className: "rounded bg-[#f1f4f9] px-1.5 py-px font-mono text-[10px] text-[#61666b]", children: item }, item))), plugin.hooks.map((item) => (_jsxs("span", { className: "rounded bg-[#f1f4f9] px-1.5 py-px font-mono text-[10px] text-[#61666b]", children: ["hook:", item] }, `hook:${item}`)))] }), plugin.statistics && (_jsx("div", { className: "marketplace-statistics mt-3 grid grid-cols-3 divide-x divide-[#e3e7ee] rounded-md border border-[#e3e7ee] bg-[#f8f9fb]", children: marketplaceStatisticItems(plugin.statistics).map((item) => (_jsxs("span", { className: "min-w-0 px-2 py-1.5", title: `${item.label} ${item.value}`, children: [_jsx("small", { className: "block truncate text-[9px] text-[#687381]", children: item.label }), _jsx("strong", { className: "mt-0.5 block truncate font-mono text-[10px] font-medium text-[#3b424b]", children: item.value })] }, item.label))) }))] }), _jsxs("footer", { className: "marketplace-card-footer", children: [_jsxs("span", { children: [plugin.author, " \u00B7 ", plugin.license] }), _jsx("a", { href: marketplaceDetailPath(plugin.id), onClick: (event) => {
1185
1222
  event.preventDefault();
1186
1223
  onOpenDetail(plugin);
1187
1224
  }, children: "\u67E5\u770B\u8BE6\u60C5 \u2192" }), _jsx("a", { href: plugin.repository, target: "_blank", rel: "noreferrer", children: "\u67E5\u770B\u6E90\u7801 \u2197" }), _jsx("button", { disabled: installedPackages.has(plugin.packageName) || installing !== undefined, onClick: () => void install(plugin), type: "button", children: installedPackages.has(plugin.packageName) ? "已安装" : installing === plugin.id ? "安装中…" : "安装" })] })] }, plugin.id))), !plugins.length && _jsx("div", { className: "empty-state", children: "\u6CA1\u6709\u5339\u914D\u7684\u63D2\u4EF6\u3002" })] }), installNotice && _jsx("p", { className: "mt-2 text-[11px] text-[#3565c5]", children: installNotice }), installError && _jsxs("p", { className: "mt-2 text-[11px] text-[#b42318]", children: ["\u5B89\u88C5\u5931\u8D25\uFF1A", installError] }), _jsxs("div", { className: "marketplace-pagination", children: [_jsx("button", { className: "marketplace-pagination-button", disabled: page === 0, onClick: () => onPageChange(page - 1), type: "button", children: "\u4E0A\u4E00\u9875" }), _jsxs("span", { children: ["\u7B2C ", page + 1, " \u9875"] }), _jsx("button", { className: "marketplace-pagination-button", disabled: !hasNext, onClick: () => onPageChange(page + 1), type: "button", children: "\u4E0B\u4E00\u9875" })] }), _jsxs("div", { className: "marketplace-contribute mt-3 flex items-center gap-2.5 rounded-[10px] border border-dashed border-[#b8ccf5] bg-[#f8f9ff] p-2.5 text-[11.5px] text-[#687381]", children: [_jsx("strong", { className: "text-[12px] text-[#0f1115]", children: "\u4F60\u6709\u4E00\u4E2A Pi Harness \u63D2\u4EF6\uFF1F" }), _jsx("span", { children: "\u5728 entries \u76EE\u5F55\u65B0\u589E\u4E00\u4E2A\u5143\u6570\u636E\u6587\u4EF6\uFF0C\u9644\u6D4B\u8BD5\u548C README \u540E\u63D0\u4EA4 PR\uFF1B\u5BA1\u6838\u901A\u8FC7\u540E\u4F1A\u51FA\u73B0\u5728\u8FD9\u91CC\u3002" }), _jsx("a", { className: "ml-auto flex-none text-[#3565c5]", href: "https://github.com/pi-harness/pi-harness/blob/main/docs/plugin-marketplace.md", target: "_blank", rel: "noreferrer", children: "\u67E5\u770B\u8D21\u732E\u89C4\u8303 \u2197" })] })] })] }) }));
@@ -1349,10 +1386,10 @@ function PromptCompletionPopover({ kind, commands, files, query, activeIndex, on
1349
1386
  const items = kind === "command"
1350
1387
  ? filterCommands(commands, query).slice(0, 12)
1351
1388
  : files.filter((file) => `${file.path} ${file.label} ${file.status}`.toLowerCase().includes(query.trim().toLowerCase())).slice(0, 12);
1352
- return (_jsxs("div", { "aria-label": kind === "command" ? "命令补全" : "文件补全", className: "prompt-completion", role: "listbox", children: [_jsx("small", { children: kind === "command" ? "命令" : "文件" }), items.length ? (items.slice(0, 12).map((item, index) => {
1389
+ return (_jsxs("div", { "aria-label": kind === "command" ? "命令补全" : "文件补全", className: "prompt-completion", id: "prompt-completion-list", role: "listbox", children: [_jsx("small", { children: kind === "command" ? "命令" : "文件" }), items.length ? (items.slice(0, 12).map((item, index) => {
1353
1390
  const label = kind === "command" ? `/${item.invocationName}` : `@${item.path}`;
1354
1391
  const detail = kind === "command" ? (item.description ?? item.source ?? "由当前运行时注册") : item.status;
1355
- return (_jsxs("button", { "aria-selected": index === activeIndex, onClick: () => onUse(label), onMouseEnter: () => onActiveIndexChange(index), role: "option", type: "button", children: [_jsx("strong", { children: label }), _jsx("span", { children: detail })] }, `${kind}:${label}`));
1392
+ return (_jsxs("button", { "aria-selected": index === activeIndex, id: `prompt-completion-option-${index}`, onClick: () => onUse(label), onMouseEnter: () => onActiveIndexChange(index), role: "option", type: "button", children: [_jsx("strong", { children: label }), _jsx("span", { children: detail })] }, `${kind}:${label}`));
1356
1393
  })) : (_jsx("span", { className: "prompt-completion-empty", children: "\u6CA1\u6709\u5339\u914D\u9879" }))] }));
1357
1394
  }
1358
1395
  function GlobalSearch({ commands, sessions, files, onClose, onUse, onOpenSession, onOpenFile, }) {
@@ -1370,6 +1407,8 @@ function GlobalSearch({ commands, sessions, files, onClose, onUse, onOpenSession
1370
1407
  .map((session) => ({ kind: "session", session })),
1371
1408
  ...files.filter((file) => matches(`${file.path} ${file.label} ${file.status}`)).map((file) => ({ kind: "file", file })),
1372
1409
  ];
1410
+ const selectedIndex = items.length ? Math.min(activeIndex, items.length - 1) : -1;
1411
+ const activeItemId = selectedIndex >= 0 ? `global-search-option-${selectedIndex}` : undefined;
1373
1412
  useEffect(() => setActiveIndex(0), [query]);
1374
1413
  const execute = (item) => {
1375
1414
  if (!item)
@@ -1385,7 +1424,7 @@ function GlobalSearch({ commands, sessions, files, onClose, onUse, onOpenSession
1385
1424
  return (_jsx("div", { className: "global-search", onClick: (event) => {
1386
1425
  if (event.target === event.currentTarget)
1387
1426
  onClose();
1388
- }, children: _jsxs("div", { "aria-label": "\u5168\u5C40\u641C\u7D22", "aria-modal": "true", className: "global-search-dialog", ref: dialogRef, role: "dialog", tabIndex: -1, children: [_jsxs("div", { className: "global-search-heading", children: [_jsx("strong", { children: "\u5168\u5C40\u641C\u7D22" }), _jsx("small", { children: "\u547D\u4EE4 \u00B7 \u4F1A\u8BDD \u00B7 \u6587\u4EF6" })] }), _jsx("input", { "aria-label": "\u5168\u5C40\u641C\u7D22", "data-dialog-initial-focus": true, onChange: (event) => setQuery(event.target.value), onKeyDown: (event) => {
1427
+ }, children: _jsxs("div", { "aria-label": "\u5168\u5C40\u641C\u7D22", "aria-modal": "true", className: "global-search-dialog", ref: dialogRef, role: "dialog", tabIndex: -1, children: [_jsxs("div", { className: "global-search-heading", children: [_jsx("strong", { children: "\u5168\u5C40\u641C\u7D22" }), _jsx("small", { children: "\u547D\u4EE4 \u00B7 \u4F1A\u8BDD \u00B7 \u6587\u4EF6" }), _jsx("button", { "aria-label": "\u5173\u95ED\u5168\u5C40\u641C\u7D22", onClick: onClose, type: "button", children: "\u00D7" })] }), _jsx("input", { "aria-activedescendant": activeItemId, "aria-autocomplete": "list", "aria-controls": "global-search-results", "aria-expanded": "true", "aria-label": "\u5168\u5C40\u641C\u7D22", "data-dialog-initial-focus": true, onChange: (event) => setQuery(event.target.value), onKeyDown: (event) => {
1389
1428
  if (event.key === "Escape") {
1390
1429
  event.preventDefault();
1391
1430
  onClose();
@@ -1400,14 +1439,14 @@ function GlobalSearch({ commands, sessions, files, onClose, onUse, onOpenSession
1400
1439
  }
1401
1440
  else if (event.key === "Enter") {
1402
1441
  event.preventDefault();
1403
- execute(items[activeIndex]);
1442
+ execute(items[selectedIndex]);
1404
1443
  }
1405
- }, placeholder: "\u641C\u7D22\u547D\u4EE4\u3001\u4F1A\u8BDD\u6216\u6587\u4EF6", value: query }), _jsx("div", { className: "global-search-results", role: "listbox", children: items.length ? (["command", "session", "file"].map((kind) => {
1444
+ }, placeholder: "\u641C\u7D22\u547D\u4EE4\u3001\u4F1A\u8BDD\u6216\u6587\u4EF6", role: "combobox", value: query }), _jsx("div", { className: "global-search-results", id: "global-search-results", role: "listbox", children: items.length ? (["command", "session", "file"].map((kind) => {
1406
1445
  const group = items.filter((item) => item.kind === kind);
1407
1446
  if (!group.length)
1408
1447
  return null;
1409
1448
  const label = kind === "command" ? "命令" : kind === "session" ? "会话" : "文件";
1410
- return (_jsxs("section", { className: "global-search-group", children: [_jsx("small", { children: label }), group.map((item) => {
1449
+ return (_jsxs("section", { "aria-label": label, className: "global-search-group", role: "group", children: [_jsx("small", { "aria-hidden": "true", children: label }), group.map((item) => {
1411
1450
  const index = items.indexOf(item);
1412
1451
  const title = item.kind === "command"
1413
1452
  ? `/${item.command.invocationName}`
@@ -1419,7 +1458,7 @@ function GlobalSearch({ commands, sessions, files, onClose, onUse, onOpenSession
1419
1458
  : item.kind === "session"
1420
1459
  ? `${value(item.session.messageCount, "0")} 条消息`
1421
1460
  : `${item.file.path} · ${item.file.status}`;
1422
- return (_jsxs("button", { "aria-selected": index === activeIndex, onClick: () => execute(item), onMouseEnter: () => setActiveIndex(index), role: "option", type: "button", children: [_jsx("strong", { children: title }), _jsx("span", { children: detail })] }, `${item.kind}:${title}:${index}`));
1461
+ return (_jsxs("button", { "aria-selected": index === selectedIndex, id: `global-search-option-${index}`, onClick: () => execute(item), onMouseEnter: () => setActiveIndex(index), role: "option", type: "button", children: [_jsx("strong", { children: title }), _jsx("span", { children: detail })] }, `${item.kind}:${title}:${index}`));
1423
1462
  })] }, kind));
1424
1463
  })) : (_jsx("div", { className: "empty-state", children: "\u6CA1\u6709\u5339\u914D\u7684\u547D\u4EE4\u3001\u4F1A\u8BDD\u6216\u6587\u4EF6\u3002" })) })] }) }));
1425
1464
  }
@@ -1531,6 +1570,7 @@ export function ControlRoomView({ api = createClientApi(), appVersion }) {
1531
1570
  .slice(0, 12);
1532
1571
  }, [data.commands, data.files, promptCompletion]);
1533
1572
  const promptCompletionOpen = Boolean(promptCompletion && !promptCompletionSuppressed && promptCompletionItems.length);
1573
+ const promptCompletionActiveIndex = Math.min(promptCompletionIndex, Math.max(promptCompletionItems.length - 1, 0));
1534
1574
  const installedPackages = useMemo(() => new Set(data.plugins.filter((plugin) => plugin.removable).map((plugin) => plugin.name)), [data.plugins]);
1535
1575
  const workspaceReady = !workspaceChooserOpen && Boolean(selectedWorkspacePath || data.status?.cwd || data.workspaces.find((workspace) => workspace.current)?.path || data.session);
1536
1576
  const installedPluginCount = useMemo(() => {
@@ -1937,6 +1977,26 @@ export function ControlRoomView({ api = createClientApi(), appVersion }) {
1937
1977
  setAnnotationSelection("");
1938
1978
  setAnnotationNote("");
1939
1979
  };
1980
+ const openCommandCompletion = () => {
1981
+ const input = promptInputRef.current;
1982
+ const caret = input?.selectionStart ?? draft.length;
1983
+ const existing = getPromptCompletion(draft, caret);
1984
+ let nextCaret = caret;
1985
+ if (existing?.kind !== "command") {
1986
+ const separator = caret > 0 && !/\s/.test(draft[caret - 1] ?? "") ? " " : "";
1987
+ const insertion = `${separator}/`;
1988
+ const nextDraft = `${draft.slice(0, caret)}${insertion}${draft.slice(caret)}`;
1989
+ nextCaret = caret + insertion.length;
1990
+ setDraft(nextDraft);
1991
+ }
1992
+ setPromptCaret(nextCaret);
1993
+ setPromptCompletionSuppressed(false);
1994
+ setPromptCompletionIndex(0);
1995
+ window.requestAnimationFrame(() => {
1996
+ input?.focus();
1997
+ input?.setSelectionRange(nextCaret, nextCaret);
1998
+ });
1999
+ };
1940
2000
  const openSession = (session) => {
1941
2001
  const path = typeof session.path === "string" ? session.path : "";
1942
2002
  if (!path)
@@ -1987,6 +2047,7 @@ export function ControlRoomView({ api = createClientApi(), appVersion }) {
1987
2047
  });
1988
2048
  };
1989
2049
  const openSessionMenu = (path, name) => {
2050
+ setSessionToolsOpen(false);
1990
2051
  setSelectedSessionPath(path);
1991
2052
  setSessionNameDraft(name ?? "");
1992
2053
  setSessionMenuPath(path);
@@ -2095,7 +2156,7 @@ export function ControlRoomView({ api = createClientApi(), appVersion }) {
2095
2156
  })) : (_jsx(Workspace, { status: data.status, workspaces: data.workspaces, onCreate: (workspace) => void createNewSession(workspace), onStarter: setDraft, onToml: () => {
2096
2157
  setDetails(undefined);
2097
2158
  setSettings("toml");
2098
- } })), streamingAssistant && data.status?.status === "running" && (_jsxs("article", { className: "turn text streaming-turn", "aria-live": "polite", children: [streamingAssistant.thinking && (_jsxs("details", { className: "reasoning message-reasoning", children: [_jsx("summary", { className: "reasoning-head", children: "\u601D\u8003\u4E2D\u2026" }), _jsx("div", { className: "reasoning-body", children: _jsx(MarkdownMessage, { text: streamingAssistant.thinking }) })] })), !streamingAssistant.thinking && !streamingAssistant.text && _jsx("div", { className: "streaming-placeholder", children: "\u6B63\u5728\u751F\u6210\u2026" }), streamingAssistant.text && _jsx(MarkdownMessage, { onMouseUp: captureAnnotationSelection, text: streamingAssistant.text })] })), pendingPrompt && !data.session?.messages.some((message) => message.role === "user" && messageText(message) === pendingPrompt) && (_jsx("article", { className: "turn user pending-turn", children: _jsx(UserMessageBubble, { text: pendingPrompt }) }))] }), _jsxs("div", { className: "composer-wrap", children: [_jsxs("div", { className: "context-line", children: [_jsx("span", { className: "context-label", children: "\u5B9E\u65F6\u4E0A\u4E0B\u6587" }), _jsxs("div", { className: "context-metrics", children: [_jsxs("span", { children: [_jsx("b", { children: data.status?.messages ?? 0 }), " \u6761\u6D88\u606F"] }), _jsxs("span", { children: [_jsx("b", { children: data.status?.events ?? events.length }), " \u4E2A\u4E8B\u4EF6"] }), _jsx("span", { children: _jsx("b", { children: value(data.status?.model) }) })] }), _jsx("button", { "aria-expanded": contextExpanded, onClick: () => setContextExpanded((current) => !current), type: "button", children: contextExpanded ? "收起" : "展开" })] }), contextExpanded && (_jsxs("div", { className: "context-breakdown", role: "status", children: [_jsxs("span", { children: ["\u6D88\u606F ", _jsx("b", { children: data.status?.messages ?? 0 })] }), _jsxs("span", { children: ["\u8FD0\u884C\u65F6\u4E8B\u4EF6 ", _jsx("b", { children: data.status?.events ?? events.length })] }), _jsxs("span", { children: ["\u6A21\u578B ", _jsx("b", { children: value(data.status?.model) })] })] })), _jsxs("div", { className: "composer-stack", children: [promptError && _jsx(PromptError, { message: promptError }), annotationSelection ? (_jsxs("div", { "aria-label": "\u6DFB\u52A0\u6279\u6CE8", className: "rounded-lg border border-[#cdddf8] bg-[#f6f8ff] px-3 py-2", children: [_jsxs("div", { className: "flex items-start gap-2", children: [_jsx("span", { className: "mt-0.5 shrink-0 rounded bg-[#dce9ff] px-1.5 py-0.5 text-[10px] text-[#315fb8]", children: "\u9009\u4E2D\u7247\u6BB5" }), _jsx("p", { className: "max-h-16 flex-1 overflow-auto whitespace-pre-wrap text-[11px] text-[#30343b]", children: annotationSelection }), _jsx("button", { "aria-label": "\u53D6\u6D88\u6279\u6CE8", className: "text-[12px] text-[#687381]", onClick: () => setAnnotationSelection(""), type: "button", children: "\u00D7" })] }), _jsxs("div", { className: "mt-2 flex items-center gap-2", children: [_jsx("input", { "aria-label": "\u6279\u6CE8\u5907\u6CE8", className: "min-w-0 flex-1 rounded-md border border-[#dce5f5] bg-white px-2 py-1.5 text-[11px] outline-none", onChange: (event) => setAnnotationNote(event.target.value), placeholder: "\u5907\u6CE8\uFF08\u53EF\u9009\uFF09", value: annotationNote }), _jsx("button", { className: "rounded-md bg-[#3565c5] px-3 py-1.5 text-[11px] font-medium text-white", onClick: addAnnotation, type: "button", children: "\u52A0\u5165\u6279\u6CE8" })] })] })) : null, annotations.length > 0 ? (_jsxs("div", { className: "flex items-center gap-2 overflow-x-auto text-[10px]", children: [_jsxs("span", { className: "shrink-0 rounded-md bg-[#edf3fe] px-2 py-1 font-medium text-[#315fb8]", children: ["\u6279\u6CE8 \u00D7", annotations.length] }), annotations.map((annotation) => (_jsxs("button", { className: "max-w-48 shrink-0 truncate rounded-md border border-[#dce5f5] bg-white px-2 py-1 text-left text-[#65707b]", onClick: () => setAnnotations((current) => current.filter((item) => item.id !== annotation.id)), title: "\u70B9\u51FB\u79FB\u9664\u6279\u6CE8", type: "button", children: ["#", annotation.id, " ", annotation.quote] }, annotation.id))), _jsx("button", { className: "shrink-0 text-[#687381]", onClick: () => setAnnotations([]), type: "button", children: "\u6E05\u7A7A" })] })) : null, _jsxs("form", { className: "composer", onSubmit: submit, children: [_jsx("textarea", { "aria-label": "Prompt", onChange: (event) => {
2159
+ } })), streamingAssistant && data.status?.status === "running" && (_jsxs("article", { className: "turn text streaming-turn", "aria-live": "polite", children: [streamingAssistant.thinking && (_jsxs("details", { className: "reasoning message-reasoning", children: [_jsx("summary", { className: "reasoning-head", children: "\u601D\u8003\u4E2D\u2026" }), _jsx("div", { className: "reasoning-body", children: _jsx(MarkdownMessage, { text: streamingAssistant.thinking }) })] })), !streamingAssistant.thinking && !streamingAssistant.text && _jsx("div", { className: "streaming-placeholder", children: "\u6B63\u5728\u751F\u6210\u2026" }), streamingAssistant.text && _jsx(MarkdownMessage, { onMouseUp: captureAnnotationSelection, text: streamingAssistant.text })] })), pendingPrompt && !data.session?.messages.some((message) => message.role === "user" && messageText(message) === pendingPrompt) && (_jsx("article", { className: "turn user pending-turn", children: _jsx(UserMessageBubble, { text: pendingPrompt }) }))] }), _jsxs("div", { className: "composer-wrap", children: [_jsxs("div", { className: "context-line", children: [_jsx("span", { className: "context-label", children: "\u5B9E\u65F6\u4E0A\u4E0B\u6587" }), _jsxs("div", { className: "context-metrics", children: [_jsxs("span", { children: [_jsx("b", { children: data.status?.messages ?? 0 }), " \u6761\u6D88\u606F"] }), _jsxs("span", { children: [_jsx("b", { children: data.status?.events ?? events.length }), " \u4E2A\u4E8B\u4EF6"] }), _jsx("span", { children: _jsx("b", { children: value(data.status?.model) }) })] }), _jsx("button", { "aria-expanded": contextExpanded, onClick: () => setContextExpanded((current) => !current), type: "button", children: contextExpanded ? "收起" : "展开" })] }), contextExpanded && (_jsxs("div", { className: "context-breakdown", role: "status", children: [_jsxs("span", { children: ["\u6D88\u606F ", _jsx("b", { children: data.status?.messages ?? 0 })] }), _jsxs("span", { children: ["\u8FD0\u884C\u65F6\u4E8B\u4EF6 ", _jsx("b", { children: data.status?.events ?? events.length })] }), _jsxs("span", { children: ["\u6A21\u578B ", _jsx("b", { children: value(data.status?.model) })] })] })), _jsxs("div", { className: "composer-stack", children: [promptError && _jsx(PromptError, { message: promptError }), annotationSelection ? (_jsxs("div", { "aria-label": "\u6DFB\u52A0\u6279\u6CE8", className: "rounded-lg border border-[#cdddf8] bg-[#f6f8ff] px-3 py-2", children: [_jsxs("div", { className: "flex items-start gap-2", children: [_jsx("span", { className: "mt-0.5 shrink-0 rounded bg-[#dce9ff] px-1.5 py-0.5 text-[10px] text-[#315fb8]", children: "\u9009\u4E2D\u7247\u6BB5" }), _jsx("p", { className: "max-h-16 flex-1 overflow-auto whitespace-pre-wrap text-[11px] text-[#30343b]", children: annotationSelection }), _jsx("button", { "aria-label": "\u53D6\u6D88\u6279\u6CE8", className: "text-[12px] text-[#687381]", onClick: () => setAnnotationSelection(""), type: "button", children: "\u00D7" })] }), _jsxs("div", { className: "mt-2 flex items-center gap-2", children: [_jsx("input", { "aria-label": "\u6279\u6CE8\u5907\u6CE8", className: "min-w-0 flex-1 rounded-md border border-[#dce5f5] bg-white px-2 py-1.5 text-[11px] outline-none", onChange: (event) => setAnnotationNote(event.target.value), placeholder: "\u5907\u6CE8\uFF08\u53EF\u9009\uFF09", value: annotationNote }), _jsx("button", { className: "rounded-md bg-[#3565c5] px-3 py-1.5 text-[11px] font-medium text-white", onClick: addAnnotation, type: "button", children: "\u52A0\u5165\u6279\u6CE8" })] })] })) : null, annotations.length > 0 ? (_jsxs("div", { className: "flex items-center gap-2 overflow-x-auto text-[10px]", children: [_jsxs("span", { className: "shrink-0 rounded-md bg-[#edf3fe] px-2 py-1 font-medium text-[#315fb8]", children: ["\u6279\u6CE8 \u00D7", annotations.length] }), annotations.map((annotation) => (_jsxs("button", { className: "max-w-48 shrink-0 truncate rounded-md border border-[#dce5f5] bg-white px-2 py-1 text-left text-[#65707b]", onClick: () => setAnnotations((current) => current.filter((item) => item.id !== annotation.id)), title: "\u70B9\u51FB\u79FB\u9664\u6279\u6CE8", type: "button", children: ["#", annotation.id, " ", annotation.quote] }, annotation.id))), _jsx("button", { className: "shrink-0 text-[#687381]", onClick: () => setAnnotations([]), type: "button", children: "\u6E05\u7A7A" })] })) : null, _jsxs("form", { className: "composer", onSubmit: submit, children: [_jsx("textarea", { "aria-activedescendant": promptCompletionOpen ? `prompt-completion-option-${promptCompletionActiveIndex}` : undefined, "aria-autocomplete": "list", "aria-controls": promptCompletionOpen ? "prompt-completion-list" : undefined, "aria-expanded": promptCompletionOpen, "aria-haspopup": "listbox", "aria-label": "Prompt", onChange: (event) => {
2099
2160
  setDraft(event.target.value);
2100
2161
  setPromptCaret(event.currentTarget.selectionStart ?? event.target.value.length);
2101
2162
  setPromptCompletionSuppressed(false);
@@ -2119,7 +2180,7 @@ export function ControlRoomView({ api = createClientApi(), appVersion }) {
2119
2180
  }
2120
2181
  if (popupOpen && (event.key === "Enter" || event.key === "Tab")) {
2121
2182
  event.preventDefault();
2122
- const item = items[promptCompletionIndex];
2183
+ const item = items[Math.min(promptCompletionIndex, items.length - 1)];
2123
2184
  if (item && completion) {
2124
2185
  const value = completion.kind === "command" ? `/${item.invocationName}` : `@${item.path}`;
2125
2186
  const replacement = replacePromptCompletion(event.currentTarget.value, completion, `${value} `);
@@ -2151,7 +2212,7 @@ export function ControlRoomView({ api = createClientApi(), appVersion }) {
2151
2212
  event.currentTarget.blur();
2152
2213
  setWorkspaceChooserOpen(true);
2153
2214
  }
2154
- }, placeholder: workspaceReady ? "描述要做的改动,⌘↵ 发送;@ 引用文件,/ 调用命令" : "先选择工作区,再描述要做的改动", readOnly: !workspaceReady, ref: promptInputRef, rows: 2, value: draft }), promptCompletionOpen && promptCompletion && (_jsx(PromptCompletionPopover, { activeIndex: promptCompletionIndex, commands: data.commands, files: data.files, kind: promptCompletion.kind, onActiveIndexChange: setPromptCompletionIndex, onUse: (value) => {
2215
+ }, placeholder: workspaceReady ? "描述要做的改动,⌘↵ 发送;@ 引用文件,/ 调用命令" : "先选择工作区,再描述要做的改动", readOnly: !workspaceReady, ref: promptInputRef, rows: 2, value: draft }), promptCompletionOpen && promptCompletion && (_jsx(PromptCompletionPopover, { activeIndex: promptCompletionActiveIndex, commands: data.commands, files: data.files, kind: promptCompletion.kind, onActiveIndexChange: setPromptCompletionIndex, onUse: (value) => {
2155
2216
  const replacement = replacePromptCompletion(draft, promptCompletion, `${value} `);
2156
2217
  setDraft(replacement.text);
2157
2218
  setPromptCaret(replacement.caret);
@@ -2162,10 +2223,23 @@ export function ControlRoomView({ api = createClientApi(), appVersion }) {
2162
2223
  input?.setSelectionRange(replacement.caret, replacement.caret);
2163
2224
  });
2164
2225
  }, query: promptCompletion.query })), _jsxs("div", { className: "composer-tools", children: [_jsx("select", { "aria-label": "\u6A21\u578B", disabled: !data.models.length || promptBusy, value: data.status?.model ?? "", onChange: (event) => {
2165
- const [provider, model] = event.target.value.split("/");
2166
- if (provider && model)
2167
- void api.selectModel(provider, model);
2168
- }, children: data.models.length ? (data.models.map((model) => (_jsx("option", { value: `${model.provider}/${model.id}`, children: model.name === model.id ? `${model.provider}/${model.id}` : `${model.name} (${model.provider})` }, `${model.provider}/${model.id}`)))) : (_jsx("option", { value: "", children: "\u6682\u65E0\u53EF\u7528\u6A21\u578B" })) }), _jsxs("button", { className: "tool-chip", onClick: () => setPermission((current) => !current), type: "button", children: ["\u25CF ", permission ? "改动前询问" : "自动允许"] }), _jsx("button", { className: "tool-chip", onClick: () => setCommandOpen(true), type: "button", children: "\uFF0F \u547D\u4EE4" }), _jsx("span", { className: "composer-hint", children: "\u2318\u21B5 \u53D1\u9001 \u00B7 \u2318K \u547D\u4EE4 \u00B7 \u2303C \u4E2D\u65AD" }), _jsx("button", { "aria-label": promptBusy ? "发送中" : "发送消息", className: "send-button", disabled: promptBusy || !draft.trim(), title: promptBusy ? "正在发送" : "发送消息(⌘↵)", type: "submit", children: promptBusy ? "…" : "↑" })] })] })] })] })] })) : view === "trajectory" ? (_jsx(Trajectory, { events: displayEvents, onSelect: setDetails })) : (_jsx(Files, { api: api, files: data.files, onDiff: (file) => void api.getFileDiff(file).then((diff) => setDetails({ type: "file_diff", path: diff.path, output: diff.diff })), onRefresh: () => void refresh() }));
2226
+ const [provider, ...modelParts] = event.target.value.split("/");
2227
+ const model = modelParts.join("/");
2228
+ if (!provider || !model)
2229
+ return;
2230
+ setPromptError("");
2231
+ void api
2232
+ .selectModel(provider, model)
2233
+ .then(refresh)
2234
+ .catch((cause) => setPromptError(cause instanceof Error ? cause.message : String(cause)));
2235
+ }, children: data.models.length ? (data.models.map((model) => (_jsx("option", { value: `${model.provider}/${model.id}`, children: model.name === model.id ? `${model.provider}/${model.id}` : `${model.name} (${model.provider})` }, `${model.provider}/${model.id}`)))) : (_jsx("option", { value: "", children: "\u6682\u65E0\u53EF\u7528\u6A21\u578B" })) }), _jsxs("button", { className: "tool-chip", onClick: () => setPermission((current) => !current), type: "button", children: ["\u25CF ", permission ? "改动前询问" : "自动允许"] }), _jsx("button", { className: "tool-chip", onClick: openCommandCompletion, type: "button", children: "\uFF0F \u547D\u4EE4" }), _jsx("span", { className: "composer-hint", children: "\u2318\u21B5 \u53D1\u9001 \u00B7 \u2318K \u547D\u4EE4 \u00B7 \u2303C \u4E2D\u65AD" }), _jsx("button", { "aria-label": promptBusy ? "发送中" : "发送消息", className: "send-button", disabled: promptBusy || !draft.trim(), title: promptBusy ? "正在发送" : "发送消息(⌘↵)", type: "submit", children: promptBusy ? "…" : "↑" })] })] })] })] })] })) : view === "trajectory" ? (_jsx(Trajectory, { events: displayEvents, onSelect: setDetails })) : (_jsx(Files, { api: api, files: data.files, onDiff: async (file) => {
2236
+ const diff = await api.getFileDiff(file);
2237
+ setDetails({
2238
+ type: "file_diff",
2239
+ path: diff.path,
2240
+ output: diff.diff || "没有可显示的差异(工作区可能已更新)。",
2241
+ });
2242
+ }, onRefresh: () => void refresh() }));
2169
2243
  const groups = sessionGroups(filteredSessions);
2170
2244
  const showCurrentSession = Boolean(data.session && !search && !filteredSessions.some((session) => session.sessionId === data.session?.sessionId));
2171
2245
  const visibleCommands = filterCommands(data.commands, commandQuery);
@@ -2226,9 +2300,14 @@ export function ControlRoomView({ api = createClientApi(), appVersion }) {
2226
2300
  setSelectedSessionPath(imported.sessionFile);
2227
2301
  });
2228
2302
  }, ref: importInputRef, type: "file" })] }), _jsxs("div", { className: "session-list-toolbar", children: [_jsxs("div", { className: "session-list-title", children: [_jsx("strong", { children: "\u4F1A\u8BDD" }), _jsx("span", { children: sessionTotal || data.sessions.length })] }), _jsxs("div", { className: "session-list-tools", children: [_jsx("button", { "aria-pressed": sessionSelectionMode, className: `session-tool-button ${sessionSelectionMode ? "active" : ""}`, onClick: () => {
2229
- setSessionSelectionMode((current) => !current);
2303
+ setSessionSelectionMode(!sessionSelectionMode);
2230
2304
  setSelectedSessionPaths(new Set());
2231
- }, type: "button", children: sessionSelectionMode ? "完成" : "选择" }), _jsxs("div", { className: "session-tools-wrap", children: [_jsx("button", { "aria-expanded": sessionToolsOpen, "aria-label": "\u4F1A\u8BDD\u5DE5\u5177", className: "session-tool-button icon", onClick: () => setSessionToolsOpen((current) => !current), type: "button", children: "\u22EF" }), sessionToolsOpen && (_jsxs("div", { className: "session-tools-popover", children: [_jsx("button", { onClick: () => window.location.reload(), type: "button", children: "\u5237\u65B0\u5217\u8868" }), _jsx("button", { onClick: () => importInputRef.current?.click(), type: "button", children: "\u5BFC\u5165\u4F1A\u8BDD" }), _jsx("button", { disabled: !activeSessionPath || sessionActionBusy, onClick: () => {
2305
+ setSessionToolsOpen(false);
2306
+ closeSessionMenu();
2307
+ }, type: "button", children: sessionSelectionMode ? "完成" : "选择" }), _jsxs("div", { className: "session-tools-wrap", children: [_jsx("button", { "aria-expanded": sessionToolsOpen, "aria-label": "\u4F1A\u8BDD\u5DE5\u5177", className: "session-tool-button icon", onClick: () => {
2308
+ closeSessionMenu();
2309
+ setSessionToolsOpen((current) => !current);
2310
+ }, type: "button", children: "\u22EF" }), sessionToolsOpen && (_jsxs("div", { className: "session-tools-popover", children: [_jsx("button", { onClick: () => window.location.reload(), type: "button", children: "\u5237\u65B0\u5217\u8868" }), _jsx("button", { onClick: () => importInputRef.current?.click(), type: "button", children: "\u5BFC\u5165\u4F1A\u8BDD" }), _jsx("button", { disabled: !activeSessionPath || sessionActionBusy, onClick: () => {
2232
2311
  if (!activeSessionPath)
2233
2312
  return;
2234
2313
  void sessionAction(async () => {
@@ -2293,7 +2372,12 @@ export function ControlRoomView({ api = createClientApi(), appVersion }) {
2293
2372
  } }))] }, index)))] }, label)))) : !showCurrentSession ? (_jsx("div", { className: "empty-state", children: "\u6682\u65E0\u5DF2\u4FDD\u5B58\u4F1A\u8BDD" })) : null, sessionTotal > 30 && (_jsxs("div", { className: "session-pagination", children: [_jsx("button", { disabled: sessionPage === 0, onClick: () => setSessionPage((page) => Math.max(0, page - 1)), type: "button", children: "\u4E0A\u4E00\u9875" }), _jsxs("span", { children: [sessionPage + 1, " / ", Math.max(1, Math.ceil(sessionTotal / 30))] }), _jsx("button", { disabled: !sessionHasNext, onClick: () => setSessionPage((page) => page + 1), type: "button", children: "\u4E0B\u4E00\u9875" })] })), betterSidebarData && (_jsxs("section", { "aria-label": "\u5DE5\u4F5C\u533A\u6982\u89C8", className: "mx-3 mt-3 rounded-lg border border-[#dce5f5] bg-[#f6f8ff] px-3 py-3", children: [_jsxs("div", { className: "flex items-center justify-between gap-2", children: [_jsx("strong", { className: "text-[11px] font-semibold text-[#253044]", children: "\u5DE5\u4F5C\u533A\u6982\u89C8" }), _jsx("span", { className: `rounded px-1.5 py-0.5 font-mono text-[9px] ${betterSidebarData.clean === true ? "bg-[#eaf8f0] text-[#14733f]" : "bg-[#fff0f0] text-[#b42318]"}`, children: betterSidebarData.clean === true ? "clean" : `${value(betterSidebarData.changedCount ?? 0)} 变更` })] }), _jsx("code", { className: "mt-2 block truncate text-[10px] text-[#315fb8]", children: value(betterSidebarData.cwd ?? "当前工作区") }), _jsx("p", { className: "mt-1 truncate font-mono text-[10px] text-[#65707b]", children: value(betterSidebarData.branch ?? "非 Git 工作区") }), Array.isArray(betterSidebarData.changedFiles) && betterSidebarData.changedFiles.length > 0 ? (_jsx("div", { className: "mt-2 grid gap-1 border-t border-[#dce5f5] pt-2", children: betterSidebarData.changedFiles.slice(0, 3).map((item, index) => {
2294
2373
  const file = item !== null && typeof item === "object" ? item : {};
2295
2374
  return (_jsxs("code", { className: "truncate text-[9px] text-[#65707b]", children: [value(file.status ?? "??"), " ", value(file.path ?? "未命名")] }, `${value(file.path ?? "file")}-${index}`));
2296
- }) })) : null] }))] }), _jsxs("footer", { className: "sidebar-footer", children: [_jsxs("div", { className: "runtime-cells", children: [_jsxs("span", { children: ["model ", _jsx("b", { children: value(data.status?.model) })] }), _jsxs("span", { children: ["msgs ", _jsx("b", { children: data.status?.messages ?? 0 })] }), _jsxs("span", { children: ["status ", _jsx("b", { children: value(data.status?.status, "connecting") })] })] }), _jsx("button", { "aria-label": "\u65B0\u5EFA\u4F1A\u8BDD", className: "sidebar-link compact-new-session", onClick: beginNewSession, title: "\u65B0\u5EFA\u4F1A\u8BDD", type: "button", children: "\uFF0B" }), _jsxs("button", { "aria-label": `插件,已安装 ${installedPluginCount} 个`, className: `sidebar-link ${page === "plugins" || page === "marketplace" ? "active" : ""}`, onClick: () => pushInstalledPluginRoute(undefined), title: `插件 · ${installedPluginCount} 个`, type: "button", children: ["\u25C8 ", _jsx("span", { children: "\u63D2\u4EF6" }), _jsx("b", { children: installedPluginCount })] }), _jsxs("button", { "aria-label": "\u8BBE\u7F6E", className: `sidebar-link ${settings ? "active" : ""}`, onClick: () => {
2375
+ }) })) : null] }))] }), _jsxs("footer", { className: "sidebar-footer", children: [_jsxs("div", { className: "runtime-cells", children: [_jsxs("span", { children: ["model ", _jsx("b", { children: value(data.status?.model) })] }), _jsxs("span", { children: ["msgs ", _jsx("b", { children: data.status?.messages ?? 0 })] }), _jsxs("span", { children: ["status ", _jsx("b", { children: value(data.status?.status, "connecting") })] })] }), _jsx("button", { "aria-label": "\u641C\u7D22\u4F1A\u8BDD\u3001\u6587\u4EF6\u548C\u547D\u4EE4", className: "sidebar-link compact-session-search", onClick: () => {
2376
+ setCommandOpen(false);
2377
+ setSessionToolsOpen(false);
2378
+ closeSessionMenu();
2379
+ setGlobalSearchOpen(true);
2380
+ }, title: "\u641C\u7D22\u4F1A\u8BDD\u3001\u6587\u4EF6\u548C\u547D\u4EE4", type: "button", children: "\u2315" }), _jsx("button", { "aria-label": "\u65B0\u5EFA\u4F1A\u8BDD", className: "sidebar-link compact-new-session", onClick: beginNewSession, title: "\u65B0\u5EFA\u4F1A\u8BDD", type: "button", children: "\uFF0B" }), _jsxs("button", { "aria-label": `插件,已安装 ${installedPluginCount} 个`, className: `sidebar-link ${page === "plugins" || page === "marketplace" ? "active" : ""}`, onClick: () => pushInstalledPluginRoute(undefined), title: `插件 · ${installedPluginCount} 个`, type: "button", children: ["\u25C8 ", _jsx("span", { children: "\u63D2\u4EF6" }), _jsx("b", { children: installedPluginCount })] }), _jsxs("button", { "aria-label": "\u8BBE\u7F6E", className: `sidebar-link ${settings ? "active" : ""}`, onClick: () => {
2297
2381
  setCommandOpen(false);
2298
2382
  setGlobalSearchOpen(false);
2299
2383
  setSessionMenuOpen(false);
@@ -2322,13 +2406,19 @@ export function ControlRoomView({ api = createClientApi(), appVersion }) {
2322
2406
  ? marketplacePluginId
2323
2407
  ? (marketplaceDetail?.name ?? marketplacePluginId)
2324
2408
  : "官方与社区 · 已审核目录"
2325
- : sessionSource(data.status, data.session) })] }), _jsx("div", { className: "header-spacer" }), data.status?.status === "running" && (_jsxs("div", { className: "run-indicator running", children: [_jsx("span", { className: "run-dot" }), _jsx("span", { children: "\u8FD0\u884C\u4E2D \u00B7 Pi agent" }), _jsx("button", { className: "stop-button", onClick: () => void api.abort().then(refresh), type: "button", children: "\u505C\u6B62" })] })), !settings && page === "session" && (_jsx("div", { "aria-label": "\u4F1A\u8BDD\u89C6\u56FE", className: "view-tabs", children: ["chat", "trajectory", "files"].map((item) => (_jsx("button", { "aria-pressed": view === item, className: `view-tab ${view === item ? "active" : ""}`, onClick: () => {
2409
+ : sessionSource(data.status, data.session) })] }), _jsx("div", { className: "header-spacer" }), data.status?.status === "running" && (_jsxs("div", { className: "run-indicator running", children: [_jsx("span", { className: "run-dot" }), _jsx("span", { children: "\u8FD0\u884C\u4E2D \u00B7 Pi agent" }), _jsx("button", { className: "stop-button", onClick: () => {
2410
+ setPromptError("");
2411
+ void api
2412
+ .abort()
2413
+ .then(refresh)
2414
+ .catch((cause) => setPromptError(cause instanceof Error ? cause.message : String(cause)));
2415
+ }, type: "button", children: "\u505C\u6B62" })] })), !settings && page === "session" && (_jsx("div", { "aria-label": "\u4F1A\u8BDD\u89C6\u56FE", className: "view-tabs", children: ["chat", "trajectory", "files"].map((item) => (_jsx("button", { "aria-pressed": view === item, className: `view-tab ${view === item ? "active" : ""}`, onClick: () => {
2326
2416
  setView(item);
2327
2417
  setDetails(undefined);
2328
2418
  }, type: "button", children: item === "chat" ? "对话" : item === "trajectory" ? "轨迹" : "产出" }, item))) })), !settings && page === "session" && _jsx("span", { "aria-hidden": "true", className: "header-divider" }), !settings && page === "session" && (_jsxs(_Fragment, { children: [_jsx("button", { className: "session-menu", onClick: () => {
2329
2419
  setSessionMenuPath(undefined);
2330
2420
  setSessionMenuOpen((current) => !current);
2331
- }, type: "button", "aria-label": "\u4F1A\u8BDD\u64CD\u4F5C", children: "\u22EF" }), _jsx("button", { "aria-pressed": details !== undefined, className: "details-toggle", onClick: () => setDetails(details ? undefined : {}), type: "button", children: "\u25E8 \u8BE6\u60C5" })] })), !settings && page === "session" && sessionMenuOpen && !sessionMenuPath && (_jsxs("div", { className: "session-menu-popover compact-session-menu", children: [_jsxs("button", { className: "session-action", disabled: !activeSessionPath || sessionActionBusy, onClick: () => {
2421
+ }, type: "button", "aria-label": "\u4F1A\u8BDD\u64CD\u4F5C", children: "\u22EF" }), _jsxs("button", { "aria-label": details !== undefined ? "关闭详情" : "打开详情", "aria-pressed": details !== undefined, className: "details-toggle", onClick: () => setDetails(details ? undefined : {}), type: "button", children: [_jsx("span", { "aria-hidden": "true", children: "\u25E8" }), " ", _jsx("span", { className: "details-toggle-label", children: "\u8BE6\u60C5" })] })] })), !settings && page === "session" && sessionMenuOpen && !sessionMenuPath && (_jsxs("div", { className: "session-menu-popover compact-session-menu", children: [_jsxs("button", { className: "session-action", disabled: !activeSessionPath || sessionActionBusy, onClick: () => {
2332
2422
  setSessionNameDraft(data.session?.sessionId?.slice(0, 12) ?? "");
2333
2423
  setSessionDialog("rename");
2334
2424
  setSessionMenuOpen(false);
@@ -2344,7 +2434,7 @@ export function ControlRoomView({ api = createClientApi(), appVersion }) {
2344
2434
  }, type: "button", children: [_jsx("strong", { children: "\u5F52\u6863\u4F1A\u8BDD" }), _jsx("small", { children: "\u4ECE\u9ED8\u8BA4\u5217\u8868\u9690\u85CF" })] }), _jsxs("button", { className: "session-action danger", disabled: !activeSessionPath || sessionActionBusy, onClick: () => {
2345
2435
  setSessionMenuOpen(false);
2346
2436
  setSessionDialog("delete");
2347
- }, type: "button", children: [_jsx("strong", { children: "\u5220\u9664\u4F1A\u8BDD" }), _jsx("small", { children: "\u6C38\u4E45\u5220\u9664\u672C\u5730\u8BB0\u5F55" })] })] }))] }), refreshIssues.length > 0 && (_jsxs("div", { "aria-live": "polite", className: "refresh-warning", role: "status", children: [_jsxs("span", { children: [_jsx("strong", { children: "\u90E8\u5206\u6570\u636E\u5237\u65B0\u5931\u8D25" }), refreshIssues.join("、"), "\u53EF\u80FD\u4E3A\u7A7A\u6216\u663E\u793A\u4E0A\u6B21\u7ED3\u679C\u3002"] }), _jsx("button", { onClick: () => void refresh(), type: "button", children: "\u91CD\u8BD5" })] })), _jsx("div", { className: "view-host", children: initialRefreshPending ? (_jsxs("div", { "aria-live": "polite", className: "initial-loading", role: "status", children: [_jsx("span", { "aria-hidden": "true" }), "\u6B63\u5728\u8FDE\u63A5 Pi runtime\u2026"] })) : (content) })] }), !settings && page === "session" && details !== undefined && (_jsx(Details, { event: Object.keys(details).length ? details : undefined, onClose: () => setDetails(undefined), onCopy: () => void navigator.clipboard?.writeText(JSON.stringify(details, null, 2)) })), globalSearchOpen && (_jsx(GlobalSearch, { commands: data.commands, files: data.files, onClose: () => setGlobalSearchOpen(false), onOpenFile: (file) => {
2437
+ }, type: "button", children: [_jsx("strong", { children: "\u5220\u9664\u4F1A\u8BDD" }), _jsx("small", { children: "\u6C38\u4E45\u5220\u9664\u672C\u5730\u8BB0\u5F55" })] })] }))] }), refreshIssues.length > 0 && (_jsxs("div", { "aria-live": "polite", className: "refresh-warning", role: "status", children: [_jsxs("span", { children: [_jsx("strong", { children: "\u90E8\u5206\u6570\u636E\u5237\u65B0\u5931\u8D25" }), refreshIssues.join("、"), "\u53EF\u80FD\u4E3A\u7A7A\u6216\u663E\u793A\u4E0A\u6B21\u7ED3\u679C\u3002"] }), _jsx("button", { onClick: () => void refresh(), type: "button", children: "\u91CD\u8BD5" })] })), _jsx("div", { className: "view-host", children: initialRefreshPending ? (_jsxs("div", { "aria-live": "polite", className: "initial-loading", role: "status", children: [_jsx("span", { "aria-hidden": "true" }), "\u6B63\u5728\u8FDE\u63A5 Pi runtime\u2026"] })) : (content) })] }), !settings && page === "session" && details !== undefined && (_jsxs(_Fragment, { children: [_jsx("button", { "aria-label": "\u5173\u95ED\u8BE6\u60C5", className: "details-backdrop", onClick: () => setDetails(undefined), type: "button" }), _jsx(Details, { event: Object.keys(details).length ? details : undefined, onClose: () => setDetails(undefined), onCopy: () => void navigator.clipboard?.writeText(JSON.stringify(details, null, 2)) })] })), globalSearchOpen && (_jsx(GlobalSearch, { commands: data.commands, files: data.files, onClose: () => setGlobalSearchOpen(false), onOpenFile: (file) => {
2348
2438
  setPage("session");
2349
2439
  setView("files");
2350
2440
  setDetails({ type: "file", path: file.path, status: file.status });