@dyyz1993/create-agent 2.1.0 → 2.1.2

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 (349) hide show
  1. package/package.json +5 -4
  2. package/src/__tests__/cli.test.ts +2 -0
  3. package/src/__tests__/copy.test.ts +110 -122
  4. package/src/__tests__/create-args.test.ts +113 -0
  5. package/src/__tests__/templates.test.ts +29 -3
  6. package/src/__tests__/update.test.ts +129 -0
  7. package/src/cli.ts +30 -32
  8. package/src/commands/create.ts +83 -24
  9. package/src/commands/update.ts +174 -0
  10. package/src/commands/workspace.ts +26 -6
  11. package/src/lib/copy.ts +233 -93
  12. package/src/lib/templates.ts +12 -0
  13. package/templates/agent/.husky/commit-msg +4 -0
  14. package/templates/agent/.husky/pre-commit +11 -0
  15. package/templates/agent/.husky/pre-push +6 -0
  16. package/templates/agent/.prettierignore +6 -0
  17. package/templates/agent/.prettierrc +9 -0
  18. package/templates/agent/CHANGELOG.md +15 -0
  19. package/templates/agent/commitlint.config.js +8 -0
  20. package/templates/agent/package.json +19 -4
  21. package/templates/agent/src/gateway/__tests__/ws-handler-token.test.ts +53 -39
  22. package/templates/agent/src/mainview/App.tsx +14 -12
  23. package/templates/agent/src/mainview/__tests__/hooks/use-rpc-init.test.ts +194 -190
  24. package/templates/agent/src/mainview/components/bash/BashPanel.tsx +210 -189
  25. package/templates/agent/src/mainview/components/chat/ChatPanel.tsx +45 -20
  26. package/templates/agent/src/mainview/components/common/ThemeToggle.tsx +41 -21
  27. package/templates/agent/src/mainview/components/debug/DebugPanel.tsx +181 -114
  28. package/templates/agent/src/mainview/components/debug/__tests__/DebugPanel.test.tsx +80 -80
  29. package/templates/agent/src/mainview/components/diff/__tests__/DiffViewerPanel.test.tsx +61 -58
  30. package/templates/agent/src/mainview/components/feed/FeedPanel.tsx +43 -41
  31. package/templates/agent/src/mainview/components/file-preview/FilePreviewOverlay.tsx +69 -50
  32. package/templates/agent/src/mainview/components/file-preview/VirtualizedCodeView.tsx +31 -18
  33. package/templates/agent/src/mainview/components/git/GitPanel.tsx +731 -490
  34. package/templates/agent/src/mainview/components/git/__tests__/GitPanel.test.tsx +157 -139
  35. package/templates/agent/src/mainview/components/layout/AppLayout.tsx +210 -161
  36. package/templates/agent/src/mainview/components/layout/__tests__/AppLayout.test.tsx +130 -122
  37. package/templates/agent/src/mainview/components/rules/RulesPanel.tsx +119 -109
  38. package/templates/agent/src/mainview/components/search/SearchPanel.tsx +123 -117
  39. package/templates/agent/src/mainview/components/search/__tests__/SearchPanel.test.tsx +64 -44
  40. package/templates/agent/src/mainview/components/sidebar/__tests__/PinButton.test.tsx +38 -38
  41. package/templates/agent/src/mainview/components/todo/TodoPanel.tsx +40 -38
  42. package/templates/agent/src/mainview/components/todo/__tests__/TodoPanel.test.tsx +72 -72
  43. package/templates/agent/src/mainview/lib/i18n/locales/en.json +175 -155
  44. package/templates/agent/src/mainview/lib/i18n/locales/zh.json +173 -155
  45. package/templates/agent/src/mainview/stores/use-app-store.ts +96 -86
  46. package/templates/agent/src/mainview/stores/use-explorer-store.ts +292 -275
  47. package/templates/agent/src/mainview/types/index.ts +23 -22
  48. package/templates/agent/src/mainview/utils/file-icon.tsx +17 -21
  49. package/templates/agent/src/shared/handlers/chat.ts +53 -53
  50. package/templates/agent/src/shared/handlers/debug.ts +13 -3
  51. package/templates/browser-agent/.env.example +19 -0
  52. package/templates/browser-agent/.husky/commit-msg +4 -0
  53. package/templates/browser-agent/.husky/pre-commit +11 -0
  54. package/templates/browser-agent/.husky/pre-push +6 -0
  55. package/templates/browser-agent/.prettierignore +6 -0
  56. package/templates/browser-agent/.prettierrc +9 -0
  57. package/templates/browser-agent/AGENTS.md +396 -0
  58. package/templates/browser-agent/CHANGELOG.md +15 -0
  59. package/templates/browser-agent/LICENSE +21 -0
  60. package/templates/browser-agent/README.md +103 -0
  61. package/templates/browser-agent/commitlint.config.js +8 -0
  62. package/templates/browser-agent/electrobun.config.ts +27 -0
  63. package/templates/browser-agent/electron/main.js +46 -0
  64. package/templates/browser-agent/electron/preload.js +5 -0
  65. package/templates/browser-agent/electron-builder.json +40 -0
  66. package/templates/browser-agent/eslint.config.mjs +79 -0
  67. package/templates/browser-agent/llms.txt +24 -0
  68. package/templates/browser-agent/package.json +140 -0
  69. package/templates/browser-agent/postcss.config.js +6 -0
  70. package/templates/browser-agent/scripts/dev.ts +138 -0
  71. package/templates/browser-agent/src/__tests__/hybrid-mode.test.ts +126 -0
  72. package/templates/browser-agent/src/__tests__/server-config-security.test.ts +55 -0
  73. package/templates/browser-agent/src/__tests__/server-config.test.ts +59 -0
  74. package/templates/browser-agent/src/bun/index.ts +130 -0
  75. package/templates/browser-agent/src/bun/three.d.ts +1 -0
  76. package/templates/browser-agent/src/gateway/http-routes.ts +1 -0
  77. package/templates/browser-agent/src/gateway/ipc-transport.ts +68 -0
  78. package/templates/browser-agent/src/gateway/sse-transport.ts +221 -0
  79. package/templates/browser-agent/src/mainview/App.tsx +45 -0
  80. package/templates/browser-agent/src/mainview/__tests__/hooks/use-rpc-init.test.ts +202 -0
  81. package/templates/browser-agent/src/mainview/__tests__/hooks/use-sidebar-resize.test.ts +127 -0
  82. package/templates/browser-agent/src/mainview/__tests__/i18n/i18n.test.ts +49 -0
  83. package/templates/browser-agent/src/mainview/__tests__/setup.ts +40 -0
  84. package/templates/browser-agent/src/mainview/__tests__/stores/use-chat-store.test.ts +73 -0
  85. package/templates/browser-agent/src/mainview/__tests__/stores/use-sidebar-store.test.ts +61 -0
  86. package/templates/browser-agent/src/mainview/__tests__/theme-variables.test.ts +36 -0
  87. package/templates/browser-agent/src/mainview/components/assets/AssetsPanel.tsx +139 -0
  88. package/templates/browser-agent/src/mainview/components/chat/ChatPanel.tsx +219 -0
  89. package/templates/browser-agent/src/mainview/components/chat/CommandBar.tsx +184 -0
  90. package/templates/browser-agent/src/mainview/components/chat/MessageBubble.tsx +249 -0
  91. package/templates/browser-agent/src/mainview/components/chat/ToolPicker.tsx +115 -0
  92. package/templates/browser-agent/src/mainview/components/common/ErrorBoundary.tsx +1 -0
  93. package/templates/browser-agent/src/mainview/components/common/LanguageSwitcher.tsx +15 -0
  94. package/templates/browser-agent/src/mainview/components/common/ThemeToggle.tsx +45 -0
  95. package/templates/browser-agent/src/mainview/components/common/__tests__/ErrorBoundary.test.tsx +74 -0
  96. package/templates/browser-agent/src/mainview/components/common/__tests__/LanguageSwitcher.test.tsx +44 -0
  97. package/templates/browser-agent/src/mainview/components/common/__tests__/ThemeToggle.test.tsx +41 -0
  98. package/templates/browser-agent/src/mainview/components/dev/NetworkPanel.tsx +155 -0
  99. package/templates/browser-agent/src/mainview/components/layout/AppLayout.tsx +311 -0
  100. package/templates/browser-agent/src/mainview/components/onboarding/SetupWizard.tsx +310 -0
  101. package/templates/browser-agent/src/mainview/components/process/ProcessPanel.tsx +329 -0
  102. package/templates/browser-agent/src/mainview/components/sidebar/SessionSidebar.tsx +122 -0
  103. package/templates/browser-agent/src/mainview/components/sidebar/SkillSidebar.tsx +115 -0
  104. package/templates/browser-agent/src/mainview/components/topbar/TopBar.tsx +350 -0
  105. package/templates/browser-agent/src/mainview/global.d.ts +13 -0
  106. package/templates/browser-agent/src/mainview/hooks/__tests__/use-breakpoint.test.ts +151 -0
  107. package/templates/browser-agent/src/mainview/hooks/use-agent-chat.ts +157 -0
  108. package/templates/browser-agent/src/mainview/hooks/use-breakpoint.ts +68 -0
  109. package/templates/browser-agent/src/mainview/hooks/use-rpc-init.ts +71 -0
  110. package/templates/browser-agent/src/mainview/hooks/use-sidebar-resize.ts +40 -0
  111. package/templates/browser-agent/src/mainview/index.css +71 -0
  112. package/templates/browser-agent/src/mainview/index.html +12 -0
  113. package/templates/browser-agent/src/mainview/lib/api-client.ts +397 -0
  114. package/templates/browser-agent/src/mainview/lib/i18n/index.ts +30 -0
  115. package/templates/browser-agent/src/mainview/lib/i18n/locales/en.json +130 -0
  116. package/templates/browser-agent/src/mainview/lib/i18n/locales/zh.json +128 -0
  117. package/templates/browser-agent/src/mainview/lib/network-bus.ts +92 -0
  118. package/templates/browser-agent/src/mainview/lib/rpc-cache.ts +94 -0
  119. package/templates/browser-agent/src/mainview/main.tsx +18 -0
  120. package/templates/browser-agent/src/mainview/stores/__tests__/use-connection-store.test.ts +26 -0
  121. package/templates/browser-agent/src/mainview/stores/__tests__/use-locale-store.test.ts +32 -0
  122. package/templates/browser-agent/src/mainview/stores/__tests__/use-log-store.test.ts +28 -0
  123. package/templates/browser-agent/src/mainview/stores/__tests__/use-theme-store.test.ts +59 -0
  124. package/templates/browser-agent/src/mainview/stores/message-batcher.ts +25 -0
  125. package/templates/browser-agent/src/mainview/stores/use-asset-store.ts +65 -0
  126. package/templates/browser-agent/src/mainview/stores/use-chat-store.ts +200 -0
  127. package/templates/browser-agent/src/mainview/stores/use-connection-store.ts +173 -0
  128. package/templates/browser-agent/src/mainview/stores/use-locale-store.ts +27 -0
  129. package/templates/browser-agent/src/mainview/stores/use-log-store.ts +16 -0
  130. package/templates/browser-agent/src/mainview/stores/use-network-panel-store.ts +18 -0
  131. package/templates/browser-agent/src/mainview/stores/use-record-store.ts +147 -0
  132. package/templates/browser-agent/src/mainview/stores/use-session-store.ts +151 -0
  133. package/templates/browser-agent/src/mainview/stores/use-sidebar-store.ts +161 -0
  134. package/templates/browser-agent/src/mainview/stores/use-theme-store.ts +46 -0
  135. package/templates/browser-agent/src/mainview/stores/use-view-store.ts +20 -0
  136. package/templates/browser-agent/src/mainview/types/index.ts +6 -0
  137. package/templates/browser-agent/src/server-config.ts +45 -0
  138. package/templates/browser-agent/src/server.ts +78 -0
  139. package/templates/browser-agent/src/shared/handlers/__tests__/chat-concurrency.test.ts +127 -0
  140. package/templates/browser-agent/src/shared/handlers/__tests__/chat-handler.test.ts +77 -0
  141. package/templates/browser-agent/src/shared/handlers/__tests__/file-handler.test.ts +100 -0
  142. package/templates/browser-agent/src/shared/handlers/__tests__/system-handler.test.ts +55 -0
  143. package/templates/browser-agent/src/shared/handlers/__tests__/timer-handler.test.ts +77 -0
  144. package/templates/browser-agent/src/shared/handlers/browser.ts +596 -0
  145. package/templates/browser-agent/src/shared/handlers/chat.ts +213 -0
  146. package/templates/browser-agent/src/shared/handlers/file.ts +99 -0
  147. package/templates/browser-agent/src/shared/handlers/index.ts +7 -0
  148. package/templates/browser-agent/src/shared/handlers/session.ts +167 -0
  149. package/templates/browser-agent/src/shared/handlers/system.ts +27 -0
  150. package/templates/browser-agent/src/shared/handlers/timer.ts +34 -0
  151. package/templates/browser-agent/src/shared/http-routes.ts +437 -0
  152. package/templates/browser-agent/src/shared/lib/__tests__/logger.test.ts +92 -0
  153. package/templates/browser-agent/src/shared/lib/__tests__/path-security.test.ts +77 -0
  154. package/templates/browser-agent/src/shared/lib/__tests__/web-server.test.ts +203 -0
  155. package/templates/browser-agent/src/shared/lib/agent.ts +384 -0
  156. package/templates/browser-agent/src/shared/lib/cdp.ts +448 -0
  157. package/templates/browser-agent/src/shared/lib/generate.ts +111 -0
  158. package/templates/browser-agent/src/shared/lib/logger.ts +152 -0
  159. package/templates/browser-agent/src/shared/lib/mock-stream.ts +147 -0
  160. package/templates/browser-agent/src/shared/lib/path-security.ts +38 -0
  161. package/templates/browser-agent/src/shared/lib/port-registry.ts +103 -0
  162. package/templates/browser-agent/src/shared/lib/web-server.ts +127 -0
  163. package/templates/browser-agent/src/shared/lib/xhs-extract.ts +71 -0
  164. package/templates/browser-agent/src/shared/modules/browser.ts +86 -0
  165. package/templates/browser-agent/src/shared/modules/chat.ts +20 -0
  166. package/templates/browser-agent/src/shared/modules/file.ts +40 -0
  167. package/templates/browser-agent/src/shared/modules/session.ts +55 -0
  168. package/templates/browser-agent/src/shared/modules/system.ts +8 -0
  169. package/templates/browser-agent/src/shared/modules/timer.ts +11 -0
  170. package/templates/browser-agent/src/shared/register-all-handlers.ts +36 -0
  171. package/templates/browser-agent/src/shared/rpc-schema.ts +34 -0
  172. package/templates/browser-agent/tailwind.config.js +15 -0
  173. package/templates/browser-agent/tsconfig.ipc.json +14 -0
  174. package/templates/browser-agent/tsconfig.json +36 -0
  175. package/templates/browser-agent/vite.config.ts +3 -0
  176. package/templates/browser-agent/vitest.config.ts +3 -0
  177. package/templates/chat/.husky/commit-msg +4 -0
  178. package/templates/chat/.husky/pre-commit +11 -0
  179. package/templates/chat/.husky/pre-push +6 -0
  180. package/templates/chat/CHANGELOG.md +15 -0
  181. package/templates/chat/commitlint.config.js +8 -0
  182. package/templates/chat/package.json +19 -4
  183. package/templates/chat/src/mainview/__tests__/hooks/use-input-history.test.ts +125 -0
  184. package/templates/chat/src/mainview/__tests__/setup.ts +32 -29
  185. package/templates/chat/src/mainview/components/chat/ChatPanel.tsx +80 -55
  186. package/templates/chat/src/mainview/components/common/ThemeToggle.tsx +40 -20
  187. package/templates/chat/src/mainview/lib/i18n/locales/en.json +175 -155
  188. package/templates/chat/src/mainview/lib/i18n/locales/zh.json +173 -155
  189. package/templates/chat/src/shared/handlers/chat.ts +17 -19
  190. package/templates/chat/src/shared/handlers/debug.ts +12 -2
  191. package/templates/cowork/.env.example +8 -0
  192. package/templates/cowork/.prettierignore +6 -0
  193. package/templates/cowork/.prettierrc +9 -0
  194. package/templates/cowork/AGENTS.md +236 -0
  195. package/templates/cowork/CHANGELOG.md +15 -0
  196. package/templates/cowork/LICENSE +21 -0
  197. package/templates/cowork/README.md +103 -0
  198. package/templates/cowork/commitlint.config.js +8 -0
  199. package/templates/cowork/docs/CODE-VIEW-PLAN.md +637 -0
  200. package/templates/cowork/docs/CODE-VIEW-V2.md +196 -0
  201. package/templates/cowork/docs/CODE-VIEW-V4.md +150 -0
  202. package/templates/cowork/electrobun.config.ts +27 -0
  203. package/templates/cowork/eslint.config.mjs +79 -0
  204. package/templates/cowork/llms.txt +24 -0
  205. package/templates/cowork/package.json +87 -0
  206. package/templates/cowork/postcss.config.js +6 -0
  207. package/templates/cowork/scripts/dev.ts +138 -0
  208. package/templates/cowork/src/__tests__/hybrid-mode.test.ts +126 -0
  209. package/templates/cowork/src/__tests__/server-config-security.test.ts +55 -0
  210. package/templates/cowork/src/__tests__/server-config.test.ts +59 -0
  211. package/templates/cowork/src/bun/index.ts +130 -0
  212. package/templates/cowork/src/bun/three.d.ts +1 -0
  213. package/templates/cowork/src/gateway/http-routes.ts +1 -0
  214. package/templates/cowork/src/gateway/ipc-transport.ts +68 -0
  215. package/templates/cowork/src/gateway/sse-transport.ts +231 -0
  216. package/templates/cowork/src/mainview/App.tsx +45 -0
  217. package/templates/cowork/src/mainview/__tests__/hooks/use-rpc-init.test.ts +202 -0
  218. package/templates/cowork/src/mainview/__tests__/hooks/use-sidebar-resize.test.ts +127 -0
  219. package/templates/cowork/src/mainview/__tests__/i18n/i18n.test.ts +49 -0
  220. package/templates/cowork/src/mainview/__tests__/setup.ts +40 -0
  221. package/templates/cowork/src/mainview/__tests__/stores/use-chat-store.test.ts +73 -0
  222. package/templates/cowork/src/mainview/__tests__/stores/use-sidebar-store.test.ts +61 -0
  223. package/templates/cowork/src/mainview/__tests__/theme-variables.test.ts +36 -0
  224. package/templates/cowork/src/mainview/components/chat/TaskChat.tsx +311 -0
  225. package/templates/cowork/src/mainview/components/chat/__tests__/localhost-links.test.ts +76 -0
  226. package/templates/cowork/src/mainview/components/common/ErrorBoundary.tsx +1 -0
  227. package/templates/cowork/src/mainview/components/common/LanguageSwitcher.tsx +15 -0
  228. package/templates/cowork/src/mainview/components/common/ThemeToggle.tsx +45 -0
  229. package/templates/cowork/src/mainview/components/common/__tests__/ErrorBoundary.test.tsx +74 -0
  230. package/templates/cowork/src/mainview/components/common/__tests__/LanguageSwitcher.test.tsx +44 -0
  231. package/templates/cowork/src/mainview/components/common/__tests__/ThemeToggle.test.tsx +41 -0
  232. package/templates/cowork/src/mainview/components/dev/NetworkPanel.tsx +155 -0
  233. package/templates/cowork/src/mainview/components/layout/AppLayout.tsx +216 -0
  234. package/templates/cowork/src/mainview/components/right/ArtifactsPanel.tsx +54 -0
  235. package/templates/cowork/src/mainview/components/right/ContextPanel.tsx +133 -0
  236. package/templates/cowork/src/mainview/components/right/ElementPicker.tsx +206 -0
  237. package/templates/cowork/src/mainview/components/right/PreviewBlock.tsx +155 -0
  238. package/templates/cowork/src/mainview/components/right/ProgressPanel.tsx +85 -0
  239. package/templates/cowork/src/mainview/components/sidebar/TaskSidebar.tsx +211 -0
  240. package/templates/cowork/src/mainview/components/topbar/TopBar.tsx +86 -0
  241. package/templates/cowork/src/mainview/global.d.ts +13 -0
  242. package/templates/cowork/src/mainview/hooks/__tests__/use-breakpoint.test.ts +123 -0
  243. package/templates/cowork/src/mainview/hooks/use-breakpoint.ts +53 -0
  244. package/templates/cowork/src/mainview/hooks/use-rpc-init.ts +26 -0
  245. package/templates/cowork/src/mainview/hooks/use-sidebar-resize.ts +40 -0
  246. package/templates/cowork/src/mainview/index.css +89 -0
  247. package/templates/cowork/src/mainview/index.html +12 -0
  248. package/templates/cowork/src/mainview/lib/api-client.ts +404 -0
  249. package/templates/cowork/src/mainview/lib/i18n/index.ts +30 -0
  250. package/templates/cowork/src/mainview/lib/i18n/locales/en.json +130 -0
  251. package/templates/cowork/src/mainview/lib/i18n/locales/zh.json +128 -0
  252. package/templates/cowork/src/mainview/lib/network-bus.ts +92 -0
  253. package/templates/cowork/src/mainview/lib/rpc-cache.ts +94 -0
  254. package/templates/cowork/src/mainview/main.tsx +18 -0
  255. package/templates/cowork/src/mainview/stores/__tests__/use-connection-store.test.ts +26 -0
  256. package/templates/cowork/src/mainview/stores/__tests__/use-locale-store.test.ts +32 -0
  257. package/templates/cowork/src/mainview/stores/__tests__/use-log-store.test.ts +28 -0
  258. package/templates/cowork/src/mainview/stores/__tests__/use-preview-store.test.ts +193 -0
  259. package/templates/cowork/src/mainview/stores/__tests__/use-sidebar-store.test.ts +81 -0
  260. package/templates/cowork/src/mainview/stores/__tests__/use-theme-store.test.ts +59 -0
  261. package/templates/cowork/src/mainview/stores/message-batcher.ts +25 -0
  262. package/templates/cowork/src/mainview/stores/use-chat-store.ts +198 -0
  263. package/templates/cowork/src/mainview/stores/use-connection-store.ts +168 -0
  264. package/templates/cowork/src/mainview/stores/use-context-store.ts +68 -0
  265. package/templates/cowork/src/mainview/stores/use-locale-store.ts +27 -0
  266. package/templates/cowork/src/mainview/stores/use-log-store.ts +16 -0
  267. package/templates/cowork/src/mainview/stores/use-network-panel-store.ts +18 -0
  268. package/templates/cowork/src/mainview/stores/use-output-store.ts +47 -0
  269. package/templates/cowork/src/mainview/stores/use-preview-store.ts +97 -0
  270. package/templates/cowork/src/mainview/stores/use-sidebar-store.ts +272 -0
  271. package/templates/cowork/src/mainview/stores/use-task-store.ts +86 -0
  272. package/templates/cowork/src/mainview/stores/use-theme-store.ts +46 -0
  273. package/templates/cowork/src/mainview/stores/use-view-store.ts +29 -0
  274. package/templates/cowork/src/mainview/types/index.ts +6 -0
  275. package/templates/cowork/src/server-config.ts +44 -0
  276. package/templates/cowork/src/server.ts +78 -0
  277. package/templates/cowork/src/shared/handlers/__tests__/chat-concurrency.test.ts +127 -0
  278. package/templates/cowork/src/shared/handlers/__tests__/chat-handler.test.ts +77 -0
  279. package/templates/cowork/src/shared/handlers/__tests__/file-handler.test.ts +100 -0
  280. package/templates/cowork/src/shared/handlers/__tests__/preview-handler.test.ts +172 -0
  281. package/templates/cowork/src/shared/handlers/__tests__/system-handler.test.ts +55 -0
  282. package/templates/cowork/src/shared/handlers/__tests__/timer-handler.test.ts +77 -0
  283. package/templates/cowork/src/shared/handlers/chat.ts +213 -0
  284. package/templates/cowork/src/shared/handlers/context.ts +72 -0
  285. package/templates/cowork/src/shared/handlers/file.ts +99 -0
  286. package/templates/cowork/src/shared/handlers/index.ts +9 -0
  287. package/templates/cowork/src/shared/handlers/output.ts +59 -0
  288. package/templates/cowork/src/shared/handlers/preview.ts +81 -0
  289. package/templates/cowork/src/shared/handlers/system.ts +27 -0
  290. package/templates/cowork/src/shared/handlers/task.ts +101 -0
  291. package/templates/cowork/src/shared/handlers/timer.ts +34 -0
  292. package/templates/cowork/src/shared/http-routes.ts +444 -0
  293. package/templates/cowork/src/shared/lib/__tests__/logger.test.ts +92 -0
  294. package/templates/cowork/src/shared/lib/__tests__/path-security.test.ts +77 -0
  295. package/templates/cowork/src/shared/lib/__tests__/web-server.test.ts +203 -0
  296. package/templates/cowork/src/shared/lib/logger.ts +152 -0
  297. package/templates/cowork/src/shared/lib/path-security.ts +38 -0
  298. package/templates/cowork/src/shared/lib/port-registry.ts +103 -0
  299. package/templates/cowork/src/shared/lib/web-server.ts +127 -0
  300. package/templates/cowork/src/shared/modules/chat.ts +20 -0
  301. package/templates/cowork/src/shared/modules/context.ts +32 -0
  302. package/templates/cowork/src/shared/modules/file.ts +40 -0
  303. package/templates/cowork/src/shared/modules/output.ts +20 -0
  304. package/templates/cowork/src/shared/modules/preview.ts +44 -0
  305. package/templates/cowork/src/shared/modules/system.ts +8 -0
  306. package/templates/cowork/src/shared/modules/task.ts +31 -0
  307. package/templates/cowork/src/shared/modules/timer.ts +11 -0
  308. package/templates/cowork/src/shared/register-all-handlers.ts +36 -0
  309. package/templates/cowork/src/shared/rpc-schema.ts +38 -0
  310. package/templates/cowork/tailwind.config.js +15 -0
  311. package/templates/cowork/test-upload.txt +0 -0
  312. package/templates/cowork/tsconfig.ipc.json +14 -0
  313. package/templates/cowork/tsconfig.json +36 -0
  314. package/templates/cowork/vite.config.ts +3 -0
  315. package/templates/cowork/vitest.config.ts +3 -0
  316. package/templates/general/.husky/commit-msg +4 -0
  317. package/templates/general/.husky/pre-commit +11 -0
  318. package/templates/general/.husky/pre-push +6 -0
  319. package/templates/general/CHANGELOG.md +15 -0
  320. package/templates/general/commitlint.config.js +8 -0
  321. package/templates/general/package.json +19 -4
  322. package/templates/general/src/mainview/__tests__/hooks/use-input-history.test.ts +125 -0
  323. package/templates/general/src/mainview/__tests__/setup.ts +32 -29
  324. package/templates/general/src/mainview/components/chat/ChatPanel.tsx +80 -55
  325. package/templates/general/src/mainview/components/common/ThemeToggle.tsx +40 -20
  326. package/templates/general/src/mainview/components/debug/DebugPanel.tsx +170 -156
  327. package/templates/general/src/mainview/components/debug/__tests__/DebugPanel.test.tsx +169 -0
  328. package/templates/general/src/mainview/components/explorer/ConfirmDialog.tsx +42 -42
  329. package/templates/general/src/mainview/components/explorer/ContextMenu.tsx +72 -67
  330. package/templates/general/src/mainview/components/feed/FeedPanel.tsx +7 -5
  331. package/templates/general/src/mainview/components/file-preview/FilePreviewOverlay.tsx +64 -45
  332. package/templates/general/src/mainview/components/file-preview/VirtualizedCodeView.tsx +24 -7
  333. package/templates/general/src/mainview/components/git/GitPanel.tsx +700 -473
  334. package/templates/general/src/mainview/components/layout/AppLayout.tsx +123 -114
  335. package/templates/general/src/mainview/components/search/SearchPanel.tsx +15 -19
  336. package/templates/general/src/mainview/lib/i18n/locales/en.json +175 -155
  337. package/templates/general/src/mainview/lib/i18n/locales/zh.json +173 -155
  338. package/templates/general/src/mainview/stores/__tests__/use-explorer-store.test.ts +259 -0
  339. package/templates/general/src/mainview/stores/__tests__/use-feed-store.test.ts +160 -0
  340. package/templates/general/src/mainview/stores/__tests__/use-git-store.test.ts +313 -0
  341. package/templates/general/src/mainview/stores/__tests__/use-notification-store.test.ts +115 -0
  342. package/templates/general/src/mainview/stores/__tests__/use-sidebar-store.test.ts +120 -0
  343. package/templates/general/src/mainview/stores/use-explorer-store.ts +277 -260
  344. package/templates/general/src/mainview/types/index.ts +21 -20
  345. package/templates/general/src/shared/handlers/chat.ts +1 -1
  346. package/templates/general/src/shared/handlers/debug.ts +12 -2
  347. package/templates/shared/components/ErrorBoundary.tsx +0 -1
  348. package/templates/shared/vite-base.config.ts +8 -7
  349. /package/templates/{shared → browser-agent}/test-upload.txt +0 -0
