@rexvane/piweb 0.3.20 → 0.3.22

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 (317) hide show
  1. package/.next/BUILD_ID +1 -1
  2. package/.next/app-path-routes-manifest.json +6 -4
  3. package/.next/build-manifest.json +5 -5
  4. package/.next/next-minimal-server.js.nft.json +1 -1
  5. package/.next/next-server.js.nft.json +1 -1
  6. package/.next/prerender-manifest.json +7 -7
  7. package/.next/react-loadable-manifest.json +12 -12
  8. package/.next/required-server-files.js +18 -2
  9. package/.next/required-server-files.json +18 -2
  10. package/.next/routes-manifest.json +12 -0
  11. package/.next/server/app/_global-error/page.js +2 -2
  12. package/.next/server/app/_global-error/page.js.nft.json +1 -1
  13. package/.next/server/app/_global-error/page_client-reference-manifest.js +1 -1
  14. package/.next/server/app/_global-error.html +1 -1
  15. package/.next/server/app/_global-error.meta +8 -0
  16. package/.next/server/app/_global-error.rsc +15 -11
  17. package/.next/server/app/_global-error.segments/_full.segment.rsc +15 -11
  18. package/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +20 -16
  19. package/.next/server/app/_global-error.segments/_tree.segment.rsc +3 -1
  20. package/.next/server/app/_not-found/page.js +1 -1
  21. package/.next/server/app/_not-found/page.js.nft.json +1 -1
  22. package/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  23. package/.next/server/app/_not-found.html +1 -1
  24. package/.next/server/app/_not-found.meta +8 -0
  25. package/.next/server/app/_not-found.rsc +17 -13
  26. package/.next/server/app/_not-found.segments/_full.segment.rsc +17 -13
  27. package/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +24 -20
  28. package/.next/server/app/_not-found.segments/_tree.segment.rsc +4 -2
  29. package/.next/server/app/api/agent/[id]/events/route.js +3 -3
  30. package/.next/server/app/api/agent/[id]/events/route.js.nft.json +1 -1
  31. package/.next/server/app/api/agent/[id]/route.js +1 -1
  32. package/.next/server/app/api/agent/[id]/route.js.nft.json +1 -1
  33. package/.next/server/app/api/agent/[id]/tree/route.js +1 -1
  34. package/.next/server/app/api/agent/[id]/tree/route.js.nft.json +1 -1
  35. package/.next/server/app/api/agent/new/route.js +1 -1
  36. package/.next/server/app/api/agent/new/route.js.nft.json +1 -1
  37. package/.next/server/app/api/config/route.js +1 -1
  38. package/.next/server/app/api/config/route.js.nft.json +1 -1
  39. package/.next/server/app/api/files/route.js +1 -1
  40. package/.next/server/app/api/files/route.js.nft.json +1 -1
  41. package/.next/server/app/api/fs/route.js +1 -1
  42. package/.next/server/app/api/fs/route.js.nft.json +1 -1
  43. package/.next/server/app/api/git/route.js +4 -4
  44. package/.next/server/app/api/git/route.js.nft.json +1 -1
  45. package/.next/server/app/api/github/route.js +5 -0
  46. package/.next/server/app/api/github/route.js.nft.json +1 -0
  47. package/.next/server/app/api/github/route_client-reference-manifest.js +1 -0
  48. package/.next/server/app/api/growth/route.js +1 -1
  49. package/.next/server/app/api/growth/route.js.nft.json +1 -1
  50. package/.next/server/app/api/health/route.js +1 -1
  51. package/.next/server/app/api/health/route.js.nft.json +1 -1
  52. package/.next/server/app/api/models/defaults/route.js +1 -0
  53. package/.next/server/app/api/models/defaults/route.js.nft.json +1 -0
  54. package/.next/server/app/api/models/defaults/route_client-reference-manifest.js +1 -0
  55. package/.next/server/app/api/models/route.js +1 -1
  56. package/.next/server/app/api/models/route.js.nft.json +1 -1
  57. package/.next/server/app/api/pi-settings/route.js +1 -1
  58. package/.next/server/app/api/pi-settings/route.js.nft.json +1 -1
  59. package/.next/server/app/api/plugins/route.js +1 -1
  60. package/.next/server/app/api/plugins/route.js.nft.json +1 -1
  61. package/.next/server/app/api/prompts/route.js +1 -1
  62. package/.next/server/app/api/prompts/route.js.nft.json +1 -1
  63. package/.next/server/app/api/remote/route.js +3 -3
  64. package/.next/server/app/api/remote/route.js.nft.json +1 -1
  65. package/.next/server/app/api/security/route.js +1 -1
  66. package/.next/server/app/api/security/route.js.nft.json +1 -1
  67. package/.next/server/app/api/sessions/[id]/export/route.js +1 -1
  68. package/.next/server/app/api/sessions/[id]/export/route.js.nft.json +1 -1
  69. package/.next/server/app/api/sessions/[id]/route.js +1 -1
  70. package/.next/server/app/api/sessions/[id]/route.js.nft.json +1 -1
  71. package/.next/server/app/api/sessions/import/route.js +2 -2
  72. package/.next/server/app/api/sessions/import/route.js.nft.json +1 -1
  73. package/.next/server/app/api/sessions/route.js +1 -1
  74. package/.next/server/app/api/sessions/route.js.nft.json +1 -1
  75. package/.next/server/app/api/skills/route.js +2 -2
  76. package/.next/server/app/api/skills/route.js.nft.json +1 -1
  77. package/.next/server/app/api/update/route.js +2 -2
  78. package/.next/server/app/api/update/route.js.nft.json +1 -1
  79. package/.next/server/app/api/version/route.js +1 -1
  80. package/.next/server/app/api/version/route.js.nft.json +1 -1
  81. package/.next/server/app/api/web-auth/route.js +1 -1
  82. package/.next/server/app/api/web-auth/route.js.nft.json +1 -1
  83. package/.next/server/app/api/workspaces/route.js +1 -1
  84. package/.next/server/app/api/workspaces/route.js.nft.json +1 -1
  85. package/.next/server/app/icon.svg/route.js +1 -1
  86. package/.next/server/app/icon.svg/route.js.nft.json +1 -1
  87. package/.next/server/app/icon.svg.meta +1 -1
  88. package/.next/server/app/index.html +1 -1
  89. package/.next/server/app/index.meta +8 -0
  90. package/.next/server/app/index.rsc +19 -13
  91. package/.next/server/app/index.segments/__PAGE__.segment.rsc +25 -19
  92. package/.next/server/app/index.segments/_full.segment.rsc +19 -13
  93. package/.next/server/app/index.segments/_tree.segment.rsc +3 -2
  94. package/.next/server/app/login/page.js +1 -1
  95. package/.next/server/app/login/page.js.nft.json +1 -1
  96. package/.next/server/app/login/page_client-reference-manifest.js +1 -1
  97. package/.next/server/app/login.html +1 -1
  98. package/.next/server/app/login.meta +8 -0
  99. package/.next/server/app/login.rsc +23 -17
  100. package/.next/server/app/login.segments/_full.segment.rsc +23 -17
  101. package/.next/server/app/login.segments/_tree.segment.rsc +4 -2
  102. package/.next/server/app/login.segments/login/__PAGE__.segment.rsc +30 -24
  103. package/.next/server/app/page.js +1 -1
  104. package/.next/server/app/page.js.nft.json +1 -1
  105. package/.next/server/app/page_client-reference-manifest.js +1 -1
  106. package/.next/server/app-paths-manifest.json +6 -4
  107. package/.next/server/chunks/229.js +1 -1
  108. package/.next/server/chunks/421.js +1 -0
  109. package/.next/server/chunks/430.js +215 -31
  110. package/.next/server/chunks/496.js +1 -1
  111. package/.next/server/chunks/538.js +1 -1
  112. package/.next/server/chunks/662.js +2 -0
  113. package/.next/server/chunks/686.js +1 -1
  114. package/.next/server/chunks/807.js +1 -1
  115. package/.next/server/chunks/813.js +23 -1
  116. package/.next/server/chunks/920.js +2 -2
  117. package/.next/server/chunks/924.js +1 -1
  118. package/.next/server/chunks/970.js +305 -0
  119. package/.next/server/instrumentation.js +1 -1
  120. package/.next/server/instrumentation.js.nft.json +1 -1
  121. package/.next/server/middleware-build-manifest.js +1 -1
  122. package/.next/server/middleware-react-loadable-manifest.js +1 -1
  123. package/.next/server/middleware.js +185 -23
  124. package/.next/server/middleware.js.nft.json +1 -1
  125. package/.next/server/pages/404.html +1 -1
  126. package/.next/server/pages/500.html +1 -1
  127. package/.next/server/preview-props.json +5 -0
  128. package/.next/server/server-reference-manifest.json +1 -1
  129. package/.next/static/WMazYvD-gbeLsCUfUxvvM/_buildManifest.js +1 -0
  130. package/.next/static/chunks/316-698d10eb465bd8ef.js +1 -0
  131. package/.next/static/chunks/3794-0978865cda773008.js +52 -0
  132. package/.next/static/chunks/4673.4cf66cbdd8364c57.js +1 -0
  133. package/.next/static/chunks/4686-09fcd0028079006c.js +1 -0
  134. package/.next/static/chunks/4778.eddd3a5813f85824.js +1 -0
  135. package/.next/static/chunks/4bd1b696-e199e788e58acf79.js +1 -0
  136. package/.next/static/chunks/{5173.91ddb803785009f8.js → 5173.dfd1cbb795b9a21c.js} +1 -1
  137. package/.next/static/chunks/618.949981da99be7182.js +4 -0
  138. package/.next/static/chunks/7411.67366dc5032a141d.js +1 -0
  139. package/.next/static/chunks/8552.25c39ef4c995eba0.js +1 -0
  140. package/.next/static/chunks/app/_global-error/page-f977af7f858990a2.js +1 -0
  141. package/.next/static/chunks/app/api/agent/[id]/events/route-f977af7f858990a2.js +1 -0
  142. package/.next/static/chunks/app/api/agent/[id]/route-f977af7f858990a2.js +1 -0
  143. package/.next/static/chunks/app/api/agent/[id]/tree/route-f977af7f858990a2.js +1 -0
  144. package/.next/static/chunks/app/api/agent/new/route-f977af7f858990a2.js +1 -0
  145. package/.next/static/chunks/app/api/config/route-f977af7f858990a2.js +1 -0
  146. package/.next/static/chunks/app/api/files/route-f977af7f858990a2.js +1 -0
  147. package/.next/static/chunks/app/api/fs/route-f977af7f858990a2.js +1 -0
  148. package/.next/static/chunks/app/api/git/route-f977af7f858990a2.js +1 -0
  149. package/.next/static/chunks/app/api/github/route-f977af7f858990a2.js +1 -0
  150. package/.next/static/chunks/app/api/growth/route-f977af7f858990a2.js +1 -0
  151. package/.next/static/chunks/app/api/health/route-f977af7f858990a2.js +1 -0
  152. package/.next/static/chunks/app/api/models/defaults/route-f977af7f858990a2.js +1 -0
  153. package/.next/static/chunks/app/api/models/route-f977af7f858990a2.js +1 -0
  154. package/.next/static/chunks/app/api/pi-settings/route-f977af7f858990a2.js +1 -0
  155. package/.next/static/chunks/app/api/plugins/route-f977af7f858990a2.js +1 -0
  156. package/.next/static/chunks/app/api/prompts/route-f977af7f858990a2.js +1 -0
  157. package/.next/static/chunks/app/api/remote/route-f977af7f858990a2.js +1 -0
  158. package/.next/static/chunks/app/api/security/route-f977af7f858990a2.js +1 -0
  159. package/.next/static/chunks/app/api/sessions/[id]/export/route-f977af7f858990a2.js +1 -0
  160. package/.next/static/chunks/app/api/sessions/[id]/route-f977af7f858990a2.js +1 -0
  161. package/.next/static/chunks/app/api/sessions/import/route-f977af7f858990a2.js +1 -0
  162. package/.next/static/chunks/app/api/sessions/route-f977af7f858990a2.js +1 -0
  163. package/.next/static/chunks/app/api/skills/route-f977af7f858990a2.js +1 -0
  164. package/.next/static/chunks/app/api/update/route-f977af7f858990a2.js +1 -0
  165. package/.next/static/chunks/app/api/version/route-f977af7f858990a2.js +1 -0
  166. package/.next/static/chunks/app/api/web-auth/route-f977af7f858990a2.js +1 -0
  167. package/.next/static/chunks/app/api/workspaces/route-f977af7f858990a2.js +1 -0
  168. package/.next/static/chunks/app/page-fc4ed2ff29b6235e.js +1 -0
  169. package/.next/static/chunks/framework-47b723d27f4a4963.js +1 -0
  170. package/.next/static/chunks/main-5403f0764579e5fe.js +5 -0
  171. package/.next/static/chunks/next/dist/client/components/builtin/app-error-f977af7f858990a2.js +1 -0
  172. package/.next/static/chunks/next/dist/client/components/builtin/forbidden-f977af7f858990a2.js +1 -0
  173. package/.next/static/chunks/next/dist/client/components/builtin/not-found-f977af7f858990a2.js +1 -0
  174. package/.next/static/chunks/next/dist/client/components/builtin/unauthorized-f977af7f858990a2.js +1 -0
  175. package/.next/static/chunks/webpack-22ebffc20adb44ae.js +1 -0
  176. package/.next/static/css/6671d0d1589fdc0e.css +1 -0
  177. package/.next/static/css/{2959bd6b626b7ffe.css → f15a853af8b5127f.css} +1 -1
  178. package/.next/types/app/api/agent/[id]/events/route.ts +3 -1
  179. package/.next/types/app/api/agent/[id]/route.ts +3 -1
  180. package/.next/types/app/api/agent/[id]/tree/route.ts +3 -1
  181. package/.next/types/app/api/agent/new/route.ts +3 -1
  182. package/.next/types/app/api/config/route.ts +3 -1
  183. package/.next/types/app/api/files/route.ts +3 -1
  184. package/.next/types/app/api/fs/route.ts +3 -1
  185. package/.next/types/app/api/git/route.ts +3 -1
  186. package/.next/types/app/api/github/route.ts +355 -0
  187. package/.next/types/app/api/growth/route.ts +3 -1
  188. package/.next/types/app/api/health/route.ts +3 -1
  189. package/.next/types/app/api/models/defaults/route.ts +355 -0
  190. package/.next/types/app/api/models/route.ts +3 -1
  191. package/.next/types/app/api/pi-settings/route.ts +3 -1
  192. package/.next/types/app/api/plugins/route.ts +3 -1
  193. package/.next/types/app/api/prompts/route.ts +3 -1
  194. package/.next/types/app/api/remote/route.ts +3 -1
  195. package/.next/types/app/api/security/route.ts +3 -1
  196. package/.next/types/app/api/sessions/[id]/export/route.ts +3 -1
  197. package/.next/types/app/api/sessions/[id]/route.ts +3 -1
  198. package/.next/types/app/api/sessions/import/route.ts +3 -1
  199. package/.next/types/app/api/sessions/route.ts +3 -1
  200. package/.next/types/app/api/skills/route.ts +3 -1
  201. package/.next/types/app/api/update/route.ts +3 -1
  202. package/.next/types/app/api/version/route.ts +3 -1
  203. package/.next/types/app/api/web-auth/route.ts +3 -1
  204. package/.next/types/app/api/workspaces/route.ts +3 -1
  205. package/.next/types/app/layout.ts +4 -1
  206. package/.next/types/app/login/page.ts +4 -1
  207. package/.next/types/app/page.ts +4 -1
  208. package/.next/types/cache-life.d.ts +11 -43
  209. package/.next/types/routes.d.ts +3 -1
  210. package/.next/types/validator.ts +76 -2
  211. package/README.md +14 -5
  212. package/next.config.ts +4 -2
  213. package/package.json +2 -2
  214. package/scripts/release.mjs +1 -1
  215. package/src/app/api/agent/[id]/events/route.ts +42 -13
  216. package/src/app/api/files/route.ts +5 -2
  217. package/src/app/api/github/route.ts +26 -0
  218. package/src/app/api/models/defaults/route.ts +18 -0
  219. package/src/app/api/models/route.ts +24 -1
  220. package/src/app/api/update/route.ts +18 -2
  221. package/src/app/globals.css +275 -0
  222. package/src/components/AppShell.tsx +89 -33
  223. package/src/components/ChatInput.tsx +245 -63
  224. package/src/components/ChatWindow.tsx +123 -108
  225. package/src/components/ContextMeter.tsx +204 -123
  226. package/src/components/DrawerPanel.tsx +19 -26
  227. package/src/components/ExtensionUI.tsx +8 -2
  228. package/src/components/GitPanel.tsx +110 -40
  229. package/src/components/MarkdownBlock.tsx +333 -37
  230. package/src/components/ProjectPanel.tsx +55 -30
  231. package/src/components/PromptPanel.tsx +2 -4
  232. package/src/components/ProviderSetupModal.module.css +20 -0
  233. package/src/components/ProviderSetupModal.tsx +134 -29
  234. package/src/components/SettingsPanel.tsx +365 -80
  235. package/src/components/TrajectoryView.tsx +4 -6
  236. package/src/components/chat/chat-scroll.ts +18 -0
  237. package/src/components/composer/ComposerImages.tsx +306 -0
  238. package/src/components/update/CopyCommand.tsx +34 -0
  239. package/src/components/update/UpdateNotice.tsx +130 -0
  240. package/src/components/update/update-store.ts +155 -0
  241. package/src/hooks/useGrowth.ts +3 -0
  242. package/src/hooks/usePiWeb.ts +54 -27
  243. package/src/hooks/useWorkspaceFiles.ts +93 -0
  244. package/src/i18n.tsx +182 -6
  245. package/src/instrumentation-node.ts +3 -0
  246. package/src/lib/agent-manager.ts +193 -119
  247. package/src/lib/chat/event-stream.ts +113 -0
  248. package/src/lib/chat/stream-settings.ts +192 -0
  249. package/src/lib/files-service.ts +100 -1
  250. package/src/lib/format.ts +56 -0
  251. package/src/lib/git-service.ts +45 -13
  252. package/src/lib/github-identity.ts +245 -0
  253. package/src/lib/growth-service.ts +6 -2
  254. package/src/lib/hero-model-memory.ts +28 -0
  255. package/src/lib/model-defaults.ts +105 -0
  256. package/src/lib/model-draft.ts +257 -9
  257. package/src/lib/model-endpoint.ts +23 -0
  258. package/src/lib/model-files.ts +33 -0
  259. package/src/lib/models-service.ts +240 -78
  260. package/src/lib/pi.ts +27 -2
  261. package/src/lib/process-format.ts +3 -2
  262. package/src/lib/session/metrics.ts +31 -0
  263. package/src/lib/thinking-level.ts +39 -0
  264. package/src/lib/trajectory.ts +17 -3
  265. package/src/lib/types.ts +19 -1
  266. package/src/lib/update/startup-check.ts +117 -0
  267. package/src/lib/update-service.ts +23 -3
  268. package/src/lib/workspace-index.ts +270 -0
  269. package/.next/server/chunks/131.js +0 -1
  270. package/.next/server/chunks/243.js +0 -1
  271. package/.next/server/chunks/741.js +0 -165
  272. package/.next/static/_Y3Lq0lI5RJ1tFSOnDi0L/_buildManifest.js +0 -1
  273. package/.next/static/chunks/316-5ea443ee6b512aa9.js +0 -1
  274. package/.next/static/chunks/3794-3679f1c05160977c.js +0 -52
  275. package/.next/static/chunks/4673.8a5a9e6855350e34.js +0 -1
  276. package/.next/static/chunks/4686-b1e9fa876d0ea671.js +0 -1
  277. package/.next/static/chunks/4778.e4c9ed093a413bc4.js +0 -1
  278. package/.next/static/chunks/4bd1b696-93bf506b236d6248.js +0 -1
  279. package/.next/static/chunks/618.ee5d5bf1c647e483.js +0 -4
  280. package/.next/static/chunks/839.d3fe337a9117c325.js +0 -1
  281. package/.next/static/chunks/8552.6bfe3e101250b20f.js +0 -1
  282. package/.next/static/chunks/app/_global-error/page-b179d9f7f6f2daf9.js +0 -1
  283. package/.next/static/chunks/app/api/agent/[id]/events/route-b179d9f7f6f2daf9.js +0 -1
  284. package/.next/static/chunks/app/api/agent/[id]/route-b179d9f7f6f2daf9.js +0 -1
  285. package/.next/static/chunks/app/api/agent/[id]/tree/route-b179d9f7f6f2daf9.js +0 -1
  286. package/.next/static/chunks/app/api/agent/new/route-b179d9f7f6f2daf9.js +0 -1
  287. package/.next/static/chunks/app/api/config/route-b179d9f7f6f2daf9.js +0 -1
  288. package/.next/static/chunks/app/api/files/route-b179d9f7f6f2daf9.js +0 -1
  289. package/.next/static/chunks/app/api/fs/route-b179d9f7f6f2daf9.js +0 -1
  290. package/.next/static/chunks/app/api/git/route-b179d9f7f6f2daf9.js +0 -1
  291. package/.next/static/chunks/app/api/growth/route-b179d9f7f6f2daf9.js +0 -1
  292. package/.next/static/chunks/app/api/health/route-b179d9f7f6f2daf9.js +0 -1
  293. package/.next/static/chunks/app/api/models/route-b179d9f7f6f2daf9.js +0 -1
  294. package/.next/static/chunks/app/api/pi-settings/route-b179d9f7f6f2daf9.js +0 -1
  295. package/.next/static/chunks/app/api/plugins/route-b179d9f7f6f2daf9.js +0 -1
  296. package/.next/static/chunks/app/api/prompts/route-b179d9f7f6f2daf9.js +0 -1
  297. package/.next/static/chunks/app/api/remote/route-b179d9f7f6f2daf9.js +0 -1
  298. package/.next/static/chunks/app/api/security/route-b179d9f7f6f2daf9.js +0 -1
  299. package/.next/static/chunks/app/api/sessions/[id]/export/route-b179d9f7f6f2daf9.js +0 -1
  300. package/.next/static/chunks/app/api/sessions/[id]/route-b179d9f7f6f2daf9.js +0 -1
  301. package/.next/static/chunks/app/api/sessions/import/route-b179d9f7f6f2daf9.js +0 -1
  302. package/.next/static/chunks/app/api/sessions/route-b179d9f7f6f2daf9.js +0 -1
  303. package/.next/static/chunks/app/api/skills/route-b179d9f7f6f2daf9.js +0 -1
  304. package/.next/static/chunks/app/api/update/route-b179d9f7f6f2daf9.js +0 -1
  305. package/.next/static/chunks/app/api/version/route-b179d9f7f6f2daf9.js +0 -1
  306. package/.next/static/chunks/app/api/web-auth/route-b179d9f7f6f2daf9.js +0 -1
  307. package/.next/static/chunks/app/api/workspaces/route-b179d9f7f6f2daf9.js +0 -1
  308. package/.next/static/chunks/app/page-2e425ef83fba0782.js +0 -1
  309. package/.next/static/chunks/framework-c4e651fe89edfa91.js +0 -1
  310. package/.next/static/chunks/main-6159b22a8cb0b7c5.js +0 -5
  311. package/.next/static/chunks/next/dist/client/components/builtin/app-error-b179d9f7f6f2daf9.js +0 -1
  312. package/.next/static/chunks/next/dist/client/components/builtin/forbidden-b179d9f7f6f2daf9.js +0 -1
  313. package/.next/static/chunks/next/dist/client/components/builtin/not-found-b179d9f7f6f2daf9.js +0 -1
  314. package/.next/static/chunks/next/dist/client/components/builtin/unauthorized-b179d9f7f6f2daf9.js +0 -1
  315. package/.next/static/chunks/webpack-3614a8337843456b.js +0 -1
  316. package/.next/static/css/7391c42e1ae617f9.css +0 -1
  317. /package/.next/static/{_Y3Lq0lI5RJ1tFSOnDi0L → WMazYvD-gbeLsCUfUxvvM}/_ssgManifest.js +0 -0
