@rexvane/piweb 0.3.1

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 (535) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +128 -0
  3. package/assets/showcase.png +0 -0
  4. package/bin/pi.js +50 -0
  5. package/bin/piweb.js +12 -0
  6. package/next.config.ts +21 -0
  7. package/package.json +88 -0
  8. package/postcss.config.mjs +7 -0
  9. package/public/file-icons/3d.svg +1 -0
  10. package/public/file-icons/LICENSE +8 -0
  11. package/public/file-icons/adobe-illustrator.svg +1 -0
  12. package/public/file-icons/adobe-photoshop.svg +1 -0
  13. package/public/file-icons/agent.svg +1 -0
  14. package/public/file-icons/asciidoc.svg +1 -0
  15. package/public/file-icons/assembly.svg +1 -0
  16. package/public/file-icons/astro.svg +1 -0
  17. package/public/file-icons/audio.svg +1 -0
  18. package/public/file-icons/authors.svg +1 -0
  19. package/public/file-icons/babel.svg +1 -0
  20. package/public/file-icons/bibliography.svg +1 -0
  21. package/public/file-icons/bicep.svg +1 -0
  22. package/public/file-icons/blender.svg +1 -0
  23. package/public/file-icons/bun.svg +1 -0
  24. package/public/file-icons/c.svg +1 -0
  25. package/public/file-icons/certificate.svg +1 -0
  26. package/public/file-icons/changelog.svg +1 -0
  27. package/public/file-icons/clojure.svg +1 -0
  28. package/public/file-icons/cmake.svg +1 -0
  29. package/public/file-icons/codeowners.svg +1 -0
  30. package/public/file-icons/commitlint.svg +1 -0
  31. package/public/file-icons/conduct.svg +1 -0
  32. package/public/file-icons/console.svg +1 -0
  33. package/public/file-icons/contributing.svg +1 -0
  34. package/public/file-icons/cpp.svg +1 -0
  35. package/public/file-icons/csharp.svg +1 -0
  36. package/public/file-icons/css.svg +1 -0
  37. package/public/file-icons/cursor.svg +1 -0
  38. package/public/file-icons/dart.svg +1 -0
  39. package/public/file-icons/database.svg +1 -0
  40. package/public/file-icons/deno.svg +1 -0
  41. package/public/file-icons/diff.svg +1 -0
  42. package/public/file-icons/docker.svg +1 -0
  43. package/public/file-icons/document.svg +1 -0
  44. package/public/file-icons/editorconfig.svg +1 -0
  45. package/public/file-icons/ejs.svg +1 -0
  46. package/public/file-icons/elixir.svg +1 -0
  47. package/public/file-icons/elm.svg +1 -0
  48. package/public/file-icons/email.svg +1 -0
  49. package/public/file-icons/erlang.svg +1 -0
  50. package/public/file-icons/eslint.svg +1 -0
  51. package/public/file-icons/favicon.svg +1 -0
  52. package/public/file-icons/file.svg +1 -0
  53. package/public/file-icons/folder-admin-open.svg +1 -0
  54. package/public/file-icons/folder-admin.svg +1 -0
  55. package/public/file-icons/folder-android-open.svg +1 -0
  56. package/public/file-icons/folder-android.svg +1 -0
  57. package/public/file-icons/folder-api-open.svg +1 -0
  58. package/public/file-icons/folder-api.svg +1 -0
  59. package/public/file-icons/folder-app-open.svg +1 -0
  60. package/public/file-icons/folder-app.svg +1 -0
  61. package/public/file-icons/folder-archive-open.svg +1 -0
  62. package/public/file-icons/folder-archive.svg +1 -0
  63. package/public/file-icons/folder-atom-open.svg +1 -0
  64. package/public/file-icons/folder-atom.svg +1 -0
  65. package/public/file-icons/folder-audio-open.svg +1 -0
  66. package/public/file-icons/folder-audio.svg +1 -0
  67. package/public/file-icons/folder-aws-open.svg +1 -0
  68. package/public/file-icons/folder-aws.svg +1 -0
  69. package/public/file-icons/folder-backup-open.svg +1 -0
  70. package/public/file-icons/folder-backup.svg +1 -0
  71. package/public/file-icons/folder-benchmark-open.svg +1 -0
  72. package/public/file-icons/folder-benchmark.svg +1 -0
  73. package/public/file-icons/folder-cart-open.svg +1 -0
  74. package/public/file-icons/folder-cart.svg +1 -0
  75. package/public/file-icons/folder-ci-open.svg +1 -0
  76. package/public/file-icons/folder-ci.svg +1 -0
  77. package/public/file-icons/folder-circleci-open.svg +1 -0
  78. package/public/file-icons/folder-circleci.svg +1 -0
  79. package/public/file-icons/folder-class-open.svg +1 -0
  80. package/public/file-icons/folder-class.svg +1 -0
  81. package/public/file-icons/folder-client-open.svg +1 -0
  82. package/public/file-icons/folder-client.svg +1 -0
  83. package/public/file-icons/folder-command-open.svg +1 -0
  84. package/public/file-icons/folder-command.svg +1 -0
  85. package/public/file-icons/folder-components-open.svg +1 -0
  86. package/public/file-icons/folder-components.svg +1 -0
  87. package/public/file-icons/folder-config-open.svg +1 -0
  88. package/public/file-icons/folder-config.svg +1 -0
  89. package/public/file-icons/folder-console-open.svg +1 -0
  90. package/public/file-icons/folder-console.svg +1 -0
  91. package/public/file-icons/folder-content-open.svg +1 -0
  92. package/public/file-icons/folder-content.svg +1 -0
  93. package/public/file-icons/folder-context-open.svg +1 -0
  94. package/public/file-icons/folder-context.svg +1 -0
  95. package/public/file-icons/folder-contract-open.svg +1 -0
  96. package/public/file-icons/folder-contract.svg +1 -0
  97. package/public/file-icons/folder-controller-open.svg +1 -0
  98. package/public/file-icons/folder-controller.svg +1 -0
  99. package/public/file-icons/folder-core-open.svg +1 -0
  100. package/public/file-icons/folder-core.svg +1 -0
  101. package/public/file-icons/folder-coverage-open.svg +1 -0
  102. package/public/file-icons/folder-coverage.svg +1 -0
  103. package/public/file-icons/folder-css-open.svg +1 -0
  104. package/public/file-icons/folder-css.svg +1 -0
  105. package/public/file-icons/folder-database-open.svg +1 -0
  106. package/public/file-icons/folder-database.svg +1 -0
  107. package/public/file-icons/folder-debug-open.svg +1 -0
  108. package/public/file-icons/folder-debug.svg +1 -0
  109. package/public/file-icons/folder-deprecated-open.svg +1 -0
  110. package/public/file-icons/folder-deprecated.svg +1 -0
  111. package/public/file-icons/folder-desktop-open.svg +1 -0
  112. package/public/file-icons/folder-desktop.svg +1 -0
  113. package/public/file-icons/folder-dist-open.svg +1 -0
  114. package/public/file-icons/folder-dist.svg +1 -0
  115. package/public/file-icons/folder-docker-open.svg +1 -0
  116. package/public/file-icons/folder-docker.svg +1 -0
  117. package/public/file-icons/folder-docs-open.svg +1 -0
  118. package/public/file-icons/folder-docs.svg +1 -0
  119. package/public/file-icons/folder-element-open.svg +1 -0
  120. package/public/file-icons/folder-element.svg +1 -0
  121. package/public/file-icons/folder-environment-open.svg +1 -0
  122. package/public/file-icons/folder-environment.svg +1 -0
  123. package/public/file-icons/folder-event-open.svg +1 -0
  124. package/public/file-icons/folder-event.svg +1 -0
  125. package/public/file-icons/folder-examples-open.svg +1 -0
  126. package/public/file-icons/folder-examples.svg +1 -0
  127. package/public/file-icons/folder-features-open.svg +1 -0
  128. package/public/file-icons/folder-features.svg +1 -0
  129. package/public/file-icons/folder-font-open.svg +1 -0
  130. package/public/file-icons/folder-font.svg +1 -0
  131. package/public/file-icons/folder-functions-open.svg +1 -0
  132. package/public/file-icons/folder-functions.svg +1 -0
  133. package/public/file-icons/folder-generator-open.svg +1 -0
  134. package/public/file-icons/folder-generator.svg +1 -0
  135. package/public/file-icons/folder-git-open.svg +1 -0
  136. package/public/file-icons/folder-git.svg +1 -0
  137. package/public/file-icons/folder-github-open.svg +1 -0
  138. package/public/file-icons/folder-github.svg +1 -0
  139. package/public/file-icons/folder-gitlab-open.svg +1 -0
  140. package/public/file-icons/folder-gitlab.svg +1 -0
  141. package/public/file-icons/folder-graphql-open.svg +1 -0
  142. package/public/file-icons/folder-graphql.svg +1 -0
  143. package/public/file-icons/folder-helper-open.svg +1 -0
  144. package/public/file-icons/folder-helper.svg +1 -0
  145. package/public/file-icons/folder-hook-open.svg +1 -0
  146. package/public/file-icons/folder-hook.svg +1 -0
  147. package/public/file-icons/folder-husky-open.svg +1 -0
  148. package/public/file-icons/folder-husky.svg +1 -0
  149. package/public/file-icons/folder-i18n-open.svg +1 -0
  150. package/public/file-icons/folder-i18n.svg +1 -0
  151. package/public/file-icons/folder-images-open.svg +1 -0
  152. package/public/file-icons/folder-images.svg +1 -0
  153. package/public/file-icons/folder-include-open.svg +1 -0
  154. package/public/file-icons/folder-include.svg +1 -0
  155. package/public/file-icons/folder-intellij-open.svg +1 -0
  156. package/public/file-icons/folder-intellij.svg +1 -0
  157. package/public/file-icons/folder-interface-open.svg +1 -0
  158. package/public/file-icons/folder-interface.svg +1 -0
  159. package/public/file-icons/folder-ios-open.svg +1 -0
  160. package/public/file-icons/folder-ios.svg +1 -0
  161. package/public/file-icons/folder-java-open.svg +1 -0
  162. package/public/file-icons/folder-java.svg +1 -0
  163. package/public/file-icons/folder-job-open.svg +1 -0
  164. package/public/file-icons/folder-job.svg +1 -0
  165. package/public/file-icons/folder-jupyter-open.svg +1 -0
  166. package/public/file-icons/folder-jupyter.svg +1 -0
  167. package/public/file-icons/folder-keys-open.svg +1 -0
  168. package/public/file-icons/folder-keys.svg +1 -0
  169. package/public/file-icons/folder-kubernetes-open.svg +1 -0
  170. package/public/file-icons/folder-kubernetes.svg +1 -0
  171. package/public/file-icons/folder-layout-open.svg +1 -0
  172. package/public/file-icons/folder-layout.svg +1 -0
  173. package/public/file-icons/folder-less-open.svg +1 -0
  174. package/public/file-icons/folder-less.svg +1 -0
  175. package/public/file-icons/folder-lib-open.svg +1 -0
  176. package/public/file-icons/folder-lib.svg +1 -0
  177. package/public/file-icons/folder-log-open.svg +1 -0
  178. package/public/file-icons/folder-log.svg +1 -0
  179. package/public/file-icons/folder-messages-open.svg +1 -0
  180. package/public/file-icons/folder-messages.svg +1 -0
  181. package/public/file-icons/folder-meta-open.svg +1 -0
  182. package/public/file-icons/folder-meta.svg +1 -0
  183. package/public/file-icons/folder-middleware-open.svg +1 -0
  184. package/public/file-icons/folder-middleware.svg +1 -0
  185. package/public/file-icons/folder-migrations-open.svg +1 -0
  186. package/public/file-icons/folder-migrations.svg +1 -0
  187. package/public/file-icons/folder-mobile-open.svg +1 -0
  188. package/public/file-icons/folder-mobile.svg +1 -0
  189. package/public/file-icons/folder-mock-open.svg +1 -0
  190. package/public/file-icons/folder-mock.svg +1 -0
  191. package/public/file-icons/folder-molecule-open.svg +1 -0
  192. package/public/file-icons/folder-molecule.svg +1 -0
  193. package/public/file-icons/folder-next-open.svg +1 -0
  194. package/public/file-icons/folder-next.svg +1 -0
  195. package/public/file-icons/folder-node-open.svg +1 -0
  196. package/public/file-icons/folder-node.svg +1 -0
  197. package/public/file-icons/folder-nuxt-open.svg +1 -0
  198. package/public/file-icons/folder-nuxt.svg +1 -0
  199. package/public/file-icons/folder-open.svg +1 -0
  200. package/public/file-icons/folder-organism-open.svg +1 -0
  201. package/public/file-icons/folder-organism.svg +1 -0
  202. package/public/file-icons/folder-packages-open.svg +1 -0
  203. package/public/file-icons/folder-packages.svg +1 -0
  204. package/public/file-icons/folder-plugin-open.svg +1 -0
  205. package/public/file-icons/folder-plugin.svg +1 -0
  206. package/public/file-icons/folder-prisma-open.svg +1 -0
  207. package/public/file-icons/folder-prisma.svg +1 -0
  208. package/public/file-icons/folder-prompts-open.svg +1 -0
  209. package/public/file-icons/folder-prompts.svg +1 -0
  210. package/public/file-icons/folder-proto-open.svg +1 -0
  211. package/public/file-icons/folder-proto.svg +1 -0
  212. package/public/file-icons/folder-public-open.svg +1 -0
  213. package/public/file-icons/folder-public.svg +1 -0
  214. package/public/file-icons/folder-python-open.svg +1 -0
  215. package/public/file-icons/folder-python.svg +1 -0
  216. package/public/file-icons/folder-queue-open.svg +1 -0
  217. package/public/file-icons/folder-queue.svg +1 -0
  218. package/public/file-icons/folder-resource-open.svg +1 -0
  219. package/public/file-icons/folder-resource.svg +1 -0
  220. package/public/file-icons/folder-review-open.svg +1 -0
  221. package/public/file-icons/folder-review.svg +1 -0
  222. package/public/file-icons/folder-robot-open.svg +1 -0
  223. package/public/file-icons/folder-robot.svg +1 -0
  224. package/public/file-icons/folder-routes-open.svg +1 -0
  225. package/public/file-icons/folder-routes.svg +1 -0
  226. package/public/file-icons/folder-rust-open.svg +1 -0
  227. package/public/file-icons/folder-rust.svg +1 -0
  228. package/public/file-icons/folder-sass-open.svg +1 -0
  229. package/public/file-icons/folder-sass.svg +1 -0
  230. package/public/file-icons/folder-scripts-open.svg +1 -0
  231. package/public/file-icons/folder-scripts.svg +1 -0
  232. package/public/file-icons/folder-secure-open.svg +1 -0
  233. package/public/file-icons/folder-secure.svg +1 -0
  234. package/public/file-icons/folder-seeders-open.svg +1 -0
  235. package/public/file-icons/folder-seeders.svg +1 -0
  236. package/public/file-icons/folder-server-open.svg +1 -0
  237. package/public/file-icons/folder-server.svg +1 -0
  238. package/public/file-icons/folder-shared-open.svg +1 -0
  239. package/public/file-icons/folder-shared.svg +1 -0
  240. package/public/file-icons/folder-skills-open.svg +1 -0
  241. package/public/file-icons/folder-skills.svg +1 -0
  242. package/public/file-icons/folder-src-open.svg +1 -0
  243. package/public/file-icons/folder-src.svg +1 -0
  244. package/public/file-icons/folder-store-open.svg +1 -0
  245. package/public/file-icons/folder-store.svg +1 -0
  246. package/public/file-icons/folder-storybook-open.svg +1 -0
  247. package/public/file-icons/folder-storybook.svg +1 -0
  248. package/public/file-icons/folder-supabase-open.svg +1 -0
  249. package/public/file-icons/folder-supabase.svg +1 -0
  250. package/public/file-icons/folder-svelte-open.svg +1 -0
  251. package/public/file-icons/folder-svelte.svg +1 -0
  252. package/public/file-icons/folder-target-open.svg +1 -0
  253. package/public/file-icons/folder-target.svg +1 -0
  254. package/public/file-icons/folder-tasks-open.svg +1 -0
  255. package/public/file-icons/folder-tasks.svg +1 -0
  256. package/public/file-icons/folder-temp-open.svg +1 -0
  257. package/public/file-icons/folder-temp.svg +1 -0
  258. package/public/file-icons/folder-template-open.svg +1 -0
  259. package/public/file-icons/folder-template.svg +1 -0
  260. package/public/file-icons/folder-terraform-open.svg +1 -0
  261. package/public/file-icons/folder-terraform.svg +1 -0
  262. package/public/file-icons/folder-test-open.svg +1 -0
  263. package/public/file-icons/folder-test.svg +1 -0
  264. package/public/file-icons/folder-theme-open.svg +1 -0
  265. package/public/file-icons/folder-theme.svg +1 -0
  266. package/public/file-icons/folder-tools-open.svg +1 -0
  267. package/public/file-icons/folder-tools.svg +1 -0
  268. package/public/file-icons/folder-turborepo-open.svg +1 -0
  269. package/public/file-icons/folder-turborepo.svg +1 -0
  270. package/public/file-icons/folder-typescript-open.svg +1 -0
  271. package/public/file-icons/folder-typescript.svg +1 -0
  272. package/public/file-icons/folder-ui-open.svg +1 -0
  273. package/public/file-icons/folder-ui.svg +1 -0
  274. package/public/file-icons/folder-utils-open.svg +1 -0
  275. package/public/file-icons/folder-utils.svg +1 -0
  276. package/public/file-icons/folder-video-open.svg +1 -0
  277. package/public/file-icons/folder-video.svg +1 -0
  278. package/public/file-icons/folder-views-open.svg +1 -0
  279. package/public/file-icons/folder-views.svg +1 -0
  280. package/public/file-icons/folder-vscode-open.svg +1 -0
  281. package/public/file-icons/folder-vscode.svg +1 -0
  282. package/public/file-icons/folder.svg +1 -0
  283. package/public/file-icons/font.svg +1 -0
  284. package/public/file-icons/fsharp.svg +1 -0
  285. package/public/file-icons/gemfile.svg +1 -0
  286. package/public/file-icons/git.svg +1 -0
  287. package/public/file-icons/go-mod.svg +1 -0
  288. package/public/file-icons/go.svg +1 -0
  289. package/public/file-icons/gradle.svg +1 -0
  290. package/public/file-icons/graphql.svg +1 -0
  291. package/public/file-icons/groovy.svg +1 -0
  292. package/public/file-icons/h.svg +1 -0
  293. package/public/file-icons/handlebars.svg +1 -0
  294. package/public/file-icons/haskell.svg +1 -0
  295. package/public/file-icons/hcl.svg +1 -0
  296. package/public/file-icons/heroku.svg +1 -0
  297. package/public/file-icons/hpp.svg +1 -0
  298. package/public/file-icons/html.svg +1 -0
  299. package/public/file-icons/husky.svg +1 -0
  300. package/public/file-icons/i18n.svg +1 -0
  301. package/public/file-icons/image.svg +1 -0
  302. package/public/file-icons/java.svg +1 -0
  303. package/public/file-icons/javascript.svg +1 -0
  304. package/public/file-icons/jenkins.svg +1 -0
  305. package/public/file-icons/jest.svg +1 -0
  306. package/public/file-icons/jsconfig.svg +1 -0
  307. package/public/file-icons/json.svg +1 -0
  308. package/public/file-icons/julia.svg +1 -0
  309. package/public/file-icons/jupyter.svg +1 -0
  310. package/public/file-icons/just.svg +1 -0
  311. package/public/file-icons/key.svg +1 -0
  312. package/public/file-icons/kotlin.svg +1 -0
  313. package/public/file-icons/lerna.svg +1 -0
  314. package/public/file-icons/less.svg +1 -0
  315. package/public/file-icons/license.svg +1 -0
  316. package/public/file-icons/lintstaged.svg +1 -0
  317. package/public/file-icons/liquid.svg +1 -0
  318. package/public/file-icons/lock.svg +1 -0
  319. package/public/file-icons/log.svg +1 -0
  320. package/public/file-icons/lua.svg +1 -0
  321. package/public/file-icons/makefile.svg +1 -0
  322. package/public/file-icons/markdown.svg +1 -0
  323. package/public/file-icons/maven.svg +1 -0
  324. package/public/file-icons/mdx.svg +1 -0
  325. package/public/file-icons/netlify.svg +1 -0
  326. package/public/file-icons/next.svg +1 -0
  327. package/public/file-icons/nim.svg +1 -0
  328. package/public/file-icons/nix.svg +1 -0
  329. package/public/file-icons/nodejs.svg +1 -0
  330. package/public/file-icons/nodemon.svg +1 -0
  331. package/public/file-icons/npm.svg +1 -0
  332. package/public/file-icons/nx.svg +1 -0
  333. package/public/file-icons/objective-c.svg +1 -0
  334. package/public/file-icons/objective-cpp.svg +1 -0
  335. package/public/file-icons/ocaml.svg +1 -0
  336. package/public/file-icons/pdf.svg +1 -0
  337. package/public/file-icons/php.svg +1 -0
  338. package/public/file-icons/pm2-ecosystem.svg +1 -0
  339. package/public/file-icons/pnpm.svg +1 -0
  340. package/public/file-icons/poetry.svg +1 -0
  341. package/public/file-icons/postcss.svg +1 -0
  342. package/public/file-icons/powerpoint.svg +1 -0
  343. package/public/file-icons/powershell.svg +1 -0
  344. package/public/file-icons/prettier.svg +1 -0
  345. package/public/file-icons/prisma.svg +1 -0
  346. package/public/file-icons/prolog.svg +1 -0
  347. package/public/file-icons/proto.svg +1 -0
  348. package/public/file-icons/pug.svg +1 -0
  349. package/public/file-icons/python-misc.svg +1 -0
  350. package/public/file-icons/python.svg +1 -0
  351. package/public/file-icons/r.svg +1 -0
  352. package/public/file-icons/razor.svg +1 -0
  353. package/public/file-icons/react.svg +1 -0
  354. package/public/file-icons/react_ts.svg +1 -0
  355. package/public/file-icons/readme.svg +1 -0
  356. package/public/file-icons/renovate.svg +1 -0
  357. package/public/file-icons/robots.svg +1 -0
  358. package/public/file-icons/rollup.svg +1 -0
  359. package/public/file-icons/ruby.svg +1 -0
  360. package/public/file-icons/rust.svg +1 -0
  361. package/public/file-icons/sass.svg +1 -0
  362. package/public/file-icons/scala.svg +1 -0
  363. package/public/file-icons/semantic-release.svg +1 -0
  364. package/public/file-icons/settings.svg +1 -0
  365. package/public/file-icons/shader.svg +1 -0
  366. package/public/file-icons/sml.svg +1 -0
  367. package/public/file-icons/solidity.svg +1 -0
  368. package/public/file-icons/stylus.svg +1 -0
  369. package/public/file-icons/svelte.svg +1 -0
  370. package/public/file-icons/svg.svg +1 -0
  371. package/public/file-icons/swift.svg +1 -0
  372. package/public/file-icons/table.svg +1 -0
  373. package/public/file-icons/tailwindcss.svg +1 -0
  374. package/public/file-icons/taskfile.svg +1 -0
  375. package/public/file-icons/terraform.svg +1 -0
  376. package/public/file-icons/test-js.svg +1 -0
  377. package/public/file-icons/test-jsx.svg +1 -0
  378. package/public/file-icons/test-ts.svg +1 -0
  379. package/public/file-icons/tex.svg +1 -0
  380. package/public/file-icons/toml.svg +1 -0
  381. package/public/file-icons/travis.svg +1 -0
  382. package/public/file-icons/tsconfig.svg +1 -0
  383. package/public/file-icons/tune.svg +1 -0
  384. package/public/file-icons/turborepo.svg +1 -0
  385. package/public/file-icons/typescript-def.svg +1 -0
  386. package/public/file-icons/typescript.svg +1 -0
  387. package/public/file-icons/unity.svg +1 -0
  388. package/public/file-icons/uv.svg +1 -0
  389. package/public/file-icons/vala.svg +1 -0
  390. package/public/file-icons/vercel.svg +1 -0
  391. package/public/file-icons/video.svg +1 -0
  392. package/public/file-icons/vim.svg +1 -0
  393. package/public/file-icons/vite.svg +1 -0
  394. package/public/file-icons/vitest.svg +1 -0
  395. package/public/file-icons/vlang.svg +1 -0
  396. package/public/file-icons/vue.svg +1 -0
  397. package/public/file-icons/webassembly.svg +1 -0
  398. package/public/file-icons/webpack.svg +1 -0
  399. package/public/file-icons/word.svg +1 -0
  400. package/public/file-icons/xml.svg +1 -0
  401. package/public/file-icons/yaml.svg +1 -0
  402. package/public/file-icons/yarn.svg +1 -0
  403. package/public/file-icons/zig.svg +1 -0
  404. package/public/file-icons/zip.svg +1 -0
  405. package/public/provider-icons/antgroup-color.svg +1 -0
  406. package/public/provider-icons/anthropic.svg +1 -0
  407. package/public/provider-icons/azure-color.svg +1 -0
  408. package/public/provider-icons/baseten.svg +1 -0
  409. package/public/provider-icons/bedrock-color.svg +1 -0
  410. package/public/provider-icons/cerebras-color.svg +1 -0
  411. package/public/provider-icons/cloudflare-color.svg +1 -0
  412. package/public/provider-icons/deepseek-color.svg +1 -0
  413. package/public/provider-icons/fireworks-color.svg +1 -0
  414. package/public/provider-icons/githubcopilot.svg +1 -0
  415. package/public/provider-icons/google-color.svg +1 -0
  416. package/public/provider-icons/googlecloud-color.svg +1 -0
  417. package/public/provider-icons/groq.svg +1 -0
  418. package/public/provider-icons/huggingface-color.svg +1 -0
  419. package/public/provider-icons/kimi-color.svg +1 -0
  420. package/public/provider-icons/minimax-color.svg +1 -0
  421. package/public/provider-icons/mistral-color.svg +1 -0
  422. package/public/provider-icons/moonshot.svg +1 -0
  423. package/public/provider-icons/nvidia-color.svg +1 -0
  424. package/public/provider-icons/openai.svg +1 -0
  425. package/public/provider-icons/opencode.svg +1 -0
  426. package/public/provider-icons/openrouter-color.svg +1 -0
  427. package/public/provider-icons/qwen-color.svg +1 -0
  428. package/public/provider-icons/together-color.svg +1 -0
  429. package/public/provider-icons/vercel.svg +1 -0
  430. package/public/provider-icons/xai.svg +1 -0
  431. package/public/provider-icons/xiaomimimo.svg +1 -0
  432. package/public/provider-icons/zai.svg +1 -0
  433. package/scripts/build-output.mjs +131 -0
  434. package/scripts/dev.mjs +18 -0
  435. package/scripts/entrypoint.mjs +8 -0
  436. package/scripts/install-build.mjs +134 -0
  437. package/scripts/launcher.mjs +249 -0
  438. package/scripts/pick-folder.ps1 +110 -0
  439. package/scripts/postinstall.mjs +12 -0
  440. package/scripts/process-runner.mjs +60 -0
  441. package/scripts/release.mjs +172 -0
  442. package/scripts/sync-file-icons.mjs +118 -0
  443. package/scripts/sync-provider-icons.mjs +41 -0
  444. package/src/app/api/agent/[id]/events/route.ts +81 -0
  445. package/src/app/api/agent/[id]/route.ts +24 -0
  446. package/src/app/api/agent/[id]/tree/route.ts +49 -0
  447. package/src/app/api/agent/new/route.ts +19 -0
  448. package/src/app/api/config/route.ts +22 -0
  449. package/src/app/api/files/route.ts +63 -0
  450. package/src/app/api/git/route.ts +32 -0
  451. package/src/app/api/growth/route.ts +50 -0
  452. package/src/app/api/health/route.ts +7 -0
  453. package/src/app/api/models/route.ts +129 -0
  454. package/src/app/api/pi-settings/route.ts +22 -0
  455. package/src/app/api/plugins/route.ts +79 -0
  456. package/src/app/api/security/route.ts +41 -0
  457. package/src/app/api/sessions/[id]/export/route.ts +25 -0
  458. package/src/app/api/sessions/[id]/route.ts +37 -0
  459. package/src/app/api/sessions/route.ts +23 -0
  460. package/src/app/api/skills/route.ts +39 -0
  461. package/src/app/api/update/route.ts +33 -0
  462. package/src/app/api/version/route.ts +8 -0
  463. package/src/app/api/web-auth/route.ts +80 -0
  464. package/src/app/api/workspaces/route.ts +78 -0
  465. package/src/app/globals.css +2039 -0
  466. package/src/app/icon.svg +21 -0
  467. package/src/app/layout.tsx +29 -0
  468. package/src/app/login/page.tsx +111 -0
  469. package/src/app/page.tsx +10 -0
  470. package/src/components/AppShell.tsx +1171 -0
  471. package/src/components/ChatInput.tsx +729 -0
  472. package/src/components/ChatWindow.tsx +1320 -0
  473. package/src/components/ContextMeter.tsx +131 -0
  474. package/src/components/DiffView.tsx +139 -0
  475. package/src/components/ExtensionUI.tsx +181 -0
  476. package/src/components/FileViewer.tsx +555 -0
  477. package/src/components/FullDiff.tsx +220 -0
  478. package/src/components/GitPanel.tsx +357 -0
  479. package/src/components/GrowthTree.tsx +317 -0
  480. package/src/components/ModelSelector.tsx +286 -0
  481. package/src/components/OutlineRail.tsx +166 -0
  482. package/src/components/PiMark.tsx +79 -0
  483. package/src/components/ProjectPanel.tsx +309 -0
  484. package/src/components/ProviderBrand.tsx +28 -0
  485. package/src/components/ProviderSetupModal.module.css +560 -0
  486. package/src/components/ProviderSetupModal.tsx +559 -0
  487. package/src/components/SessionSidebar.tsx +1066 -0
  488. package/src/components/SettingsPanel.tsx +2216 -0
  489. package/src/components/TrajectoryAnalyzer.module.css +225 -0
  490. package/src/components/TrajectoryView.tsx +289 -0
  491. package/src/components/icons.tsx +443 -0
  492. package/src/hooks/useFileViewer.ts +58 -0
  493. package/src/hooks/useGrowth.ts +635 -0
  494. package/src/hooks/usePiWeb.ts +900 -0
  495. package/src/i18n.tsx +1043 -0
  496. package/src/lib/agent-manager.ts +1348 -0
  497. package/src/lib/app-root.ts +4 -0
  498. package/src/lib/auth.ts +51 -0
  499. package/src/lib/command-validation.ts +116 -0
  500. package/src/lib/enter-behavior.ts +40 -0
  501. package/src/lib/extension-ui.ts +134 -0
  502. package/src/lib/file-icons.json +557 -0
  503. package/src/lib/file-icons.ts +39 -0
  504. package/src/lib/files-service.ts +280 -0
  505. package/src/lib/git-service.ts +255 -0
  506. package/src/lib/growth-rounds.ts +84 -0
  507. package/src/lib/growth-service.ts +586 -0
  508. package/src/lib/growth-tracker.ts +328 -0
  509. package/src/lib/growth-tree.ts +245 -0
  510. package/src/lib/growth-turns.ts +23 -0
  511. package/src/lib/highlight.ts +59 -0
  512. package/src/lib/model-draft.ts +103 -0
  513. package/src/lib/models-service.ts +852 -0
  514. package/src/lib/path-security.ts +101 -0
  515. package/src/lib/pi-settings.ts +115 -0
  516. package/src/lib/pi.ts +367 -0
  517. package/src/lib/plugins-service.ts +312 -0
  518. package/src/lib/process-format.ts +261 -0
  519. package/src/lib/provider-display.ts +57 -0
  520. package/src/lib/security-service.ts +52 -0
  521. package/src/lib/semver.ts +39 -0
  522. package/src/lib/session-reader.ts +204 -0
  523. package/src/lib/settings-write-lock.ts +45 -0
  524. package/src/lib/skills-service.ts +97 -0
  525. package/src/lib/text-sanitize.ts +20 -0
  526. package/src/lib/theme.ts +137 -0
  527. package/src/lib/trajectory.ts +294 -0
  528. package/src/lib/types.ts +300 -0
  529. package/src/lib/update-service.ts +129 -0
  530. package/src/lib/version.ts +6 -0
  531. package/src/lib/web-auth-shared.ts +35 -0
  532. package/src/lib/web-auth.ts +53 -0
  533. package/src/lib/workspace-store.ts +261 -0
  534. package/src/proxy.ts +53 -0
  535. package/tsconfig.json +49 -0
