@pi-harness/pi-harness 0.1.10 → 0.1.12

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 (250) hide show
  1. package/apps/web/dist/assets/index-D6dtMTPb.js +20 -0
  2. package/apps/web/dist/assets/index-De8Rm01Z.css +2 -0
  3. package/apps/web/dist/assets/{react-vendor-BD7-wQo9.js → react-vendor-BxoMtjVy.js} +1 -1
  4. package/apps/web/dist/index.html +3 -3
  5. package/apps/web/package.json +6 -6
  6. package/apps/web/src/style.css +30 -30
  7. package/node_modules/@pi-harness/api-gateway/dist/marketplace-entries/official/agent-teams.json +1 -1
  8. package/node_modules/@pi-harness/api-gateway/dist/marketplace-entries/official/anchored-standard.json +1 -1
  9. package/node_modules/@pi-harness/api-gateway/dist/marketplace-entries/official/annotation.json +1 -1
  10. package/node_modules/@pi-harness/api-gateway/dist/marketplace-entries/official/archify.json +1 -1
  11. package/node_modules/@pi-harness/api-gateway/dist/marketplace-entries/official/at-file.json +1 -1
  12. package/node_modules/@pi-harness/api-gateway/dist/marketplace-entries/official/auto-mode.json +1 -1
  13. package/node_modules/@pi-harness/api-gateway/dist/marketplace-entries/official/better-sidebar.json +1 -1
  14. package/node_modules/@pi-harness/api-gateway/dist/marketplace-entries/official/browser-fetch.json +1 -1
  15. package/node_modules/@pi-harness/api-gateway/dist/marketplace-entries/official/browser-session.json +1 -1
  16. package/node_modules/@pi-harness/api-gateway/dist/marketplace-entries/official/canvas-draw.json +1 -1
  17. package/node_modules/@pi-harness/api-gateway/dist/marketplace-entries/official/change-verifier.json +1 -1
  18. package/node_modules/@pi-harness/api-gateway/dist/marketplace-entries/official/cleaner.json +1 -1
  19. package/node_modules/@pi-harness/api-gateway/dist/marketplace-entries/official/cli-notifier.json +1 -1
  20. package/node_modules/@pi-harness/api-gateway/dist/marketplace-entries/official/code2skill.json +1 -1
  21. package/node_modules/@pi-harness/api-gateway/dist/marketplace-entries/official/colleague-skill.json +1 -1
  22. package/node_modules/@pi-harness/api-gateway/dist/marketplace-entries/official/context-doctor.json +1 -1
  23. package/node_modules/@pi-harness/api-gateway/dist/marketplace-entries/official/context.json +1 -1
  24. package/node_modules/@pi-harness/api-gateway/dist/marketplace-entries/official/cost-meter.json +1 -1
  25. package/node_modules/@pi-harness/api-gateway/dist/marketplace-entries/official/dependency-checker.json +1 -1
  26. package/node_modules/@pi-harness/api-gateway/dist/marketplace-entries/official/docker-sandbox.json +1 -1
  27. package/node_modules/@pi-harness/api-gateway/dist/marketplace-entries/official/fail-logger.json +1 -1
  28. package/node_modules/@pi-harness/api-gateway/dist/marketplace-entries/official/genui.json +1 -1
  29. package/node_modules/@pi-harness/api-gateway/dist/marketplace-entries/official/git-time-capsule.json +1 -1
  30. package/node_modules/@pi-harness/api-gateway/dist/marketplace-entries/official/graph-memory.json +1 -1
  31. package/node_modules/@pi-harness/api-gateway/dist/marketplace-entries/official/history-compressor.json +1 -1
  32. package/node_modules/@pi-harness/api-gateway/dist/marketplace-entries/official/hol-guard.json +1 -1
  33. package/node_modules/@pi-harness/api-gateway/dist/marketplace-entries/official/i18n-pair.json +1 -1
  34. package/node_modules/@pi-harness/api-gateway/dist/marketplace-entries/official/image-compressor.json +1 -1
  35. package/node_modules/@pi-harness/api-gateway/dist/marketplace-entries/official/llm-verifier.json +1 -1
  36. package/node_modules/@pi-harness/api-gateway/dist/marketplace-entries/official/mcp-client.json +1 -1
  37. package/node_modules/@pi-harness/api-gateway/dist/marketplace-entries/official/mcp-panel.json +1 -1
  38. package/node_modules/@pi-harness/api-gateway/dist/marketplace-entries/official/memory.json +1 -1
  39. package/node_modules/@pi-harness/api-gateway/dist/marketplace-entries/official/mirage-bridge.json +1 -1
  40. package/node_modules/@pi-harness/api-gateway/dist/marketplace-entries/official/mock-server.json +1 -1
  41. package/node_modules/@pi-harness/api-gateway/dist/marketplace-entries/official/modlens.json +1 -1
  42. package/node_modules/@pi-harness/api-gateway/dist/marketplace-entries/official/module-search.json +1 -1
  43. package/node_modules/@pi-harness/api-gateway/dist/marketplace-entries/official/obsidian-sync.json +1 -1
  44. package/node_modules/@pi-harness/api-gateway/dist/marketplace-entries/official/openpets.json +1 -1
  45. package/node_modules/@pi-harness/api-gateway/dist/marketplace-entries/official/plan-execute.json +1 -1
  46. package/node_modules/@pi-harness/api-gateway/dist/marketplace-entries/official/plugin-check.json +1 -1
  47. package/node_modules/@pi-harness/api-gateway/dist/marketplace-entries/official/plugin-dev.json +1 -1
  48. package/node_modules/@pi-harness/api-gateway/dist/marketplace-entries/official/plugin-finder.json +1 -1
  49. package/node_modules/@pi-harness/api-gateway/dist/marketplace-entries/official/plugin-radar.json +1 -1
  50. package/node_modules/@pi-harness/api-gateway/dist/marketplace-entries/official/plugin-stars.json +1 -1
  51. package/node_modules/@pi-harness/api-gateway/dist/marketplace-entries/official/prompt-guard.json +1 -1
  52. package/node_modules/@pi-harness/api-gateway/dist/marketplace-entries/official/prompt-library.json +1 -1
  53. package/node_modules/@pi-harness/api-gateway/dist/marketplace-entries/official/readme-gen.json +1 -1
  54. package/node_modules/@pi-harness/api-gateway/dist/marketplace-entries/official/recall-unread.json +1 -1
  55. package/node_modules/@pi-harness/api-gateway/dist/marketplace-entries/official/reverse-skill.json +1 -1
  56. package/node_modules/@pi-harness/api-gateway/dist/marketplace-entries/official/reviewer-bot.json +1 -1
  57. package/node_modules/@pi-harness/api-gateway/dist/marketplace-entries/official/runtime-doctor.json +1 -1
  58. package/node_modules/@pi-harness/api-gateway/dist/marketplace-entries/official/secure-audit.json +1 -1
  59. package/node_modules/@pi-harness/api-gateway/dist/marketplace-entries/official/session-bookmarks.json +1 -1
  60. package/node_modules/@pi-harness/api-gateway/dist/marketplace-entries/official/session-bridge.json +1 -1
  61. package/node_modules/@pi-harness/api-gateway/dist/marketplace-entries/official/session-compare.json +1 -1
  62. package/node_modules/@pi-harness/api-gateway/dist/marketplace-entries/official/session-export.json +1 -1
  63. package/node_modules/@pi-harness/api-gateway/dist/marketplace-entries/official/session-insights.json +1 -1
  64. package/node_modules/@pi-harness/api-gateway/dist/marketplace-entries/official/session-search.json +1 -1
  65. package/node_modules/@pi-harness/api-gateway/dist/marketplace-entries/official/skill-catalog.json +1 -1
  66. package/node_modules/@pi-harness/api-gateway/dist/marketplace-entries/official/skill-guard.json +1 -1
  67. package/node_modules/@pi-harness/api-gateway/dist/marketplace-entries/official/sql-lens.json +1 -1
  68. package/node_modules/@pi-harness/api-gateway/dist/marketplace-entries/official/synapse.json +1 -1
  69. package/node_modules/@pi-harness/api-gateway/dist/marketplace-entries/official/tab-manager.json +1 -1
  70. package/node_modules/@pi-harness/api-gateway/dist/marketplace-entries/official/taskboard.json +1 -1
  71. package/node_modules/@pi-harness/api-gateway/dist/marketplace-entries/official/telemetry-blocker.json +1 -1
  72. package/node_modules/@pi-harness/api-gateway/dist/marketplace-entries/official/test-harness.json +1 -1
  73. package/node_modules/@pi-harness/api-gateway/dist/marketplace-entries/official/theme-studio.json +1 -1
  74. package/node_modules/@pi-harness/api-gateway/dist/marketplace-entries/official/token-guard.json +1 -1
  75. package/node_modules/@pi-harness/api-gateway/dist/marketplace-entries/official/turn-rewind.json +1 -1
  76. package/node_modules/@pi-harness/api-gateway/dist/marketplace-entries/official/undo-savepoint.json +1 -1
  77. package/node_modules/@pi-harness/api-gateway/dist/marketplace-entries/official/vision-toolkit.json +1 -1
  78. package/node_modules/@pi-harness/api-gateway/dist/marketplace-entries/official/web-research.json +1 -1
  79. package/node_modules/@pi-harness/api-gateway/dist/marketplace-entries/official/workspace-navigator.json +1 -1
  80. package/node_modules/@pi-harness/api-gateway/dist/marketplace-entries/official/workspace-search.json +1 -1
  81. package/node_modules/@pi-harness/api-gateway/dist/marketplace-entries/official/yaml-validator.json +1 -1
  82. package/node_modules/@pi-harness/api-gateway/package.json +1 -1
  83. package/node_modules/@pi-harness/cli/package.json +1 -1
  84. package/node_modules/@pi-harness/core/package.json +1 -1
  85. package/node_modules/@pi-harness/host-webserver/package.json +1 -1
  86. package/node_modules/@pi-harness/web-app/package.json +1 -1
  87. package/package.json +8 -7
  88. package/packages/api-gateway/dist/marketplace-entries/official/agent-teams.json +1 -1
  89. package/packages/api-gateway/dist/marketplace-entries/official/anchored-standard.json +1 -1
  90. package/packages/api-gateway/dist/marketplace-entries/official/annotation.json +1 -1
  91. package/packages/api-gateway/dist/marketplace-entries/official/archify.json +1 -1
  92. package/packages/api-gateway/dist/marketplace-entries/official/at-file.json +1 -1
  93. package/packages/api-gateway/dist/marketplace-entries/official/auto-mode.json +1 -1
  94. package/packages/api-gateway/dist/marketplace-entries/official/better-sidebar.json +1 -1
  95. package/packages/api-gateway/dist/marketplace-entries/official/browser-fetch.json +1 -1
  96. package/packages/api-gateway/dist/marketplace-entries/official/browser-session.json +1 -1
  97. package/packages/api-gateway/dist/marketplace-entries/official/canvas-draw.json +1 -1
  98. package/packages/api-gateway/dist/marketplace-entries/official/change-verifier.json +1 -1
  99. package/packages/api-gateway/dist/marketplace-entries/official/cleaner.json +1 -1
  100. package/packages/api-gateway/dist/marketplace-entries/official/cli-notifier.json +1 -1
  101. package/packages/api-gateway/dist/marketplace-entries/official/code2skill.json +1 -1
  102. package/packages/api-gateway/dist/marketplace-entries/official/colleague-skill.json +1 -1
  103. package/packages/api-gateway/dist/marketplace-entries/official/context-doctor.json +1 -1
  104. package/packages/api-gateway/dist/marketplace-entries/official/context.json +1 -1
  105. package/packages/api-gateway/dist/marketplace-entries/official/cost-meter.json +1 -1
  106. package/packages/api-gateway/dist/marketplace-entries/official/dependency-checker.json +1 -1
  107. package/packages/api-gateway/dist/marketplace-entries/official/docker-sandbox.json +1 -1
  108. package/packages/api-gateway/dist/marketplace-entries/official/fail-logger.json +1 -1
  109. package/packages/api-gateway/dist/marketplace-entries/official/genui.json +1 -1
  110. package/packages/api-gateway/dist/marketplace-entries/official/git-time-capsule.json +1 -1
  111. package/packages/api-gateway/dist/marketplace-entries/official/graph-memory.json +1 -1
  112. package/packages/api-gateway/dist/marketplace-entries/official/history-compressor.json +1 -1
  113. package/packages/api-gateway/dist/marketplace-entries/official/hol-guard.json +1 -1
  114. package/packages/api-gateway/dist/marketplace-entries/official/i18n-pair.json +1 -1
  115. package/packages/api-gateway/dist/marketplace-entries/official/image-compressor.json +1 -1
  116. package/packages/api-gateway/dist/marketplace-entries/official/llm-verifier.json +1 -1
  117. package/packages/api-gateway/dist/marketplace-entries/official/mcp-client.json +1 -1
  118. package/packages/api-gateway/dist/marketplace-entries/official/mcp-panel.json +1 -1
  119. package/packages/api-gateway/dist/marketplace-entries/official/memory.json +1 -1
  120. package/packages/api-gateway/dist/marketplace-entries/official/mirage-bridge.json +1 -1
  121. package/packages/api-gateway/dist/marketplace-entries/official/mock-server.json +1 -1
  122. package/packages/api-gateway/dist/marketplace-entries/official/modlens.json +1 -1
  123. package/packages/api-gateway/dist/marketplace-entries/official/module-search.json +1 -1
  124. package/packages/api-gateway/dist/marketplace-entries/official/obsidian-sync.json +1 -1
  125. package/packages/api-gateway/dist/marketplace-entries/official/openpets.json +1 -1
  126. package/packages/api-gateway/dist/marketplace-entries/official/plan-execute.json +1 -1
  127. package/packages/api-gateway/dist/marketplace-entries/official/plugin-check.json +1 -1
  128. package/packages/api-gateway/dist/marketplace-entries/official/plugin-dev.json +1 -1
  129. package/packages/api-gateway/dist/marketplace-entries/official/plugin-finder.json +1 -1
  130. package/packages/api-gateway/dist/marketplace-entries/official/plugin-radar.json +1 -1
  131. package/packages/api-gateway/dist/marketplace-entries/official/plugin-stars.json +1 -1
  132. package/packages/api-gateway/dist/marketplace-entries/official/prompt-guard.json +1 -1
  133. package/packages/api-gateway/dist/marketplace-entries/official/prompt-library.json +1 -1
  134. package/packages/api-gateway/dist/marketplace-entries/official/readme-gen.json +1 -1
  135. package/packages/api-gateway/dist/marketplace-entries/official/recall-unread.json +1 -1
  136. package/packages/api-gateway/dist/marketplace-entries/official/reverse-skill.json +1 -1
  137. package/packages/api-gateway/dist/marketplace-entries/official/reviewer-bot.json +1 -1
  138. package/packages/api-gateway/dist/marketplace-entries/official/runtime-doctor.json +1 -1
  139. package/packages/api-gateway/dist/marketplace-entries/official/secure-audit.json +1 -1
  140. package/packages/api-gateway/dist/marketplace-entries/official/session-bookmarks.json +1 -1
  141. package/packages/api-gateway/dist/marketplace-entries/official/session-bridge.json +1 -1
  142. package/packages/api-gateway/dist/marketplace-entries/official/session-compare.json +1 -1
  143. package/packages/api-gateway/dist/marketplace-entries/official/session-export.json +1 -1
  144. package/packages/api-gateway/dist/marketplace-entries/official/session-insights.json +1 -1
  145. package/packages/api-gateway/dist/marketplace-entries/official/session-search.json +1 -1
  146. package/packages/api-gateway/dist/marketplace-entries/official/skill-catalog.json +1 -1
  147. package/packages/api-gateway/dist/marketplace-entries/official/skill-guard.json +1 -1
  148. package/packages/api-gateway/dist/marketplace-entries/official/sql-lens.json +1 -1
  149. package/packages/api-gateway/dist/marketplace-entries/official/synapse.json +1 -1
  150. package/packages/api-gateway/dist/marketplace-entries/official/tab-manager.json +1 -1
  151. package/packages/api-gateway/dist/marketplace-entries/official/taskboard.json +1 -1
  152. package/packages/api-gateway/dist/marketplace-entries/official/telemetry-blocker.json +1 -1
  153. package/packages/api-gateway/dist/marketplace-entries/official/test-harness.json +1 -1
  154. package/packages/api-gateway/dist/marketplace-entries/official/theme-studio.json +1 -1
  155. package/packages/api-gateway/dist/marketplace-entries/official/token-guard.json +1 -1
  156. package/packages/api-gateway/dist/marketplace-entries/official/turn-rewind.json +1 -1
  157. package/packages/api-gateway/dist/marketplace-entries/official/undo-savepoint.json +1 -1
  158. package/packages/api-gateway/dist/marketplace-entries/official/vision-toolkit.json +1 -1
  159. package/packages/api-gateway/dist/marketplace-entries/official/web-research.json +1 -1
  160. package/packages/api-gateway/dist/marketplace-entries/official/workspace-navigator.json +1 -1
  161. package/packages/api-gateway/dist/marketplace-entries/official/workspace-search.json +1 -1
  162. package/packages/api-gateway/dist/marketplace-entries/official/yaml-validator.json +1 -1
  163. package/packages/api-gateway/package.json +1 -1
  164. package/packages/api-gateway/src/marketplace-entries/official/agent-teams.json +1 -1
  165. package/packages/api-gateway/src/marketplace-entries/official/anchored-standard.json +1 -1
  166. package/packages/api-gateway/src/marketplace-entries/official/annotation.json +1 -1
  167. package/packages/api-gateway/src/marketplace-entries/official/archify.json +1 -1
  168. package/packages/api-gateway/src/marketplace-entries/official/at-file.json +1 -1
  169. package/packages/api-gateway/src/marketplace-entries/official/auto-mode.json +1 -1
  170. package/packages/api-gateway/src/marketplace-entries/official/better-sidebar.json +1 -1
  171. package/packages/api-gateway/src/marketplace-entries/official/browser-fetch.json +1 -1
  172. package/packages/api-gateway/src/marketplace-entries/official/browser-session.json +1 -1
  173. package/packages/api-gateway/src/marketplace-entries/official/canvas-draw.json +1 -1
  174. package/packages/api-gateway/src/marketplace-entries/official/change-verifier.json +1 -1
  175. package/packages/api-gateway/src/marketplace-entries/official/cleaner.json +1 -1
  176. package/packages/api-gateway/src/marketplace-entries/official/cli-notifier.json +1 -1
  177. package/packages/api-gateway/src/marketplace-entries/official/code2skill.json +1 -1
  178. package/packages/api-gateway/src/marketplace-entries/official/colleague-skill.json +1 -1
  179. package/packages/api-gateway/src/marketplace-entries/official/context-doctor.json +1 -1
  180. package/packages/api-gateway/src/marketplace-entries/official/context.json +1 -1
  181. package/packages/api-gateway/src/marketplace-entries/official/cost-meter.json +1 -1
  182. package/packages/api-gateway/src/marketplace-entries/official/dependency-checker.json +1 -1
  183. package/packages/api-gateway/src/marketplace-entries/official/docker-sandbox.json +1 -1
  184. package/packages/api-gateway/src/marketplace-entries/official/fail-logger.json +1 -1
  185. package/packages/api-gateway/src/marketplace-entries/official/genui.json +1 -1
  186. package/packages/api-gateway/src/marketplace-entries/official/git-time-capsule.json +1 -1
  187. package/packages/api-gateway/src/marketplace-entries/official/graph-memory.json +1 -1
  188. package/packages/api-gateway/src/marketplace-entries/official/history-compressor.json +1 -1
  189. package/packages/api-gateway/src/marketplace-entries/official/hol-guard.json +1 -1
  190. package/packages/api-gateway/src/marketplace-entries/official/i18n-pair.json +1 -1
  191. package/packages/api-gateway/src/marketplace-entries/official/image-compressor.json +1 -1
  192. package/packages/api-gateway/src/marketplace-entries/official/llm-verifier.json +1 -1
  193. package/packages/api-gateway/src/marketplace-entries/official/mcp-client.json +1 -1
  194. package/packages/api-gateway/src/marketplace-entries/official/mcp-panel.json +1 -1
  195. package/packages/api-gateway/src/marketplace-entries/official/memory.json +1 -1
  196. package/packages/api-gateway/src/marketplace-entries/official/mirage-bridge.json +1 -1
  197. package/packages/api-gateway/src/marketplace-entries/official/mock-server.json +1 -1
  198. package/packages/api-gateway/src/marketplace-entries/official/modlens.json +1 -1
  199. package/packages/api-gateway/src/marketplace-entries/official/module-search.json +1 -1
  200. package/packages/api-gateway/src/marketplace-entries/official/obsidian-sync.json +1 -1
  201. package/packages/api-gateway/src/marketplace-entries/official/openpets.json +1 -1
  202. package/packages/api-gateway/src/marketplace-entries/official/plan-execute.json +1 -1
  203. package/packages/api-gateway/src/marketplace-entries/official/plugin-check.json +1 -1
  204. package/packages/api-gateway/src/marketplace-entries/official/plugin-dev.json +1 -1
  205. package/packages/api-gateway/src/marketplace-entries/official/plugin-finder.json +1 -1
  206. package/packages/api-gateway/src/marketplace-entries/official/plugin-radar.json +1 -1
  207. package/packages/api-gateway/src/marketplace-entries/official/plugin-stars.json +1 -1
  208. package/packages/api-gateway/src/marketplace-entries/official/prompt-guard.json +1 -1
  209. package/packages/api-gateway/src/marketplace-entries/official/prompt-library.json +1 -1
  210. package/packages/api-gateway/src/marketplace-entries/official/readme-gen.json +1 -1
  211. package/packages/api-gateway/src/marketplace-entries/official/recall-unread.json +1 -1
  212. package/packages/api-gateway/src/marketplace-entries/official/reverse-skill.json +1 -1
  213. package/packages/api-gateway/src/marketplace-entries/official/reviewer-bot.json +1 -1
  214. package/packages/api-gateway/src/marketplace-entries/official/runtime-doctor.json +1 -1
  215. package/packages/api-gateway/src/marketplace-entries/official/secure-audit.json +1 -1
  216. package/packages/api-gateway/src/marketplace-entries/official/session-bookmarks.json +1 -1
  217. package/packages/api-gateway/src/marketplace-entries/official/session-bridge.json +1 -1
  218. package/packages/api-gateway/src/marketplace-entries/official/session-compare.json +1 -1
  219. package/packages/api-gateway/src/marketplace-entries/official/session-export.json +1 -1
  220. package/packages/api-gateway/src/marketplace-entries/official/session-insights.json +1 -1
  221. package/packages/api-gateway/src/marketplace-entries/official/session-search.json +1 -1
  222. package/packages/api-gateway/src/marketplace-entries/official/skill-catalog.json +1 -1
  223. package/packages/api-gateway/src/marketplace-entries/official/skill-guard.json +1 -1
  224. package/packages/api-gateway/src/marketplace-entries/official/sql-lens.json +1 -1
  225. package/packages/api-gateway/src/marketplace-entries/official/synapse.json +1 -1
  226. package/packages/api-gateway/src/marketplace-entries/official/tab-manager.json +1 -1
  227. package/packages/api-gateway/src/marketplace-entries/official/taskboard.json +1 -1
  228. package/packages/api-gateway/src/marketplace-entries/official/telemetry-blocker.json +1 -1
  229. package/packages/api-gateway/src/marketplace-entries/official/test-harness.json +1 -1
  230. package/packages/api-gateway/src/marketplace-entries/official/theme-studio.json +1 -1
  231. package/packages/api-gateway/src/marketplace-entries/official/token-guard.json +1 -1
  232. package/packages/api-gateway/src/marketplace-entries/official/turn-rewind.json +1 -1
  233. package/packages/api-gateway/src/marketplace-entries/official/undo-savepoint.json +1 -1
  234. package/packages/api-gateway/src/marketplace-entries/official/vision-toolkit.json +1 -1
  235. package/packages/api-gateway/src/marketplace-entries/official/web-research.json +1 -1
  236. package/packages/api-gateway/src/marketplace-entries/official/workspace-navigator.json +1 -1
  237. package/packages/api-gateway/src/marketplace-entries/official/workspace-search.json +1 -1
  238. package/packages/api-gateway/src/marketplace-entries/official/yaml-validator.json +1 -1
  239. package/packages/api-gateway/test/release-package.test.ts +1 -0
  240. package/packages/bundle-web-app/package.json +1 -1
  241. package/packages/cli/package.json +1 -1
  242. package/packages/client-web/dist/react-room.d.ts.map +1 -1
  243. package/packages/client-web/dist/react-room.js +199 -59
  244. package/packages/client-web/dist/react-room.js.map +1 -1
  245. package/packages/client-web/package.json +1 -1
  246. package/packages/client-web/src/react-room.tsx +350 -187
  247. package/packages/core/package.json +1 -1
  248. package/packages/host-webserver/package.json +1 -1
  249. package/apps/web/dist/assets/index-CW3u-pEm.css +0 -2
  250. package/apps/web/dist/assets/index-DNYYxKc7.js +0 -20