@@ -9,307 +9,324 @@ import { useConnectionStore } from "./use-connection-store";
9
9
  import { uploadEntriesWeb, importFilesDesktop, type DropEntry } from "../utils/drop-handler";
10
10
 
11
11
  interface ExplorerState {
12
- treeNodes: TreeNode[];
13
- currentPath: string;
14
- selectedPath: string | null;
15
- filePreview: FilePreview | null;
16
- loadingFile: boolean;
17
- editingNode: EditingNode | null;
12
+ treeNodes: TreeNode[];
13
+ currentPath: string;
14
+ selectedPath: string | null;
15
+ filePreview: FilePreview | null;
16
+ loadingFile: boolean;
17
+ editingNode: EditingNode | null;
18
+ scrollToLine: number | null;
18
19
 
19
- setCurrentPath: (path: string) => void;
20
- listRootDir: () => Promise<void>;
21
- toggleNode: (nodePath: string) => Promise<void>;
22
- openFile: (node: TreeNode) => Promise<void>;
23
- closePreview: () => void;
20
+ setCurrentPath: (path: string) => void;
21
+ listRootDir: () => Promise<void>;
22
+ toggleNode: (nodePath: string) => Promise<void>;
23
+ openFile: (node: TreeNode) => Promise<void>;
24
+ openFileAtLine: (node: TreeNode, line: number) => Promise<void>;
25
+ clearScrollToLine: () => void;
26
+ closePreview: () => void;
24
27
 
25
- createFile: (dirPath: string, name: string) => Promise<void>;
26
- createDir: (dirPath: string, name: string) => Promise<void>;
27
- renameNode: (oldPath: string, newName: string) => Promise<void>;
28
- deleteNode: (path: string) => Promise<void>;
29
- refreshDir: (dirPath: string) => Promise<void>;
30
- startEditing: (path: string, type: EditingNode["type"]) => void;
31
- cancelEditing: () => void;
32
- importFiles: (entries: DropEntry[], destDir: string) => Promise<number>;
28
+ createFile: (dirPath: string, name: string) => Promise<void>;
29
+ createDir: (dirPath: string, name: string) => Promise<void>;
30
+ renameNode: (oldPath: string, newName: string) => Promise<void>;
31
+ deleteNode: (path: string) => Promise<void>;
32
+ refreshDir: (dirPath: string) => Promise<void>;
33
+ startEditing: (path: string, type: EditingNode["type"]) => void;
34
+ cancelEditing: () => void;
35
+ importFiles: (entries: DropEntry[], destDir: string) => Promise<number>;
33
36
  }