@@ -0,0 +1,4 @@
1
+ import path from "node:path";
2
+
3
+ /** The launchers set this explicitly; direct `next` invocations still use their working directory. */
4
+ export const APP_ROOT = path.resolve(process.env.PI_WEB_ROOT ?? process.cwd());
@@ -0,0 +1,51 @@
1
+ const BASIC_PREFIX = "Basic ";
2
+
3
+ /** 恒定时间字符串比较(长度不同也走完全程,不早退) */
4
+ export function equalText(left: string, right: string): boolean {
5
+ const max = Math.max(left.length, right.length);
6
+ let mismatch = left.length ^ right.length;
7
+ for (let i = 0; i < max; i += 1) {
8
+ mismatch |= (left.charCodeAt(i) || 0) ^ (right.charCodeAt(i) || 0);
9
+ }
10
+ return mismatch === 0;
11
+ }
12
+
13
+ export function validBasicAuthorization(header: string | null, password: string): boolean {
14
+ if (!header?.startsWith(BASIC_PREFIX)) return false;
15
+ try {
16
+ const decoded = Buffer.from(header.slice(BASIC_PREFIX.length), "base64").toString("utf8");
17
+ const separator = decoded.indexOf(":");
18
+ if (separator < 0) return false;
19
+ return equalText(decoded.slice(0, separator), "pi") && equalText(decoded.slice(separator + 1), password);
20
+ } catch {
21
+ return false;
22
+ }
23
+ }
24
+
25
+ export function isSafeOrigin(request: Request): boolean {
26
+ const origin = request.headers.get("origin");
27
+ if (!origin) return true;
28
+ try {
29
+ const originUrl = new URL(origin);
30
+ const expectedHost = request.headers.get("host") || new URL(request.url).host;
31
+ return originUrl.host.toLowerCase() === expectedHost.toLowerCase();
32
+ } catch {
33
+ return false;
34
+ }
35
+ }
36
+
37
+ export function isSafeHost(request: Request, passwordConfigured: boolean): boolean {
38
+ if (passwordConfigured) return true;
39
+ try {
40
+ const host = request.headers.get("host") || new URL(request.url).host;
41
+ const parsed = new URL(`http://${host}`);
42
+ return !parsed.username && !parsed.password && parsed.host.toLowerCase() === host.toLowerCase() && isLoopbackHostname(parsed.hostname);
43
+ } catch {
44
+ return false;
45
+ }
46
+ }
47
+
48
+ export function isLoopbackHostname(hostname: string): boolean {
49
+ const host = hostname.trim().toLowerCase().replace(/^\[|\]$/g, "");
50
+ return host === "localhost" || host === "::1" || host === "0:0:0:0:0:0:0:1" || /^127(?:\.\d{1,3}){3}$/.test(host);
51
+ }
@@ -0,0 +1,116 @@
1
+ import type { AgentCommand, ImageAttachment, ToolPreset } from "./types";
2
+
3
+ const COMMANDS = new Set<AgentCommand["cmd"]>([
4
+ "prepare",
5
+ "prompt",
6
+ "steer",
7
+ "followUp",
8
+ "abort",
9
+ "compact",
10
+ "setModel",
11
+ "setThinkingLevel",
12
+ "setToolPreset",
13
+ "setActiveTools",
14
+ "rename",
15
+ "fork",
16
+ "cycleModel",
17
+ "navigate",
18
+ "clearQueue",
19
+ "extensionUiResponse",
20
+ "reload",
21
+ ]);
22
+ const TOOL_PRESETS = new Set<ToolPreset>(["readonly", "standard", "full"]);
23
+ const STREAMING_BEHAVIORS = new Set(["steer", "followUp"]);
24
+ const IMAGE_TYPES = new Set(["image/jpeg", "image/png", "image/webp", "image/gif"]);
25
+ const MAX_TEXT = 1_000_000;
26
+ const MAX_IMAGES = 20;
27
+ const MAX_IMAGE_BASE64 = 28_000_000; // dsh: 单图 20MB
28
+ const MAX_IMAGES_BASE64 = 268_000_000; // dsh: 单条消息图片总量 200MB
29
+
30
+ export type CommandParseResult = { ok: true; command: AgentCommand } | { ok: false; error: string };
31
+
32
+ function optionalText(value: unknown, field: string): string | undefined {
33
+ if (value === undefined) return undefined;
34
+ if (typeof value !== "string") throw new Error(`${field} must be a string`);
35
+ if (value.length > MAX_TEXT) throw new Error(`${field} is too long`);
36
+ return value;
37
+ }
38
+
39
+ function parseImages(value: unknown): ImageAttachment[] | undefined {
40
+ if (value === undefined) return undefined;
41
+ if (!Array.isArray(value) || value.length > MAX_IMAGES) throw new Error(`images must contain at most ${MAX_IMAGES} items`);
42
+ let total = 0;
43
+ return value.map((raw) => {
44
+ if (!raw || typeof raw !== "object") throw new Error("invalid image");
45
+ const image = raw as Record<string, unknown>;
46
+ if (image.type !== "image" || typeof image.data !== "string" || typeof image.mimeType !== "string") {
47
+ throw new Error("invalid image");
48
+ }
49
+ if (!IMAGE_TYPES.has(image.mimeType)) throw new Error(`unsupported image type ${image.mimeType}`);
50
+ if (image.data.length === 0 || image.data.length > MAX_IMAGE_BASE64 || !/^[A-Za-z0-9+/]*={0,2}$/.test(image.data)) {
51
+ throw new Error("invalid or oversized image data");
52
+ }
53
+ total += image.data.length;
54
+ if (total > MAX_IMAGES_BASE64) throw new Error("image attachments are too large");
55
+ return { type: "image", data: image.data, mimeType: image.mimeType };
56
+ });
57
+ }
58
+
59
+ export function parseAgentCommand(value: unknown): CommandParseResult {
60
+ try {
61
+ if (!value || typeof value !== "object" || Array.isArray(value)) throw new Error("command must be an object");
62
+ const raw = value as Record<string, unknown>;
63
+ if (typeof raw.cmd !== "string" || !COMMANDS.has(raw.cmd as AgentCommand["cmd"])) throw new Error("unknown command");
64
+
65
+ const command: AgentCommand = {
66
+ cmd: raw.cmd as AgentCommand["cmd"],
67
+ text: optionalText(raw.text, "text"),
68
+ instructions: optionalText(raw.instructions, "instructions"),
69
+ provider: optionalText(raw.provider, "provider"),
70
+ modelId: optionalText(raw.modelId, "modelId"),
71
+ level: optionalText(raw.level, "level"),
72
+ entryId: optionalText(raw.entryId, "entryId"),
73
+ images: parseImages(raw.images),
74
+ };
75
+
76
+ if (raw.preset !== undefined) {
77
+ if (typeof raw.preset !== "string" || !TOOL_PRESETS.has(raw.preset as ToolPreset)) throw new Error("invalid tool preset");
78
+ command.preset = raw.preset as ToolPreset;
79
+ }
80
+ if (raw.behavior !== undefined) {
81
+ if (typeof raw.behavior !== "string" || !STREAMING_BEHAVIORS.has(raw.behavior)) throw new Error("invalid streaming behavior");
82
+ command.behavior = raw.behavior as "steer" | "followUp";
83
+ }
84
+ if (raw.direction !== undefined) {
85
+ if (raw.direction !== "forward" && raw.direction !== "backward") throw new Error("invalid cycle direction");
86
+ command.direction = raw.direction;
87
+ }
88
+ if (raw.names !== undefined) {
89
+ // setActiveTools 的目标工具名列表:数量与长度上限防滥用(未知名字由 agent-manager 求交集丢弃)
90
+ if (!Array.isArray(raw.names) || raw.names.length === 0 || raw.names.length > 64 || raw.names.some((n) => typeof n !== "string" || !n || n.length > 100)) {
91
+ throw new Error("names must be a non-empty array of tool names");
92
+ }
93
+ command.names = raw.names as string[];
94
+ }
95
+
96
+ if (raw.requestId !== undefined) command.requestId = optionalText(raw.requestId, "requestId");
97
+ if (raw.value !== undefined) command.value = optionalText(raw.value, "value");
98
+ if (raw.confirmed !== undefined) {
99
+ if (typeof raw.confirmed !== "boolean") throw new Error("confirmed must be a boolean");
100
+ command.confirmed = raw.confirmed;
101
+ }
102
+ if (raw.cancelled !== undefined) {
103
+ if (typeof raw.cancelled !== "boolean") throw new Error("cancelled must be a boolean");
104
+ command.cancelled = raw.cancelled;
105
+ }
106
+ if (command.cmd === "extensionUiResponse" && !command.requestId) throw new Error("requestId is required");
107
+ if (command.cmd === "setModel" && (!command.provider || !command.modelId)) throw new Error("provider and modelId are required");
108
+ if (command.cmd === "setThinkingLevel" && !command.level) throw new Error("level is required");
109
+ if (command.cmd === "setToolPreset" && !command.preset) throw new Error("preset is required");
110
+ if (command.cmd === "setActiveTools" && !command.names) throw new Error("names are required");
111
+ if (command.cmd === "navigate" && !command.entryId) throw new Error("entryId is required");
112
+ return { ok: true, command };
113
+ } catch (error) {
114
+ return { ok: false, error: error instanceof Error ? error.message : "invalid command" };
115
+ }
116
+ }
@@ -0,0 +1,40 @@
1
+ /**
2
+ * 繁忙时 Enter 的发送方式:steer(插话,当前工具跑完就送达)/ queue(排队,本轮结束后发送)。
3
+ * 存 localStorage;用 useSyncExternalStore 让输入卡和设置面板同步(改设置立刻反映到提示文案)。
4
+ */
5
+ import { useSyncExternalStore } from "react";
6
+
7
+ export type EnterBehavior = "steer" | "queue";
8
+
9
+ const KEY = "piweb.enterBehavior";
10
+ /** 默认插话:与 pi 终端的 Enter 一致,也是 Claude Code 排队消息「尽快送达」的行为 */
11
+ export const DEFAULT_ENTER_BEHAVIOR: EnterBehavior = "steer";
12
+ const listeners = new Set<() => void>();
13
+
14
+ export function getEnterBehavior(): EnterBehavior {
15
+ if (typeof window === "undefined") return DEFAULT_ENTER_BEHAVIOR;
16
+ const v = localStorage.getItem(KEY);
17
+ return v === "queue" || v === "steer" ? v : DEFAULT_ENTER_BEHAVIOR;
18
+ }
19
+
20
+ export function setEnterBehavior(v: EnterBehavior): void {
21
+ localStorage.setItem(KEY, v);
22
+ for (const l of listeners) l();
23
+ }
24
+
25
+ export function otherBehavior(v: EnterBehavior): EnterBehavior {
26
+ return v === "steer" ? "queue" : "steer";
27
+ }
28
+
29
+ function subscribe(l: () => void): () => void {
30
+ listeners.add(l);
31
+ window.addEventListener("storage", l);
32
+ return () => {
33
+ listeners.delete(l);
34
+ window.removeEventListener("storage", l);
35
+ };
36
+ }
37
+
38
+ export function useEnterBehavior(): EnterBehavior {
39
+ return useSyncExternalStore(subscribe, getEnterBehavior, () => DEFAULT_ENTER_BEHAVIOR);
40
+ }
@@ -0,0 +1,134 @@
1
+ /**
2
+ * extension-ui:把 pi 扩展的界面请求(select / confirm / input / notify / setStatus)
3
+ * 桥接到浏览器——与 pi 自己的 RPC 模式(extension_ui_request / extension_ui_response)同一套语义。
4
+ * 没有页面在看时(无订阅者)select/input 返回 undefined、confirm 返回 false,与 pi 的 noOp 行为一致。
5
+ */
6
+ import { randomUUID } from "node:crypto";
7
+ import type { WebEvent } from "./types";
8
+
9
+ type UiMethod = Extract<WebEvent, { type: "extension_ui" }>["method"];
10
+ type UiEvent = Extract<WebEvent, { type: "extension_ui" }>;
11
+
12
+ interface PendingUi {
13
+ request: UiEvent;
14
+ resolve: (response: { value?: string; confirmed?: boolean; cancelled?: boolean }) => void;
15
+ timer?: ReturnType<typeof setTimeout>;
16
+ removeAbortListener?: () => void;
17
+ }
18
+
19
+ export interface ExtensionUiBridge {
20
+ /** 供 AgentSession.bindExtensions 的 uiContext(只实现 Web 能承接的部分,其余 no-op) */
21
+ uiContext: Record<string, unknown>;
22
+ /** 浏览器应答 */
23
+ respond(requestId: string, response: { value?: string; confirmed?: boolean; cancelled?: boolean }): boolean;
24
+ /** 当前仍待应答的请求,不含已答/取消/过期请求。 */
25
+ getPendingRequests(): UiEvent[];
26
+ /** 会话销毁:把挂起的对话框全部按取消结束 */
27
+ dispose(): void;
28
+ /** 扩展通过 setStatus 设的状态文本(页脚展示) */
29
+ statuses: Map<string, string>;
30
+ }
31
+
32
+ const DEFAULT_TIMEOUT_MS = 10 * 60 * 1000;
33
+
34
+ export function createExtensionUiBridge(opts: {
35
+ publish: (evt: WebEvent) => void;
36
+ hasViewers: () => boolean;
37
+ }): ExtensionUiBridge {
38
+ const pending = new Map<string, PendingUi>();
39
+ const statuses = new Map<string, string>();
40
+ let disposed = false;
41
+
42
+ const ask = <T,>(
43
+ request: Omit<UiEvent, "type" | "id" | "ts">,
44
+ dialogOpts: { signal?: AbortSignal; timeout?: number } | undefined,
45
+ fallback: T,
46
+ parse: (r: { value?: string; confirmed?: boolean; cancelled?: boolean }) => T,
47
+ ): Promise<T> => {
48
+ // 没有浏览器在看:不能永远挂着扩展,按 pi 的 noOp 语义立即返回默认值
49
+ if (disposed || !opts.hasViewers() || dialogOpts?.signal?.aborted) return Promise.resolve(fallback);
50
+ const id = randomUUID();
51
+ return new Promise<T>((resolve) => {
52
+ const finish = (r: { value?: string; confirmed?: boolean; cancelled?: boolean } | null) => {
53
+ const entry = pending.get(id);
54
+ if (!entry) return;
55
+ pending.delete(id);
56
+ if (entry.timer) clearTimeout(entry.timer);
57
+ entry.removeAbortListener?.();
58
+ opts.publish({ type: "extension_ui_resolved", id, ts: Date.now() });
59
+ resolve(r ? parse(r) : fallback);
60
+ };
61
+ const event: UiEvent = { type: "extension_ui", id, ...request, timeout: dialogOpts?.timeout, ts: Date.now() };
62
+ const entry: PendingUi = { request: event, resolve: (r) => finish(r) };
63
+ const timeout = dialogOpts?.timeout ?? DEFAULT_TIMEOUT_MS;
64
+ entry.timer = setTimeout(() => finish(null), timeout);
65
+ entry.timer.unref?.();
66
+ const onAbort = () => finish(null);
67
+ dialogOpts?.signal?.addEventListener("abort", onAbort, { once: true });
68
+ entry.removeAbortListener = () => dialogOpts?.signal?.removeEventListener("abort", onAbort);
69
+ pending.set(id, entry);
70
+ opts.publish(event);
71
+ });
72
+ };
73
+
74
+ const uiContext: Record<string, unknown> = {
75
+ select: (title: string, options: string[], o?: { signal?: AbortSignal; timeout?: number }) =>
76
+ ask<string | undefined>({ method: "select", title, options }, o, undefined, (r) => (r.cancelled ? undefined : r.value)),
77
+ confirm: (title: string, message: string, o?: { signal?: AbortSignal; timeout?: number }) =>
78
+ ask<boolean>({ method: "confirm", title, message }, o, false, (r) => (r.cancelled ? false : r.confirmed === true)),
79
+ input: (title: string, placeholder?: string, o?: { signal?: AbortSignal; timeout?: number }) =>
80
+ ask<string | undefined>({ method: "input", title, placeholder }, o, undefined, (r) => (r.cancelled ? undefined : r.value)),
81
+ notify: (message: string, type?: "info" | "warning" | "error") => {
82
+ opts.publish({ type: "extension_ui", id: randomUUID(), method: "notify", message, notifyType: type ?? "info", ts: Date.now() });
83
+ },
84
+ setStatus: (key: string, text: string | undefined) => {
85
+ if (text === undefined) statuses.delete(key);
86
+ else statuses.set(key, text);
87
+ opts.publish({ type: "extension_ui", id: randomUUID(), method: "setStatus", statusKey: key, statusText: text, ts: Date.now() });
88
+ },
89
+ setWorkingMessage: (message?: string) => {
90
+ opts.publish({ type: "extension_ui", id: randomUUID(), method: "setWorkingMessage", message, ts: Date.now() });
91
+ },
92
+ // 终端专属能力:Web 无对应物,按 pi 的 noOp 处理
93
+ onTerminalInput: () => () => {},
94
+ setWorkingVisible: () => {},
95
+ setWorkingIndicator: () => {},
96
+ setHiddenThinkingLabel: () => {},
97
+ setWidget: () => {},
98
+ setFooter: () => {},
99
+ setHeader: () => {},
100
+ setTitle: () => {},
101
+ custom: async () => undefined,
102
+ pasteToEditor: () => {},
103
+ setEditorText: () => {},
104
+ getEditorText: () => "",
105
+ editor: async () => undefined,
106
+ addAutocompleteProvider: () => {},
107
+ setEditorComponent: () => {},
108
+ getEditorComponent: () => undefined,
109
+ getAllThemes: () => [],
110
+ getTheme: () => undefined,
111
+ setTheme: () => {},
112
+ getToolsExpanded: () => false,
113
+ setToolsExpanded: () => {},
114
+ };
115
+
116
+ return {
117
+ uiContext,
118
+ statuses,
119
+ getPendingRequests: () => [...pending.values()].map(({ request }) => ({ ...request, options: request.options ? [...request.options] : undefined })),
120
+ respond(requestId, response) {
121
+ const entry = pending.get(requestId);
122
+ if (!entry) return false;
123
+ entry.resolve(response);
124
+ return true;
125
+ },
126
+ dispose() {
127
+ disposed = true;
128
+ for (const entry of pending.values()) entry.resolve({ cancelled: true });
129
+ pending.clear();
130
+ },
131
+ };
132
+ }
133
+
134
+ export type { UiMethod };