@@ -1,4 +1,5 @@
1
1
  import { useCallback, useEffect, useMemo, useRef, useState, type FormEvent } from "react";
2
+ import { createPortal } from "react-dom";
2
3
  import {
3
4
  createClientApi,
4
5
  failedRefreshLabels,
@@ -645,35 +646,55 @@ function ConfirmDialog({
645
646
 
646
647
  function SessionActionMenu({
647
648
  busy,
649
+ position,
648
650
  onRename,
649
651
  onFork,
650
652
  onArchive,
651
653
  onDelete,
652
654
  }: {
653
655
  busy: boolean;
656
+ position: { left: number; top: number };
654
657
  onRename: () => void;
655
658
  onFork: () => void;
656
659
  onArchive: () => void;
657
660
  onDelete: () => void;
658
661
  }) {
659
- return (
660
- <div className="session-row-menu-popover" onClick={(event) => event.stopPropagation()}>
661
- <button disabled={busy} onClick={onRename} type="button">
662
+ return createPortal(
663
+ <div className="session-row-menu-popover" data-session-popover onClick={(event) => event.stopPropagation()} role="menu" style={position}>
664
+ <button autoFocus disabled={busy} onClick={onRename} role="menuitem" type="button">
662
665
  重命名
663
666
  </button>
664
- <button disabled={busy} onClick={onFork} type="button">
667
+ <button disabled={busy} onClick={onFork} role="menuitem" type="button">
665
668
  复制会话
666
669
  </button>
667
- <button disabled={busy} onClick={onArchive} type="button">
670
+ <button disabled={busy} onClick={onArchive} role="menuitem" type="button">
668
671
  归档会话
669
672
  </button>
670
- <button className="danger" disabled={busy} onClick={onDelete} type="button">
673
+ <button className="danger" disabled={busy} onClick={onDelete} role="menuitem" type="button">
671
674
  删除会话
672
675
  </button>
673
- </div>
676
+ </div>,
677
+ document.body,
674
678
  );
675
679
  }
676
680
 
681
+ const sidebarPopoverPosition = (
682
+ trigger: HTMLButtonElement,
683
+ width: number,
684
+ estimatedHeight: number,
685
+ placement: "beside" | "below",
686
+ ): { left: number; top: number } => {
687
+ const rect = trigger.getBoundingClientRect();
688
+ const gutter = 8;
689
+ const spaceOnRight = window.innerWidth - rect.right - gutter;
690
+ const left = spaceOnRight >= width ? rect.right + gutter : Math.max(gutter, rect.left - width - gutter);
691
+ const preferredTop = placement === "below" ? rect.bottom + 6 : rect.top - 4;
692
+ return {
693
+ left: Math.round(left),
694
+ top: Math.round(Math.min(Math.max(gutter, preferredTop), Math.max(gutter, window.innerHeight - estimatedHeight - gutter))),
695
+ };
696
+ };
697
+
677
698
  function PromptError({ message }: { message: string }) {
678
699
  const everyApiAuth = /No API key found for everyapi/i.test(message);
679
700
  const requiresAuth = everyApiAuth || /No API key found|authentication|未配置认证/i.test(message);
@@ -5679,7 +5700,13 @@ function PromptCompletionPopover({
5679
5700
  ? filterCommands(commands, query).slice(0, 12)
5680
5701
  : files.filter((file) => `${file.path} ${file.label} ${file.status}`.toLowerCase().includes(query.trim().toLowerCase())).slice(0, 12);
5681
5702
  return (
5682
- <div aria-label={kind === "command" ? "命令补全" : "文件补全"} className="prompt-completion" id="prompt-completion-list" role="listbox">
5703
+ <div
5704
+ aria-label={kind === "command" ? "命令补全" : "文件补全"}
5705
+ className="prompt-completion"
5706
+ data-prompt-completion
5707
+ id="prompt-completion-list"
5708
+ role="listbox"
5709
+ >
5683
5710
  <small>{kind === "command" ? "命令" : "文件"}</small>
5684
5711
  {items.length ? (
5685
5712
  items.slice(0, 12).map((item, index) => {
@@ -5874,9 +5901,12 @@ export function ControlRoomView({ api = createClientApi(), appVersion }: { api?:
5874
5901
  const [commandIndex, setCommandIndex] = useState(0);
5875
5902
  const [sessionMenuOpen, setSessionMenuOpen] = useState(false);
5876
5903
  const [sessionMenuPath, setSessionMenuPath] = useState<string>();
5904
+ const [sessionMenuPosition, setSessionMenuPosition] = useState<{ left: number; top: number }>();
5877
5905
  const [sessionToolsOpen, setSessionToolsOpen] = useState(false);
5906
+ const [sessionToolsPosition, setSessionToolsPosition] = useState<{ left: number; top: number }>();
5878
5907
  const [sessionSelectionMode, setSessionSelectionMode] = useState(false);
5879
5908
  const [sessionDialog, setSessionDialog] = useState<"rename" | "delete" | "archive" | "batch-delete">();
5909
+ const [sessionActionTarget, setSessionActionTarget] = useState<{ name: string; path: string }>();
5880
5910
  const [sessionNameDraft, setSessionNameDraft] = useState("");
5881
5911
  const [workspaceChooserOpen, setWorkspaceChooserOpen] = useState(false);
5882
5912
  const [workspaceError, setWorkspaceError] = useState("");
@@ -5902,7 +5932,6 @@ export function ControlRoomView({ api = createClientApi(), appVersion }: { api?:
5902
5932
  const [installedPluginMetadata, setInstalledPluginMetadata] = useState<ClientMarketplacePlugin>();
5903
5933
  const [marketplacePage, setMarketplacePage] = useState(initialQueryState.marketplacePage);
5904
5934
  const [permission, setPermission] = useState(true);
5905
- const [contextExpanded, setContextExpanded] = useState(false);
5906
5935
  const [promptError, setPromptError] = useState("");
5907
5936
  const [promptBusy, setPromptBusy] = useState(false);
5908
5937
  const [pendingPrompt, setPendingPrompt] = useState("");
@@ -5913,6 +5942,7 @@ export function ControlRoomView({ api = createClientApi(), appVersion }: { api?:
5913
5942
  const [sessionHasNext, setSessionHasNext] = useState(false);
5914
5943
  const [selectedSessionPaths, setSelectedSessionPaths] = useState<ReadonlySet<string>>(new Set());
5915
5944
  const importInputRef = useRef<HTMLInputElement>(null);
5945
+ const sessionPopoverTriggerRef = useRef<HTMLButtonElement | null>(null);
5916
5946
  const [streamingAssistant, setStreamingAssistant] = useState<{ thinking: string; text: string }>();
5917
5947
  const promptInputRef = useRef<HTMLTextAreaElement>(null);
5918
5948
  const chatScrollRef = useRef<HTMLDivElement>(null);
@@ -5961,6 +5991,78 @@ export function ControlRoomView({ api = createClientApi(), appVersion }: { api?:
5961
5991
  useEffect(() => {
5962
5992
  setPromptCompletionIndex(0);
5963
5993
  }, [promptCompletion?.kind, promptCompletion?.query]);
5994
+ useEffect(() => {
5995
+ if (!sessionMenuOpen && !sessionToolsOpen) return;
5996
+ const dismissOnPointerDown = (event: PointerEvent) => {
5997
+ if (event.target instanceof Element && event.target.closest("[data-session-popover]")) return;
5998
+ setSessionMenuOpen(false);
5999
+ setSessionMenuPath(undefined);
6000
+ setSessionMenuPosition(undefined);
6001
+ setSessionToolsOpen(false);
6002
+ setSessionToolsPosition(undefined);
6003
+ setSessionActionTarget(undefined);
6004
+ };
6005
+ const dismissOnEscape = (event: KeyboardEvent) => {
6006
+ if (event.key !== "Escape") return;
6007
+ event.preventDefault();
6008
+ event.stopPropagation();
6009
+ setSessionMenuOpen(false);
6010
+ setSessionMenuPath(undefined);
6011
+ setSessionMenuPosition(undefined);
6012
+ setSessionToolsOpen(false);
6013
+ setSessionToolsPosition(undefined);
6014
+ setSessionActionTarget(undefined);
6015
+ sessionPopoverTriggerRef.current?.focus();
6016
+ };
6017
+ const dismissOnViewportChange = () => {
6018
+ setSessionMenuOpen(false);
6019
+ setSessionMenuPath(undefined);
6020
+ setSessionMenuPosition(undefined);
6021
+ setSessionToolsOpen(false);
6022
+ setSessionToolsPosition(undefined);
6023
+ setSessionActionTarget(undefined);
6024
+ };
6025
+ document.addEventListener("pointerdown", dismissOnPointerDown);
6026
+ document.addEventListener("keydown", dismissOnEscape);
6027
+ document.addEventListener("scroll", dismissOnViewportChange, true);
6028
+ window.addEventListener("resize", dismissOnViewportChange);
6029
+ return () => {
6030
+ document.removeEventListener("pointerdown", dismissOnPointerDown);
6031
+ document.removeEventListener("keydown", dismissOnEscape);
6032
+ document.removeEventListener("scroll", dismissOnViewportChange, true);
6033
+ window.removeEventListener("resize", dismissOnViewportChange);
6034
+ };
6035
+ }, [sessionMenuOpen, sessionToolsOpen]);
6036
+ useEffect(() => {
6037
+ if (!commandOpen && !promptCompletionOpen) return;
6038
+ const dismissTransientLists = (event: PointerEvent) => {
6039
+ const target = event.target instanceof Element ? event.target : undefined;
6040
+ if (commandOpen && !target?.closest("[data-command-palette]")) {
6041
+ setCommandOpen(false);
6042
+ setCommandQuery("");
6043
+ }
6044
+ if (promptCompletionOpen && !target?.closest("[data-prompt-completion]")) setPromptCompletionSuppressed(true);
6045
+ };
6046
+ document.addEventListener("pointerdown", dismissTransientLists);
6047
+ return () => document.removeEventListener("pointerdown", dismissTransientLists);
6048
+ }, [commandOpen, promptCompletionOpen]);
6049
+ useEffect(() => {
6050
+ const dismissProviderModels = (event: PointerEvent | KeyboardEvent) => {
6051
+ if (event instanceof KeyboardEvent && event.key !== "Escape") return;
6052
+ const target = event.target instanceof Element ? event.target : undefined;
6053
+ document.querySelectorAll<HTMLDetailsElement>("details.provider-models-details[open]").forEach((details) => {
6054
+ if (event instanceof PointerEvent && target && details.contains(target)) return;
6055
+ details.removeAttribute("open");
6056
+ if (event instanceof KeyboardEvent) details.querySelector<HTMLElement>("summary")?.focus();
6057
+ });
6058
+ };
6059
+ document.addEventListener("pointerdown", dismissProviderModels);
6060
+ document.addEventListener("keydown", dismissProviderModels);
6061
+ return () => {
6062
+ document.removeEventListener("pointerdown", dismissProviderModels);
6063
+ document.removeEventListener("keydown", dismissProviderModels);
6064
+ };
6065
+ }, []);
5964
6066
  useEffect(() => {
5965
6067
  if (typeof window === "undefined") return;
5966
6068
  const params = new URLSearchParams(window.location.search);
@@ -6370,7 +6472,10 @@ export function ControlRoomView({ api = createClientApi(), appVersion }: { api?:
6370
6472
  setSelectedSessionPaths(new Set());
6371
6473
  setSessionMenuOpen(false);
6372
6474
  setSessionMenuPath(undefined);
6475
+ setSessionMenuPosition(undefined);
6373
6476
  setSessionToolsOpen(false);
6477
+ setSessionToolsPosition(undefined);
6478
+ setSessionActionTarget(undefined);
6374
6479
  setSessionSelectionMode(false);
6375
6480
  setSessionDialog(undefined);
6376
6481
  } catch (cause: unknown) {
@@ -6388,16 +6493,25 @@ export function ControlRoomView({ api = createClientApi(), appVersion }: { api?:
6388
6493
  return next;
6389
6494
  });
6390
6495
  };
6391
- const openSessionMenu = (path: string, name?: string) => {
6496
+ const openSessionMenu = (path: string, name: string | undefined, trigger: HTMLButtonElement) => {
6497
+ if (sessionMenuOpen && sessionMenuPath === path) {
6498
+ closeSessionMenu();
6499
+ setSessionActionTarget(undefined);
6500
+ return;
6501
+ }
6392
6502
  setSessionToolsOpen(false);
6393
- setSelectedSessionPath(path);
6503
+ setSessionToolsPosition(undefined);
6504
+ sessionPopoverTriggerRef.current = trigger;
6394
6505
  setSessionNameDraft(name ?? "");
6506
+ setSessionActionTarget({ name: name || "未命名会话", path });
6395
6507
  setSessionMenuPath(path);
6508
+ setSessionMenuPosition(sidebarPopoverPosition(trigger, 142, 190, "beside"));
6396
6509
  setSessionMenuOpen(true);
6397
6510
  };
6398
6511
  const closeSessionMenu = () => {
6399
6512
  setSessionMenuOpen(false);
6400
6513
  setSessionMenuPath(undefined);
6514
+ setSessionMenuPosition(undefined);
6401
6515
  };
6402
6516
  const pushInstalledPluginRoute = (pluginId: string | undefined) => {
6403
6517
  const params = new URLSearchParams(window.location.search);
@@ -6638,36 +6752,22 @@ export function ControlRoomView({ api = createClientApi(), appVersion }: { api?:
6638
6752
  )}
6639
6753
  </div>
6640
6754
  <div className="composer-wrap">
6641
- <div className="context-line">
6642
- <span className="context-label">实时上下文</span>
6643
- <div className="context-metrics">
6644
- <span>
6645
- <b>{data.status?.messages ?? 0}</b> 条消息
6646
- </span>
6647
- <span>
6648
- <b>{data.status?.events ?? events.length}</b> 个事件
6649
- </span>
6650
- <span>
6651
- <b>{value(data.status?.model)}</b>
6652
- </span>
6755
+ <div className="context-shell">
6756
+ <div className="context-line">
6757
+ <span className="context-label">实时上下文</span>
6758
+ <div className="context-metrics">
6759
+ <span>
6760
+ <b>{data.status?.messages ?? 0}</b> 条消息
6761
+ </span>
6762
+ <span>
6763
+ <b>{data.status?.events ?? events.length}</b> 个事件
6764
+ </span>
6765
+ <span>
6766
+ <b>{value(data.status?.model)}</b>
6767
+ </span>
6768
+ </div>
6653
6769
  </div>
6654
- <button aria-expanded={contextExpanded} onClick={() => setContextExpanded((current) => !current)} type="button">
6655
- {contextExpanded ? "收起" : "展开"}
6656
- </button>
6657
6770
  </div>
6658
- {contextExpanded && (
6659
- <div className="context-breakdown" role="status">
6660
- <span>
6661
- 消息 <b>{data.status?.messages ?? 0}</b>
6662
- </span>
6663
- <span>
6664
- 运行时事件 <b>{data.status?.events ?? events.length}</b>
6665
- </span>
6666
- <span>
6667
- 模型 <b>{value(data.status?.model)}</b>
6668
- </span>
6669
- </div>
6670
- )}
6671
6771
  <div className="composer-stack">
6672
6772
  {promptError && <PromptError message={promptError} />}
6673
6773
  {annotationSelection ? (
@@ -6783,6 +6883,7 @@ export function ControlRoomView({ api = createClientApi(), appVersion }: { api?:
6783
6883
  readOnly={!workspaceReady}
6784
6884
  ref={promptInputRef}
6785
6885
  rows={2}
6886
+ data-prompt-completion
6786
6887
  value={draft}
6787
6888
  ></textarea>
6788
6889
  {promptCompletionOpen && promptCompletion && (
@@ -6910,7 +7011,7 @@ export function ControlRoomView({ api = createClientApi(), appVersion }: { api?:
6910
7011
  <button className="new-session" onClick={beginNewSession} type="button" aria-expanded={workspaceChooserOpen}>
6911
7012
  + 新建会话
6912
7013
  </button>
6913
- <div className="session-search">
7014
+ <div className="session-search" data-command-palette>
6914
7015
  <input
6915
7016
  aria-controls={commandOpen ? "command-menu" : undefined}
6916
7017
  aria-expanded={commandOpen}
@@ -6989,56 +7090,79 @@ export function ControlRoomView({ api = createClientApi(), appVersion }: { api?:
6989
7090
  >
6990
7091
  {sessionSelectionMode ? "完成" : "选择"}
6991
7092
  </button>
6992
- <div className="session-tools-wrap">
7093
+ <div className="session-tools-wrap" data-session-popover>
6993
7094
  <button
6994
7095
  aria-expanded={sessionToolsOpen}
7096
+ aria-haspopup="menu"
6995
7097
  aria-label="会话工具"
6996
7098
  className="session-tool-button icon"
6997
- onClick={() => {
7099
+ onClick={(event) => {
6998
7100
  closeSessionMenu();
6999
- setSessionToolsOpen((current) => !current);
7101
+ sessionPopoverTriggerRef.current = event.currentTarget;
7102
+ setSessionActionTarget(undefined);
7103
+ if (sessionToolsOpen) {
7104
+ setSessionToolsOpen(false);
7105
+ setSessionToolsPosition(undefined);
7106
+ } else {
7107
+ setSessionToolsPosition(sidebarPopoverPosition(event.currentTarget, 150, 190, "below"));
7108
+ setSessionToolsOpen(true);
7109
+ }
7000
7110
  }}
7001
7111
  type="button"
7002
7112
  >
7003
7113
  ⋯
7004
7114
  </button>
7005
- {sessionToolsOpen && (
7006
- <div className="session-tools-popover">
7007
- <button onClick={() => window.location.reload()} type="button">
7008
- 刷新列表
7009
- </button>
7010
- <button onClick={() => importInputRef.current?.click()} type="button">
7011
- 导入会话
7012
- </button>
7013
- <button
7014
- disabled={!activeSessionPath || sessionActionBusy}
7015
- onClick={() => {
7016
- if (!activeSessionPath) return;
7017
- void sessionAction(async () => {
7018
- const blob = await api.exportSession(activeSessionPath);
7019
- const link = document.createElement("a");
7020
- link.href = URL.createObjectURL(blob);
7021
- link.download = `${data.session?.sessionId ?? "session"}.jsonl`;
7022
- link.click();
7023
- URL.revokeObjectURL(link.href);
7024
- });
7025
- }}
7026
- type="button"
7027
- >
7028
- 导出当前会话
7029
- </button>
7030
- <button
7031
- onClick={() => {
7032
- setIncludeArchivedSessions((current) => !current);
7033
- setSessionToolsOpen(false);
7034
- void refresh();
7035
- }}
7036
- type="button"
7037
- >
7038
- {includeArchivedSessions ? "隐藏归档会话" : "显示归档会话"}
7039
- </button>
7040
- </div>
7041
- )}
7115
+ {sessionToolsOpen &&
7116
+ sessionToolsPosition &&
7117
+ createPortal(
7118
+ <div className="session-tools-popover" data-session-popover role="menu" style={sessionToolsPosition}>
7119
+ <button autoFocus onClick={() => window.location.reload()} role="menuitem" type="button">
7120
+ 刷新列表
7121
+ </button>
7122
+ <button
7123
+ onClick={() => {
7124
+ setSessionToolsOpen(false);
7125
+ setSessionToolsPosition(undefined);
7126
+ importInputRef.current?.click();
7127
+ }}
7128
+ role="menuitem"
7129
+ type="button"
7130
+ >
7131
+ 导入会话
7132
+ </button>
7133
+ <button
7134
+ disabled={!activeSessionPath || sessionActionBusy}
7135
+ onClick={() => {
7136
+ if (!activeSessionPath) return;
7137
+ void sessionAction(async () => {
7138
+ const blob = await api.exportSession(activeSessionPath);
7139
+ const link = document.createElement("a");
7140
+ link.href = URL.createObjectURL(blob);
7141
+ link.download = `${data.session?.sessionId ?? "session"}.jsonl`;
7142
+ link.click();
7143
+ URL.revokeObjectURL(link.href);
7144
+ });
7145
+ }}
7146
+ role="menuitem"
7147
+ type="button"
7148
+ >
7149
+ 导出当前会话
7150
+ </button>
7151
+ <button
7152
+ onClick={() => {
7153
+ setIncludeArchivedSessions((current) => !current);
7154
+ setSessionToolsOpen(false);
7155
+ setSessionToolsPosition(undefined);
7156
+ void refresh();
7157
+ }}
7158
+ role="menuitem"
7159
+ type="button"
7160
+ >
7161
+ {includeArchivedSessions ? "隐藏归档会话" : "显示归档会话"}
7162
+ </button>
7163
+ </div>,
7164
+ document.body,
7165
+ )}
7042
7166
  </div>
7043
7167
  </div>
7044
7168
  </div>
@@ -7054,6 +7178,7 @@ export function ControlRoomView({ api = createClientApi(), appVersion }: { api?:
7054
7178
  <button
7055
7179
  className="danger"
7056
7180
  onClick={() => {
7181
+ setSessionActionTarget(undefined);
7057
7182
  setSessionDialog("batch-delete");
7058
7183
  }}
7059
7184
  type="button"
@@ -7097,17 +7222,23 @@ export function ControlRoomView({ api = createClientApi(), appVersion }: { api?:
7097
7222
  </button>
7098
7223
  {!sessionSelectionMode && activeSessionPath && (
7099
7224
  <button
7225
+ aria-expanded={sessionMenuOpen && sessionMenuPath === activeSessionPath}
7226
+ aria-haspopup="menu"
7100
7227
  aria-label="当前会话操作"
7101
7228
  className="session-row-more"
7102
- onClick={() => openSessionMenu(activeSessionPath, data.session?.messages.length ? data.session?.sessionId.slice(0, 12) : "新会话")}
7229
+ data-session-popover
7230
+ onClick={(event) =>
7231
+ openSessionMenu(activeSessionPath, data.session?.messages.length ? data.session?.sessionId.slice(0, 12) : "新会话", event.currentTarget)
7232
+ }
7103
7233
  type="button"
7104
7234
  >
7105
7235
  ⋯
7106
7236
  </button>
7107
7237
  )}
7108
- {!sessionSelectionMode && activeSessionPath && sessionMenuPath === activeSessionPath && sessionMenuOpen && (
7238
+ {!sessionSelectionMode && activeSessionPath && sessionMenuPath === activeSessionPath && sessionMenuOpen && sessionMenuPosition && (
7109
7239
  <SessionActionMenu
7110
7240
  busy={sessionActionBusy}
7241
+ position={sessionMenuPosition}
7111
7242
  onArchive={() => {
7112
7243
  closeSessionMenu();
7113
7244
  setSessionDialog("archive");
@@ -7166,43 +7297,51 @@ export function ControlRoomView({ api = createClientApi(), appVersion }: { api?:
7166
7297
  </button>
7167
7298
  {!sessionSelectionMode && typeof session.path === "string" && (
7168
7299
  <button
7300
+ aria-expanded={sessionMenuOpen && sessionMenuPath === session.path}
7301
+ aria-haspopup="menu"
7169
7302
  aria-label={`会话操作 ${value(session.name ?? session.firstMessage, "未命名会话")}`}
7170
7303
  className="session-row-more"
7304
+ data-session-popover
7171
7305
  onClick={(event) => {
7172
7306
  event.stopPropagation();
7173
- openSessionMenu(session.path as string, value(session.name ?? session.firstMessage, ""));
7307
+ openSessionMenu(session.path as string, value(session.name ?? session.firstMessage, ""), event.currentTarget);
7174
7308
  }}
7175
7309
  type="button"
7176
7310
  >
7177
7311
  ⋯
7178
7312
  </button>
7179
7313
  )}
7180
- {!sessionSelectionMode && typeof session.path === "string" && sessionMenuPath === session.path && sessionMenuOpen && (
7181
- <SessionActionMenu
7182
- busy={sessionActionBusy}
7183
- onArchive={() => {
7184
- closeSessionMenu();
7185
- setSessionDialog("archive");
7186
- }}
7187
- onDelete={() => {
7188
- closeSessionMenu();
7189
- setSessionDialog("delete");
7190
- }}
7191
- onFork={() =>
7192
- void sessionAction(async () => {
7193
- const result = await api.forkSession(session.path as string);
7194
- if (result.sessionFile) {
7195
- setSelectedSessionPath(result.sessionFile);
7196
- await api.openSession(result.sessionFile);
7197
- }
7198
- })
7199
- }
7200
- onRename={() => {
7201
- closeSessionMenu();
7202
- setSessionDialog("rename");
7203
- }}
7204
- />
7205
- )}
7314
+ {!sessionSelectionMode &&
7315
+ typeof session.path === "string" &&
7316
+ sessionMenuPath === session.path &&
7317
+ sessionMenuOpen &&
7318
+ sessionMenuPosition && (
7319
+ <SessionActionMenu
7320
+ busy={sessionActionBusy}
7321
+ position={sessionMenuPosition}
7322
+ onArchive={() => {
7323
+ closeSessionMenu();
7324
+ setSessionDialog("archive");
7325
+ }}
7326
+ onDelete={() => {
7327
+ closeSessionMenu();
7328
+ setSessionDialog("delete");
7329
+ }}
7330
+ onFork={() =>
7331
+ void sessionAction(async () => {
7332
+ const result = await api.forkSession(session.path as string);
7333
+ if (result.sessionFile) {
7334
+ setSelectedSessionPath(result.sessionFile);
7335
+ await api.openSession(result.sessionFile);
7336
+ }
7337
+ })
7338
+ }
7339
+ onRename={() => {
7340
+ closeSessionMenu();
7341
+ setSessionDialog("rename");
7342
+ }}
7343
+ />
7344
+ )}
7206
7345
  </div>
7207
7346
  ))}
7208
7347
  </div>
@@ -7308,7 +7447,7 @@ export function ControlRoomView({ api = createClientApi(), appVersion }: { api?:
7308
7447
  </footer>
7309
7448
  </aside>
7310
7449
  <section className="main-pane">
7311
- <header className="main-header">
7450
+ <header className={`main-header ${!settings && page === "session" ? "session-track" : ""}`}>
7312
7451
  <div className="active-heading">
7313
7452
  <strong>
7314
7453
  {settings
@@ -7380,17 +7519,91 @@ export function ControlRoomView({ api = createClientApi(), appVersion }: { api?:
7380
7519
  {!settings && page === "session" && <span aria-hidden="true" className="header-divider"></span>}
7381
7520
  {!settings && page === "session" && (
7382
7521
  <>
7383
- <button
7384
- className="session-menu"
7385
- onClick={() => {
7386
- setSessionMenuPath(undefined);
7387
- setSessionMenuOpen((current) => !current);
7388
- }}
7389
- type="button"
7390
- aria-label="会话操作"
7391
- >
7392
- ⋯
7393
- </button>
7522
+ <div className="session-menu-wrap" data-session-popover>
7523
+ <button
7524
+ aria-expanded={sessionMenuOpen && !sessionMenuPath}
7525
+ aria-haspopup="menu"
7526
+ className="session-menu"
7527
+ onClick={(event) => {
7528
+ const closeCurrentMenu = sessionMenuOpen && !sessionMenuPath;
7529
+ setSessionMenuPath(undefined);
7530
+ setSessionMenuPosition(undefined);
7531
+ sessionPopoverTriggerRef.current = event.currentTarget;
7532
+ setSessionActionTarget(
7533
+ !closeCurrentMenu && activeSessionPath
7534
+ ? { name: data.session?.sessionId?.slice(0, 12) || "当前会话", path: activeSessionPath }
7535
+ : undefined,
7536
+ );
7537
+ setSessionMenuOpen(!closeCurrentMenu);
7538
+ }}
7539
+ type="button"
7540
+ aria-label="会话操作"
7541
+ >
7542
+ ⋯
7543
+ </button>
7544
+ {sessionMenuOpen && !sessionMenuPath && (
7545
+ <div className="session-menu-popover compact-session-menu" role="menu">
7546
+ <button
7547
+ className="session-action"
7548
+ disabled={!activeSessionPath || sessionActionBusy}
7549
+ onClick={() => {
7550
+ setSessionNameDraft(data.session?.sessionId?.slice(0, 12) ?? "");
7551
+ setSessionDialog("rename");
7552
+ setSessionMenuOpen(false);
7553
+ }}
7554
+ role="menuitem"
7555
+ type="button"
7556
+ >
7557
+ <strong>重命名</strong>
7558
+ <small>设置一个容易识别的名称</small>
7559
+ </button>
7560
+ <button
7561
+ className="session-action"
7562
+ disabled={!activeSessionPath || sessionActionBusy}
7563
+ onClick={() =>
7564
+ void sessionAction(async () => {
7565
+ const result = await api.forkSession(activeSessionPath as string);
7566
+ if (result.sessionFile) {
7567
+ setSelectedSessionPath(result.sessionFile);
7568
+ await api.openSession(result.sessionFile);
7569
+ }
7570
+ })
7571
+ }
7572
+ role="menuitem"
7573
+ type="button"
7574
+ >
7575
+ <strong>复制会话</strong>
7576
+ <small>复制上下文并打开副本</small>
7577
+ </button>
7578
+ <button
7579
+ className="session-action"
7580
+ disabled={!activeSessionPath || sessionActionBusy}
7581
+ onClick={() => {
7582
+ setSessionMenuOpen(false);
7583
+ setSessionDialog("archive");
7584
+ }}
7585
+ role="menuitem"
7586
+ type="button"
7587
+ >
7588
+ <strong>归档会话</strong>
7589
+ <small>从默认列表隐藏</small>
7590
+ </button>
7591
+ <button
7592
+ className="session-action danger"
7593
+ disabled={!activeSessionPath || sessionActionBusy}
7594
+ onClick={() => {
7595
+ setSessionMenuOpen(false);
7596
+ setSessionDialog("delete");
7597
+ }}
7598
+ role="menuitem"
7599
+ type="button"
7600
+ >
7601
+ <strong>删除会话</strong>
7602
+ <small>永久删除本地记录</small>
7603
+ </button>
7604
+ </div>
7605
+ )}
7606
+ </div>
7394
7607
  <button
7395
7608
  aria-label={details !== undefined ? "关闭详情" : "打开详情"}
7396
7609
  aria-pressed={details !== undefined}
@@ -7402,64 +7615,6 @@ export function ControlRoomView({ api = createClientApi(), appVersion }: { api?:
7402
7615
  </button>
7403
7616
  </>
7404
7617
  )}
7405
- {!settings && page === "session" && sessionMenuOpen && !sessionMenuPath && (
7406
- <div className="session-menu-popover compact-session-menu">
7407
- <button
7408
- className="session-action"
7409
- disabled={!activeSessionPath || sessionActionBusy}
7410
- onClick={() => {
7411
- setSessionNameDraft(data.session?.sessionId?.slice(0, 12) ?? "");
7412
- setSessionDialog("rename");
7413
- setSessionMenuOpen(false);
7414
- }}
7415
- type="button"
7416
- >
7417
- <strong>重命名</strong>
7418
- <small>设置一个容易识别的名称</small>
7419
- </button>
7420
- <button
7421
- className="session-action"
7422
- disabled={!activeSessionPath || sessionActionBusy}
7423
- onClick={() =>
7424
- void sessionAction(async () => {
7425
- const result = await api.forkSession(activeSessionPath as string);
7426
- if (result.sessionFile) {
7427
- setSelectedSessionPath(result.sessionFile);
7428
- await api.openSession(result.sessionFile);
7429
- }
7430
- })
7431
- }
7432
- type="button"
7433
- >
7434
- <strong>复制会话</strong>
7435
- <small>复制上下文并打开副本</small>
7436
- </button>
7437
- <button
7438
- className="session-action"
7439
- disabled={!activeSessionPath || sessionActionBusy}
7440
- onClick={() => {
7441
- setSessionMenuOpen(false);
7442
- setSessionDialog("archive");
7443
- }}
7444
- type="button"
7445
- >
7446
- <strong>归档会话</strong>
7447
- <small>从默认列表隐藏</small>
7448
- </button>
7449
- <button
7450
- className="session-action danger"
7451
- disabled={!activeSessionPath || sessionActionBusy}
7452
- onClick={() => {
7453
- setSessionMenuOpen(false);
7454
- setSessionDialog("delete");
7455
- }}
7456
- type="button"
7457
- >
7458
- <strong>删除会话</strong>
7459
- <small>永久删除本地记录</small>
7460
- </button>
7461
- </div>
7462
- )}
7463
7618
  </header>
7464
7619
  {refreshIssues.length > 0 && (
7465
7620
  <div aria-live="polite" className="refresh-warning" role="status">
@@ -7522,19 +7677,27 @@ export function ControlRoomView({ api = createClientApi(), appVersion }: { api?:
7522
7677
  busy={sessionActionBusy}
7523
7678
  count={sessionDialog === "batch-delete" ? selectedSessionPaths.size : undefined}
7524
7679
  kind={sessionDialog}
7525
- name={sessionDialog === "rename" ? undefined : value(data.session?.sessionId, "当前会话")}
7680
+ name={
7681
+ sessionDialog === "rename" || sessionDialog === "batch-delete"
7682
+ ? undefined
7683
+ : (sessionActionTarget?.name ?? value(data.session?.sessionId, "当前会话"))
7684
+ }
7526
7685
  onChange={setSessionNameDraft}
7527
- onClose={() => setSessionDialog(undefined)}
7686
+ onClose={() => {
7687
+ setSessionDialog(undefined);
7688
+ setSessionActionTarget(undefined);
7689
+ }}
7528
7690
  onConfirm={() => {
7529
- if (sessionDialog === "rename" && activeSessionPath) {
7530
- void sessionAction(() => api.renameSession(activeSessionPath, sessionNameDraft.trim()).then(() => undefined));
7531
- } else if (sessionDialog === "archive" && activeSessionPath) {
7691
+ const targetPath = sessionActionTarget?.path ?? activeSessionPath;
7692
+ if (sessionDialog === "rename" && targetPath) {
7693
+ void sessionAction(() => api.renameSession(targetPath, sessionNameDraft.trim()).then(() => undefined));
7694
+ } else if (sessionDialog === "archive" && targetPath) {
7532
7695
  void sessionAction(async () => {
7533
- await api.setSessionMetadata(activeSessionPath, { archived: true });
7696
+ await api.setSessionMetadata(targetPath, { archived: true });
7534
7697
  });
7535
- } else if (sessionDialog === "delete" && activeSessionPath) {
7698
+ } else if (sessionDialog === "delete" && targetPath) {
7536
7699
  void sessionAction(async () => {
7537
- await api.deleteSession(activeSessionPath);
7700
+ await api.deleteSession(targetPath);
7538
7701
  });
7539
7702
  } else if (sessionDialog === "batch-delete") {
7540
7703
  void sessionAction(() => api.batchSessions("delete", [...selectedSessionPaths]).then(() => undefined));