34
37
 
35
38
  function entriesToTreeNodes(entries: RPCMethods["file.listDir"]["result"]["entries"]): TreeNode[] {
36
- return entries.map((e) => ({
37
- name: e.name,
38
- path: e.path,
39
- type: e.type,
40
- size: e.size,
41
- children: e.type === "directory" ? [] : undefined,
42
- expanded: false,
43
- loaded: false,
44
- }));
39
+ return entries.map((e) => ({
40
+ name: e.name,
41
+ path: e.path,
42
+ type: e.type,
43
+ size: e.size,
44
+ children: e.type === "directory" ? [] : undefined,
45
+ expanded: false,
46
+ loaded: false,
47
+ }));
45
48
  }
46
49
 
47
50
  function getFileUrl(filePath: string): string {
48
- const mode = useConnectionStore.getState().mode;
49
- if (mode === "desktop") return `file://${filePath}`;
50
- const baseUrl = apiClient.getBaseUrl();
51
- const token = apiClient.getAuthToken();
52
- return `${baseUrl}/file/${encodeURIComponent(filePath)}?token=${token}`;
51
+ const mode = useConnectionStore.getState().mode;
52
+ if (mode === "desktop") return `file://${filePath}`;
53
+ const baseUrl = apiClient.getBaseUrl();
54
+ const token = apiClient.getAuthToken();
55
+ return `${baseUrl}/file/${encodeURIComponent(filePath)}?token=${token}`;
53
56
  }
