@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
@@ -1,320 +1,337 @@
1
- import { create } from "zustand";
2
- import { apiClient } from "../lib/api-client";
3
- import type { RPCMethods } from "../lib/api-client";
4
- import { rpcCache } from "../lib/rpc-cache";
5
- import type { TreeNode, FilePreview, EditingNode } from "../types";
6
- import { isTextFile, isImageFile, formatSize } from "../utils/file-utils";
7
- import { MAX_PREVIEW_SIZE } from "../utils/constants";
8
- import { useLogStore } from "./use-log-store";
9
- import { useConnectionStore } from "./use-connection-store";
10
- import { uploadEntriesWeb, importFilesDesktop, type DropEntry } from "../utils/drop-handler";
1
+ import { create } from 'zustand';
2
+ import { apiClient } from '../lib/api-client';
3
+ import type { RPCMethods } from '../lib/api-client';
4
+ import { rpcCache } from '../lib/rpc-cache';
5
+ import type { TreeNode, FilePreview, EditingNode } from '../types';
6
+ import { isTextFile, isImageFile, formatSize } from '../utils/file-utils';
7
+ import { MAX_PREVIEW_SIZE } from '../utils/constants';
8
+ import { useLogStore } from './use-log-store';
9
+ import { useConnectionStore } from './use-connection-store';
10
+ import { uploadEntriesWeb, importFilesDesktop, type DropEntry } from '../utils/drop-handler';
11
11
 
12
12
  interface ExplorerState {
13
- treeNodes: TreeNode[];
14
- currentPath: string;
15
- selectedPath: string | null;
16
- filePreview: FilePreview | null;
17
- loadingFile: boolean;
18
- editingNode: EditingNode | null;
13
+ treeNodes: TreeNode[];
14
+ currentPath: string;
15
+ selectedPath: string | null;
16
+ filePreview: FilePreview | null;
17
+ loadingFile: boolean;
18
+ editingNode: EditingNode | null;
19
+ scrollToLine: number | null;
19
20
 
20
- setCurrentPath: (path: string) => void;
21
- listRootDir: () => Promise<void>;
22
- toggleNode: (nodePath: string) => Promise<void>;
23
- openFile: (node: TreeNode) => Promise<void>;
24
- closePreview: () => void;
21
+ setCurrentPath: (path: string) => void;
22
+ listRootDir: () => Promise<void>;
23
+ toggleNode: (nodePath: string) => Promise<void>;
24
+ openFile: (node: TreeNode) => Promise<void>;
25
+ openFileAtLine: (node: TreeNode, line: number) => Promise<void>;
26
+ clearScrollToLine: () => void;
27
+ closePreview: () => void;
25
28
 
26
- createFile: (dirPath: string, name: string) => Promise<void>;
27
- createDir: (dirPath: string, name: string) => Promise<void>;
28
- renameNode: (oldPath: string, newName: string) => Promise<void>;
29
- deleteNode: (path: string) => Promise<void>;
30
- refreshDir: (dirPath: string) => Promise<void>;
31
- startEditing: (path: string, type: EditingNode["type"]) => void;
32
- cancelEditing: () => void;
33
- importFiles: (entries: DropEntry[], destDir: string) => Promise<number>;
29
+ createFile: (dirPath: string, name: string) => Promise<void>;
30
+ createDir: (dirPath: string, name: string) => Promise<void>;
31
+ renameNode: (oldPath: string, newName: string) => Promise<void>;
32
+ deleteNode: (path: string) => Promise<void>;
33
+ refreshDir: (dirPath: string) => Promise<void>;
34
+ startEditing: (path: string, type: EditingNode['type']) => void;
35
+ cancelEditing: () => void;
36
+ importFiles: (entries: DropEntry[], destDir: string) => Promise<number>;
34
37
  }
35
38
 
