@thaut/claude-ide 0.0.0-stage → 1.0.0

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 (559) hide show
  1. package/bin/claude-ide.js +23 -0
  2. package/package.json +75 -4
  3. package/server/add-dirs.ts +126 -0
  4. package/server/auth.test.ts +148 -0
  5. package/server/auth.ts +235 -0
  6. package/server/bang.ts +54 -0
  7. package/server/btw-instructions.ts +19 -0
  8. package/server/btw-policy.ts +17 -0
  9. package/server/btw.ts +143 -0
  10. package/server/child-env.ts +29 -0
  11. package/server/cli-control.ts +255 -0
  12. package/server/cli-files.ts +55 -0
  13. package/server/cli-info.ts +73 -0
  14. package/server/config/account.ts +65 -0
  15. package/server/config/agents.ts +186 -0
  16. package/server/config/mcp.ts +86 -0
  17. package/server/config/memory.ts +193 -0
  18. package/server/config/paths.ts +156 -0
  19. package/server/config/plugins.ts +204 -0
  20. package/server/config/routes.ts +95 -0
  21. package/server/config/settings.ts +246 -0
  22. package/server/config.ts +46 -0
  23. package/server/db.ts +121 -0
  24. package/server/directories.test.ts +180 -0
  25. package/server/directories.ts +87 -0
  26. package/server/dist-version.ts +69 -0
  27. package/server/durable-tasks.ts +61 -0
  28. package/server/file-transfers.ts +79 -0
  29. package/server/files.test.ts +127 -0
  30. package/server/files.ts +386 -0
  31. package/server/fs-queue.ts +14 -0
  32. package/server/fsutil.ts +144 -0
  33. package/server/git.ts +86 -0
  34. package/server/goal-state.test.ts +64 -0
  35. package/server/goal-state.ts +78 -0
  36. package/server/history-pages.test.ts +619 -0
  37. package/server/history-pages.ts +513 -0
  38. package/server/http-channel-manager.ts +468 -0
  39. package/server/http-channel-pull.test.ts +314 -0
  40. package/server/http-channel-push.test.ts +364 -0
  41. package/server/http-channel-registration.test.ts +291 -0
  42. package/server/http-channel-routes.ts +73 -0
  43. package/server/http-channel-store.ts +142 -0
  44. package/server/http-channel-test-fixture.ts +103 -0
  45. package/server/ide-messages-mcp.test.ts +82 -0
  46. package/server/ide-messages-mcp.ts +47 -0
  47. package/server/idecron-mcp.ts +65 -0
  48. package/server/idecron.test.ts +79 -0
  49. package/server/idecron.ts +271 -0
  50. package/server/index.ts +336 -0
  51. package/server/live-session.test.ts +1126 -0
  52. package/server/live-session.ts +1165 -0
  53. package/server/project-routes.ts +46 -0
  54. package/server/projects.test.ts +28 -0
  55. package/server/push.ts +315 -0
  56. package/server/report-cache.ts +28 -0
  57. package/server/session-aliases.test.ts +57 -0
  58. package/server/session-aliases.ts +50 -0
  59. package/server/session-branch.ts +71 -0
  60. package/server/session-dirs.ts +189 -0
  61. package/server/session-file.test.ts +134 -0
  62. package/server/session-file.ts +113 -0
  63. package/server/session-messaging.test.ts +297 -0
  64. package/server/session-messaging.ts +165 -0
  65. package/server/sessions.ts +638 -0
  66. package/server/subagents.test.ts +43 -0
  67. package/server/subagents.ts +58 -0
  68. package/server/tool-images.test.ts +76 -0
  69. package/server/tool-images.ts +45 -0
  70. package/server/turn-changes.ts +308 -0
  71. package/server/worktree-git.ts +87 -0
  72. package/server/worktrees.ts +138 -0
  73. package/server/ws.test.ts +195 -0
  74. package/server/ws.ts +197 -0
  75. package/shared/agent-model.ts +15 -0
  76. package/shared/agent-tasks.ts +12 -0
  77. package/shared/config.ts +175 -0
  78. package/shared/directories.ts +12 -0
  79. package/shared/files.ts +51 -0
  80. package/shared/http-channels.ts +63 -0
  81. package/shared/message-window.ts +189 -0
  82. package/shared/project.ts +77 -0
  83. package/shared/protocol.ts +203 -0
  84. package/shared/task-types.ts +4 -0
  85. package/shared/todos.ts +40 -0
  86. package/shared/write-policy.ts +9 -0
  87. package/web/dist/assets/AgentView-wrTKgcJ3.js +1 -0
  88. package/web/dist/assets/AnnotationSelection-BZmNmwVW.js +7 -0
  89. package/web/dist/assets/ChangesView-G1WTzh4q.js +2 -0
  90. package/web/dist/assets/FilesView-DPgAbYt_.js +6 -0
  91. package/web/dist/assets/Form-BKTwiYyw.js +1 -0
  92. package/web/dist/assets/HomePage-Dq2apjBe.js +1 -0
  93. package/web/dist/assets/Markdown-IwP-xF29.js +67 -0
  94. package/web/dist/assets/MessageList-DD9GypSy.js +15 -0
  95. package/web/dist/assets/Modal-g2rv8I_n.js +1 -0
  96. package/web/dist/assets/NotificationSettings-DpuLO_Bv.js +1 -0
  97. package/web/dist/assets/OverviewPage-Dx6miabE.js +1 -0
  98. package/web/dist/assets/ProjectHome-CousyTLL.js +1 -0
  99. package/web/dist/assets/SessionView-CPVL7_HO.js +2 -0
  100. package/web/dist/assets/SettingsPage-D_oYSnW2.js +7 -0
  101. package/web/dist/assets/WorktreesView-C7e0HV__.js +1 -0
  102. package/web/dist/assets/abap-CLvhMVsD.js +1 -0
  103. package/web/dist/assets/actionscript-3-Cs6JJXT5.js +1 -0
  104. package/web/dist/assets/ada-X3Y2mlq2.js +1 -0
  105. package/web/dist/assets/ahk-DwADkxyd.js +1 -0
  106. package/web/dist/assets/ahk2-_6rzW9BS.js +1 -0
  107. package/web/dist/assets/andromeeda-vGVdxbeo.js +1 -0
  108. package/web/dist/assets/angular-html-Cw29lun8.js +1 -0
  109. package/web/dist/assets/angular-ts-BXz-O3SJ.js +1 -0
  110. package/web/dist/assets/apache-U0d_L8uA.js +1 -0
  111. package/web/dist/assets/apex-KFGFyKwG.js +1 -0
  112. package/web/dist/assets/apl-xBchdRhy.js +1 -0
  113. package/web/dist/assets/applescript-CCn79oCD.js +1 -0
  114. package/web/dist/assets/ara-4CJ0cIlV.js +1 -0
  115. package/web/dist/assets/asciidoc-SCjQUq34.js +1 -0
  116. package/web/dist/assets/asm-Cmm7eHzH.js +1 -0
  117. package/web/dist/assets/astro-1q6DGuN3.js +1 -0
  118. package/web/dist/assets/aurora-x-CDeNXAV0.js +1 -0
  119. package/web/dist/assets/awk-BWXHIvNe.js +1 -0
  120. package/web/dist/assets/ayu-dark-DluEY0Gj.js +1 -0
  121. package/web/dist/assets/ayu-light-C3h-C4tm.js +1 -0
  122. package/web/dist/assets/ayu-mirage-Bqwy1Gya.js +1 -0
  123. package/web/dist/assets/ballerina-B7ZEbQpA.js +1 -0
  124. package/web/dist/assets/bat-qdxAdBWQ.js +1 -0
  125. package/web/dist/assets/beancount-C2IN4Vk8.js +1 -0
  126. package/web/dist/assets/berry-DKpUyyne.js +1 -0
  127. package/web/dist/assets/bibtex-Ci_nEsc7.js +1 -0
  128. package/web/dist/assets/bicep-CUHmPFLl.js +1 -0
  129. package/web/dist/assets/bird2-CYIUgYqB.js +1 -0
  130. package/web/dist/assets/blade-Cqdv4oZN.js +1 -0
  131. package/web/dist/assets/bsl-CVED6lJ1.js +1 -0
  132. package/web/dist/assets/c-D9gIxSfx.js +1 -0
  133. package/web/dist/assets/c3-D0apG41V.js +1 -0
  134. package/web/dist/assets/cadence-CQ2zXKGN.js +1 -0
  135. package/web/dist/assets/cairo-DRAZz75c.js +1 -0
  136. package/web/dist/assets/catppuccin-frappe-C9Q5zVZY.js +1 -0
  137. package/web/dist/assets/catppuccin-latte-CtsyrtPq.js +1 -0
  138. package/web/dist/assets/catppuccin-macchiato-R8nAf1vR.js +1 -0
  139. package/web/dist/assets/catppuccin-mocha-DjuaAJKi.js +1 -0
  140. package/web/dist/assets/chapel-CuseGurG.js +1 -0
  141. package/web/dist/assets/clarity-CtfBBspg.js +1 -0
  142. package/web/dist/assets/clojure-DqKBuwfJ.js +1 -0
  143. package/web/dist/assets/cmake-Bj61d0ZC.js +1 -0
  144. package/web/dist/assets/cobol-Bdpj3Qoh.js +1 -0
  145. package/web/dist/assets/codeowners-C8r90Shi.js +1 -0
  146. package/web/dist/assets/codeql-oeQT6MSM.js +1 -0
  147. package/web/dist/assets/coffee-Sk8pHFjs.js +1 -0
  148. package/web/dist/assets/common-lisp-Cv5bFMCO.js +1 -0
  149. package/web/dist/assets/coq-CGaSfhwR.js +1 -0
  150. package/web/dist/assets/core-BxI4V4-T.js +12 -0
  151. package/web/dist/assets/cpp-BQfBYKx3.js +1 -0
  152. package/web/dist/assets/crystal-B3j7DoOZ.js +1 -0
  153. package/web/dist/assets/csharp-dmMxLVLW.js +1 -0
  154. package/web/dist/assets/css-DpFbYmRh.js +1 -0
  155. package/web/dist/assets/csv-Dx-8-gkx.js +1 -0
  156. package/web/dist/assets/cue-CE9AQfxI.js +1 -0
  157. package/web/dist/assets/cypher-ClKdZ_lG.js +1 -0
  158. package/web/dist/assets/d-qD-0Kul2.js +1 -0
  159. package/web/dist/assets/dark-plus-Cs2F2srj.js +1 -0
  160. package/web/dist/assets/dart-CHZY9Xhz.js +1 -0
  161. package/web/dist/assets/dax-BkyTk9wS.js +1 -0
  162. package/web/dist/assets/desktop-Dlh5hvp9.js +1 -0
  163. package/web/dist/assets/diff-woXpYk--.js +1 -0
  164. package/web/dist/assets/docker-IyjqRm3v.js +1 -0
  165. package/web/dist/assets/dotenv-CuH5my32.js +1 -0
  166. package/web/dist/assets/dracula-BHWKrbxM.js +1 -0
  167. package/web/dist/assets/dracula-soft-5eyTD99u.js +1 -0
  168. package/web/dist/assets/dream-maker-DW3nJb8Q.js +1 -0
  169. package/web/dist/assets/edge-DXQWmXTB.js +1 -0
  170. package/web/dist/assets/elixir-CrDIY2Vo.js +1 -0
  171. package/web/dist/assets/elm-BslFEg8K.js +1 -0
  172. package/web/dist/assets/emacs-lisp-DR2vtWaj.js +1 -0
  173. package/web/dist/assets/engine-javascript-DJg3-Vu2.js +141 -0
  174. package/web/dist/assets/erb-CUYhg0Ws.js +1 -0
  175. package/web/dist/assets/erlang-BXdgHVfi.js +1 -0
  176. package/web/dist/assets/everforest-dark-sB-x3p7T.js +1 -0
  177. package/web/dist/assets/everforest-light-Df2xbC6M.js +1 -0
  178. package/web/dist/assets/fennel-DQxkIbk2.js +1 -0
  179. package/web/dist/assets/fish-BJitypiv.js +1 -0
  180. package/web/dist/assets/fluent-C03EYrpw.js +1 -0
  181. package/web/dist/assets/format-DrBC7w0A.js +1 -0
  182. package/web/dist/assets/fortran-fixed-form-D6mN0kjy.js +1 -0
  183. package/web/dist/assets/fortran-free-form-CYNrtFtB.js +1 -0
  184. package/web/dist/assets/fsharp-CwoEF39o.js +1 -0
  185. package/web/dist/assets/gdresource-CZkFGo3B.js +1 -0
  186. package/web/dist/assets/gdscript-DYvRD3hg.js +1 -0
  187. package/web/dist/assets/gdshader-CBce3t8t.js +1 -0
  188. package/web/dist/assets/genie-CV2tkWYe.js +1 -0
  189. package/web/dist/assets/gherkin-DExj1W_8.js +1 -0
  190. package/web/dist/assets/git-commit-dUGrvm6Q.js +1 -0
  191. package/web/dist/assets/git-rebase-4zCRqF28.js +1 -0
  192. package/web/dist/assets/github-dark-C-LZuMrd.js +1 -0
  193. package/web/dist/assets/github-dark-default-DXG-b-1a.js +1 -0
  194. package/web/dist/assets/github-dark-dimmed-Bx1FflLF.js +1 -0
  195. package/web/dist/assets/github-dark-high-contrast-B_tTalzw.js +1 -0
  196. package/web/dist/assets/github-light-EUqPIrTm.js +1 -0
  197. package/web/dist/assets/github-light-default-BXViO-2h.js +1 -0
  198. package/web/dist/assets/github-light-high-contrast-B68TUdTA.js +1 -0
  199. package/web/dist/assets/gleam-CSRkHgEL.js +1 -0
  200. package/web/dist/assets/glimmer-js-7adTRvH4.js +1 -0
  201. package/web/dist/assets/glimmer-ts-IdSskDYw.js +1 -0
  202. package/web/dist/assets/glsl-Qzy53KDt.js +1 -0
  203. package/web/dist/assets/gn-CAXYAgV-.js +1 -0
  204. package/web/dist/assets/gnuplot-7GGW24-e.js +1 -0
  205. package/web/dist/assets/go-rLFTqkRN.js +1 -0
  206. package/web/dist/assets/graphql-CV_puxbB.js +1 -0
  207. package/web/dist/assets/groovy-CacY0gHj.js +1 -0
  208. package/web/dist/assets/gruvbox-dark-hard-C820rvS2.js +1 -0
  209. package/web/dist/assets/gruvbox-dark-medium-BPjhmG05.js +1 -0
  210. package/web/dist/assets/gruvbox-dark-soft-MrdJrrXF.js +1 -0
  211. package/web/dist/assets/gruvbox-light-hard-BC_s9l72.js +1 -0
  212. package/web/dist/assets/gruvbox-light-medium-BAWPOn9u.js +1 -0
  213. package/web/dist/assets/gruvbox-light-soft-BSMLrYjP.js +1 -0
  214. package/web/dist/assets/hack-C5GPjiLV.js +1 -0
  215. package/web/dist/assets/haml-BQ5YR1DQ.js +1 -0
  216. package/web/dist/assets/handlebars-DnhRK9A1.js +1 -0
  217. package/web/dist/assets/haskell-D8IpX4py.js +1 -0
  218. package/web/dist/assets/haxe-C8oCnwJJ.js +1 -0
  219. package/web/dist/assets/hcl-Dh228itO.js +1 -0
  220. package/web/dist/assets/highlight.worker-CY-ZeDAl.js +1 -0
  221. package/web/dist/assets/hjson-CxZEssPk.js +1 -0
  222. package/web/dist/assets/hlsl-Cvrh5tZx.js +1 -0
  223. package/web/dist/assets/horizon-CE9ld1lL.js +1 -0
  224. package/web/dist/assets/horizon-bright-DSNQnXHK.js +1 -0
  225. package/web/dist/assets/houston-CsvMBhTu.js +1 -0
  226. package/web/dist/assets/html-CmHiEKV9.js +1 -0
  227. package/web/dist/assets/html-derivative-BXE-rYIK.js +1 -0
  228. package/web/dist/assets/http-DPjjqtcL.js +1 -0
  229. package/web/dist/assets/hurl-AMBpSloO.js +1 -0
  230. package/web/dist/assets/hxml-D7Y24wKF.js +1 -0
  231. package/web/dist/assets/hy-CZbG8q4J.js +1 -0
  232. package/web/dist/assets/imba-DsUTQ-LC.js +1 -0
  233. package/web/dist/assets/index-DNPnl4sh.css +2 -0
  234. package/web/dist/assets/index-DQh72lEx.js +14 -0
  235. package/web/dist/assets/ini-B5eOa1yu.js +1 -0
  236. package/web/dist/assets/java-CEM7Tdw_.js +1 -0
  237. package/web/dist/assets/javascript-CU3PLowd.js +1 -0
  238. package/web/dist/assets/jinja-fR0x_9Xy.js +1 -0
  239. package/web/dist/assets/jison-BoKsxs5M.js +1 -0
  240. package/web/dist/assets/json-BM2FWsRB.js +1 -0
  241. package/web/dist/assets/json5-BR5RXkoi.js +1 -0
  242. package/web/dist/assets/jsonc-CYpm1nAK.js +1 -0
  243. package/web/dist/assets/jsonl-CmCQp5Yx.js +1 -0
  244. package/web/dist/assets/jsonnet-CJTPZ8u_.js +1 -0
  245. package/web/dist/assets/jssm-DXw9l8Rf.js +1 -0
  246. package/web/dist/assets/jsx-B5fTecQP.js +1 -0
  247. package/web/dist/assets/julia-DGyDG39_.js +1 -0
  248. package/web/dist/assets/just-B-RyLcSR.js +1 -0
  249. package/web/dist/assets/kanagawa-dragon-Bozd7klA.js +1 -0
  250. package/web/dist/assets/kanagawa-lotus-BmuOG9ZW.js +1 -0
  251. package/web/dist/assets/kanagawa-wave-BML5mF2J.js +1 -0
  252. package/web/dist/assets/kdl-CsD5j6eV.js +1 -0
  253. package/web/dist/assets/kotlin-DhhofPvG.js +1 -0
  254. package/web/dist/assets/kusto-C7mF5XQf.js +1 -0
  255. package/web/dist/assets/langs-CciPKqaF.js +1 -0
  256. package/web/dist/assets/laserwave-C_8bwKvT.js +1 -0
  257. package/web/dist/assets/latex-CV1pQelm.js +1 -0
  258. package/web/dist/assets/lean-CewbzKMR.js +1 -0
  259. package/web/dist/assets/less-DVTAwKKz.js +1 -0
  260. package/web/dist/assets/lib-DlcVMUqT.js +1 -0
  261. package/web/dist/assets/light-plus-DVQuIRkW.js +1 -0
  262. package/web/dist/assets/liquid-9p05RSzQ.js +1 -0
  263. package/web/dist/assets/llvm-DqHOMS-s.js +1 -0
  264. package/web/dist/assets/log-BNLmms1o.js +1 -0
  265. package/web/dist/assets/logo-Cluzi2Zq.js +1 -0
  266. package/web/dist/assets/lua-CIPVh0S-.js +1 -0
  267. package/web/dist/assets/luau-VGWVLoqX.js +1 -0
  268. package/web/dist/assets/make-Dixweg8N.js +1 -0
  269. package/web/dist/assets/markdown-BYOwaDjH.js +1 -0
  270. package/web/dist/assets/marko-h2h6YUMS.js +1 -0
  271. package/web/dist/assets/material-theme-Bm3Qr25_.js +1 -0
  272. package/web/dist/assets/material-theme-darker-2IIEA8gg.js +1 -0
  273. package/web/dist/assets/material-theme-lighter-uhdI0v04.js +1 -0
  274. package/web/dist/assets/material-theme-ocean-CHQ94UKr.js +1 -0
  275. package/web/dist/assets/material-theme-palenight-B5W6OYN7.js +1 -0
  276. package/web/dist/assets/matlab-D7qyCx1q.js +1 -0
  277. package/web/dist/assets/mdc-yAyZMdoC.js +1 -0
  278. package/web/dist/assets/mdx-DQZ5AkYe.js +1 -0
  279. package/web/dist/assets/mermaid-BxpfTIzt.js +1 -0
  280. package/web/dist/assets/message-font-size-DoaZfwkT.js +1 -0
  281. package/web/dist/assets/min-dark-BSWPekZh.js +1 -0
  282. package/web/dist/assets/min-light-DDpmG2fV.js +1 -0
  283. package/web/dist/assets/mipsasm-BMqwQI7S.js +1 -0
  284. package/web/dist/assets/mojo-fg40_ntQ.js +1 -0
  285. package/web/dist/assets/monokai-BscAjiQZ.js +1 -0
  286. package/web/dist/assets/moonbit-D9W784E_.js +1 -0
  287. package/web/dist/assets/move-BYnGhZoM.js +1 -0
  288. package/web/dist/assets/narrat-_X_XdTYD.js +1 -0
  289. package/web/dist/assets/nextflow-BoTnSBQ_.js +1 -0
  290. package/web/dist/assets/nextflow-groovy-D7IRt6Pp.js +1 -0
  291. package/web/dist/assets/nginx-jWINucNT.js +1 -0
  292. package/web/dist/assets/night-owl-DhmEMT88.js +1 -0
  293. package/web/dist/assets/night-owl-light-eJ-hLW7d.js +1 -0
  294. package/web/dist/assets/nim-Dtt9DZ0t.js +1 -0
  295. package/web/dist/assets/nix-Cl_7024t.js +1 -0
  296. package/web/dist/assets/nord-Cb4Vim4T.js +1 -0
  297. package/web/dist/assets/nsis-CjNLYCLg.js +1 -0
  298. package/web/dist/assets/nushell--0fT3PI1.js +1 -0
  299. package/web/dist/assets/objective-c-D1A_Heim.js +1 -0
  300. package/web/dist/assets/objective-cpp-BsSzOQcm.js +1 -0
  301. package/web/dist/assets/ocaml-O90oeIOV.js +1 -0
  302. package/web/dist/assets/odin-B1RWQWA5.js +1 -0
  303. package/web/dist/assets/one-dark-pro-C6YyR8OD.js +1 -0
  304. package/web/dist/assets/one-light-D7Lr4KcI.js +1 -0
  305. package/web/dist/assets/openscad-BUDT5pXO.js +1 -0
  306. package/web/dist/assets/org-ClD5huOa.js +1 -0
  307. package/web/dist/assets/pascal-4ZHwLPI5.js +1 -0
  308. package/web/dist/assets/perl-BFegxyeJ.js +1 -0
  309. package/web/dist/assets/php-B-qOA3Y4.js +1 -0
  310. package/web/dist/assets/pkl-ot-7Btpt.js +1 -0
  311. package/web/dist/assets/plastic-DQwYfKfQ.js +1 -0
  312. package/web/dist/assets/plsql-DGHpHOYJ.js +1 -0
  313. package/web/dist/assets/po-BiJDBrnU.js +1 -0
  314. package/web/dist/assets/poimandres-DRFjx7u4.js +1 -0
  315. package/web/dist/assets/polar-C7UOKdEL.js +1 -0
  316. package/web/dist/assets/postcss-BXeXVLqQ.js +1 -0
  317. package/web/dist/assets/powerquery-DNMTfnFr.js +1 -0
  318. package/web/dist/assets/powershell-QtSnmXcu.js +1 -0
  319. package/web/dist/assets/prisma-DMgNhnYS.js +1 -0
  320. package/web/dist/assets/prolog-iXnhIJG7.js +1 -0
  321. package/web/dist/assets/proto-DB4EqR-F.js +1 -0
  322. package/web/dist/assets/pug-Cq0NBPu-.js +1 -0
  323. package/web/dist/assets/puppet-CDv2pdJW.js +1 -0
  324. package/web/dist/assets/purescript-9MfHhQsQ.js +1 -0
  325. package/web/dist/assets/python-gzcpVVnB.js +1 -0
  326. package/web/dist/assets/qml-CYUsa87k.js +1 -0
  327. package/web/dist/assets/qmldir-DCQb3MpD.js +1 -0
  328. package/web/dist/assets/qss-Fe1Jh2GI.js +1 -0
  329. package/web/dist/assets/r-4xJhgbOf.js +1 -0
  330. package/web/dist/assets/racket-DcIDlBhZ.js +1 -0
  331. package/web/dist/assets/raku-B3gFvitq.js +1 -0
  332. package/web/dist/assets/razor-BlHaOHx8.js +1 -0
  333. package/web/dist/assets/rbs-C0MpOrj4.js +1 -0
  334. package/web/dist/assets/react-B4UGpcWC.js +1 -0
  335. package/web/dist/assets/red-CYlAq2b0.js +1 -0
  336. package/web/dist/assets/reg-CRGYupPL.js +1 -0
  337. package/web/dist/assets/regexp-BUSxjv2o.js +1 -0
  338. package/web/dist/assets/rel-BtDbiS_P.js +1 -0
  339. package/web/dist/assets/riscv-Ckw8ddFX.js +1 -0
  340. package/web/dist/assets/ron-VUp2lXgN.js +1 -0
  341. package/web/dist/assets/rose-pine-aMsaMD1D.js +1 -0
  342. package/web/dist/assets/rose-pine-dawn-8PwUyIlP.js +1 -0
  343. package/web/dist/assets/rose-pine-moon-DO-qXg-I.js +1 -0
  344. package/web/dist/assets/rosmsg-CAekHB0j.js +1 -0
  345. package/web/dist/assets/rst-zjfguEh7.js +1 -0
  346. package/web/dist/assets/ruby-4oR9waBK.js +1 -0
  347. package/web/dist/assets/rust-DpiIf5JO.js +1 -0
  348. package/web/dist/assets/sas-Do4V_2i9.js +1 -0
  349. package/web/dist/assets/sass-DXrisJhu.js +1 -0
  350. package/web/dist/assets/scala-BiFt_7mu.js +1 -0
  351. package/web/dist/assets/scheme-DQCgrYNe.js +1 -0
  352. package/web/dist/assets/scss-CW6JPtag.js +1 -0
  353. package/web/dist/assets/sdbl-bTVj8UrX.js +1 -0
  354. package/web/dist/assets/shaderlab-C4rnI7x0.js +1 -0
  355. package/web/dist/assets/shellscript-zBmOMFJh.js +1 -0
  356. package/web/dist/assets/shellsession-DxGUTWaf.js +1 -0
  357. package/web/dist/assets/slack-dark-DnToyrRv.js +1 -0
  358. package/web/dist/assets/slack-ochin-B2OO5cIa.js +1 -0
  359. package/web/dist/assets/smalltalk-BHpQb5nM.js +1 -0
  360. package/web/dist/assets/smithy-Dgaz7aur.js +1 -0
  361. package/web/dist/assets/snazzy-light-4G7pJPwS.js +1 -0
  362. package/web/dist/assets/solarized-dark-b97czJOk.js +1 -0
  363. package/web/dist/assets/solarized-light-DFPxpIYQ.js +1 -0
  364. package/web/dist/assets/solidity-Bj7E1mRn.js +1 -0
  365. package/web/dist/assets/soy-DVtw20Yn.js +1 -0
  366. package/web/dist/assets/sparql-C0ia5K60.js +1 -0
  367. package/web/dist/assets/splunk-BC2Px7Mm.js +1 -0
  368. package/web/dist/assets/sql-DGCM-jag.js +1 -0
  369. package/web/dist/assets/ssh-config-BgfXC-Er.js +1 -0
  370. package/web/dist/assets/stata-DfuMOXna.js +1 -0
  371. package/web/dist/assets/stylus-B6D30XZt.js +1 -0
  372. package/web/dist/assets/surrealql-CBoRWxfR.js +1 -0
  373. package/web/dist/assets/svelte-g_uCUem5.js +1 -0
  374. package/web/dist/assets/swift-CyEgAFGc.js +1 -0
  375. package/web/dist/assets/synthwave-84-nFMaYfgc.js +1 -0
  376. package/web/dist/assets/system-verilog-Dh5ihWbf.js +1 -0
  377. package/web/dist/assets/systemd-BxMlprV5.js +1 -0
  378. package/web/dist/assets/talonscript-CohzipZa.js +1 -0
  379. package/web/dist/assets/tasl-DMoTqEGO.js +1 -0
  380. package/web/dist/assets/tcl-CZd0xW_V.js +1 -0
  381. package/web/dist/assets/templ-N5C1hJ4Y.js +1 -0
  382. package/web/dist/assets/terraform-DswuEJGm.js +1 -0
  383. package/web/dist/assets/tex-mx6xDwuK.js +1 -0
  384. package/web/dist/assets/themes-CNElGaXw.js +1 -0
  385. package/web/dist/assets/tokyo-night-oM2G3aXe.js +1 -0
  386. package/web/dist/assets/toml-CcmNWLt0.js +1 -0
  387. package/web/dist/assets/ts-tags-C-j71FRb.js +1 -0
  388. package/web/dist/assets/tsv-sltzmVWM.js +1 -0
  389. package/web/dist/assets/tsx-z04y7qR9.js +1 -0
  390. package/web/dist/assets/turtle-ByJddavk.js +1 -0
  391. package/web/dist/assets/twig-Bz57G_nl.js +1 -0
  392. package/web/dist/assets/typescript-RMqVTmzR.js +1 -0
  393. package/web/dist/assets/typespec-BGhR2SqG.js +1 -0
  394. package/web/dist/assets/typst-BgkqA2Pg.js +1 -0
  395. package/web/dist/assets/v-DBj4K9d_.js +1 -0
  396. package/web/dist/assets/vala-zf12oZj6.js +1 -0
  397. package/web/dist/assets/vb-DOwzaHZ0.js +1 -0
  398. package/web/dist/assets/verilog-B2gwz-sJ.js +1 -0
  399. package/web/dist/assets/vesper-DdrHHSXu.js +1 -0
  400. package/web/dist/assets/vhdl-BroJfC0k.js +1 -0
  401. package/web/dist/assets/viml-DvXPmvsu.js +1 -0
  402. package/web/dist/assets/vitesse-black-fwtXNY1n.js +1 -0
  403. package/web/dist/assets/vitesse-dark-BZCL-v6S.js +1 -0
  404. package/web/dist/assets/vitesse-light-VbXTXTou.js +1 -0
  405. package/web/dist/assets/vue-CQgvQytb.js +1 -0
  406. package/web/dist/assets/vue-html-Dhl6fh9l.js +1 -0
  407. package/web/dist/assets/vue-vine-Dw_uU59K.js +1 -0
  408. package/web/dist/assets/vyper-CgoNMtux.js +1 -0
  409. package/web/dist/assets/wasm-ByWQv1Qj.js +1 -0
  410. package/web/dist/assets/wenyan-C8pVoKbM.js +1 -0
  411. package/web/dist/assets/wgsl-BsKzXJz4.js +1 -0
  412. package/web/dist/assets/wikitext-ClFFjSW2.js +1 -0
  413. package/web/dist/assets/wit-DdvCle-K.js +1 -0
  414. package/web/dist/assets/wolfram-DLL8P-h_.js +1 -0
  415. package/web/dist/assets/xml-B49klYQ3.js +1 -0
  416. package/web/dist/assets/xsl-D7tnD3qq.js +1 -0
  417. package/web/dist/assets/yaml-DNQ5_xkL.js +1 -0
  418. package/web/dist/assets/zenscript-BnlCZFoB.js +1 -0
  419. package/web/dist/assets/zig-CMLA9XwU.js +1 -0
  420. package/web/dist/icons/apple-touch-icon.png +0 -0
  421. package/web/dist/icons/badge-96.png +0 -0
  422. package/web/dist/icons/icon-192.png +0 -0
  423. package/web/dist/icons/icon-512.png +0 -0
  424. package/web/dist/icons/icon.svg +4 -0
  425. package/web/dist/icons/maskable-192.png +0 -0
  426. package/web/dist/icons/maskable-512.png +0 -0
  427. package/web/dist/index.html +30 -0
  428. package/web/dist/manifest.webmanifest +20 -0
  429. package/web/dist/sw.js +176 -0
  430. package/web/dist/version.json +1 -0
  431. package/web/src/App.tsx +97 -0
  432. package/web/src/agent-history-flow.test.ts +191 -0
  433. package/web/src/agent-history-flow.ts +58 -0
  434. package/web/src/agent-owners.ts +30 -0
  435. package/web/src/annotation-selection.test.ts +36 -0
  436. package/web/src/annotation-selection.ts +123 -0
  437. package/web/src/annotations.test.ts +279 -0
  438. package/web/src/annotations.ts +191 -0
  439. package/web/src/api.ts +27 -0
  440. package/web/src/auth-events.ts +11 -0
  441. package/web/src/auth-lifecycle.test.ts +105 -0
  442. package/web/src/clipboard.ts +32 -0
  443. package/web/src/components/ActivityNotifier.tsx +22 -0
  444. package/web/src/components/AnnotationEditor.tsx +135 -0
  445. package/web/src/components/AnnotationSelection.tsx +122 -0
  446. package/web/src/components/Automation.tsx +390 -0
  447. package/web/src/components/CodeView.tsx +121 -0
  448. package/web/src/components/Composer.tsx +552 -0
  449. package/web/src/components/ComposerAnnotations.tsx +45 -0
  450. package/web/src/components/Confirmation.tsx +91 -0
  451. package/web/src/components/CopyButton.tsx +23 -0
  452. package/web/src/components/DiffView.tsx +94 -0
  453. package/web/src/components/DirectoryPicker.tsx +230 -0
  454. package/web/src/components/FileCopyMenu.tsx +58 -0
  455. package/web/src/components/FileEntryActions.tsx +95 -0
  456. package/web/src/components/ImagePreview.tsx +68 -0
  457. package/web/src/components/Items.tsx +608 -0
  458. package/web/src/components/Markdown.tsx +73 -0
  459. package/web/src/components/MessageList.tsx +458 -0
  460. package/web/src/components/Modal.tsx +72 -0
  461. package/web/src/components/NotificationEntry.tsx +31 -0
  462. package/web/src/components/NotificationSettings.tsx +226 -0
  463. package/web/src/components/NotificationToasts.tsx +59 -0
  464. package/web/src/components/OpenTabs.tsx +112 -0
  465. package/web/src/components/Pending.tsx +175 -0
  466. package/web/src/components/ProjectExtras.tsx +299 -0
  467. package/web/src/components/ProjectNav.tsx +47 -0
  468. package/web/src/components/SessionActions.tsx +271 -0
  469. package/web/src/components/SessionChannels.tsx +288 -0
  470. package/web/src/components/SessionExtras.tsx +263 -0
  471. package/web/src/components/SettingsLink.tsx +21 -0
  472. package/web/src/components/Shell.tsx +320 -0
  473. package/web/src/components/SvgPreview.tsx +18 -0
  474. package/web/src/components/TodoPanel.tsx +37 -0
  475. package/web/src/components/VersionNotice.tsx +40 -0
  476. package/web/src/components/icons.tsx +43 -0
  477. package/web/src/diff.ts +62 -0
  478. package/web/src/drafts.test.ts +80 -0
  479. package/web/src/drafts.ts +64 -0
  480. package/web/src/format.ts +5 -0
  481. package/web/src/highlight.worker.ts +49 -0
  482. package/web/src/hooks.ts +59 -0
  483. package/web/src/item-view-state.test.ts +51 -0
  484. package/web/src/item-view-state.ts +43 -0
  485. package/web/src/live-history-flow.test.ts +77 -0
  486. package/web/src/live-merge.test.ts +64 -0
  487. package/web/src/live-merge.ts +50 -0
  488. package/web/src/live-message-hydration.test.ts +253 -0
  489. package/web/src/live.ts +404 -0
  490. package/web/src/main.tsx +27 -0
  491. package/web/src/markdown-assets.test.ts +70 -0
  492. package/web/src/markdown-assets.ts +78 -0
  493. package/web/src/markdown-html.test.ts +84 -0
  494. package/web/src/markdown-html.ts +60 -0
  495. package/web/src/markdown-svg.test.ts +37 -0
  496. package/web/src/markdown-svg.ts +42 -0
  497. package/web/src/markdown-tables.test.ts +34 -0
  498. package/web/src/markdown-tables.ts +17 -0
  499. package/web/src/message-bodies.ts +117 -0
  500. package/web/src/message-cache.test.ts +208 -0
  501. package/web/src/message-cache.ts +158 -0
  502. package/web/src/message-font-size.test.ts +41 -0
  503. package/web/src/message-font-size.ts +57 -0
  504. package/web/src/message-time.test.ts +32 -0
  505. package/web/src/message-time.ts +26 -0
  506. package/web/src/notify.ts +132 -0
  507. package/web/src/open-tabs.test.ts +69 -0
  508. package/web/src/open-tabs.ts +110 -0
  509. package/web/src/pages/AgentView.tsx +138 -0
  510. package/web/src/pages/ChangesView.tsx +193 -0
  511. package/web/src/pages/FilesView.tsx +406 -0
  512. package/web/src/pages/HomePage.tsx +96 -0
  513. package/web/src/pages/LoginPage.tsx +56 -0
  514. package/web/src/pages/OverviewPage.tsx +124 -0
  515. package/web/src/pages/ProjectHome.tsx +136 -0
  516. package/web/src/pages/SessionView.tsx +238 -0
  517. package/web/src/pages/WorktreesView.tsx +197 -0
  518. package/web/src/pages/settings/AccountPage.tsx +149 -0
  519. package/web/src/pages/settings/AgentsPage.tsx +182 -0
  520. package/web/src/pages/settings/ConfigPage.tsx +181 -0
  521. package/web/src/pages/settings/GeneralPage.tsx +27 -0
  522. package/web/src/pages/settings/HooksPage.tsx +244 -0
  523. package/web/src/pages/settings/McpPage.tsx +310 -0
  524. package/web/src/pages/settings/MemoryPage.tsx +309 -0
  525. package/web/src/pages/settings/PermissionsPage.tsx +160 -0
  526. package/web/src/pages/settings/PluginsPage.tsx +251 -0
  527. package/web/src/pages/settings/SettingsPage.tsx +141 -0
  528. package/web/src/pages/settings/SkillsPage.tsx +142 -0
  529. package/web/src/pages/settings/ui.tsx +165 -0
  530. package/web/src/preference-document.ts +133 -0
  531. package/web/src/project-actions.ts +18 -0
  532. package/web/src/project-files-state.test.ts +49 -0
  533. package/web/src/project-files-state.ts +64 -0
  534. package/web/src/project-images.ts +13 -0
  535. package/web/src/pwa.ts +33 -0
  536. package/web/src/queryClient.ts +5 -0
  537. package/web/src/reasoning-preference.test.ts +39 -0
  538. package/web/src/reasoning-preference.ts +42 -0
  539. package/web/src/session-confirmations.ts +36 -0
  540. package/web/src/stream-replay.test.ts +163 -0
  541. package/web/src/styles.css +231 -0
  542. package/web/src/tool-result-body.test.ts +115 -0
  543. package/web/src/transcript.test.ts +186 -0
  544. package/web/src/transcript.ts +583 -0
  545. package/web/src/turns.test.ts +338 -0
  546. package/web/src/turns.ts +178 -0
  547. package/web/src/ui/Button.tsx +60 -0
  548. package/web/src/ui/Controls.tsx +227 -0
  549. package/web/src/ui/Form.tsx +203 -0
  550. package/web/src/ui/MenuPopover.tsx +144 -0
  551. package/web/src/ui/Popover.tsx +147 -0
  552. package/web/src/ui/ResizablePane.tsx +110 -0
  553. package/web/src/ui/Select.tsx +150 -0
  554. package/web/src/ui/icons.tsx +270 -0
  555. package/web/src/unsaved.tsx +99 -0
  556. package/web/src/user-text.test.ts +60 -0
  557. package/web/src/user-text.ts +35 -0
  558. package/web/src/uuid.ts +12 -0
  559. package/README.md +0 -3