54
57
 
55
58
  function findNode(nodes: TreeNode[], path: string): TreeNode | null {
56
- for (const n of nodes) {
57
- if (n.path === path) return n;
58
- if (n.children) {
59
- const found = findNode(n.children, path);
60
- if (found) return found;
61
- }
62
- }
63
- return null;
59
+ for (const n of nodes) {
60
+ if (n.path === path) return n;
61
+ if (n.children) {
62
+ const found = findNode(n.children, path);
63
+ if (found) return found;
64
+ }
65
+ }
66
+ return null;
64
67
  }
65
68
 
66
69
  function updateExpanded(nodes: TreeNode[], path: string, expanded: boolean): TreeNode[] {
67
- return nodes.map((n) => {
68
- if (n.path === path) return { ...n, expanded };
69
- if (n.children) return { ...n, children: updateExpanded(n.children, path, expanded) };
70
- return n;
71
- });
70
+ return nodes.map((n) => {
71
+ if (n.path === path) return { ...n, expanded };
72
+ if (n.children) return { ...n, children: updateExpanded(n.children, path, expanded) };
73
+ return n;
74
+ });
72
75
  }
73
76
 
74
77
  function loadChildren(nodes: TreeNode[], nodePath: string, children: TreeNode[]): TreeNode[] {
75
- return nodes.map((n) => {
76
- if (n.path === nodePath) return { ...n, children, expanded: true, loaded: true };
77
- if (n.children) return { ...n, children: loadChildren(n.children, nodePath, children) };
78
- return n;
79
- });
78
+ return nodes.map((n) => {
79
+ if (n.path === nodePath) return { ...n, children, expanded: true, loaded: true };
80
+ if (n.children) return { ...n, children: loadChildren(n.children, nodePath, children) };
81
+ return n;
82
+ });
80
83
  }