36
- function entriesToTreeNodes(entries: RPCMethods["file.listDir"]["result"]["entries"]): TreeNode[] {
37
- return entries.map((e) => ({
38
- name: e.name,
39
- path: e.path,
40
- type: e.type,
41
- size: e.size,
42
- children: e.type === "directory" ? [] : undefined,
43
- expanded: false,
44
- loaded: false,
45
- }));
39
+ function entriesToTreeNodes(entries: RPCMethods['file.listDir']['result']['entries']): TreeNode[] {
40
+ return entries.map((e) => ({
41
+ name: e.name,
42
+ path: e.path,
43
+ type: e.type,
44
+ size: e.size,
45
+ children: e.type === 'directory' ? [] : undefined,
46
+ expanded: false,
47
+ loaded: false,
48
+ }));
46
49
  }
47
50
 
48
51
  function getFileUrl(filePath: string): string {
49
- const mode = useConnectionStore.getState().mode;
50
- if (mode === "desktop") return `file://${filePath}`;
51
- const baseUrl = apiClient.getBaseUrl();
52
- const token = apiClient.getAuthToken();
53
- return `${baseUrl}/file/${encodeURIComponent(filePath)}?token=${token}`;
52
+ const mode = useConnectionStore.getState().mode;
53
+ if (mode === 'desktop') return `file://${filePath}`;
54
+ const baseUrl = apiClient.getBaseUrl();
55
+ const token = apiClient.getAuthToken();
56
+ return `${baseUrl}/file/${encodeURIComponent(filePath)}?token=${token}`;
54
57
  }
55
58
 
56
59
  function findNode(nodes: TreeNode[], path: string): TreeNode | null {
57
- for (const n of nodes) {
58
- if (n.path === path) return n;
59
- if (n.children) {
60
- const found = findNode(n.children, path);
61
- if (found) return found;
62
- }
63
- }
64
- return null;
60
+ for (const n of nodes) {
61
+ if (n.path === path) return n;
62
+ if (n.children) {
63
+ const found = findNode(n.children, path);
64
+ if (found) return found;
65
+ }
66
+ }
67
+ return null;
65
68
  }
66
69
 
67
70
  function updateExpanded(nodes: TreeNode[], path: string, expanded: boolean): TreeNode[] {
68
- return nodes.map((n) => {
69
- if (n.path === path) return { ...n, expanded };
70
- if (n.children) return { ...n, children: updateExpanded(n.children, path, expanded) };
71
- return n;
72
- });
71
+ return nodes.map((n) => {
72
+ if (n.path === path) return { ...n, expanded };
73
+ if (n.children) return { ...n, children: updateExpanded(n.children, path, expanded) };
74
+ return n;
75
+ });
73
76
  }
74
77
 
75
78
  function loadChildren(nodes: TreeNode[], nodePath: string, children: TreeNode[]): TreeNode[] {
76
- return nodes.map((n) => {
77
- if (n.path === nodePath) return { ...n, children, expanded: true, loaded: true };
78
- if (n.children) return { ...n, children: loadChildren(n.children, nodePath, children) };
79
- return n;
80
- });
79
+ return nodes.map((n) => {
80
+ if (n.path === nodePath) return { ...n, children, expanded: true, loaded: true };
81
+ if (n.children) return { ...n, children: loadChildren(n.children, nodePath, children) };
82
+ return n;
83
+ });
81
84
  }
82
85
 
83
86
  function removeNode(nodes: TreeNode[], path: string): TreeNode[] {
84
- return nodes
85
- .filter((n) => n.path !== path)
86
- .map((n) => {
87
- if (n.children) return { ...n, children: removeNode(n.children, path) };
88
- return n;
89
- });
87
+ return nodes
88
+ .filter((n) => n.path !== path)
89
+ .map((n) => {
90
+ if (n.children) return { ...n, children: removeNode(n.children, path) };
91
+ return n;
92
+ });
90
93
  }
91
94
 
92
- function renameInTree(nodes: TreeNode[], oldPath: string, newPath: string, newName: string): TreeNode[] {
93
- return nodes.map((n) => {
94
- if (n.path === oldPath) {
95
- return { ...n, name: newName, path: newPath };
96
- }
97
- if (n.children) {
98
- return { ...n, children: renameInTree(n.children, oldPath, newPath, newName) };
99
- }
100
- return n;
101
- });
95
+ function renameInTree(
96
+ nodes: TreeNode[],
97
+ oldPath: string,
98
+ newPath: string,
99
+ newName: string,
100
+ ): TreeNode[] {
101
+ return nodes.map((n) => {
102
+ if (n.path === oldPath) {
103
+ return { ...n, name: newName, path: newPath };
104
+ }
105
+ if (n.children) {
106
+ return { ...n, children: renameInTree(n.children, oldPath, newPath, newName) };
107
+ }
108
+ return n;
109
+ });
102
110
  }
103
111
 
104
112
  export const useExplorerStore = create<ExplorerState>((set, get) => ({
105
- treeNodes: [],
106
- currentPath: "",
107
- selectedPath: null,
108
- filePreview: null,
109
- loadingFile: false,
110
- editingNode: null,
113
+ treeNodes: [],
114
+ currentPath: '',
115
+ selectedPath: null,
116
+ filePreview: null,
117
+ loadingFile: false,
118
+ editingNode: null,
119
+ scrollToLine: null,
111
120
 
112
- setCurrentPath: (path) => set({ currentPath: path }),
121
+ setCurrentPath: (path) => set({ currentPath: path }),
113
122
 
114
- listRootDir: async () => {
115
- const { currentPath } = get();
116
- const addLog = useLogStore.getState().addLog;
117
- addLog(`ListDir: ${currentPath}`);
118
- try {
119
- const res = await apiClient.call("file.listDir", { path: currentPath });
120
- set({
121
- treeNodes: entriesToTreeNodes(res.entries),
122
- // Update currentPath to the resolved absolute path
123
- currentPath: res.basePath,
124
- });
125
- addLog(`Found ${res.entries.length} items`);
126
- } catch (err) {
127
- addLog(`Error: ${err instanceof Error ? err.message : String(err)}`);
128
- }
129
- },
123
+ listRootDir: async () => {
124
+ const { currentPath } = get();
125
+ const addLog = useLogStore.getState().addLog;
126
+ addLog(`ListDir: ${currentPath}`);
127
+ try {
128
+ const res = await apiClient.call('file.listDir', { path: currentPath });
129
+ set({
130
+ treeNodes: entriesToTreeNodes(res.entries),
131
+ // Update currentPath to the resolved absolute path
132
+ currentPath: res.basePath,
133
+ });
134
+ addLog(`Found ${res.entries.length} items`);
135
+ } catch (err) {
136
+ addLog(`Error: ${err instanceof Error ? err.message : String(err)}`);
137
+ }
138
+ },
130
139
 
131
- toggleNode: async (nodePath: string) => {
132
- const { treeNodes } = get();
133
- const addLog = useLogStore.getState().addLog;
134
- const target = findNode(treeNodes, nodePath);
135
- if (!target) return;
140
+ toggleNode: async (nodePath: string) => {
141
+ const { treeNodes } = get();
142
+ const addLog = useLogStore.getState().addLog;
143
+ const target = findNode(treeNodes, nodePath);
144
+ if (!target) return;
136
145
 
137
- if (target.expanded) {
138
- set({ treeNodes: updateExpanded(treeNodes, nodePath, false) });
139
- } else if (target.loaded) {
140
- set({ treeNodes: updateExpanded(treeNodes, nodePath, true) });
141
- } else {
142
- addLog(`ListDir: ${nodePath}`);
143
- try {
144
- const res = await apiClient.call("file.listDir", { path: nodePath });
145
- const children = entriesToTreeNodes(res.entries);
146
- addLog(`Found ${res.entries.length} items`);
147
- set({ treeNodes: loadChildren(treeNodes, nodePath, children) });
148
- } catch (err) {
149
- addLog(`Error: ${err instanceof Error ? err.message : String(err)}`);
150
- }
151
- }
152
- },
146
+ if (target.expanded) {
147
+ set({ treeNodes: updateExpanded(treeNodes, nodePath, false) });
148
+ } else if (target.loaded) {
149
+ set({ treeNodes: updateExpanded(treeNodes, nodePath, true) });
150
+ } else {
151
+ addLog(`ListDir: ${nodePath}`);
152
+ try {
153
+ const res = await apiClient.call('file.listDir', { path: nodePath });
154
+ const children = entriesToTreeNodes(res.entries);
155
+ addLog(`Found ${res.entries.length} items`);
156
+ set({ treeNodes: loadChildren(treeNodes, nodePath, children) });
157
+ } catch (err) {
158
+ addLog(`Error: ${err instanceof Error ? err.message : String(err)}`);
159
+ }
160
+ }
161
+ },
153
162
 
154
- openFile: async (node: TreeNode) => {
155
- if (node.type === "directory") return;
156
- const addLog = useLogStore.getState().addLog;
157
- set({ selectedPath: node.path, loadingFile: true });
163
+ openFile: async (node: TreeNode) => {
164
+ if (node.type === 'directory') return;
165
+ const addLog = useLogStore.getState().addLog;
166
+ set({ selectedPath: node.path, loadingFile: true });
158
167
 
159
- const fileSize = node.size || 0;
160
- const preview: FilePreview = {
161
- path: node.path,
162
- name: node.name,
163
- content: null,
164
- imageUrl: null,
165
- mimeType: "",
166
- size: fileSize,
167
- isText: isTextFile(node.name),
168
- isImage: isImageFile(node.name),
169
- };
168
+ const fileSize = node.size || 0;
169
+ const preview: FilePreview = {
170
+ path: node.path,
171
+ name: node.name,
172
+ content: null,
173
+ imageUrl: null,
174
+ mimeType: '',
175
+ size: fileSize,
176
+ isText: isTextFile(node.name),
177
+ isImage: isImageFile(node.name),
178
+ };
170
179
 
171
- try {
172
- const mode = useConnectionStore.getState().mode;
180
+ try {
181
+ const mode = useConnectionStore.getState().mode;
173
182
 
174
- if (preview.isImage) {
175
- preview.imageUrl = getFileUrl(node.path);
176
- } else if (preview.isText) {
177
- if (fileSize > MAX_PREVIEW_SIZE) {
178
- 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.`;
179
- set({ filePreview: preview, loadingFile: false });
180
- addLog(`Skipped large file: ${node.name} (${formatSize(fileSize)})`);
181
- return;
182
- }
183
+ if (preview.isImage) {
184
+ preview.imageUrl = getFileUrl(node.path);
185
+ } else if (preview.isText) {
186
+ if (fileSize > MAX_PREVIEW_SIZE) {
187
+ 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.`;
188
+ set({ filePreview: preview, loadingFile: false });
189
+ addLog(`Skipped large file: ${node.name} (${formatSize(fileSize)})`);
190
+ return;
191
+ }
183
192
 
184
- let text: string;
185
- if (mode === "desktop") {
186
- // Desktop: read file via RPC (file:// blocked by webview)
187
- const res = await apiClient.call("file.readFile", { path: node.path });
188
- text = res.content;
189
- preview.mimeType = "text/plain";
190
- } else {
191
- // Web: fetch via HTTP file server
192
- const url = getFileUrl(node.path);
193
- const res = await fetch(url);
194
- if (!res.ok) throw new Error(`HTTP ${res.status}`);
195
- text = await res.text();
196
- preview.mimeType = res.headers.get("content-type") || "text/plain";
197
- }
198
- preview.content = text;
199
- preview.totalLines = text.split("\n").length;
200
- } else {
201
- preview.content = `[Binary file: ${node.name} (${formatSize(preview.size)})]`;
202
- preview.isText = true;
203
- }
204
- addLog(`Opened: ${node.name}`);
205
- } catch (err) {
206
- preview.content = `Failed to load: ${err instanceof Error ? err.message : String(err)}`;
207
- preview.isText = true;
208
- addLog(`Error opening ${node.name}: ${err instanceof Error ? err.message : String(err)}`);
209
- }
193
+ let text: string;
194
+ if (mode === 'desktop') {
195
+ // Desktop: read file via RPC (file:// blocked by webview)
196
+ const res = await apiClient.call('file.readFile', { path: node.path });
197
+ text = res.content;
198
+ preview.mimeType = 'text/plain';
199
+ } else {
200
+ // Web: fetch via HTTP file server
201
+ const url = getFileUrl(node.path);
202
+ const res = await fetch(url);
203
+ if (!res.ok) throw new Error(`HTTP ${res.status}`);
204
+ text = await res.text();
205
+ preview.mimeType = res.headers.get('content-type') || 'text/plain';
206
+ }
207
+ preview.content = text;
208
+ preview.totalLines = text.split('\n').length;
209
+ } else {
210
+ preview.content = `[Binary file: ${node.name} (${formatSize(preview.size)})]`;
211
+ preview.isText = true;
212
+ }
213
+ addLog(`Opened: ${node.name}`);
214
+ } catch (err) {
215
+ preview.content = `Failed to load: ${err instanceof Error ? err.message : String(err)}`;
216
+ preview.isText = true;
217
+ addLog(`Error opening ${node.name}: ${err instanceof Error ? err.message : String(err)}`);
218
+ }
210
219
 
211
- set({ filePreview: preview, loadingFile: false });
212
- },
220
+ set({ filePreview: preview, loadingFile: false });
221
+ },
213
222
 
214
- closePreview: () => set({ filePreview: null, selectedPath: null }),
223
+ openFileAtLine: async (node: TreeNode, line: number) => {
224
+ await get().openFile(node);
225
+ set({ scrollToLine: line });
226
+ },
215
227
 
216
- createFile: async (dirPath: string, name: string) => {
217
- const addLog = useLogStore.getState().addLog;
218
- try {
219
- await apiClient.call("file.createFile", { dirPath, name });
220
- rpcCache.invalidate("file.listDir");
221
- addLog(`Created file: ${name}`);
222
- set({ editingNode: null });
223
- await get().refreshDir(dirPath);
224
- } catch (err) {
225
- addLog(`Error creating file: ${err instanceof Error ? err.message : String(err)}`);
226
- set({ editingNode: null });
227
- }
228
- },
228
+ clearScrollToLine: () => set({ scrollToLine: null }),
229
229
 
230
- createDir: async (dirPath: string, name: string) => {
231
- const addLog = useLogStore.getState().addLog;
232
- try {
233
- await apiClient.call("file.createDir", { dirPath, name });
234
- rpcCache.invalidate("file.listDir");
235
- addLog(`Created directory: ${name}`);
236
- set({ editingNode: null });
237
- await get().refreshDir(dirPath);
238
- } catch (err) {
239
- addLog(`Error creating directory: ${err instanceof Error ? err.message : String(err)}`);
240
- set({ editingNode: null });
241
- }
242
- },
230
+ closePreview: () => set({ filePreview: null, selectedPath: null, scrollToLine: null }),
243
231
 
244
- renameNode: async (oldPath: string, newName: string) => {
245
- const { treeNodes } = get();
246
- const addLog = useLogStore.getState().addLog;
247
- try {
248
- const res = await apiClient.call("file.rename", { oldPath, newName });
249
- rpcCache.invalidate("file.listDir");
250
- addLog(`Renamed to: ${newName}`);
251
- set({
252
- treeNodes: renameInTree(treeNodes, oldPath, res.newPath, newName),
253
- editingNode: null,
254
- selectedPath: res.newPath,
255
- });
256
- } catch (err) {
257
- addLog(`Error renaming: ${err instanceof Error ? err.message : String(err)}`);
258
- set({ editingNode: null });
259
- }
260
- },
232
+ createFile: async (dirPath: string, name: string) => {
233
+ const addLog = useLogStore.getState().addLog;
234
+ try {
235
+ await apiClient.call('file.createFile', { dirPath, name });
236
+ rpcCache.invalidate('file.listDir');
237
+ addLog(`Created file: ${name}`);
238
+ set({ editingNode: null });
239
+ await get().refreshDir(dirPath);
240
+ } catch (err) {
241
+ addLog(`Error creating file: ${err instanceof Error ? err.message : String(err)}`);
242
+ set({ editingNode: null });
243
+ }
244
+ },
261
245
 
262
- deleteNode: async (path: string) => {
263
- const { treeNodes } = get();
264
- const addLog = useLogStore.getState().addLog;
265
- try {
266
- await apiClient.call("file.delete", { path });
267
- rpcCache.invalidate("file.listDir");
268
- addLog(`Deleted: ${path}`);
269
- // Find parent dir to refresh
270
- const pathParts = path.split("/");
271
- pathParts.pop();
272
- const parentPath = pathParts.join("/") || get().currentPath;
273
- set({ treeNodes: removeNode(treeNodes, path) });
274
- // Clear preview if the deleted file was being previewed
275
- const { selectedPath } = get();
276
- if (selectedPath === path) {
277
- set({ selectedPath: null, filePreview: null });
278
- }
279
- await get().refreshDir(parentPath);
280
- } catch (err) {
281
- addLog(`Error deleting: ${err instanceof Error ? err.message : String(err)}`);
282
- }
283
- },
246
+ createDir: async (dirPath: string, name: string) => {
247
+ const addLog = useLogStore.getState().addLog;
248
+ try {
249
+ await apiClient.call('file.createDir', { dirPath, name });
250
+ rpcCache.invalidate('file.listDir');
251
+ addLog(`Created directory: ${name}`);
252
+ set({ editingNode: null });
253
+ await get().refreshDir(dirPath);
254
+ } catch (err) {
255
+ addLog(`Error creating directory: ${err instanceof Error ? err.message : String(err)}`);
256
+ set({ editingNode: null });
257
+ }
258
+ },
284
259
 
285
- refreshDir: async (dirPath: string) => {
286
- const { treeNodes, currentPath } = get();
287
- const addLog = useLogStore.getState().addLog;
288
- // If refreshing root
289
- if (dirPath === currentPath) {
290
- await get().listRootDir();
291
- return;
292
- }
293
- try {
294
- const res = await apiClient.call("file.listDir", { path: dirPath });
295
- const children = entriesToTreeNodes(res.entries);
296
- set({ treeNodes: loadChildren(treeNodes, dirPath, children) });
297
- } catch (err) {
298
- addLog(`Error refreshing: ${err instanceof Error ? err.message : String(err)}`);
299
- }
300
- },
260
+ renameNode: async (oldPath: string, newName: string) => {
261
+ const { treeNodes } = get();
262
+ const addLog = useLogStore.getState().addLog;
263
+ try {
264
+ const res = await apiClient.call('file.rename', { oldPath, newName });
265
+ rpcCache.invalidate('file.listDir');
266
+ addLog(`Renamed to: ${newName}`);
267
+ set({
268
+ treeNodes: renameInTree(treeNodes, oldPath, res.newPath, newName),
269
+ editingNode: null,
270
+ selectedPath: res.newPath,
271
+ });
272
+ } catch (err) {
273
+ addLog(`Error renaming: ${err instanceof Error ? err.message : String(err)}`);
274
+ set({ editingNode: null });
275
+ }
276
+ },
301
277
 
302
- startEditing: (path, type) => set({ editingNode: { path, type } }),
303
- cancelEditing: () => set({ editingNode: null }),
278
+ deleteNode: async (path: string) => {
279
+ const { treeNodes } = get();
280
+ const addLog = useLogStore.getState().addLog;
281
+ try {
282
+ await apiClient.call('file.delete', { path });
283
+ rpcCache.invalidate('file.listDir');
284
+ addLog(`Deleted: ${path}`);
285
+ // Find parent dir to refresh
286
+ const pathParts = path.split('/');
287
+ pathParts.pop();
288
+ const parentPath = pathParts.join('/') || get().currentPath;
289
+ set({ treeNodes: removeNode(treeNodes, path) });
290
+ // Clear preview if the deleted file was being previewed
291
+ const { selectedPath } = get();
292
+ if (selectedPath === path) {
293
+ set({ selectedPath: null, filePreview: null });
294
+ }
295
+ await get().refreshDir(parentPath);
296
+ } catch (err) {
297
+ addLog(`Error deleting: ${err instanceof Error ? err.message : String(err)}`);
298
+ }
299
+ },
304
300
 
305
- importFiles: async (entries, destDir) => {
306
- const addLog = useLogStore.getState().addLog;
307
- const mode = useConnectionStore.getState().mode;
308
- try {
309
- const count = mode === "desktop"
310
- ? await importFilesDesktop(entries, destDir)
311
- : await uploadEntriesWeb(entries, destDir);
312
- addLog(`Imported ${count} items to ${destDir}`);
313
- await get().refreshDir(destDir);
314
- return count;
315
- } catch (err) {
316
- addLog(`Import error: ${err instanceof Error ? err.message : String(err)}`);
317
- throw err;
318
- }
319
- },
301
+ refreshDir: async (dirPath: string) => {
302
+ const { treeNodes, currentPath } = get();
303
+ const addLog = useLogStore.getState().addLog;
304
+ // If refreshing root
305
+ if (dirPath === currentPath) {
306
+ await get().listRootDir();
307
+ return;
308
+ }
309
+ try {
310
+ const res = await apiClient.call('file.listDir', { path: dirPath });
311
+ const children = entriesToTreeNodes(res.entries);
312
+ set({ treeNodes: loadChildren(treeNodes, dirPath, children) });
313
+ } catch (err) {
314
+ addLog(`Error refreshing: ${err instanceof Error ? err.message : String(err)}`);
315
+ }
316
+ },
317
+
318
+ startEditing: (path, type) => set({ editingNode: { path, type } }),
319
+ cancelEditing: () => set({ editingNode: null }),
320
+
321
+ importFiles: async (entries, destDir) => {
322
+ const addLog = useLogStore.getState().addLog;
323
+ const mode = useConnectionStore.getState().mode;
324
+ try {
325
+ const count =
326
+ mode === 'desktop'
327
+ ? await importFilesDesktop(entries, destDir)
328
+ : await uploadEntriesWeb(entries, destDir);
329
+ addLog(`Imported ${count} items to ${destDir}`);
330
+ await get().refreshDir(destDir);
331
+ return count;
332
+ } catch (err) {
333
+ addLog(`Import error: ${err instanceof Error ? err.message : String(err)}`);
334
+ throw err;
335
+ }
336
+ },
320
337
  }));