@@ -39,6 +39,8 @@ export interface GrowthApi {
39
39
  range: { from: string; to: string } | null;
40
40
  /** 所选范围的变更 */
41
41
  changes: GrowthChange[];
42
+ /** 当前范围的完整 diff 已读取;加载或失败时不能将 [] 当成零改动。 */
43
+ changesReady?: boolean;
42
44
  tree: TreeNode;
43
45
  /** 所选轮的全部文件路径(快速打开用) */
44
46
  filePaths: string[];
@@ -618,6 +620,7 @@ export function useGrowth({
618
620
  scope,
619
621
  range,
620
622
  changes,
623
+ changesReady: inlineChanges !== null || Boolean(range && rangeChanges?.key === `${key}|${range.from}..${range.to}` && !rangeChanges.error),
621
624
  tree,
622
625
  filePaths,
623
626
  expanded,
@@ -4,6 +4,7 @@
4
4
  * 中央状态:会话列表轮询、当前会话 SSE 订阅、事件折叠(快照→增量)、命令发送。
5
5
  */
6
6
  import { useCallback, useEffect, useMemo, useRef, useState } from "react";
7
+ import { pollEventStream, usesEventPolling } from "@/lib/chat/event-stream";
7
8
  import { createEventBatcher, type EventBatcher } from "@/lib/event-batcher";
8
9
  import type {
9
10
  GrowthConsentRequest,
@@ -75,6 +76,10 @@ export const foldPiWebEvent = (state: PiWebState, evt: WebEvent): PiWebState =>
75
76
  const s = { ...state };
76
77
  switch (evt.type) {
77
78
  case "delta": {
79
+ // 又开始出 token 了:不管有没有收到重试结束事件,重试行都收掉。
80
+ // 只靠 retry_end 不够——SDK 换了事件名、某条路径没发结束事件、旧服务端压根不发,
81
+ // 界面就会挂着「重试 1/5」而模型其实早在正常输出了(用户已反馈两次)。
82
+ s.retryNotice = null;
78
83
  // 新服务端按稳定流 ID 定位;旧协议仍按最近 pending 消息兼容。
79
84
  const msgs = [...s.messages];
80
85
  // 从尾部找:增量几乎总是落在最后一条消息上,长会话里从头扫每个 token 都是一次全表遍历
@@ -194,6 +199,9 @@ export const foldPiWebEvent = (state: PiWebState, evt: WebEvent): PiWebState =>
194
199
  tools: { active: evt.active, all: evt.all.map((n) => ({ name: n })) },
195
200
  };
196
201
  return s;
202
+ case "context_breakdown":
203
+ if (s.snapshot) s.snapshot = { ...s.snapshot, contextBreakdown: evt.breakdown };
204
+ return s;
197
205
  case "resources":
198
206
  if (s.snapshot) {
199
207
  s.snapshot = {
@@ -247,11 +255,17 @@ export const foldPiWebEvent = (state: PiWebState, evt: WebEvent): PiWebState =>
247
255
  s.growth = { ...s.growth, consent: evt.request };
248
256
  return s;
249
257
  case "retry":
250
- // 只保留最后一次尝试:重试过程在界面上是「重试 3/5」这一条,而不是五行报错
258
+ // 只保留最后一次尝试:重试过程在界面上是「重试 3/5」这一条,而不是五行报错
251
259
  s.retryNotice = evt.maxAttempts
252
260
  ? `重试 ${evt.attempt}/${evt.maxAttempts}${evt.message ? `:${evt.message}` : ""}`
253
261
  : `重试 ${evt.attempt}${evt.message ? `:${evt.message}` : ""}`;
254
262
  return s;
263
+ case "retry_end":
264
+ // 重试区间结束(SDK auto_retry_end):立刻收掉重试行。
265
+ // 重试常发生在下一轮 agent 开始之前,而那段间隙不会发 status:isStreaming=false,
266
+ // 所以不能只等 agent_settled —— 否则用户会一直看到「重试 1/5 terminated」。
267
+ s.retryNotice = null;
268
+ return s;
255
269
  case "error":
256
270
  // 旧服务端把自动重试塞在 error 里:同样按通知处理,不占错误条
257
271
  if (/^(自动重试|重试)/.test(evt.message)) s.retryNotice = evt.message;
@@ -350,7 +364,7 @@ export function usePiWeb() {
350
364
  const [orderBy, setOrderByState] = useState<"updated" | "manual">("updated");
351
365
  const [resyncNonce, setResyncNonce] = useState(0);
352
366
  const [snapshotEpoch, setSnapshotEpoch] = useState(0);
353
- const esRef = useRef<EventSource | null>(null);
367
+ const esRef = useRef<{ close(): void } | null>(null);
354
368
  const promptRequestsRef = useRef(new Set<string>());
355
369
  const presetRestoreAttemptRef = useRef<string | null>(null);
356
370
  const presetRestorePromiseRef = useRef<Promise<void> | null>(null);
@@ -674,38 +688,21 @@ export function usePiWeb() {
674
688
  ? { ...current, connected: false }
675
689
  : { ...emptyState(savedToolPreset()), connected: false });
676
690
  let retryDelayMs = 2000;
691
+ const eventsUrl = `/api/agent/${currentId}/events`;
677
692
  const connect = () => {
678
- const es = new EventSource(`/api/agent/${currentId}/events`);
679
- esRef.current = es;
680
- es.onopen = () => {
681
- retryDelayMs = 2000;
682
- setState((s) => ({ ...s, connected: true }));
683
- };
684
- es.onerror = () => {
685
- setState((s) => ({ ...s, connected: false }));
686
- if (esRef.current !== es) return;
687
- // 网络断开时浏览器自己会重连,并带上 Last-Event-ID 让服务端从环形缓冲续放
688
- // (只补漏掉的几条事件)。这时手动重建就等于丢掉续传、每次都拉一份全量快照。
689
- if (es.readyState !== EventSource.CLOSED) return;
690
- // 浏览器对非 200 响应会永久放弃自动重连:这里手动兜底,关掉后延迟重建,
691
- // 重建时会拿到全新快照。
692
- es.close();
693
- esRef.current = null;
694
- const delay = retryDelayMs;
695
- retryDelayMs = Math.min(retryDelayMs * 2, 30_000);
696
- retryTimer = setTimeout(() => {
697
- if (esRef.current === null) connect();
698
- }, delay);
699
- };
700
693
  // 流式事件按帧合并:token 速率再高也只每 16ms 渲染一次(见 event-batcher)
701
694
  const batcher = createEventBatcher<WebEvent>({
702
695
  flush: (events) => setState((s) => events.reduce((acc, evt) => foldPiWebEvent(acc, evt), s)),
703
696
  });
704
697
  batcherRef.current = batcher;
705
- es.onmessage = (e) => {
698
+ const onOpen = () => {
699
+ retryDelayMs = 2000;
700
+ setState((s) => ({ ...s, connected: true }));
701
+ };
702
+ const onMessage = (data: string) => {
706
703
  let parsed: any;
707
704
  try {
708
- parsed = JSON.parse(e.data);
705
+ parsed = JSON.parse(data);
709
706
  } catch {
710
707
  // 单帧损坏(代理截断等)只丢弃该帧,不能让 EventSource 回调抛异常
711
708
  return;
@@ -737,6 +734,36 @@ export function usePiWeb() {
737
734
  batcher.push(parsed as WebEvent);
738
735
  }
739
736
  };
737
+ // Cloudflare 快速隧道不支持 SSE(流被攒成 128KB 一块,快照永远收不全):改用长轮询,
738
+ // 失败退避、Last-Event-ID 续传都在 pollEventStream 里
739
+ if (usesEventPolling(window.location.hostname)) {
740
+ esRef.current = pollEventStream(eventsUrl, {
741
+ onOpen,
742
+ onMessage,
743
+ onError: () => setState((s) => ({ ...s, connected: false })),
744
+ });
745
+ return;
746
+ }
747
+ const es = new EventSource(eventsUrl);
748
+ esRef.current = es;
749
+ es.onopen = onOpen;
750
+ es.onerror = () => {
751
+ setState((s) => ({ ...s, connected: false }));
752
+ if (esRef.current !== es) return;
753
+ // 网络断开时浏览器自己会重连,并带上 Last-Event-ID 让服务端从环形缓冲续放
754
+ // (只补漏掉的几条事件)。这时手动重建就等于丢掉续传、每次都拉一份全量快照。
755
+ if (es.readyState !== EventSource.CLOSED) return;
756
+ // 浏览器对非 200 响应会永久放弃自动重连:这里手动兜底,关掉后延迟重建,
757
+ // 重建时会拿到全新快照。
758
+ es.close();
759
+ esRef.current = null;
760
+ const delay = retryDelayMs;
761
+ retryDelayMs = Math.min(retryDelayMs * 2, 30_000);
762
+ retryTimer = setTimeout(() => {
763
+ if (esRef.current === null) connect();
764
+ }, delay);
765
+ };
766
+ es.onmessage = (e) => onMessage(e.data);
740
767
  };
741
768
  let retryTimer: ReturnType<typeof setTimeout> | undefined;
742
769
  connect();
@@ -792,7 +819,7 @@ export function usePiWeb() {
792
819
  const id = idOf(p);
793
820
  // 工具预设取通用设置的持久化选择;模型/思考级别由 Hero 页显式下发
794
821
  const setup: Record<string, unknown>[] = [{ cmd: "setToolPreset", preset: savedToolPreset() }];
795
- if (overrides.provider && overrides.modelId) setup.push({ cmd: "setModel", provider: overrides.provider, modelId: overrides.modelId });
822
+ if (overrides.provider && overrides.modelId) setup.push({ cmd: "setModel", provider: overrides.provider, modelId: overrides.modelId, fresh: true });
796
823
  if (overrides.thinking) setup.push({ cmd: "setThinkingLevel", level: overrides.thinking });
797
824
  for (const command of setup) {
798
825
  const configured = await sendCommand(command, id);
@@ -0,0 +1,93 @@
1
+ "use client";
2
+
3
+ /**
4
+ * @ 引用的候选文件:打开候选菜单时拉一次全量清单,之后每个字符都在本地匹配。
5
+ * 按工作区缓存(含 in-flight 去重),过期后**先用手上的旧清单渲染、再后台刷新**——
6
+ * 打字过程中绝不能因为一次刷新把候选列表清空。
7
+ */
8
+ import { useEffect, useRef, useState } from "react";
9
+ import { buildCandidates, dirsOfPaths, type PathCandidate } from "@/lib/workspace-index";
10
+
11
+ export interface WorkspaceFiles {
12
+ status: "idle" | "loading" | "ready" | "error";
13
+ candidates: PathCandidate[];
14
+ source: "git" | "disk" | null;
15
+ truncated: boolean;
16
+ error: string | null;
17
+ }
18
+
19
+ /** 清单多久算过期:agent 刚写出来的新文件也要能被引用到,所以不能一直用内存里那份 */
20
+ const TTL_MS = 30_000;
21
+
22
+ interface CachedIndex {
23
+ at: number;
24
+ candidates: PathCandidate[];
25
+ source: "git" | "disk";
26
+ truncated: boolean;
27
+ }
28
+
29
+ const cache = new Map<string, CachedIndex>();
30
+ const inflight = new Map<string, Promise<CachedIndex>>();
31
+
32
+ async function fetchIndex(cwd: string): Promise<CachedIndex> {
33
+ const response = await fetch(`/api/files?index=1&cwd=${encodeURIComponent(cwd)}`, { cache: "no-store" });
34
+ const payload = await response.json();
35
+ if (!response.ok || !payload.success) throw new Error(payload.error || `request failed (${response.status})`);
36
+ const data = payload.data as { files: string[]; source: "git" | "disk"; truncated: boolean };
37
+ const entry: CachedIndex = {
38
+ at: Date.now(),
39
+ candidates: buildCandidates(data.files, dirsOfPaths(data.files)),
40
+ source: data.source,
41
+ truncated: data.truncated,
42
+ };
43
+ cache.set(cwd, entry);
44
+ return entry;
45
+ }
46
+
47
+ /**
48
+ * 同一工作区并发只发一次请求;失败不进缓存(下次打开会重试)。
49
+ * 共享的请求不挂任何一方的 AbortSignal:先打开的输入框关了(中止),后来的还在等同一个结果,
50
+ * 不能让它跟着收到 AbortError。各方用完就不理结果,请求本身让它跑完、顺手填进缓存。
51
+ */
52
+ function loadIndex(cwd: string): Promise<CachedIndex> {
53
+ const running = inflight.get(cwd);
54
+ if (running) return running;
55
+ const promise = fetchIndex(cwd).finally(() => inflight.delete(cwd));
56
+ inflight.set(cwd, promise);
57
+ return promise;
58
+ }
59
+
60
+ export function useWorkspaceFiles(cwd: string, enabled: boolean): WorkspaceFiles {
61
+ const [state, setState] = useState<WorkspaceFiles>({ status: "idle", candidates: [], source: null, truncated: false, error: null });
62
+ const scope = useRef<{ cwd: string; controller: AbortController }>({ cwd: "", controller: new AbortController() });
63
+
64
+ useEffect(() => {
65
+ if (!enabled || !cwd) return;
66
+ const controller = new AbortController();
67
+ scope.current = { cwd, controller };
68
+ const isCurrent = () => scope.current.controller === controller && !controller.signal.aborted;
69
+
70
+ const cached = cache.get(cwd);
71
+ if (cached) {
72
+ // 有旧清单就先显示(旧的总比空白好),过期只在后台补
73
+ setState({ status: "ready", candidates: cached.candidates, source: cached.source, truncated: cached.truncated, error: null });
74
+ if (Date.now() - cached.at < TTL_MS) return () => controller.abort();
75
+ } else {
76
+ setState({ status: "loading", candidates: [], source: null, truncated: false, error: null });
77
+ }
78
+
79
+ void loadIndex(cwd).then(
80
+ (entry) => {
81
+ if (isCurrent()) setState({ status: "ready", candidates: entry.candidates, source: entry.source, truncated: entry.truncated, error: null });
82
+ },
83
+ (error: unknown) => {
84
+ // 有旧清单时忽略这次失败:刷新失败不该把可用候选变成错误页
85
+ if (!isCurrent() || cache.has(cwd)) return;
86
+ setState({ status: "error", candidates: [], source: null, truncated: false, error: error instanceof Error ? error.message : String(error) });
87
+ },
88
+ );
89
+ return () => controller.abort();
90
+ }, [cwd, enabled]);
91
+
92
+ return state;
93
+ }
package/src/i18n.tsx CHANGED
@@ -38,13 +38,37 @@ const zh = {
38
38
  capacityFields: "容量",
39
39
  contextWindowLabel: "上下文窗口",
40
40
  maxTokensLabel: "最大输出",
41
- capacityHint: "留空用默认 · 支持 256K / 2M 写法",
41
+ capacityHint: "留空按 pi 缺省(128K / 16K)· 支持 256K / 2M 写法",
42
+ // 模型能力(视觉)与配置体检
43
+ visionLabel: "视觉",
44
+ visionHint: "该模型能否接收图片。未声明时 pi 按纯文本处理:你贴的截图会被换成一行占位文字(提示语还会说成「模型不支持图像」)",
45
+ visionUnset: "未声明",
46
+ visionOn: "有视觉",
47
+ visionOff: "纯文本",
48
+ imagePreview: "点击放大预览",
49
+ imageViewer: "图片预览",
50
+ imagePrev: "上一张",
51
+ imageNext: "下一张",
52
+ chipExpandMore: "展开 {n}",
53
+ chipCollapse: "收起",
54
+ modelAuditTitle: "{n} 个模型没写能力字段",
55
+ modelAuditDesc: "没写的字段,pi 会按上下文 {ctx}、输出上限 {out}、纯文本处理。端点没报的值不会替你猜:照模型的真实规格填上(支持 256K / 1M 写法),保存时只补空着的格子,已写的值不改。",
56
+ modelAuditColModel: "模型",
57
+ modelAuditColContext: "上下文",
58
+ modelAuditColOutput: "输出上限",
59
+ modelAuditColVision: "视觉",
60
+ modelAuditVisionQuick: "视觉一起设:",
61
+ modelAuditVisionOn: "都有视觉",
62
+ modelAuditVisionOff: "都是纯文本",
63
+ modelAuditSave: "保存 {n} 个模型",
64
+ modelAuditSaveIdle: "填好后保存",
42
65
  capacityInvalid: "第 {n} 行:容量需为数字(可用 K/M)",
43
66
  discoverEmpty: "远端没有返回可识别的模型",
44
67
  candidateTitle: "发现 {count} 个模型,勾选要添加的",
45
68
  candidateAll: "全选",
46
69
  candidatePicked: "已选 {n} 个",
47
70
  candidateAdopt: "添加 {n} 个",
71
+ candidateInCatalog: "已在目录中",
48
72
  runningTool: "Running {name}…",
49
73
  thinkingLive: "Thinking",
50
74
  stepRunning: "Running",
@@ -127,6 +151,10 @@ const zh = {
127
151
  gitNoChanges: "工作区干净,没有变更",
128
152
  gitStatusUnknown: "Git 状态读取失败,无法确认是否有变更",
129
153
  gitRecentCommits: "最近提交",
154
+ gitStagingArea: "暂存区",
155
+ gitWorkingTree: "工作区",
156
+ gitGithubAccount: "GitHub 账号",
157
+ gitGithubUnknown: "未识别到 GitHub 账号",
130
158
  toolNoOutput: "无输出",
131
159
  toolFailed: "失败",
132
160
  thinkingClamped: "当前模型不支持「{requested}」,已改为「{level}」",
@@ -285,6 +313,11 @@ const zh = {
285
313
  extensionStatus: "扩展状态",
286
314
  contextUsed: "上下文已用",
287
315
  contextUnknown: "上下文用量未知",
316
+ contextEstimated: "上下文估算",
317
+ contextApiInput: "最近请求输入(API)",
318
+ contextCharacterBreakdown: "内容分类(字符数)",
319
+ contextCharacterHint: "按当前文本统计,图片不计。API 未提供分类 token 用量。",
320
+ contextChars: "字符",
288
321
  contextSystem: "系统提示词",
289
322
  contextMessages: "对话消息",
290
323
  contextTools: "工具",
@@ -323,6 +356,23 @@ const zh = {
323
356
  contextCompacted: "Compacted Data",
324
357
  contextAutoCompactBuffer: "Auto-Compact Buffer",
325
358
  contextFreeSpace: "Free Space",
359
+ contextColdHint: "会话还没启动,System Prompt 和工具定义要启动后才算得出",
360
+ contextCountNow: "现在统计",
361
+ contextCounting: "统计中…",
362
+ contextWindowEdit: "修改",
363
+ contextWindowBuiltin: "pi 内置目录:{value}",
364
+ contextWindowUseBuiltin: "填入",
365
+ contextWindowScope: "改的是这个模型的全局配置(models.json),pi 终端和其它会话一起生效",
366
+ contextWindowOverBuiltin: "比内置目录大:超过模型实际上限时,请求会被上游拒绝(400)",
367
+ contextWindowNoBuiltin: "填模型实际支持的上限;填大了,请求会被上游拒绝(400)",
368
+ contextWindowInvalid: "填正整数,可带 K / M(如 200K)",
369
+ // @ 文件引用(输入 @ 弹候选)
370
+ mentionFiles: "@ 引用文件 / 文件夹",
371
+ mentionHint: "↑↓ 选择 · Enter 引用 · Esc 关闭",
372
+ mentionNoMatch: "没有匹配的文件",
373
+ mentionTruncated: "候选已截断",
374
+ mentionDiskSource: "目录遍历",
375
+ mentionDiskSourceHint: "当前工作区不是 git 仓库(或没装 git):候选来自目录遍历,会跳过 node_modules、构建产物等目录;列为 git 仓库后候选会自动按 .gitignore 过滤",
326
376
  // 统计条
327
377
  turns: "轮次",
328
378
  toolCalls: "工具调用",
@@ -331,6 +381,8 @@ const zh = {
331
381
  cacheHits: "缓存命中",
332
382
  statsCounts: "{turns} 轮 · {steps} 步",
333
383
  ttftAverage: "首 token 平均",
384
+ statsMeasuredSteps: "实时测量 {n} 步",
385
+ statsTimedCoverage: "{n}/{total} 条有计时",
334
386
  inputTokens: "输入",
335
387
  outputTokens: "输出",
336
388
  // 视图
@@ -504,10 +556,14 @@ const zh = {
504
556
  allowPrivateDiscovery: "允许本机/私网",
505
557
  allowPrivateHint: "模型地址指向本机或私有网络(如 Ollama、LM Studio、one-api 本地网关)时勾选,本次探测放行;发消息不受此限制",
506
558
  discoveringModels: "获取中...",
559
+ autoDiscovering: "正在自动识别模型…",
560
+ autoDiscovered: "已自动识别 {n} 个模型(端点报了容量与视觉的已一并填好)",
561
+ autoDiscoverFailed: "自动识别失败,可点「获取可用模型」重试",
507
562
  usingDefaultModels: "正在使用 Pi SDK 默认模型 · {count} 个",
508
563
  customModelsRequired: "至少添加一个可发送的模型 ID",
509
564
  modelCatalogEmptyBuiltin: "未添加覆盖模型;模型选择器会继续实时使用 Pi SDK 的官方模型目录。",
510
565
  modelCatalogEmptyCustom: "模型选择器中将不显示任何模型;请至少添加一个模型 ID。",
566
+ catalogOverrideHint: "与内置目录同 ID 的行只覆盖填写的字段,推理、价格、思考档位等沿用内置值。",
511
567
  addModel: "添加模型",
512
568
  modelId: "模型 ID",
513
569
  createProvider: "创建提供方",
@@ -532,9 +588,9 @@ const zh = {
532
588
  trustAlways: "总是信任",
533
589
  trustNever: "从不信任",
534
590
  versionPiweb: "PiWeb 版本",
535
- versionPiwebDesc: "本 Web 应用的版本,点击打开仓库",
591
+ versionPiwebDesc: "本 Web 应用的版本;启动时自动检查,有新版本在这里直接更新(点版本号打开仓库)",
536
592
  versionPi: "Pi 版本",
537
- versionPiDesc: "pi 智能体内核的版本,点击打开仓库",
593
+ versionPiDesc: "pi 智能体内核的版本;启动时自动检查,有新版本在这里直接更新(点版本号打开仓库)",
538
594
  checkUpdate: "检查更新",
539
595
  checkingUpdate: "检查中…",
540
596
  upToDate: "已是最新",
@@ -543,6 +599,18 @@ const zh = {
543
599
  updatedTo: "已更新到 {v},重启 PiWeb 后生效",
544
600
  updateFailed: "更新失败",
545
601
  updateManualHint: "先停止 piweb,再执行上面的命令(Windows 无法替换运行中的安装目录)",
602
+ updateCopied: "已复制",
603
+ updateBusy: "有会话正在运行,等它结束再更新",
604
+ updateCommits: "main 新增 {n} 个提交",
605
+ updateNoticeTitle: "有新版本",
606
+ updateNoticeUpdatingTitle: "正在更新…",
607
+ updateNoticeDoneTitle: "更新完成",
608
+ updateNoticeUpdate: "更新",
609
+ updateNoticeLater: "稍后",
610
+ updateNoticeOk: "知道了",
611
+ updateNoticeUpdated: "已更新到 {v}",
612
+ updateRunCommandHint: "先停掉 piweb,再在终端运行:",
613
+ updateRestartHint: "重启 PiWeb 后生效:在运行 piweb 的终端按 Ctrl+C,再重新运行 piweb",
546
614
  skillsDesc: "关闭后技能不会出现在系统提示中,仅可通过 /skill:名称 手动调用",
547
615
  confirmDeleteSkill: "确定删除技能“{name}”吗?将删除其所在技能目录,不可恢复。",
548
616
  deleteSkillHint: "删除技能目录",
@@ -653,6 +721,26 @@ const zh = {
653
721
  viewerNotInSnapshot: "此文件不在快照里(超过 1MB 或被排除),显示磁盘上的当前内容",
654
722
  viewerLoadFailed: "加载失败",
655
723
  viewerEmpty: "从左侧树里选择一个文件",
724
+ // 流式输出
725
+ streamStyleTitle: "流式输出样式",
726
+ streamStyleDesc: "回答逐字出现的方式;改完立刻生效,正在流出的回答也一并换",
727
+ streamStyleSmooth: "平滑",
728
+ streamStyleSmoothDesc: "匀速长出,不跳字",
729
+ streamStyleTypewriter: "打字机",
730
+ streamStyleTypewriterDesc: "固定节奏逐字敲出",
731
+ streamStyleFade: "渐显",
732
+ streamStyleFadeDesc: "整词推进,段落柔和淡入",
733
+ streamStyleInstant: "即时",
734
+ streamStyleInstantDesc: "立即显示,不做平滑",
735
+ streamSpeedTitle: "推进速度",
736
+ streamSpeedDesc: "平滑 / 打字机 / 渐显的节奏快慢;即时样式不受影响",
737
+ streamSpeed1: "慢",
738
+ streamSpeed2: "较慢",
739
+ streamSpeed3: "标准",
740
+ streamSpeed4: "快",
741
+ streamSpeed5: "极快",
742
+ streamCaretTitle: "打字光标",
743
+ streamCaretDesc: "流式期间在文字末尾显示闪烁光标",
656
744
  } as const;
657
745
 
658
746
  type Dict = { [K in keyof typeof zh]: string };
@@ -688,13 +776,37 @@ const en: Dict = {
688
776
  capacityFields: "Capacities",
689
777
  contextWindowLabel: "Context window",
690
778
  maxTokensLabel: "Max output",
691
- capacityHint: "blank = default · 256K / 2M accepted",
779
+ capacityHint: "blank = pi default (128K / 16K) · 256K / 2M accepted",
780
+ // model capability (vision) and config checkup
781
+ visionLabel: "Vision",
782
+ visionHint: "Whether this model accepts images. Left unset, pi treats it as text-only and replaces pasted screenshots with a placeholder line.",
783
+ visionUnset: "Not declared",
784
+ visionOn: "Vision",
785
+ visionOff: "Text only",
786
+ imagePreview: "Click to preview",
787
+ imageViewer: "Image preview",
788
+ imagePrev: "Previous image",
789
+ imageNext: "Next image",
790
+ chipExpandMore: "Show {n} more",
791
+ chipCollapse: "Collapse",
792
+ modelAuditTitle: "{n} models don't declare their capabilities",
793
+ modelAuditDesc: "For fields left out, pi assumes a {ctx} context window, {out} max output and text-only input. Values the endpoint didn't report are never guessed: fill in the model's real limits (256K / 1M accepted). Saving only fills empty cells and never changes values already set.",
794
+ modelAuditColModel: "Model",
795
+ modelAuditColContext: "Context",
796
+ modelAuditColOutput: "Max output",
797
+ modelAuditColVision: "Vision",
798
+ modelAuditVisionQuick: "Set vision for all:",
799
+ modelAuditVisionOn: "All vision",
800
+ modelAuditVisionOff: "All text only",
801
+ modelAuditSave: "Save {n} models",
802
+ modelAuditSaveIdle: "Fill in, then save",
692
803
  capacityInvalid: "Row {n}: capacity must be a number (K/M ok)",
693
804
  discoverEmpty: "No recognizable models returned",
694
805
  candidateTitle: "Found {count} models — pick ones to add",
695
806
  candidateAll: "Select all",
696
807
  candidatePicked: "{n} selected",
697
808
  candidateAdopt: "Add {n}",
809
+ candidateInCatalog: "Already in catalog",
698
810
  runningTool: "Running {name}…",
699
811
  thinkingLive: "Thinking",
700
812
  stepRunning: "Running",
@@ -777,6 +889,10 @@ const en: Dict = {
777
889
  gitNoChanges: "Working tree clean",
778
890
  gitStatusUnknown: "Git status unavailable; changes could not be determined",
779
891
  gitRecentCommits: "Recent commits",
892
+ gitStagingArea: "Staging area",
893
+ gitWorkingTree: "Working tree",
894
+ gitGithubAccount: "GitHub account",
895
+ gitGithubUnknown: "No GitHub account detected",
780
896
  toolNoOutput: "no output",
781
897
  toolFailed: "failed",
782
898
  thinkingClamped: "This model does not support \"{requested}\"; using \"{level}\" instead",
@@ -933,6 +1049,11 @@ const en: Dict = {
933
1049
  extensionStatus: "Extension status",
934
1050
  contextUsed: "context used",
935
1051
  contextUnknown: "Context usage unknown",
1052
+ contextEstimated: "estimated context usage",
1053
+ contextApiInput: "Last request input (API)",
1054
+ contextCharacterBreakdown: "Content breakdown (characters)",
1055
+ contextCharacterHint: "Counts current text; excludes images. The API does not report tokens by category.",
1056
+ contextChars: "chars",
936
1057
  contextSystem: "System prompt",
937
1058
  contextMessages: "Messages",
938
1059
  contextTools: "Tools",
@@ -971,6 +1092,23 @@ const en: Dict = {
971
1092
  contextCompacted: "Compacted Data",
972
1093
  contextAutoCompactBuffer: "Auto-Compact Buffer",
973
1094
  contextFreeSpace: "Free Space",
1095
+ contextColdHint: "Session not started yet — System Prompt and tool definitions are counted once it starts",
1096
+ contextCountNow: "Count now",
1097
+ contextCounting: "Counting…",
1098
+ contextWindowEdit: "Edit",
1099
+ contextWindowBuiltin: "pi built-in catalog: {value}",
1100
+ contextWindowUseBuiltin: "Use",
1101
+ contextWindowScope: "Changes this model's global config (models.json); the pi terminal and other sessions use it too",
1102
+ contextWindowOverBuiltin: "Larger than the built-in catalog: requests past the model's real limit are rejected upstream (400)",
1103
+ contextWindowNoBuiltin: "Enter the model's real limit; a larger value gets requests rejected upstream (400)",
1104
+ contextWindowInvalid: "Enter a positive integer; K / M allowed (e.g. 200K)",
1105
+ // @ file references (type @ to open the picker)
1106
+ mentionFiles: "@ reference files / folders",
1107
+ mentionHint: "↑↓ select · Enter reference · Esc close",
1108
+ mentionNoMatch: "No matching files",
1109
+ mentionTruncated: "list truncated",
1110
+ mentionDiskSource: "directory scan",
1111
+ mentionDiskSourceHint: "This workspace is not a git repository (or git is missing): candidates come from a directory scan that skips node_modules and build output; in a git repository they follow .gitignore automatically",
974
1112
  turns: "turns",
975
1113
  toolCalls: "tool calls",
976
1114
  cost: "cost",
@@ -978,6 +1116,8 @@ const en: Dict = {
978
1116
  cacheHits: "cache hits",
979
1117
  statsCounts: "{turns} turns · {steps} steps",
980
1118
  ttftAverage: "TTFT avg",
1119
+ statsMeasuredSteps: "{n} steps measured live",
1120
+ statsTimedCoverage: "{n}/{total} entries timed",
981
1121
  inputTokens: "Input",
982
1122
  outputTokens: "Output",
983
1123
  tabChat: "Chat",
@@ -1147,10 +1287,14 @@ const en: Dict = {
1147
1287
  allowPrivateDiscovery: "Allow local/private",
1148
1288
  allowPrivateHint: "Check this when the model address points to loopback or a private network (Ollama, LM Studio, one-api gateways) to allow this probe; chat requests are not affected",
1149
1289
  discoveringModels: "Fetching...",
1290
+ autoDiscovering: "Detecting models…",
1291
+ autoDiscovered: "Detected {n} models (capacity and vision filled in where the endpoint reports them)",
1292
+ autoDiscoverFailed: "Automatic detection failed — use the button to retry",
1150
1293
  usingDefaultModels: "Using Pi SDK default models · {count}",
1151
1294
  customModelsRequired: "Add at least one model ID that can be sent",
1152
1295
  modelCatalogEmptyBuiltin: "No model overrides. The model selector will continue using Pi SDK's live official catalog.",
1153
1296
  modelCatalogEmptyCustom: "No models will appear in the selector. Add at least one model ID.",
1297
+ catalogOverrideHint: "Rows matching a built-in model ID only override the fields you fill in; reasoning, pricing and thinking levels stay built-in.",
1154
1298
  addModel: "Add model",
1155
1299
  modelId: "Model ID",
1156
1300
  createProvider: "Create provider",
@@ -1175,9 +1319,9 @@ const en: Dict = {
1175
1319
  trustAlways: "Always trust",
1176
1320
  trustNever: "Never trust",
1177
1321
  versionPiweb: "PiWeb version",
1178
- versionPiwebDesc: "Version of this web app; click to open the repo",
1322
+ versionPiwebDesc: "Version of this web app; checked at startup and updatable right here (click the version to open the repo)",
1179
1323
  versionPi: "Pi version",
1180
- versionPiDesc: "Version of the pi agent engine; click to open the repo",
1324
+ versionPiDesc: "Version of the pi agent engine; checked at startup and updatable right here (click the version to open the repo)",
1181
1325
  checkUpdate: "Check for updates",
1182
1326
  checkingUpdate: "Checking…",
1183
1327
  upToDate: "Up to date",
@@ -1186,6 +1330,18 @@ const en: Dict = {
1186
1330
  updatedTo: "Updated to {v}; restart PiWeb to apply",
1187
1331
  updateFailed: "Update failed",
1188
1332
  updateManualHint: "Stop piweb, then run the command above (Windows cannot replace a running installation)",
1333
+ updateCopied: "Copied",
1334
+ updateBusy: "A session is running; update after it finishes",
1335
+ updateCommits: "{n} new commits on main",
1336
+ updateNoticeTitle: "Update available",
1337
+ updateNoticeUpdatingTitle: "Updating…",
1338
+ updateNoticeDoneTitle: "Update installed",
1339
+ updateNoticeUpdate: "Update",
1340
+ updateNoticeLater: "Later",
1341
+ updateNoticeOk: "Got it",
1342
+ updateNoticeUpdated: "Updated to {v}",
1343
+ updateRunCommandHint: "Stop piweb, then run in a terminal:",
1344
+ updateRestartHint: "Restart PiWeb to apply: press Ctrl+C in the terminal running piweb, then run piweb again",
1189
1345
  skillsDesc: "Disabled skills are hidden from the system prompt and only callable via /skill:name",
1190
1346
  confirmDeleteSkill: "Delete skill \"{name}\"? Its skill directory will be removed and cannot be recovered.",
1191
1347
  deleteSkillHint: "Delete the skill directory",
@@ -1295,6 +1451,26 @@ const en: Dict = {
1295
1451
  viewerNotInSnapshot: "Not in the snapshot (over 1MB or excluded); showing the current content from disk",
1296
1452
  viewerLoadFailed: "Failed to load",
1297
1453
  viewerEmpty: "Pick a file from the tree on the left",
1454
+ // Streaming text
1455
+ streamStyleTitle: "Streaming text",
1456
+ streamStyleDesc: "How the answer appears while it arrives; applies to the stream already running",
1457
+ streamStyleSmooth: "Smooth",
1458
+ streamStyleSmoothDesc: "Even pace, no jumps",
1459
+ streamStyleTypewriter: "Typewriter",
1460
+ streamStyleTypewriterDesc: "Fixed cadence, character by character",
1461
+ streamStyleFade: "Fade in",
1462
+ streamStyleFadeDesc: "Whole words, paragraphs fade in",
1463
+ streamStyleInstant: "Instant",
1464
+ streamStyleInstantDesc: "Show immediately, no smoothing",
1465
+ streamSpeedTitle: "Streaming speed",
1466
+ streamSpeedDesc: "Cadence for Smooth / Typewriter / Fade in; Instant ignores it",
1467
+ streamSpeed1: "Slow",
1468
+ streamSpeed2: "Relaxed",
1469
+ streamSpeed3: "Standard",
1470
+ streamSpeed4: "Fast",
1471
+ streamSpeed5: "Fastest",
1472
+ streamCaretTitle: "Typing caret",
1473
+ streamCaretDesc: "Blinking caret at the end of the text while streaming",
1298
1474
  };
1299
1475
 
1300
1476
  const dicts: Record<string, Dict> = { zh, en };
@@ -12,6 +12,9 @@ export async function registerNode() {
12
12
  const { authEnabled } = await import("./lib/web-auth");
13
13
  if (authEnabled()) (await import("./lib/web-launch-token")).publishLaunchToken();
14
14
 
15
+ // 每次启动检查一次 PiWeb / pi 有没有新版本:不等结果,查到了在终端打一行,页面右下角也会提醒
16
+ void (await import("./lib/update/startup-check")).ensureStartupCheck().catch(() => {});
17
+
15
18
  const { remoteBlocked, remoteLanEnabled, startLan } = await import("./lib/remote/supervisor");
16
19
  if (!remoteLanEnabled()) return;
17
20