@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,390 @@
1
+ import { uuid } from "../uuid.ts";
2
+ import { useEffect, useRef, useState } from "react";
3
+ import { useNavigate } from "react-router";
4
+ import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
5
+ import type { AgentTask, BgTask, CronInfo, LiveSnapshot } from "@shared/protocol.ts";
6
+ import { isSubagentTaskType } from "@shared/task-types.ts";
7
+ import { api } from "../api.ts";
8
+ import { agentMetadataQuery, backgroundAgentToolUseId, type SubagentInfo } from "../agent-history-flow.ts";
9
+ import { sendClient } from "../live.ts";
10
+ import { Modal } from "./Modal.tsx";
11
+ import { Button } from "../ui/Button.tsx";
12
+ import { Chip, Panel } from "../ui/Controls.tsx";
13
+ import { ClockIcon, TargetIcon, TerminalIcon, ChatIcon, ShieldIcon, HelpIcon, QuestionIcon } from "../ui/icons.tsx";
14
+ import { useConfirm } from "./Confirmation.tsx";
15
+ import { useClearGoal } from "../session-confirmations.ts";
16
+ import { setAnnotationMode, useAnnotations } from "../annotations.ts";
17
+
18
+ type Goal = { condition: string; status: "active" | "met" | "failed" | "cleared"; reason?: string; iterations?: number } | null;
19
+
20
+ function send(pid: string, sid: string, text: string) {
21
+ sendClient({ t: "send", sessionId: sid, projectId: pid, uuid: uuid(), text });
22
+ }
23
+
24
+ function inTime(ms?: number) {
25
+ if (!ms) return "";
26
+ const s = Math.round((ms - Date.now()) / 1000);
27
+ if (s <= 0) return "即将执行";
28
+ if (s < 60) return `${s} 秒后`;
29
+ if (s < 3600) return `${Math.round(s / 60)} 分钟后`;
30
+ return new Date(ms).toLocaleString();
31
+ }
32
+
33
+ /** Long-running work of a session: its /goal, scheduled tasks and background commands. */
34
+ export function AutomationBar({ pid, sid, snapshot, turns, historyRevision, resetCount }: {
35
+ pid: string; sid: string; snapshot: LiveSnapshot; turns: number; historyRevision?: string; resetCount: number;
36
+ }) {
37
+ const clearGoal = useClearGoal(pid, sid);
38
+ const annotationMode = useAnnotations((s) => s.mode);
39
+ const [open, setOpen] = useState<"goal" | "crons" | "tasks" | "guide" | null>(null);
40
+ const guideButton = useRef<HTMLButtonElement>(null);
41
+ const goal = useQuery({
42
+ // Goal state and its editor belong to the selected history branch. A cold
43
+ // rewind need not change either the run state or the number of live results.
44
+ queryKey: ["goal", sid, pid, historyRevision, resetCount, turns, snapshot.state],
45
+ queryFn: ({ signal }) => api<Goal>(`/api/projects/${pid}/sessions/${sid}/goal`, { signal }),
46
+ });
47
+ const g = goal.data;
48
+ const running = snapshot.tasks.filter((t) => t.status === "running");
49
+ const toggle = (k: typeof open) => setOpen((cur) => (cur === k ? null : k));
50
+ const close = () => setOpen(null);
51
+
52
+ return (
53
+ <div className="mb-2 text-sm" data-testid="automation-bar">
54
+ <div className="flex items-start gap-1">
55
+ <div className="flex min-w-0 flex-1 flex-wrap items-center gap-1">
56
+ <Chip
57
+ compactOnMobile
58
+ shape="rounded"
59
+ active={open === "goal"}
60
+ onClick={() => toggle("goal")}
61
+ testId="goal-chip"
62
+ tone={g?.status === "active" ? "accent" : g?.status === "met" ? "ok" : "neutral"}
63
+ icon={<TargetIcon />}
64
+ onRemove={g?.status === "active" ? () => { void clearGoal(g.condition); } : undefined}
65
+ removeLabel="清除目标"
66
+ >
67
+ {g?.status === "active" ? `目标:${g.condition}` : g?.status === "met" ? "目标已达成" : "目标"}
68
+ </Chip>
69
+ <Chip compactOnMobile shape="rounded" checked={annotationMode} onClick={() => setAnnotationMode(!annotationMode)} ariaLabel="批注模式" tone={annotationMode ? "accent" : "neutral"} icon={<ChatIcon />}>批注</Chip>
70
+ <AskQuestionsSwitch key={`${pid}:${sid}`} pid={pid} sid={sid} enabled={snapshot.askQuestionsEnabled !== false} />
71
+ <ReadOnlySwitch key={`read-only:${pid}:${sid}`} pid={pid} sid={sid} readOnly={snapshot.writeEnabled === false} />
72
+ {snapshot.crons.length > 0 && (
73
+ <Chip compactOnMobile shape="rounded" active={open === "crons"} onClick={() => toggle("crons")} testId="crons-chip" tone="accent" icon={<ClockIcon />}>
74
+ 定时任务 {snapshot.crons.length}
75
+ </Chip>
76
+ )}
77
+ {snapshot.tasks.length > 0 && (
78
+ <Chip compactOnMobile shape="rounded" active={open === "tasks"} onClick={() => toggle("tasks")} testId="tasks-chip" tone={running.length ? "accent" : "neutral"} icon={<TerminalIcon />}>
79
+ 后台 {running.length}/{snapshot.tasks.length}
80
+ </Chip>
81
+ )}
82
+ </div>
83
+ <button ref={guideButton} type="button" aria-label="使用引导" title="使用引导" aria-haspopup="dialog" aria-expanded={open === "guide"}
84
+ onClick={() => toggle("guide")}
85
+ className="inline-flex size-7 shrink-0 items-center justify-center rounded-md text-muted transition-colors hover:bg-surface-2 hover:text-text focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-accent/40">
86
+ <HelpIcon size={16} />
87
+ </button>
88
+ </div>
89
+ {open && (
90
+ <div className="mt-2">
91
+ {open === "goal" && <GoalPanel key={`${pid}:${sid}:${historyRevision ?? ""}:${resetCount}`} pid={pid} sid={sid} goal={g ?? null} onDone={close} />}
92
+ {open === "crons" && <CronPanel pid={pid} sid={sid} crons={snapshot.crons} onClose={close} />}
93
+ {open === "tasks" && <TaskPanel key={`${pid}:${sid}`} pid={pid} sid={sid} tasks={snapshot.tasks} agentTasks={snapshot.agentTasks ?? []} onClose={close} />}
94
+ {open === "guide" && <ComposerGuide onClose={() => { close(); guideButton.current?.focus(); }} />}
95
+ </div>
96
+ )}
97
+ </div>
98
+ );
99
+ }
100
+
101
+ function ComposerGuide({ onClose }: { onClose: () => void }) {
102
+ return (
103
+ <Modal title="会话使用引导" onClose={onClose} testId="composer-guide">
104
+ <p className="mb-4 text-sm text-muted">批注、提问和只读高亮时表示已开启。</p>
105
+ <dl className="space-y-4 text-sm leading-relaxed">
106
+ <div>
107
+ <dt className="mb-1 flex items-center gap-2 font-medium"><TargetIcon />目标</dt>
108
+ <dd className="text-muted">写下完成条件,Claude 会持续推进,直到达成。点击目标可以查看、修改或清除。</dd>
109
+ </div>
110
+ <div>
111
+ <dt className="mb-1 flex items-center gap-2 font-medium"><ChatIcon />批注</dt>
112
+ <dd className="text-muted">开启后,选中消息中的文字即可添加批注。文件源码始终支持批注。批注会保留在输入框上方,可编辑或删除,随下一条消息发送。</dd>
113
+ </div>
114
+ <div>
115
+ <dt className="mb-1 flex items-center gap-2 font-medium"><QuestionIcon />提问</dt>
116
+ <dd className="text-muted">控制 Claude 是否可以通过提问卡片向你确认信息。关闭后,Claude 会根据已有信息继续,或在回复中说明缺少的信息。</dd>
117
+ </div>
118
+ <div>
119
+ <dt className="mb-1 flex items-center gap-2 font-medium"><ShieldIcon />只读</dt>
120
+ <dd className="text-muted">开启后只放行已知的只读工具,禁用命令执行、文件写入和无法确认用途的扩展工具。普通会话默认关闭,btw 默认开启;btw 还禁止创建或操作子代理。它与计划模式独立,只影响后续操作,不会停止或撤销已经开始的操作。</dd>
121
+ </div>
122
+ <div>
123
+ <dt className="mb-1 flex items-center gap-2 font-medium"><ClockIcon />定时任务</dt>
124
+ <dd className="text-muted">有定时任务时会显示入口,分为“本会话”“本项目持久”和“IDECron”三类,可以查看执行时间和任务内容,也可以取消任务。</dd>
125
+ </div>
126
+ <div>
127
+ <dt className="mb-1 flex items-center gap-2 font-medium"><TerminalIcon />后台</dt>
128
+ <dd className="text-muted">有后台任务时会显示入口,可以查看状态和输出、打开子代理过程,或停止仍在运行的任务。</dd>
129
+ </div>
130
+ </dl>
131
+ <div className="mt-5 flex justify-end"><Button onClick={onClose}>知道了</Button></div>
132
+ </Modal>
133
+ );
134
+ }
135
+
136
+ function AskQuestionsSwitch({ pid, sid, enabled }: { pid: string; sid: string; enabled: boolean }) {
137
+ const questions = useMutation({ mutationFn: (on: boolean) => api(`/api/projects/${pid}/sessions/${sid}`, { method: "PATCH", json: { askQuestionsEnabled: on } }) });
138
+ return <>
139
+ <Chip compactOnMobile shape="rounded" checked={enabled} onClick={() => questions.mutate(!enabled)} ariaLabel="允许提问" disabled={questions.isPending} tone={enabled ? "accent" : "neutral"} icon={<QuestionIcon />}>提问</Chip>
140
+ {questions.error && <p role="alert" className="basis-full text-xs text-danger">提问设置保存失败:{questions.error.message}</p>}
141
+ </>;
142
+ }
143
+
144
+ function ReadOnlySwitch({ pid, sid, readOnly }: { pid: string; sid: string; readOnly: boolean }) {
145
+ const setting = useMutation({ mutationFn: (on: boolean) => api(`/api/projects/${pid}/sessions/${sid}`, { method: "PATCH", json: { writeEnabled: !on } }) });
146
+ return <>
147
+ <Chip compactOnMobile shape="rounded" checked={readOnly} onClick={() => setting.mutate(!readOnly)} ariaLabel="只读" disabled={setting.isPending} tone={readOnly ? "accent" : "neutral"} icon={<ShieldIcon />}>只读</Chip>
148
+ {setting.error && <p role="alert" className="basis-full text-xs text-danger">只读设置保存失败:{setting.error.message}</p>}
149
+ </>;
150
+ }
151
+
152
+ function GoalPanel({ pid, sid, goal, onDone }: { pid: string; sid: string; goal: Goal; onDone: () => void }) {
153
+ const clearGoal = useClearGoal(pid, sid);
154
+ const [text, setText] = useState(goal?.status === "active" ? goal.condition : "");
155
+ const dirty = useRef(false);
156
+ useEffect(() => {
157
+ if (!dirty.current) setText(goal?.status === "active" ? goal.condition : "");
158
+ }, [goal?.condition, goal?.status]);
159
+ const active = goal?.status === "active";
160
+ return (
161
+ <Modal title={active ? "修改目标" : "设定目标"} onClose={onDone} testId="goal-panel" scrollBody={false} panelClassName="goal-dialog sm:max-w-2xl">
162
+ <p className="mb-3 max-h-28 shrink-0 overflow-y-auto overscroll-contain text-sm leading-relaxed text-muted">
163
+ {goal?.status === "active" && "Claude 会一直推进,直到这个条件成立;每一轮结束都会检查一次。"}
164
+ {goal?.status === "met" && <>上一个目标已达成:{goal.condition}</>}
165
+ {goal?.status === "failed" && <>上一个目标无法达成:{goal.reason ?? goal.condition}</>}
166
+ {(!goal || goal.status === "cleared") && "写下「完成」的条件。设定后 Claude 会持续工作,直到条件成立。"}
167
+ {goal?.reason && goal.status === "active" && <span className="mt-1 block">最近一次检查:{goal.reason}</span>}
168
+ </p>
169
+ <form
170
+ className="flex min-h-0 flex-1 flex-col"
171
+ onSubmit={(e) => {
172
+ e.preventDefault();
173
+ if (!text.trim()) return;
174
+ send(pid, sid, `/goal ${text.trim()}`);
175
+ onDone();
176
+ }}
177
+ >
178
+ <textarea
179
+ aria-label="目标条件"
180
+ placeholder="例如:所有测试通过,并且 README 写了使用说明"
181
+ value={text}
182
+ rows={12}
183
+ onChange={(e) => { dirty.current = true; setText(e.target.value); }}
184
+ onKeyDown={(e) => {
185
+ if (e.key === "Enter" && (e.ctrlKey || e.metaKey) && !e.nativeEvent.isComposing) {
186
+ e.preventDefault();
187
+ e.currentTarget.form?.requestSubmit();
188
+ }
189
+ }}
190
+ className="block min-h-0 w-full flex-1 resize-none touch-pan-y overflow-y-auto overscroll-contain rounded-md border border-border bg-bg px-3 py-3 text-base leading-relaxed outline-none focus:border-accent/60 focus:ring-2 focus:ring-accent/20"
191
+ />
192
+ <div className="mt-3 flex shrink-0 flex-wrap items-center justify-end gap-2">
193
+ {active && (
194
+ <Button
195
+ variant="outline-danger"
196
+ className="mr-auto"
197
+ onClick={async () => {
198
+ if (await clearGoal(goal.condition)) onDone();
199
+ }}
200
+ >
201
+ 清除目标
202
+ </Button>
203
+ )}
204
+ <Button variant="ghost" onClick={onDone}>
205
+ 取消
206
+ </Button>
207
+ <Button type="submit" variant="primary" disabled={!text.trim()}>
208
+ {active ? "修改目标" : "设定目标"}
209
+ </Button>
210
+ </div>
211
+ </form>
212
+ </Modal>
213
+ );
214
+ }
215
+
216
+ function CronPanel({ pid, sid, crons, onClose }: { pid: string; sid: string; crons: CronInfo[]; onClose: () => void }) {
217
+ const confirm = useConfirm();
218
+ const [cancelling, setCancelling] = useState<Set<string>>(new Set());
219
+ // A cancel request is a model turn; if it fails, let the row become actionable again.
220
+ useEffect(() => {
221
+ if (!cancelling.size) return;
222
+ const timer = setTimeout(() => setCancelling(new Set()), 30_000);
223
+ return () => clearTimeout(timer);
224
+ }, [cancelling]);
225
+ useEffect(() => {
226
+ setCancelling((cur) => {
227
+ const next = new Set([...cur].filter((id) => crons.some((c) => c.id === id)));
228
+ return next.size === cur.size ? cur : next;
229
+ });
230
+ }, [crons]);
231
+ const groups = [
232
+ { key: "session", label: "本会话", items: crons.filter((c) => !c.durable && !c.server) },
233
+ { key: "project", label: "本项目持久", items: crons.filter((c) => c.durable) },
234
+ { key: "idecron", label: "IDECron", items: crons.filter((c) => c.server) },
235
+ ].filter((g) => g.items.length > 0);
236
+ const row = (c: CronInfo) => (
237
+ <li key={c.id} className="flex items-center gap-3 py-2" data-testid="cron-item">
238
+ <ClockIcon className="shrink-0 text-muted" />
239
+ <div className="min-w-0 flex-1">
240
+ <div className="truncate">{c.prompt}</div>
241
+ <div className="mt-0.5 flex flex-wrap gap-x-2 text-xs text-muted">
242
+ <span className="font-mono">{c.schedule}</span>
243
+ <span>{c.recurring ? "循环" : "一次"}</span>
244
+ {c.server && <span>{c.mode === "next_turn" ? "下一轮" : c.mode === "interrupt" ? "中断后" : "steer"}</span>}
245
+ {c.nextRun && <span>下次:{inTime(c.nextRun)}</span>}
246
+ </div>
247
+ </div>
248
+ <Button
249
+ size="sm"
250
+ variant="outline-danger"
251
+ loading={cancelling.has(c.id)}
252
+ onClick={async () => {
253
+ const scope = c.server
254
+ ? "这是 claude-ide 服务端的 IDECron 任务。"
255
+ : c.durable
256
+ ? "这是项目级持久任务,取消后该项目所有会话都不会再执行它。"
257
+ : "这是本会话的定时任务。";
258
+ if (!(await confirm({
259
+ title: "取消定时任务?",
260
+ description: `任务:${c.prompt}。计划:${c.schedule}(${c.recurring ? "循环" : "一次"})。${scope}取消后不再按此计划触发,已经执行的操作不会撤销。`,
261
+ confirmLabel: "取消任务",
262
+ cancelLabel: "保留任务",
263
+ danger: true,
264
+ })).confirmed) return;
265
+ setCancelling((s) => new Set(s).add(c.id));
266
+ if (c.server) {
267
+ sendClient({ t: "cancelIdecron", sessionId: sid, projectId: pid, id: c.id });
268
+ } else {
269
+ // The CLI has no control request for this; Claude removes it with CronDelete.
270
+ send(pid, sid, `Cancel the scheduled task with id ${c.id} by calling CronDelete. Do nothing else.`);
271
+ }
272
+ }}
273
+ >
274
+ {cancelling.has(c.id) ? "取消中" : "取消"}
275
+ </Button>
276
+ </li>
277
+ );
278
+ return (
279
+ <Panel title={`定时任务 · ${crons.length}`} onClose={onClose} testId="crons-panel">
280
+ {groups.map((g) => (
281
+ <section key={g.key} data-testid={`cron-group-${g.key}`} className="mt-2 first:mt-0">
282
+ <h4 className="mb-1 text-xs font-medium text-muted">{g.label} · {g.items.length}</h4>
283
+ <ul className="-my-1 divide-y divide-border">{g.items.map(row)}</ul>
284
+ </section>
285
+ ))}
286
+ </Panel>
287
+ );
288
+ }
289
+
290
+ function TaskPanel({ pid, sid, tasks, agentTasks, onClose }: { pid: string; sid: string; tasks: BgTask[]; agentTasks: AgentTask[]; onClose: () => void }) {
291
+ const confirm = useConfirm();
292
+ const navigate = useNavigate();
293
+ const qc = useQueryClient();
294
+ const traceRequest = useRef(0);
295
+ // A lookup finishing after the panel closes must not navigate out of a newer view.
296
+ useEffect(() => () => { traceRequest.current++; }, [pid, sid]);
297
+ const [opening, setOpening] = useState<string>();
298
+ const [viewing, setViewing] = useState<BgTask | null>(null);
299
+ const [stopping, setStopping] = useState<Set<string>>(new Set());
300
+ const [error, setError] = useState<string>();
301
+ const openTrace = async (task: BgTask) => {
302
+ const request = ++traceRequest.current;
303
+ setOpening(task.id); setError(undefined);
304
+ try {
305
+ let toolUseId = backgroundAgentToolUseId(task, agentTasks);
306
+ if (!toolUseId) {
307
+ const agents = await qc.fetchQuery(agentMetadataQuery(pid, sid,
308
+ () => api<SubagentInfo[]>(`/api/projects/${pid}/sessions/${sid}/subagents`)));
309
+ toolUseId = backgroundAgentToolUseId(task, agentTasks, agents);
310
+ }
311
+ if (request !== traceRequest.current) return;
312
+ if (!toolUseId) throw new Error("暂未找到这个子 agent 的过程记录,请稍后重试。");
313
+ onClose();
314
+ navigate(`/p/${pid}/s/${sid}/agent/${encodeURIComponent(toolUseId)}`);
315
+ } catch (error) {
316
+ if (request === traceRequest.current) setError(error instanceof Error ? error.message : String(error));
317
+ } finally {
318
+ if (request === traceRequest.current) setOpening(undefined);
319
+ }
320
+ };
321
+ return (
322
+ <Modal title={`后台任务 · ${tasks.filter((t) => t.status === "running").length} 个运行中`} onClose={onClose} testId="tasks-panel" panelClassName="sm:max-w-3xl">
323
+ <ul className="-my-1 divide-y divide-border">
324
+ {tasks.map((t) => (
325
+ <li key={t.id} className="flex items-center gap-2.5 py-2.5" data-testid="task-item" data-status={t.status}>
326
+ <span className={`size-2 shrink-0 rounded-full ${t.status === "running" ? "animate-pulse bg-accent" : t.status === "completed" ? "bg-ok" : "bg-muted/50"}`} aria-hidden />
327
+ <div className="min-w-0 flex-1">
328
+ <div className="truncate" title={t.description || t.command}>{t.description || t.command}</div>
329
+ <div className="mt-0.5 text-xs text-muted">
330
+ {TYPE_ZH[t.type] ?? t.type} · {STATUS_ZH[t.status] ?? t.status}
331
+ </div>
332
+ </div>
333
+ <div className="flex shrink-0 items-center gap-1.5">
334
+ {isSubagentTaskType(t.type) && <Button size="sm" loading={opening === t.id} onClick={() => void openTrace(t)} data-testid="task-trace">
335
+ 查看过程
336
+ </Button>}
337
+ <Button size="sm" onClick={() => setViewing(t)}>
338
+ 输出
339
+ </Button>
340
+ {t.status === "running" && (
341
+ <Button size="sm" variant="outline-danger" loading={stopping.has(t.id)} onClick={async () => {
342
+ if (!(await confirm({
343
+ title: "停止后台任务?",
344
+ description: `任务:${t.description || t.command || t.id}。停止后不会撤销它已经产生的文件改动或其他操作。`,
345
+ confirmLabel: "停止任务",
346
+ cancelLabel: "继续运行",
347
+ danger: true,
348
+ })).confirmed) return;
349
+ setStopping((ids) => new Set(ids).add(t.id));
350
+ setError(undefined);
351
+ try {
352
+ await api(`/api/projects/${pid}/sessions/${sid}/tasks/${t.id}/stop`, { method: "POST" });
353
+ } catch (error) {
354
+ setError(error instanceof Error ? error.message : String(error));
355
+ } finally {
356
+ setStopping((ids) => { const next = new Set(ids); next.delete(t.id); return next; });
357
+ }
358
+ }}>
359
+ 停止
360
+ </Button>
361
+ )}
362
+ </div>
363
+ </li>
364
+ ))}
365
+ </ul>
366
+ {error && <p role="alert" className="mt-2 text-xs text-danger">{error}</p>}
367
+ {viewing && <TaskOutput pid={pid} sid={sid} task={viewing} onClose={() => setViewing(null)} />}
368
+ </Modal>
369
+ );
370
+ }
371
+
372
+ const TYPE_ZH: Record<string, string> = { local_bash: "命令", shell: "命令", local_agent: "子 agent", subagent: "子 agent", monitor: "监控" };
373
+ const STATUS_ZH: Record<string, string> = { running: "运行中", completed: "已完成", failed: "失败", killed: "已停止", stopped: "已停止" };
374
+
375
+ function TaskOutput({ pid, sid, task, onClose }: { pid: string; sid: string; task: BgTask; onClose: () => void }) {
376
+ const out = useQuery({
377
+ queryKey: ["task-output", sid, task.id],
378
+ queryFn: () => api<{ output: string; truncated?: boolean }>(`/api/projects/${pid}/sessions/${sid}/tasks/${task.id}/output`),
379
+ refetchInterval: 2000,
380
+ });
381
+ return (
382
+ <Modal title={task.description || task.command || "后台任务"} onClose={onClose} testId="task-output">
383
+ {task.command && <pre className="mb-2 whitespace-pre-wrap break-all rounded bg-surface-2 px-2 py-1 font-mono text-xs">$ {task.command}</pre>}
384
+ <pre className="max-h-[60dvh] overflow-auto whitespace-pre-wrap break-words font-mono text-xs" data-testid="task-output-text">
385
+ {out.data?.truncated && <span className="text-muted">…(只显示最后一部分){"\n"}</span>}
386
+ {out.data?.output || (out.isPending ? "加载中…" : "(暂无输出)")}
387
+ </pre>
388
+ </Modal>
389
+ );
390
+ }
@@ -0,0 +1,121 @@
1
+ import { useEffect, useMemo, useRef, useState } from "react";
2
+
3
+ /** Largest file we syntax-highlight; bigger ones render as plain text. */
4
+ const MAX_HIGHLIGHT_CHARS = 300_000;
5
+ /** Longer files render (and highlight) their first lines until "show all". */
6
+ const FIRST_LINES = 3000;
7
+
8
+ const EXT_ALIAS: Record<string, string> = {
9
+ mjs: "js",
10
+ cjs: "js",
11
+ mts: "ts",
12
+ cts: "ts",
13
+ htm: "html",
14
+ h: "c",
15
+ hpp: "cpp",
16
+ cc: "cpp",
17
+ hh: "cpp",
18
+ conf: "ini",
19
+ cfg: "ini",
20
+ env: "dotenv",
21
+ lock: "json",
22
+ svg: "xml",
23
+ };
24
+ const NAME_LANG: Record<string, string> = {
25
+ dockerfile: "docker",
26
+ makefile: "make",
27
+ gnumakefile: "make",
28
+ cmakelists: "cmake",
29
+ ".gitignore": "text",
30
+ ".bashrc": "bash",
31
+ ".zshrc": "zsh",
32
+ };
33
+
34
+ export function guessLang(path: string): string {
35
+ const base = path.slice(path.lastIndexOf("/") + 1).toLowerCase();
36
+ if (NAME_LANG[base]) return NAME_LANG[base]!;
37
+ const stem = base.replace(/\.txt$/, "");
38
+ if (NAME_LANG[stem]) return NAME_LANG[stem]!;
39
+ const dot = base.lastIndexOf(".");
40
+ if (dot <= 0) return "text";
41
+ const ext = base.slice(dot + 1);
42
+ return EXT_ALIAS[ext] ?? ext;
43
+ }
44
+
45
+ let worker: Worker | undefined;
46
+ let nextId = 0;
47
+ const waiting = new Map<number, (html: string | null) => void>();
48
+
49
+ /** Highlighted HTML of the code's lines, computed in a worker (see highlight.worker.ts). */
50
+ function highlight(code: string, lang: string): Promise<string | null> {
51
+ if (!worker) {
52
+ worker = new Worker(new URL("../highlight.worker.ts", import.meta.url), { type: "module" });
53
+ worker.onmessage = (e: MessageEvent<{ id: number; html: string | null }>) => {
54
+ waiting.get(e.data.id)?.(e.data.html);
55
+ waiting.delete(e.data.id);
56
+ };
57
+ }
58
+ const id = nextId++;
59
+ return new Promise((resolve) => {
60
+ waiting.set(id, resolve);
61
+ worker!.postMessage({ id, code, lang });
62
+ });
63
+ }
64
+
65
+ /** Read-only source view with line numbers, lazy highlighting and jump-to-line. */
66
+ export function CodeView({ code, path, line }: { code: string; path: string; line?: number }) {
67
+ const [html, setHtml] = useState<string | null>(null);
68
+ const [all, setAll] = useState(false);
69
+ const codeRef = useRef<HTMLElement>(null);
70
+ const lang = guessLang(path);
71
+ const lines = useMemo(() => {
72
+ const ls = code.split("\n");
73
+ if (ls.length > 1 && ls[ls.length - 1] === "") ls.pop();
74
+ return ls;
75
+ }, [code]);
76
+ const shownCount = all || lines.length <= FIRST_LINES || (line ?? 0) > FIRST_LINES ? lines.length : FIRST_LINES;
77
+ const shown = useMemo(() => (shownCount === lines.length ? code : lines.slice(0, shownCount).join("\n")), [code, lines, shownCount]);
78
+
79
+ useEffect(() => {
80
+ setHtml(null);
81
+ if (lang === "text" || shown.length > MAX_HIGHLIGHT_CHARS) return;
82
+ let cancelled = false;
83
+ highlight(shown, lang)
84
+ .then((h) => !cancelled && setHtml(h))
85
+ .catch(() => undefined);
86
+ return () => {
87
+ cancelled = true;
88
+ };
89
+ }, [shown, lang]);
90
+
91
+ useEffect(() => {
92
+ const el = codeRef.current;
93
+ if (!el || !line) return;
94
+ el.querySelectorAll(".line-target").forEach((n) => n.classList.remove("line-target"));
95
+ const target = el.children[line - 1] as HTMLElement | undefined;
96
+ if (!target) return;
97
+ target.classList.add("line-target");
98
+ target.scrollIntoView({ block: "center" });
99
+ }, [line, html, lines]);
100
+
101
+ return (
102
+ <pre className="code-view" data-testid="code-view" data-lang={lang} data-highlighted={html ? "true" : "false"}>
103
+ {html ? (
104
+ <code ref={codeRef} dangerouslySetInnerHTML={{ __html: html }} />
105
+ ) : (
106
+ <code ref={codeRef}>
107
+ {lines.slice(0, shownCount).map((l, i) => (
108
+ <span key={i} className="line">
109
+ {l}
110
+ </span>
111
+ ))}
112
+ </code>
113
+ )}
114
+ {shownCount < lines.length && (
115
+ <button className="mt-2 text-xs text-accent hover:underline" onClick={() => setAll(true)} data-testid="show-all">
116
+ 显示全部(共 {lines.length} 行)
117
+ </button>
118
+ )}
119
+ </pre>
120
+ );
121
+ }