@@ -0,0 +1,124 @@
1
+ import { Link } from "react-router";
2
+ import { useOverview, type OverviewItem } from "../hooks.ts";
3
+ import { StateDot } from "../components/Shell.tsx";
4
+ import { Button } from "../ui/Button.tsx";
5
+ import { EmptyState, Skeleton, Tag } from "../ui/Form.tsx";
6
+ import { ChevronRightIcon, ClockIcon, FolderIcon, ListIcon, RefreshIcon, SparkIcon } from "../ui/icons.tsx";
7
+
8
+ /** Everything that is running or waiting for the user, across projects. */
9
+ export function OverviewPage() {
10
+ const overview = useOverview();
11
+ const items = overview.data ?? [];
12
+ const waiting = items.filter((o) => o.waitingFor.length > 0 || o.state === "requires_action").length;
13
+ const running = items.filter((o) => o.state === "running").length;
14
+ const crons = items.some((o) => o.cronIds !== undefined)
15
+ ? new Set(items.flatMap((o) => o.cronIds ?? [])).size
16
+ : items.reduce((n, o) => n + o.crons, 0);
17
+ const tasks = items.reduce((n, o) => n + o.tasks, 0);
18
+
19
+ // Waiting first (they need the user), then running, then the rest; grouped by project.
20
+ const rank = (o: OverviewItem) => (o.waitingFor.length || o.state === "requires_action" ? 0 : o.state === "running" ? 1 : 2);
21
+ const groups = new Map<string, { name: string; items: OverviewItem[] }>();
22
+ for (const o of [...items].sort((a, b) => rank(a) - rank(b))) {
23
+ const g = groups.get(o.projectId) ?? { name: o.projectName, items: [] };
24
+ g.items.push(o);
25
+ groups.set(o.projectId, g);
26
+ }
27
+
28
+ return (
29
+ <main className="min-h-0 flex-1 overflow-y-auto">
30
+ <div className="mx-auto max-w-4xl px-4 py-8 md:px-8 md:py-10">
31
+ <div className="flex flex-wrap items-start gap-3">
32
+ <div className="min-w-0 flex-1">
33
+ <h1 className="text-3xl">运行总览</h1>
34
+ <p className="mt-1 text-sm text-muted">所有项目里正在运行、等你处理,或有定时任务的会话。每 30 秒自动刷新。</p>
35
+ </div>
36
+ <Button size="sm" variant="ghost" icon={<RefreshIcon />} onClick={() => overview.refetch()} loading={overview.isFetching}>
37
+ 刷新
38
+ </Button>
39
+ </div>
40
+
41
+ <div className="mt-6 grid grid-cols-2 gap-3 md:grid-cols-4">
42
+ <Stat label="等你处理" value={waiting} tone={waiting ? "warn" : undefined} />
43
+ <Stat label="运行中" value={running} tone={running ? "accent" : undefined} />
44
+ <Stat label="定时任务" value={crons} />
45
+ <Stat label="后台任务" value={tasks} />
46
+ </div>
47
+
48
+ {overview.isPending && (
49
+ <div className="mt-8">
50
+ <Skeleton rows={3} />
51
+ </div>
52
+ )}
53
+ <div className="mt-8 space-y-6" data-testid="overview-list">
54
+ {[...groups.entries()].map(([pid, g]) => (
55
+ <section key={pid}>
56
+ <div className="mb-2 flex items-center gap-2">
57
+ <FolderIcon className="text-muted" />
58
+ <Link to={`/p/${pid}`} className="text-sm font-medium hover:text-accent">
59
+ {g.name}
60
+ </Link>
61
+ <span className="text-xs text-muted">{g.items.length} 个会话</span>
62
+ </div>
63
+ <ul className="grid gap-2 md:grid-cols-2">
64
+ {g.items.map((o) => (
65
+ <li key={o.sessionId}>
66
+ <Link
67
+ to={`/p/${o.projectId}/s/${o.sessionId}`}
68
+ className="flex h-full items-start gap-3 rounded-xl border border-border bg-surface px-4 py-3 shadow-sm transition-colors hover:border-muted/40"
69
+ data-testid="overview-item"
70
+ data-state={o.state}
71
+ >
72
+ <span className="mt-1.5">
73
+ <StateDot state={o.state} />
74
+ </span>
75
+ <span className="min-w-0 flex-1">
76
+ <span className="line-clamp-2 text-sm font-medium">{o.title}</span>
77
+ <span className="mt-1.5 flex flex-wrap gap-1.5">
78
+ {o.waitingFor.includes("ask") && <Tag tone="warn">等你回答问题</Tag>}
79
+ {o.waitingFor.includes("plan") && <Tag tone="warn">等你审批计划</Tag>}
80
+ {o.state === "running" && (
81
+ <Tag tone="accent" icon={<SparkIcon size={11} />}>
82
+ 运行中
83
+ </Tag>
84
+ )}
85
+ {o.crons > 0 && <Tag icon={<ClockIcon size={11} />}>{o.crons} 个定时任务</Tag>}
86
+ {o.tasks > 0 && <Tag icon={<ListIcon size={11} />}>{o.tasks} 个后台任务</Tag>}
87
+ </span>
88
+ </span>
89
+ <ChevronRightIcon className="mt-1 shrink-0 text-muted" />
90
+ </Link>
91
+ </li>
92
+ ))}
93
+ </ul>
94
+ </section>
95
+ ))}
96
+ </div>
97
+ {overview.isSuccess && items.length === 0 && (
98
+ <div className="mt-2">
99
+ <EmptyState
100
+ icon={<SparkIcon size={16} />}
101
+ title="现在没有正在运行的会话"
102
+ action={
103
+ <Link to="/" className="inline-flex h-10 items-center rounded-md border border-border bg-surface px-3 text-sm font-medium hover:bg-surface-2 md:h-8">
104
+ 去项目列表
105
+ </Link>
106
+ }
107
+ >
108
+ 会话开始运行、需要你回答问题或审批计划、设置了定时任务时,会出现在这里,方便在多个项目之间来回照看。
109
+ </EmptyState>
110
+ </div>
111
+ )}
112
+ </div>
113
+ </main>
114
+ );
115
+ }
116
+
117
+ function Stat({ label, value, tone }: { label: string; value: number; tone?: "accent" | "warn" }) {
118
+ return (
119
+ <div className="rounded-xl border border-border bg-surface px-4 py-3">
120
+ <div className="text-xs text-muted">{label}</div>
121
+ <div className={`mt-1 font-serif text-2xl tabular-nums ${tone === "accent" ? "text-accent" : tone === "warn" ? "text-amber-600 dark:text-amber-400" : ""}`}>{value}</div>
122
+ </div>
123
+ );
124
+ }
@@ -0,0 +1,136 @@
1
+ import { Link, useNavigate } from "react-router";
2
+ import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
3
+ import type { GitStatus } from "@shared/files.ts";
4
+ import type { SessionListItem } from "@shared/protocol.ts";
5
+ import { api } from "../api.ts";
6
+ import { relativeTime, useProjects, useSessions } from "../hooks.ts";
7
+ import { Button } from "../ui/Button.tsx";
8
+ import { BranchIcon, FolderIcon } from "../ui/icons.tsx";
9
+ import { PlusIcon } from "../components/icons.tsx";
10
+ import { StateDot } from "../components/Shell.tsx";
11
+ import { CopyButton } from "../components/CopyButton.tsx";
12
+ import { projectFilesUrl, useProjectFilesState } from "../project-files-state.ts";
13
+
14
+ /** Desktop landing page of a project: what is going on in it, and the way back into recent work. */
15
+ export function ProjectHome({ pid }: { pid: string }) {
16
+ const files = useProjectFilesState(pid);
17
+ const project = useProjects().data?.find((p) => p.id === pid);
18
+ const sessions = useSessions(pid);
19
+ const git = useQuery({ queryKey: ["git-status", pid], queryFn: () => api<GitStatus>(`/api/projects/${pid}/git/status`), staleTime: 15_000 });
20
+ const navigate = useNavigate();
21
+ const qc = useQueryClient();
22
+ const create = useMutation({
23
+ mutationFn: () => api<{ id: string }>(`/api/projects/${pid}/sessions`, { method: "POST" }),
24
+ onSuccess: ({ id }) => {
25
+ qc.invalidateQueries({ queryKey: ["sessions", pid] });
26
+ navigate(`/p/${pid}/s/${id}`);
27
+ },
28
+ });
29
+
30
+ const all = (sessions.data ?? []).filter((s) => !s.archived);
31
+ const running = all.filter((s) => s.state === "running").length;
32
+ const waiting = all.filter((s) => s.state === "requires_action" || s.needsAttention).length;
33
+ const changes = git.data?.isRepo ? git.data.changes.length : undefined;
34
+ const recent = [...all].sort((a, b) => b.lastModified - a.lastModified).slice(0, 8);
35
+
36
+ return (
37
+ <main className="min-h-0 flex-1 overflow-y-auto" data-testid="project-home">
38
+ <div className="mx-auto max-w-4xl px-8 py-10">
39
+ <div className="flex items-start gap-4">
40
+ <div className="min-w-0 flex-1">
41
+ <h1 className="truncate text-3xl">{project?.name ?? "项目"}</h1>
42
+ <div className="mt-1 flex min-w-0 flex-wrap items-center gap-x-2 gap-y-1 text-xs text-muted">
43
+ <span className="inline-flex min-w-0 max-w-full items-center gap-1.5 font-mono">
44
+ <FolderIcon className="shrink-0" />
45
+ <span className="min-w-0 truncate" title={project?.path}>{project?.path}</span>
46
+ {project?.path && <CopyButton key={project.path} text={project.path} label="复制项目路径" />}
47
+ </span>
48
+ {git.data?.isRepo && git.data.branch && (
49
+ <span className="inline-flex min-w-0 items-center gap-1.5 font-mono">
50
+ <BranchIcon className="shrink-0" />
51
+ <span className="truncate">{git.data.branch}</span>
52
+ </span>
53
+ )}
54
+ </div>
55
+ </div>
56
+ <Button variant="primary" icon={<PlusIcon />} onClick={() => create.mutate()} loading={create.isPending}>
57
+ 新会话
58
+ </Button>
59
+ </div>
60
+
61
+ <div className="mt-8 grid grid-cols-2 gap-3 lg:grid-cols-4">
62
+ <Stat label="会话" value={all.length} />
63
+ <Stat label="运行中" value={running} tone={running ? "accent" : undefined} />
64
+ <Stat label="等你处理" value={waiting} tone={waiting ? "warn" : undefined} />
65
+ <Stat label="未提交改动" value={changes ?? "—"} to={changes !== undefined ? `/p/${pid}/changes` : undefined} />
66
+ </div>
67
+
68
+ <section className="mt-10">
69
+ <div className="mb-3 flex items-baseline justify-between">
70
+ <h2 className="text-lg">最近的会话</h2>
71
+ <span className="text-xs text-muted">{all.length > recent.length ? `另有 ${all.length - recent.length} 个在左侧列表` : ""}</span>
72
+ </div>
73
+ {recent.length ? (
74
+ <ul className="grid gap-2 md:grid-cols-2">
75
+ {recent.map((s) => (
76
+ <li key={s.id}>
77
+ <RecentCard pid={pid} s={s} />
78
+ </li>
79
+ ))}
80
+ </ul>
81
+ ) : (
82
+ <div className="rounded-xl border border-dashed border-border px-6 py-10 text-center text-sm text-muted">
83
+ 这个项目还没有会话。点右上角"新会话"开始;在终端里用 <code className="font-mono">claude</code> 开的会话也会出现在这里。
84
+ </div>
85
+ )}
86
+ </section>
87
+
88
+ <section className="mt-10 grid gap-3 md:grid-cols-3">
89
+ <Shortcut to={projectFilesUrl(pid, files.file)} title="浏览文件" body="文件树、代码高亮、按名称和内容搜索。" />
90
+ <Shortcut to={`/p/${pid}/changes`} title="查看变更" body="git 状态和每个文件的 diff。" />
91
+ <Shortcut to={`/p/${pid}/worktrees`} title="工作区" body="为一个任务开独立的 git worktree,并行改代码。" />
92
+ </section>
93
+ </div>
94
+ </main>
95
+ );
96
+ }
97
+
98
+ function Stat({ label, value, tone, to }: { label: string; value: number | string; tone?: "accent" | "warn"; to?: string }) {
99
+ const body = (
100
+ <>
101
+ <div className="text-xs text-muted">{label}</div>
102
+ <div className={`mt-1 font-serif text-2xl tabular-nums ${tone === "accent" ? "text-accent" : tone === "warn" ? "text-amber-600 dark:text-amber-400" : ""}`}>{value}</div>
103
+ </>
104
+ );
105
+ const cls = "block rounded-xl border border-border bg-surface px-4 py-3";
106
+ return to ? (
107
+ <Link to={to} className={`${cls} transition-colors hover:border-muted/40`}>
108
+ {body}
109
+ </Link>
110
+ ) : (
111
+ <div className={cls}>{body}</div>
112
+ );
113
+ }
114
+
115
+ function RecentCard({ pid, s }: { pid: string; s: SessionListItem }) {
116
+ return (
117
+ <Link to={`/p/${pid}/s/${s.id}`} className="flex h-full flex-col rounded-xl border border-border bg-surface px-4 py-3 transition-colors hover:border-muted/40">
118
+ <span className="line-clamp-2 text-sm font-medium">{s.title || (s.draft ? "新会话" : "未命名会话")}</span>
119
+ <span className="mt-auto flex items-center gap-1.5 pt-2 text-xs text-muted">
120
+ {s.state !== "idle" && <StateDot state={s.state} />}
121
+ {relativeTime(s.lastModified)}
122
+ {s.worktree && <span className="truncate font-mono">· ⎇ {s.worktree}</span>}
123
+ {s.pinned && <span className="text-accent">· 置顶</span>}
124
+ </span>
125
+ </Link>
126
+ );
127
+ }
128
+
129
+ function Shortcut({ to, title, body }: { to: string; title: string; body: string }) {
130
+ return (
131
+ <Link to={to} className="rounded-xl border border-border px-4 py-3 transition-colors hover:border-muted/40 hover:bg-surface">
132
+ <div className="text-sm font-medium">{title} →</div>
133
+ <div className="mt-1 text-xs leading-relaxed text-muted">{body}</div>
134
+ </Link>
135
+ );
136
+ }
@@ -0,0 +1,238 @@
1
+ import { useEffect, useMemo, useState } from "react";
2
+ import { useNavigate, useParams } from "react-router";
3
+ import { useQuery, useQueryClient } from "@tanstack/react-query";
4
+ import type { LiveEvent, SessionListItem } from "@shared/protocol.ts";
5
+ import { api } from "../api.ts";
6
+ import { emptySnapshot, hydrateSessionMessage, loadSessionHistory, sendClient, subscribeSession, useLive, useSessionLive } from "../live.ts";
7
+ import { Transcript, deriveTodos } from "../transcript.ts";
8
+ import { useModels, useProjects, useSessions } from "../hooks.ts";
9
+ import { TranscriptContext } from "../components/Items.tsx";
10
+ import { MessageList } from "../components/MessageList.tsx";
11
+ import { TodoPanel } from "../components/TodoPanel.tsx";
12
+ import { AutomationBar } from "../components/Automation.tsx";
13
+ import { Composer } from "../components/Composer.tsx";
14
+ import { PendingCard } from "../components/Pending.tsx";
15
+ import { StateDot, useOpenDrawer } from "../components/Shell.tsx";
16
+ import { MenuIcon } from "../components/icons.tsx";
17
+ import { Select } from "../ui/Select.tsx";
18
+ import { BrainIcon, PlanIcon, SparkIcon } from "../ui/icons.tsx";
19
+
20
+ const EFFORT_ZH: Record<string, string> = { low: "低", medium: "中", high: "高", xhigh: "很高", max: "最高" };
21
+ import { Lineage, SessionMenu, UserMessageActions } from "../components/SessionActions.tsx";
22
+ import { getDraft, restoreRewindDraft } from "../drafts.ts";
23
+ import { BtwBanner, BtwPanel, ContextMeter, useAskBtw } from "../components/SessionExtras.tsx";
24
+ import { SessionContextBar, useAddDir, useTurnChangeSlots } from "../components/ProjectExtras.tsx";
25
+ import { useConfirm } from "../components/Confirmation.tsx";
26
+ import { rememberAnnotationSession, useAnnotations } from "../annotations.ts";
27
+ import { AnnotationSelection } from "../components/AnnotationSelection.tsx";
28
+
29
+ const NO_EVENTS: LiveEvent[] = [];
30
+
31
+ /** /clear on the web: start a new session and go there; the old one stays as it is. */
32
+ function useClearSession(pid: string) {
33
+ const qc = useQueryClient();
34
+ const navigate = useNavigate();
35
+ const confirm = useConfirm();
36
+ return async () => {
37
+ if (!(await confirm({
38
+ title: "新开会话?",
39
+ description: "将新建并切换到一个没有当前上下文的会话。原会话和历史记录保留,正在运行的任务不会停止。",
40
+ confirmLabel: "新开会话",
41
+ })).confirmed) return false;
42
+ const { id } = await api<{ id: string }>(`/api/projects/${pid}/sessions`, { method: "POST" });
43
+ qc.invalidateQueries({ queryKey: ["sessions", pid] });
44
+ navigate(`/p/${pid}/s/${id}`);
45
+ return true;
46
+ };
47
+ }
48
+
49
+ export function SessionView() {
50
+ const { pid, sid } = useParams() as { pid: string; sid: string };
51
+ const navigate = useNavigate();
52
+ const live = useSessionLive(sid);
53
+ const snapshot = live?.snapshot ?? emptySnapshot;
54
+ const annotationMode = useAnnotations((s) => s.mode);
55
+
56
+ useEffect(() => subscribeSession(sid, pid), [sid, pid]);
57
+ useEffect(() => rememberAnnotationSession(pid, sid), [pid, sid]);
58
+
59
+ const history = live?.history?.source === "main" ? live.history : undefined;
60
+ const resetCount = live?.resetCount;
61
+ useEffect(() => {
62
+ if (resetCount !== undefined) void loadSessionHistory(sid, pid);
63
+ }, [sid, pid, resetCount]);
64
+
65
+ const historical = history?.mode === "history";
66
+ // Rebuilt windows release the aggregator's old message maps as well as raw payloads.
67
+ const transcript = useMemo(() => new Transcript(), [sid]);
68
+ const visibleEvents = historical ? NO_EVENTS : live?.events ?? NO_EVENTS;
69
+ const items = useMemo(() => transcript.update(history?.items, visibleEvents), [transcript, history?.items, visibleEvents]);
70
+ const historyControls = useMemo(() => ({
71
+ older: !!history?.older || !historical && !!live?.evicted, newer: !!history?.newer, historical,
72
+ loading: !!history?.loading, error: history?.error,
73
+ load: (direction: "latest" | "older" | "newer") => loadSessionHistory(sid, pid, direction),
74
+ }), [history?.older, history?.newer, historical, history?.loading, history?.error, live?.evicted, sid, pid]);
75
+ const sessions = useSessions(pid);
76
+ const meta = sessions.data?.find((s) => s.id === sid);
77
+ const todos = useMemo(() => live?.todos ?? deriveTodos(items), [live?.todos, items]);
78
+ const todoNames = useMemo(() => new Map<string, string>(), [sid]);
79
+ for (const t of todos) todoNames.set(t.id, t.subject);
80
+ while (todoNames.size > 1024) todoNames.delete(todoNames.keys().next().value!);
81
+ const project = useProjects().data?.find((p) => p.id === pid);
82
+ const transcriptCtx = useMemo(
83
+ () => ({
84
+ root: project?.path,
85
+ sessionUrl: `/p/${pid}/s/${sid}`,
86
+ loadMessage: (uuid: string) => hydrateSessionMessage(sid, pid, uuid),
87
+ readImageUrl: (toolId: string) => `/api/projects/${pid}/sessions/${sid}/tools/${encodeURIComponent(toolId)}/image`,
88
+ userActions: (uuid: string) => <UserMessageActions pid={pid} sid={sid} uuid={uuid} />,
89
+ backgroundTool: snapshot.state !== "idle" ? async (toolUseId: string) => {
90
+ await api(`/api/projects/${pid}/sessions/${sid}/tools/${encodeURIComponent(toolUseId)}/background`, { method: "POST" });
91
+ } : undefined,
92
+ todoNames,
93
+ }),
94
+ [project?.path, pid, sid, todoNames, snapshot.state],
95
+ );
96
+ // A conversation rewind waiting for the next message: show it and put the message back in the composer.
97
+ const pendingRewind = useQuery({
98
+ queryKey: ["rewind-draft", sid, live?.resetCount ?? 0, snapshot.state],
99
+ queryFn: async () => {
100
+ const generation = getDraft(sid).generation;
101
+ const result = await api<{ rewindText: string | null; rewindId: string | null }>(`/api/projects/${pid}/sessions/${sid}/draft`);
102
+ return { ...result, generation };
103
+ },
104
+ });
105
+ useEffect(() => {
106
+ if (pendingRewind.data?.rewindText != null && pendingRewind.data.rewindId) restoreRewindDraft(sid, pendingRewind.data.rewindText, pendingRewind.data.rewindId, pendingRewind.data.generation);
107
+ }, [pendingRewind.data, sid]);
108
+
109
+ const [btwOpen, setBtwOpen] = useState(false);
110
+ useEffect(() => setBtwOpen(false), [sid]);
111
+ const askBtw = useAskBtw(pid, sid, (id) => navigate(`/p/${pid}/s/${id}`));
112
+ const clear = useClearSession(pid);
113
+ const addDir = useAddDir(pid, sid);
114
+ const turnSlots = useTurnChangeSlots(pid, sid, items, snapshot.state !== "idle");
115
+
116
+ return (
117
+ <div className="flex min-h-0 flex-1 flex-col">
118
+ <Header pid={pid} sid={sid} meta={meta} sessions={sessions.data} onClear={clear} />
119
+ <BtwBanner pid={pid} sid={sid} />
120
+ <SessionContextBar pid={pid} sid={sid} meta={meta} />
121
+ <TranscriptContext.Provider value={transcriptCtx}>
122
+ <AnnotationSelection projectId={pid} sessionId={sid} enabled={annotationMode} className="flex min-h-0 flex-1 flex-col">
123
+ <MessageList key={sid} items={items} loading={!history || !!history.loading && !history.items.length}
124
+ running={!historical && snapshot.state === "running"} waiting={!historical && snapshot.state === "requires_action"} turnSlots={turnSlots}
125
+ history={historyControls} />
126
+ </AnnotationSelection>
127
+ </TranscriptContext.Provider>
128
+ <div className="mx-auto w-full max-w-4xl px-3 pb-[max(env(safe-area-inset-bottom),0.75rem)] md:px-6">
129
+ {pendingRewind.data?.rewindText != null && (
130
+ <div className="mb-2 rounded-lg bg-surface-2 px-3 py-2 text-xs text-muted" data-testid="rewind-banner">
131
+ 已回退到这条消息之前。发送消息后,对话从这里继续。
132
+ </div>
133
+ )}
134
+ <AutomationBar pid={pid} sid={sid} snapshot={snapshot} turns={items.filter((i) => i.kind === "result").length}
135
+ historyRevision={live?.historyRevision} resetCount={resetCount ?? 0} />
136
+ <TodoPanel todos={todos} />
137
+ {snapshot.pending.map((p) => (
138
+ <PendingCard key={p.id} req={p} sessionId={sid} />
139
+ ))}
140
+ <BtwPanel pid={pid} sid={sid} open={btwOpen} setOpen={setBtwOpen} />
141
+ <ConnectionNotice />
142
+ <Composer key={sid} projectId={pid} sessionId={sid} state={snapshot.state} cronsStale={snapshot.cronsStale} onBtw={askBtw} onClear={clear} onAddDir={addDir} />
143
+ </div>
144
+ </div>
145
+ );
146
+ }
147
+
148
+ function Header({ pid, sid, meta, sessions, onClear }: { pid: string; sid: string; meta?: SessionListItem; sessions?: SessionListItem[]; onClear: () => Promise<boolean> }) {
149
+ const live = useSessionLive(sid);
150
+ const snap = live?.snapshot ?? emptySnapshot;
151
+ const models = useModels(pid);
152
+ const model = models.data?.find((m) => m.value === snap.model) ?? models.data?.[0];
153
+ const efforts = model?.efforts ?? [];
154
+ const openDrawer = useOpenDrawer();
155
+
156
+ return (
157
+ <header className="flex flex-wrap items-center gap-x-3 gap-y-2 border-b border-border px-2 py-2 md:flex-nowrap md:px-5">
158
+ <div className="flex min-w-0 flex-1 basis-48 items-center gap-2">
159
+ <button className="rounded-md p-2.5 hover:bg-surface-2 md:hidden" onClick={openDrawer} aria-label="打开导航">
160
+ <MenuIcon />
161
+ </button>
162
+ <StateDot state={snap.state} />
163
+ <div className="flex min-w-0 flex-col">
164
+ <h1 className="truncate text-lg md:max-w-[300px]" title={meta?.title}>
165
+ {meta?.pinned && <span className="mr-1 text-accent" aria-label="已置顶">•</span>}
166
+ {meta?.title || "新会话"}
167
+ </h1>
168
+ <Lineage pid={pid} meta={meta} sessions={sessions} />
169
+ </div>
170
+ <SessionMenu pid={pid} sid={sid} meta={meta} />
171
+ </div>
172
+ {/* Phones: one compact row under the title. */}
173
+ <div className="flex w-full min-w-0 items-center gap-1.5 md:w-auto md:gap-2">
174
+ <Select
175
+ label="模型"
176
+ value={snap.model ?? ""}
177
+ onChange={(v) => sendClient({ t: "setModel", sessionId: sid, projectId: pid, model: v })}
178
+ options={[
179
+ { value: "", label: "默认模型", text: "默认", hint: "使用设置里的默认值" },
180
+ ...(models.data ?? []).map((m) => ({ value: m.value, label: m.displayName, text: m.displayName.replace(/\s*\(.*\)$/, ""), hint: m.description })),
181
+ ]}
182
+ icon={<SparkIcon className="shrink-0 text-muted" />}
183
+ className="min-w-0 flex-1 md:w-40 md:flex-none"
184
+ menuWidth={280}
185
+ testId="model-select"
186
+ />
187
+ {efforts.length > 0 && (
188
+ <Select
189
+ label="思考强度"
190
+ value={snap.effort ?? ""}
191
+ onChange={(v) => sendClient({ t: "setEffort", sessionId: sid, projectId: pid, effort: v })}
192
+ options={[{ value: "", label: "默认强度", text: "默认" }, ...efforts.map((e) => ({ value: e, label: EFFORT_ZH[e] ?? e, text: EFFORT_ZH[e] ?? e }))]}
193
+ icon={<BrainIcon className="shrink-0 text-muted" />}
194
+ className="min-w-0 flex-1 md:w-32 md:flex-none"
195
+ testId="effort-select"
196
+ />
197
+ )}
198
+ <Select
199
+ label="计划模式"
200
+ value={snap.planMode ? "plan" : "execute"}
201
+ onChange={(mode) => sendClient({ t: "setPlanMode", sessionId: sid, projectId: pid, on: mode === "plan" })}
202
+ options={[
203
+ { value: "execute", label: "执行", hint: "直接执行任务" },
204
+ { value: "plan", label: "计划", hint: "先制定计划,再确认执行" },
205
+ ]}
206
+ icon={<PlanIcon className={snap.planMode ? "text-accent" : "text-muted"} />}
207
+ className="shrink-0"
208
+ testId="plan-select"
209
+ />
210
+ <ContextMeter pid={pid} sid={sid} onClear={onClear} />
211
+ {snap.costUsd !== undefined && (
212
+ <span className="hidden whitespace-nowrap text-xs tabular-nums text-muted lg:inline" title="会话累计花费">
213
+ ${snap.costUsd.toFixed(3)}
214
+ </span>
215
+ )}
216
+ </div>
217
+ </header>
218
+ );
219
+ }
220
+
221
+ /** Shown while the live connection is down: messages typed now are sent once it is back. */
222
+ function ConnectionNotice() {
223
+ const connected = useLive((s) => s.connected);
224
+ const [show, setShow] = useState(false);
225
+ // Brief blips (page load, a phone waking up) are not worth a banner.
226
+ useEffect(() => {
227
+ if (connected) return setShow(false);
228
+ const t = setTimeout(() => setShow(true), 1500);
229
+ return () => clearTimeout(t);
230
+ }, [connected]);
231
+ if (!show) return null;
232
+ return (
233
+ <div className="mb-2 flex items-center gap-2 rounded-lg border border-amber-500/40 bg-amber-500/5 px-3 py-2 text-xs text-amber-700 dark:text-amber-400" role="status" data-testid="connection-notice">
234
+ <span className="size-1.5 animate-pulse rounded-full bg-amber-500" aria-hidden />
235
+ 连接已断开,正在重连…现在发出的消息会在重连后送达。
236
+ </div>
237
+ );
238
+ }