81
84
 
82
85
  function removeNode(nodes: TreeNode[], path: string): TreeNode[] {
83
- return nodes
84
- .filter((n) => n.path !== path)
85
- .map((n) => {
86
- if (n.children) return { ...n, children: removeNode(n.children, path) };
87
- return n;
88
- });
86
+ return nodes
87
+ .filter((n) => n.path !== path)
88
+ .map((n) => {
89
+ if (n.children) return { ...n, children: removeNode(n.children, path) };
90
+ return n;
91
+ });
89
92
  }
90
93
 
91
- function renameInTree(nodes: TreeNode[], oldPath: string, newPath: string, newName: string): TreeNode[] {
92
- return nodes.map((n) => {
93
- if (n.path === oldPath) {
94
- return { ...n, name: newName, path: newPath };
95
- }
96
- if (n.children) {
97
- return { ...n, children: renameInTree(n.children, oldPath, newPath, newName) };
98
- }
99
- return n;
100
- });
94
+ function renameInTree(
95
+ nodes: TreeNode[],
96
+ oldPath: string,
97
+ newPath: string,
98
+ newName: string
99
+ ): TreeNode[] {
100
+ return nodes.map((n) => {
101
+ if (n.path === oldPath) {
102
+ return { ...n, name: newName, path: newPath };
103
+ }
104
+ if (n.children) {
105
+ return { ...n, children: renameInTree(n.children, oldPath, newPath, newName) };
106
+ }
107
+ return n;
108
+ });
101
109
  }
102
110
 
103
111
  export const useExplorerStore = create<ExplorerState>((set, get) => ({
104
- treeNodes: [],
105
- currentPath: "",
106
- selectedPath: null,
107
- filePreview: null,
108
- loadingFile: false,
109
- editingNode: null,
112
+ treeNodes: [],
113
+ currentPath: "",
114
+ selectedPath: null,
115
+ filePreview: null,
116
+ loadingFile: false,
117
+ editingNode: null,
118
+ scrollToLine: null,
110
119
 
111
- setCurrentPath: (path) => set({ currentPath: path }),
120
+ setCurrentPath: (path) => set({ currentPath: path }),
112
121
 
113
- listRootDir: async () => {
114
- const { currentPath } = get();
115
- const addLog = useLogStore.getState().addLog;
116
- addLog(`ListDir: ${currentPath}`);
117
- try {
118
- const res = await apiClient.call("file.listDir", { path: currentPath });
119
- set({
120
- treeNodes: entriesToTreeNodes(res.entries),
121
- // Update currentPath to the resolved absolute path
122
- currentPath: res.basePath,
123
- });
124
- addLog(`Found ${res.entries.length} items`);
125
- } catch (err) {
126
- addLog(`Error: ${err instanceof Error ? err.message : String(err)}`);
127
- }
128
- },
122
+ listRootDir: async () => {
123
+ const { currentPath } = get();
124
+ const addLog = useLogStore.getState().addLog;
125
+ addLog(`ListDir: ${currentPath}`);
126
+ try {
127
+ const res = await apiClient.call("file.listDir", { path: currentPath });
128
+ set({
129
+ treeNodes: entriesToTreeNodes(res.entries),
130
+ // Update currentPath to the resolved absolute path
131
+ currentPath: res.basePath,
132
+ });
133
+ addLog(`Found ${res.entries.length} items`);
134
+ } catch (err) {
135
+ addLog(`Error: ${err instanceof Error ? err.message : String(err)}`);
136
+ }
137
+ },
129
138
 
130
- toggleNode: async (nodePath: string) => {
131
- const { treeNodes } = get();
132
- const addLog = useLogStore.getState().addLog;
133
- const target = findNode(treeNodes, nodePath);
134
- if (!target) return;
139
+ toggleNode: async (nodePath: string) => {
140
+ const { treeNodes } = get();
141
+ const addLog = useLogStore.getState().addLog;
142
+ const target = findNode(treeNodes, nodePath);
143
+ if (!target) return;
135
144
 
136
- if (target.expanded) {
137
- set({ treeNodes: updateExpanded(treeNodes, nodePath, false) });
138
- } else if (target.loaded) {
139
- set({ treeNodes: updateExpanded(treeNodes, nodePath, true) });
140
- } else {
141
- addLog(`ListDir: ${nodePath}`);
142
- try {
143
- const res = await apiClient.call("file.listDir", { path: nodePath });
144
- const children = entriesToTreeNodes(res.entries);
145
- addLog(`Found ${res.entries.length} items`);
146
- set({ treeNodes: loadChildren(treeNodes, nodePath, children) });
147
- } catch (err) {
148
- addLog(`Error: ${err instanceof Error ? err.message : String(err)}`);
149
- }
150
- }
151
- },
145
+ if (target.expanded) {
146
+ set({ treeNodes: updateExpanded(treeNodes, nodePath, false) });
147
+ } else if (target.loaded) {
148
+ set({ treeNodes: updateExpanded(treeNodes, nodePath, true) });
149
+ } else {
150
+ addLog(`ListDir: ${nodePath}`);
151
+ try {
152
+ const res = await apiClient.call("file.listDir", { path: nodePath });
153
+ const children = entriesToTreeNodes(res.entries);
154
+ addLog(`Found ${res.entries.length} items`);
155
+ set({ treeNodes: loadChildren(treeNodes, nodePath, children) });
156
+ } catch (err) {
157
+ addLog(`Error: ${err instanceof Error ? err.message : String(err)}`);
158
+ }
159
+ }
160
+ },
152
161
 
153
- openFile: async (node: TreeNode) => {
154
- if (node.type === "directory") return;
155
- const addLog = useLogStore.getState().addLog;
156
- set({ selectedPath: node.path, loadingFile: true });
162
+ openFile: async (node: TreeNode) => {
163
+ if (node.type === "directory") return;
164
+ const addLog = useLogStore.getState().addLog;
165
+ set({ selectedPath: node.path, loadingFile: true });
157
166
 
158
- const fileSize = node.size || 0;
159
- const preview: FilePreview = {
160
- path: node.path,
161
- name: node.name,
162
- content: null,
163
- imageUrl: null,
164
- mimeType: "",
165
- size: fileSize,
166
- isText: isTextFile(node.name),
167
- isImage: isImageFile(node.name),
168
- };
167
+ const fileSize = node.size || 0;
168
+ const preview: FilePreview = {
169
+ path: node.path,
170
+ name: node.name,
171
+ content: null,
172
+ imageUrl: null,
173
+ mimeType: "",
174
+ size: fileSize,
175
+ isText: isTextFile(node.name),
176
+ isImage: isImageFile(node.name),
177
+ };
169
178
 
170
- try {
171
- const mode = useConnectionStore.getState().mode;
179
+ try {
180
+ const mode = useConnectionStore.getState().mode;
172
181
 
173
- if (preview.isImage) {
174
- preview.imageUrl = getFileUrl(node.path);
175
- } else if (preview.isText) {
176
- if (fileSize > MAX_PREVIEW_SIZE) {
177
- preview.content = `[File too large to preview: ${formatSize(fileSize)}]\n\nThis file exceeds the 500KB preview limit.\nUse an external editor to view this file.`;
178
- set({ filePreview: preview, loadingFile: false });
179
- addLog(`Skipped large file: ${node.name} (${formatSize(fileSize)})`);
180
- return;
181
- }
182
+ if (preview.isImage) {
183
+ preview.imageUrl = getFileUrl(node.path);
184
+ } else if (preview.isText) {
185
+ if (fileSize > MAX_PREVIEW_SIZE) {
186
+ preview.content = `[File too large to preview: ${formatSize(fileSize)}]\n\nThis file exceeds the 500KB preview limit.\nUse an external editor to view this file.`;
187
+ set({ filePreview: preview, loadingFile: false });
188
+ addLog(`Skipped large file: ${node.name} (${formatSize(fileSize)})`);
189
+ return;
190
+ }
182
191
 
183
- let text: string;
184
- if (mode === "desktop") {
185
- // Desktop: read file via RPC (file:// blocked by webview)
186
- const res = await apiClient.call("file.readFile", { path: node.path });
187
- text = res.content;
188
- preview.mimeType = "text/plain";
189
- } else {
190
- // Web: fetch via HTTP file server
191
- const url = getFileUrl(node.path);
192
- const res = await fetch(url);
193
- if (!res.ok) throw new Error(`HTTP ${res.status}`);
194
- text = await res.text();
195
- preview.mimeType = res.headers.get("content-type") || "text/plain";
196
- }
197
- preview.content = text;
198
- preview.totalLines = text.split("\n").length;
199
- } else {
200
- preview.content = `[Binary file: ${node.name} (${formatSize(preview.size)})]`;
201
- preview.isText = true;
202
- }
203
- addLog(`Opened: ${node.name}`);
204
- } catch (err) {
205
- preview.content = `Failed to load: ${err instanceof Error ? err.message : String(err)}`;
206
- preview.isText = true;
207
- addLog(`Error opening ${node.name}: ${err instanceof Error ? err.message : String(err)}`);
208
- }
192
+ let text: string;
193
+ if (mode === "desktop") {
194
+ // Desktop: read file via RPC (file:// blocked by webview)
195
+ const res = await apiClient.call("file.readFile", { path: node.path });
196
+ text = res.content;
197
+ preview.mimeType = "text/plain";
198
+ } else {
199
+ // Web: fetch via HTTP file server
200
+ const url = getFileUrl(node.path);
201
+ const res = await fetch(url);
202
+ if (!res.ok) throw new Error(`HTTP ${res.status}`);
203
+ text = await res.text();
204
+ preview.mimeType = res.headers.get("content-type") || "text/plain";
205
+ }
206
+ preview.content = text;
207
+ preview.totalLines = text.split("\n").length;
208
+ } else {
209
+ preview.content = `[Binary file: ${node.name} (${formatSize(preview.size)})]`;
210
+ preview.isText = true;
211
+ }
212
+ addLog(`Opened: ${node.name}`);
213
+ } catch (err) {
214
+ preview.content = `Failed to load: ${err instanceof Error ? err.message : String(err)}`;
215
+ preview.isText = true;
216
+ addLog(`Error opening ${node.name}: ${err instanceof Error ? err.message : String(err)}`);
217
+ }
209
218
 
210
- set({ filePreview: preview, loadingFile: false });
211
- },
219
+ set({ filePreview: preview, loadingFile: false });
220
+ },
212
221
 
213
- closePreview: () => set({ filePreview: null, selectedPath: null }),
222
+ openFileAtLine: async (node: TreeNode, line: number) => {
223
+ await get().openFile(node);
224
+ set({ scrollToLine: line });
225
+ },
214
226
 
215
- createFile: async (dirPath: string, name: string) => {
216
- const addLog = useLogStore.getState().addLog;
217
- try {
218
- await apiClient.call("file.createFile", { dirPath, name });
219
- addLog(`Created file: ${name}`);
220
- set({ editingNode: null });
221
- await get().refreshDir(dirPath);
222
- } catch (err) {
223
- addLog(`Error creating file: ${err instanceof Error ? err.message : String(err)}`);
224
- set({ editingNode: null });
225
- }
226
- },
227
+ clearScrollToLine: () => set({ scrollToLine: null }),
227
228
 
228
- createDir: async (dirPath: string, name: string) => {
229
- const addLog = useLogStore.getState().addLog;
230
- try {
231
- await apiClient.call("file.createDir", { dirPath, name });
232
- addLog(`Created directory: ${name}`);
233
- set({ editingNode: null });
234
- await get().refreshDir(dirPath);
235
- } catch (err) {
236
- addLog(`Error creating directory: ${err instanceof Error ? err.message : String(err)}`);
237
- set({ editingNode: null });
238
- }
239
- },
229
+ closePreview: () => set({ filePreview: null, selectedPath: null, scrollToLine: null }),
240
230
 
241
- renameNode: async (oldPath: string, newName: string) => {
242
- const { treeNodes } = get();
243
- const addLog = useLogStore.getState().addLog;
244
- try {
245
- const res = await apiClient.call("file.rename", { oldPath, newName });
246
- addLog(`Renamed to: ${newName}`);
247
- set({
248
- treeNodes: renameInTree(treeNodes, oldPath, res.newPath, newName),
249
- editingNode: null,
250
- selectedPath: res.newPath,
251
- });
252
- } catch (err) {
253
- addLog(`Error renaming: ${err instanceof Error ? err.message : String(err)}`);
254
- set({ editingNode: null });
255
- }
256
- },
231
+ createFile: async (dirPath: string, name: string) => {
232
+ const addLog = useLogStore.getState().addLog;
233
+ try {
234
+ await apiClient.call("file.createFile", { dirPath, name });
235
+ addLog(`Created file: ${name}`);
236
+ set({ editingNode: null });
237
+ await get().refreshDir(dirPath);
238
+ } catch (err) {
239
+ addLog(`Error creating file: ${err instanceof Error ? err.message : String(err)}`);
240
+ set({ editingNode: null });
241
+ }
242
+ },
257
243
 
258
- deleteNode: async (path: string) => {
259
- const { treeNodes } = get();
260
- const addLog = useLogStore.getState().addLog;
261
- try {
262
- await apiClient.call("file.delete", { path });
263
- addLog(`Deleted: ${path}`);
264
- // Find parent dir to refresh
265
- const pathParts = path.split("/");
266
- pathParts.pop();
267
- const parentPath = pathParts.join("/") || get().currentPath;
268
- set({ treeNodes: removeNode(treeNodes, path) });
269
- // Clear preview if the deleted file was being previewed
270
- const { selectedPath } = get();
271
- if (selectedPath === path) {
272
- set({ selectedPath: null, filePreview: null });
273
- }
274
- await get().refreshDir(parentPath);
275
- } catch (err) {
276
- addLog(`Error deleting: ${err instanceof Error ? err.message : String(err)}`);
277
- }
278
- },
244
+ createDir: async (dirPath: string, name: string) => {
245
+ const addLog = useLogStore.getState().addLog;
246
+ try {
247
+ await apiClient.call("file.createDir", { dirPath, name });
248
+ addLog(`Created directory: ${name}`);
249
+ set({ editingNode: null });
250
+ await get().refreshDir(dirPath);
251
+ } catch (err) {
252
+ addLog(`Error creating directory: ${err instanceof Error ? err.message : String(err)}`);
253
+ set({ editingNode: null });
254
+ }
255
+ },
279
256
 
280
- refreshDir: async (dirPath: string) => {
281
- const { treeNodes, currentPath } = get();
282
- const addLog = useLogStore.getState().addLog;
283
- // If refreshing root
284
- if (dirPath === currentPath) {
285
- await get().listRootDir();
286
- return;
287
- }
288
- try {
289
- const res = await apiClient.call("file.listDir", { path: dirPath });
290
- const children = entriesToTreeNodes(res.entries);
291
- set({ treeNodes: loadChildren(treeNodes, dirPath, children) });
292
- } catch (err) {
293
- addLog(`Error refreshing: ${err instanceof Error ? err.message : String(err)}`);
294
- }
295
- },
257
+ renameNode: async (oldPath: string, newName: string) => {
258
+ const { treeNodes } = get();
259
+ const addLog = useLogStore.getState().addLog;
260
+ try {
261
+ const res = await apiClient.call("file.rename", { oldPath, newName });
262
+ addLog(`Renamed to: ${newName}`);
263
+ set({
264
+ treeNodes: renameInTree(treeNodes, oldPath, res.newPath, newName),
265
+ editingNode: null,
266
+ selectedPath: res.newPath,
267
+ });
268
+ } catch (err) {
269
+ addLog(`Error renaming: ${err instanceof Error ? err.message : String(err)}`);
270
+ set({ editingNode: null });
271
+ }
272
+ },
296
273
 
297
- startEditing: (path, type) => set({ editingNode: { path, type } }),
298
- cancelEditing: () => set({ editingNode: null }),
274
+ deleteNode: async (path: string) => {
275
+ const { treeNodes } = get();
276
+ const addLog = useLogStore.getState().addLog;
277
+ try {
278
+ await apiClient.call("file.delete", { path });
279
+ addLog(`Deleted: ${path}`);
280
+ // Find parent dir to refresh
281
+ const pathParts = path.split("/");
282
+ pathParts.pop();
283
+ const parentPath = pathParts.join("/") || get().currentPath;
284
+ set({ treeNodes: removeNode(treeNodes, path) });
285
+ // Clear preview if the deleted file was being previewed
286
+ const { selectedPath } = get();
287
+ if (selectedPath === path) {
288
+ set({ selectedPath: null, filePreview: null });
289
+ }
290
+ await get().refreshDir(parentPath);
291
+ } catch (err) {
292
+ addLog(`Error deleting: ${err instanceof Error ? err.message : String(err)}`);
293
+ }
294
+ },
299
295
 
300
- importFiles: async (entries, destDir) => {
301
- const addLog = useLogStore.getState().addLog;
302
- const mode = useConnectionStore.getState().mode;
303
- try {
304
- const count = mode === "desktop"
305
- ? await importFilesDesktop(entries, destDir)
306
- : await uploadEntriesWeb(entries, destDir);
307
- addLog(`Imported ${count} items to ${destDir}`);
308
- await get().refreshDir(destDir);
309
- return count;
310
- } catch (err) {
311
- addLog(`Import error: ${err instanceof Error ? err.message : String(err)}`);
312
- throw err;
313
- }
314
- },
296
+ refreshDir: async (dirPath: string) => {
297
+ const { treeNodes, currentPath } = get();
298
+ const addLog = useLogStore.getState().addLog;
299
+ // If refreshing root
300
+ if (dirPath === currentPath) {
301
+ await get().listRootDir();
302
+ return;
303
+ }
304
+ try {
305
+ const res = await apiClient.call("file.listDir", { path: dirPath });
306
+ const children = entriesToTreeNodes(res.entries);
307
+ set({ treeNodes: loadChildren(treeNodes, dirPath, children) });
308
+ } catch (err) {
309
+ addLog(`Error refreshing: ${err instanceof Error ? err.message : String(err)}`);
310
+ }
311
+ },
312
+
313
+ startEditing: (path, type) => set({ editingNode: { path, type } }),
314
+ cancelEditing: () => set({ editingNode: null }),
315
+
316
+ importFiles: async (entries, destDir) => {
317
+ const addLog = useLogStore.getState().addLog;
318
+ const mode = useConnectionStore.getState().mode;
319
+ try {
320
+ const count =
321
+ mode === "desktop"
322
+ ? await importFilesDesktop(entries, destDir)
323
+ : await uploadEntriesWeb(entries, destDir);
324
+ addLog(`Imported ${count} items to ${destDir}`);
325
+ await get().refreshDir(destDir);
326
+ return count;
327
+ } catch (err) {
328
+ addLog(`Import error: ${err instanceof Error ? err.message : String(err)}`);
329
+ throw err;
330
+ }
331
+ },
315
332
  }));
@@ -1,32 +1,33 @@
1
1
  export type TreeNode = {
2
- name: string;
3
- path: string;
4
- type: "file" | "directory";
5
- size?: number;
6
- children?: TreeNode[];
7
- expanded?: boolean;
8
- loaded?: boolean;
2
+ name: string;
3
+ path: string;
4
+ type: "file" | "directory";
5
+ size?: number;
6
+ children?: TreeNode[];
7
+ expanded?: boolean;
8
+ loaded?: boolean;
9
9
  };
10
10
 
11
11
  export type DemoMethod = "system.ping" | "system.hello" | "system.echo" | "chat.send";
12
12
 
13
13
  export type FilePreview = {
14
- path: string;
15
- name: string;
16
- content: string | null;
17
- imageUrl: string | null;
18
- mimeType: string;
19
- size: number;
20
- isText: boolean;
21
- isImage: boolean;
22
- totalLines?: number;
14
+ path: string;
15
+ name: string;
16
+ content: string | null;
17
+ imageUrl: string | null;
18
+ mimeType: string;
19
+ size: number;
20
+ isText: boolean;
21
+ isImage: boolean;
22
+ totalLines?: number;
23
+ scrollToLine?: number;
23
24
  };
24
25
 
25
26
  export type ChatMessage = {
26
- id: string;
27
- role: "user" | "assistant";
28
- content: string;
29
- timestamp: number;
27
+ id: string;
28
+ role: "user" | "assistant";
29
+ content: string;
30
+ timestamp: number;
30
31
  };
31
32
 
32
33
  export type EditingType = "rename" | "newFile" | "newDir";
@@ -60,7 +60,7 @@ type RegisterFn = <K extends keyof RPCMethods & string>(
60
60
  handler: (params: MethodParams<RPCMethods, K>) => Promise<MethodResult<RPCMethods, K>>
61
61
  ) => void;
62
62
 
63
- function generateReply(input: string): string {
63
+ export function generateReply(input: string): string {
64
64
  const lower = input.toLowerCase().trim();
65
65
 
66
66
  if (/^(hi|hello|hey|howdy|hola|yo|sup)\b/i.test(lower)) {