@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
@@ -0,0 +1,198 @@
1
+ /**
2
+ * Chat Store — Agent 流式聊天
3
+ *
4
+ * 对应 PRD §4.2-4.3:SSE 流式渲染、轮次展示、工具调用卡片
5
+ */
6
+
7
+ import { create } from 'zustand';
8
+
9
+ export interface ToolCall {
10
+ id: string;
11
+ tool: string;
12
+ input: string;
13
+ output: string;
14
+ status: 'running' | 'done';
15
+ }
16
+
17
+ export interface Turn {
18
+ turn: number;
19
+ toolCalls: ToolCall[];
20
+ text: string;
21
+ done: boolean;
22
+ }
23
+
24
+ export interface AgentMessage {
25
+ id: string;
26
+ role: 'agent';
27
+ text: string;
28
+ turns: Turn[];
29
+ thinking: string;
30
+ steps?: { label: string; status: string; detail?: string }[];
31
+ summary?: unknown;
32
+ error?: string;
33
+ at: number;
34
+ }
35
+
36
+ interface ChatState {
37
+ messages: (AgentMessage | { id: string; role: 'user'; text: string; at: number })[];
38
+
39
+ // 流式回调,由 ChatPanel 绑定
40
+ _onEventCallback: ((event: unknown) => void) | null;
41
+ setOnEventCallback: (cb: ((event: unknown) => void) | null) => void;
42
+
43
+ addUserMessage: (text: string) => void;
44
+ addAgentPlaceholder: (id: string) => void;
45
+ patchLastAgent: (patch: Partial<AgentMessage>) => void;
46
+ pushToolCall: (tc: ToolCall) => void;
47
+ updateToolCall: (tcId: string, output: string) => void;
48
+ appendThinking: (delta: string) => void;
49
+ appendText: (delta: string) => void;
50
+ markTurnDone: (turnNum: number) => void;
51
+ setMessages: (msgs: unknown[]) => void;
52
+ }
53
+
54
+ export const useChatStore = create<ChatState>((set, get) => ({
55
+ messages: [],
56
+ _onEventCallback: null,
57
+
58
+ setOnEventCallback: (cb) => set({ _onEventCallback: cb }),
59
+
60
+ addUserMessage: (text) =>
61
+ set((s) => ({
62
+ messages: [
63
+ ...s.messages,
64
+ { id: `msg_${Date.now()}`, role: 'user' as const, text, at: Date.now() },
65
+ ],
66
+ })),
67
+
68
+ addAgentPlaceholder: (id) =>
69
+ set((s) => ({
70
+ messages: [
71
+ ...s.messages,
72
+ {
73
+ id,
74
+ role: 'agent' as const,
75
+ text: '',
76
+ turns: [],
77
+ thinking: '',
78
+ at: Date.now(),
79
+ },
80
+ ],
81
+ })),
82
+
83
+ patchLastAgent: (patch) => {
84
+ const msgs = [...get().messages];
85
+ for (let i = msgs.length - 1; i >= 0; i--) {
86
+ const entry = msgs[i];
87
+ if (!entry || entry.role !== 'agent') continue;
88
+ msgs[i] = { ...(entry as AgentMessage), ...patch };
89
+ break;
90
+ }
91
+ set({ messages: msgs });
92
+ },
93
+
94
+ pushToolCall: (tc) => {
95
+ const msgs = [...get().messages];
96
+ for (let i = msgs.length - 1; i >= 0; i--) {
97
+ const entry = msgs[i];
98
+ if (!entry || entry.role !== 'agent') continue;
99
+ const m = entry as AgentMessage;
100
+ const turns = [...(m.turns || [])];
101
+ const lastTurn = turns[turns.length - 1];
102
+ if (turns.length === 0 || !lastTurn || lastTurn.done) {
103
+ turns.push({
104
+ turn: turns.length + 1,
105
+ toolCalls: [],
106
+ text: '',
107
+ done: false,
108
+ });
109
+ }
110
+ const currentTurn = turns[turns.length - 1];
111
+ if (currentTurn) {
112
+ currentTurn.toolCalls = [...currentTurn.toolCalls, tc];
113
+ }
114
+ msgs[i] = { ...m, turns };
115
+ break;
116
+ }
117
+ set({ messages: msgs });
118
+ },
119
+
120
+ updateToolCall: (tcId, output) => {
121
+ const msgs = [...get().messages];
122
+ for (let i = msgs.length - 1; i >= 0; i--) {
123
+ const entry = msgs[i];
124
+ if (!entry || entry.role !== 'agent') continue;
125
+ const m = entry as AgentMessage;
126
+ const turns = [...(m.turns || [])];
127
+ const cur = turns[turns.length - 1];
128
+ if (cur) {
129
+ const calls = [...cur.toolCalls];
130
+ const idx = calls.findIndex((c) => c.id === tcId);
131
+ if (idx >= 0) {
132
+ const c = calls[idx];
133
+ if (c) calls[idx] = { ...c, output, status: 'done' as const };
134
+ } else {
135
+ const ri =
136
+ calls.length - 1 - [...calls].reverse().findIndex((c) => c.status === 'running');
137
+ const c = calls[ri];
138
+ if (c) calls[ri] = { ...c, output, status: 'done' as const };
139
+ }
140
+ cur.toolCalls = calls;
141
+ }
142
+ msgs[i] = { ...m, turns };
143
+ break;
144
+ }
145
+ set({ messages: msgs });
146
+ },
147
+
148
+ appendThinking: (delta) => {
149
+ const msgs = [...get().messages];
150
+ for (let i = msgs.length - 1; i >= 0; i--) {
151
+ const entry = msgs[i];
152
+ if (!entry || entry.role !== 'agent') continue;
153
+ const m = entry as AgentMessage;
154
+ msgs[i] = { ...m, thinking: (m.thinking || '') + delta };
155
+ break;
156
+ }
157
+ set({ messages: msgs });
158
+ },
159
+
160
+ appendText: (delta) => {
161
+ const msgs = [...get().messages];
162
+ for (let i = msgs.length - 1; i >= 0; i--) {
163
+ const entry = msgs[i];
164
+ if (!entry || entry.role !== 'agent') continue;
165
+ const m = entry as AgentMessage;
166
+ const turns = [...(m.turns || [])];
167
+ const openTurn = turns.find((t) => !t.done);
168
+ if (openTurn) {
169
+ openTurn.text = (openTurn.text || '') + delta;
170
+ }
171
+ msgs[i] = { ...m, turns, text: (m.text || '') + delta };
172
+ break;
173
+ }
174
+ set({ messages: msgs });
175
+ },
176
+
177
+ markTurnDone: (_turnNum) => {
178
+ const msgs = [...get().messages];
179
+ for (let i = msgs.length - 1; i >= 0; i--) {
180
+ const entry = msgs[i];
181
+ if (!entry || entry.role !== 'agent') continue;
182
+ const m = entry as AgentMessage;
183
+ const turns = [...(m.turns || [])];
184
+ const lastTurn = turns[turns.length - 1];
185
+ if (turns.length > 0 && lastTurn && !lastTurn.done) {
186
+ turns[turns.length - 1] = {
187
+ ...lastTurn,
188
+ done: true,
189
+ };
190
+ }
191
+ msgs[i] = { ...m, turns };
192
+ break;
193
+ }
194
+ set({ messages: msgs });
195
+ },
196
+
197
+ setMessages: (msgs) => set({ messages: msgs }),
198
+ }));
@@ -0,0 +1,168 @@
1
+ import { create } from 'zustand';
2
+ import { apiClient } from '../lib/api-client';
3
+ import { useLogStore } from './use-log-store';
4
+ import { networkBus } from '../lib/network-bus';
5
+
6
+ export interface BrowserTab {
7
+ index: number;
8
+ url: string;
9
+ title: string;
10
+ active: boolean;
11
+ }
12
+
13
+ // 轮询定时器(模块级,避免重复创建)
14
+ let _pollTimer: ReturnType<typeof setInterval> | null = null;
15
+
16
+ interface ConnectionState {
17
+ mode: 'web' | 'desktop';
18
+ ready: boolean;
19
+ // 浏览器连接状态
20
+ browserStatus: 'offline' | 'online';
21
+ browsers: { pluginId: string; name: string; tabs: number }[];
22
+ tabs: BrowserTab[];
23
+ activeTabIndex: number;
24
+ selectedTabIndex: number | null; // null = 用活跃 tab
25
+ // 插件
26
+ plugins: { name: string; description: string }[];
27
+ activePlugins: string[];
28
+ // 系统信息
29
+ systemInfo: unknown;
30
+
31
+ setReady: (ready: boolean) => void;
32
+ setMode: (mode: 'web' | 'desktop') => void;
33
+ setBrowserStatus: (status: 'offline' | 'online', browsers?: unknown[]) => void;
34
+ setTabs: (tabs: BrowserTab[], activeIndex: number) => void;
35
+ selectTab: (index: number | null) => void;
36
+ setPlugins: (plugins: unknown[]) => void;
37
+ setActivePlugins: (plugins: string[]) => void;
38
+ toggleActivePlugin: (name: string) => void;
39
+ initializeConnection: () => void;
40
+ checkBrowser: () => Promise<void>;
41
+ loadTabs: () => Promise<void>;
42
+ startConnectionPolling: () => void;
43
+ stopConnectionPolling: () => void;
44
+ }
45
+
46
+ export const useConnectionStore = create<ConnectionState>((set, get) => ({
47
+ mode: 'web',
48
+ ready: false,
49
+ browserStatus: 'offline',
50
+ browsers: [],
51
+ tabs: [],
52
+ activeTabIndex: 0,
53
+ selectedTabIndex: null,
54
+ plugins: [],
55
+ activePlugins: [],
56
+ systemInfo: null,
57
+
58
+ setReady: (ready) => set({ ready }),
59
+ setMode: (mode) => set({ mode }),
60
+
61
+ setBrowserStatus: (status, browsers) => set({ browserStatus: status, browsers: browsers || [] }),
62
+
63
+ setTabs: (tabs, activeIndex) => set({ tabs, activeTabIndex: activeIndex }),
64
+
65
+ selectTab: (index) => set({ selectedTabIndex: index }),
66
+
67
+ setPlugins: (plugins) => set({ plugins }),
68
+
69
+ setActivePlugins: (plugins) => set({ activePlugins: plugins }),
70
+
71
+ toggleActivePlugin: (name) => {
72
+ const current = get().activePlugins;
73
+ if (current.includes(name)) {
74
+ set({ activePlugins: current.filter((p) => p !== name) });
75
+ } else {
76
+ set({ activePlugins: [...current, name] });
77
+ }
78
+ },
79
+
80
+ initializeConnection: () => {
81
+ const MAX_RETRIES = 5;
82
+ let retries = 0;
83
+ const init = async () => {
84
+ try {
85
+ await apiClient.initialize();
86
+ const transport = apiClient.getTransport();
87
+ set({
88
+ mode: transport === 'ipc' ? 'desktop' : 'web',
89
+ ready: true,
90
+ });
91
+ useLogStore
92
+ .getState()
93
+ .addLog(`${transport === 'ipc' ? 'Desktop' : 'Web'} mode - ${transport.toUpperCase()}`);
94
+
95
+ // 初始检测浏览器连接 + 加载标签页
96
+ await get().checkBrowser();
97
+ if (get().browserStatus === 'online') {
98
+ await get().loadTabs();
99
+ } else {
100
+ // 未连接 → 启动轮询,用户装好扩展后自动发现
101
+ get().startConnectionPolling();
102
+ }
103
+ } catch {
104
+ retries++;
105
+ if (retries < MAX_RETRIES) {
106
+ setTimeout(init, 1000);
107
+ } else {
108
+ useLogStore.getState().addLog(`Failed to connect after ${MAX_RETRIES} retries`);
109
+ }
110
+ }
111
+ };
112
+ init();
113
+ },
114
+
115
+ checkBrowser: async () => {
116
+ try {
117
+ const result = await apiClient.call('browser.checkConnection', {});
118
+ const wasOffline = get().browserStatus === 'offline';
119
+ set({
120
+ browserStatus: result.connected ? 'online' : 'offline',
121
+ browsers: result.browsers,
122
+ });
123
+ // 刚连上时,立即加载标签页
124
+ if (wasOffline && result.connected) {
125
+ await get().loadTabs();
126
+ }
127
+ } catch {
128
+ set({ browserStatus: 'offline', browsers: [] });
129
+ }
130
+ },
131
+
132
+ loadTabs: async () => {
133
+ try {
134
+ const result = await apiClient.call('browser.listTabs', {});
135
+ set({ tabs: result.tabs, activeTabIndex: result.activeIndex });
136
+ } catch (err) {
137
+ networkBus.emitStatus(`加载标签页失败: ${err instanceof Error ? err.message : String(err)}`);
138
+ }
139
+ },
140
+
141
+ /**
142
+ * 启动连接轮询 — 未连接时每 3 秒检测一次,连上后停止。
143
+ * 用户安装扩展后会自动发现。
144
+ */
145
+ startConnectionPolling: () => {
146
+ // 已连接就不轮询
147
+ if (get().browserStatus === 'online') return;
148
+ if (_pollTimer) return; // 已有轮询在跑
149
+
150
+ _pollTimer = setInterval(async () => {
151
+ await get().checkBrowser();
152
+ // 连上了就停止轮询
153
+ if (get().browserStatus === 'online') {
154
+ if (_pollTimer) {
155
+ clearInterval(_pollTimer);
156
+ _pollTimer = null;
157
+ }
158
+ }
159
+ }, 3000);
160
+ },
161
+
162
+ stopConnectionPolling: () => {
163
+ if (_pollTimer) {
164
+ clearInterval(_pollTimer);
165
+ _pollTimer = null;
166
+ }
167
+ },
168
+ }));
@@ -0,0 +1,68 @@
1
+ /**
2
+ * Context Store — 上下文管理(文件夹 / 连接器 / 工作文件)
3
+ */
4
+ import { create } from "zustand";
5
+ import { apiClient } from "../lib/api-client";
6
+ import { useLogStore } from "./use-log-store";
7
+
8
+ export interface ContextFolder { path: string; name: string }
9
+ export interface Connector { id: string; name: string; type: string; enabled: boolean }
10
+ export interface WorkingFile { path: string; name: string; size?: number }
11
+
12
+ interface ContextState {
13
+ folders: ContextFolder[];
14
+ connectors: Connector[];
15
+ workingFiles: WorkingFile[];
16
+ fetchAll: () => Promise<void>;
17
+ addFolder: (path: string) => Promise<void>;
18
+ removeFolder: (path: string) => Promise<void>;
19
+ toggleConnector: (id: string, enabled: boolean) => Promise<void>;
20
+ addWorkingFile: (path: string) => Promise<void>;
21
+ removeWorkingFile: (path: string) => Promise<void>;
22
+ }
23
+
24
+ export const useContextStore = create<ContextState>((set) => ({
25
+ folders: [],
26
+ connectors: [],
27
+ workingFiles: [],
28
+
29
+ fetchAll: async () => {
30
+ try {
31
+ const [f, c, w] = await Promise.all([
32
+ apiClient.call("context.listFolders", {}),
33
+ apiClient.call("context.listConnectors", {}),
34
+ apiClient.call("context.listWorkingFiles", {}),
35
+ ]);
36
+ set({ folders: f.folders, connectors: c.connectors, workingFiles: w.files });
37
+ } catch (err) {
38
+ useLogStore.getState().addLog("error", "Failed to fetch context", { error: err });
39
+ }
40
+ },
41
+
42
+ addFolder: async (path) => {
43
+ const res = await apiClient.call("context.addFolder", { path });
44
+ set((s) => ({ folders: [...s.folders, res.folder] }));
45
+ },
46
+
47
+ removeFolder: async (path) => {
48
+ await apiClient.call("context.removeFolder", { path });
49
+ set((s) => ({ folders: s.folders.filter((f) => f.path !== path) }));
50
+ },
51
+
52
+ toggleConnector: async (id, enabled) => {
53
+ const res = await apiClient.call("context.toggleConnector", { id, enabled });
54
+ set((s) => ({
55
+ connectors: s.connectors.map((c) => (c.id === id ? res.connector : c)),
56
+ }));
57
+ },
58
+
59
+ addWorkingFile: async (path) => {
60
+ const res = await apiClient.call("context.addWorkingFile", { path });
61
+ set((s) => ({ workingFiles: [...s.workingFiles, res.file] }));
62
+ },
63
+
64
+ removeWorkingFile: async (path) => {
65
+ await apiClient.call("context.removeWorkingFile", { path });
66
+ set((s) => ({ workingFiles: s.workingFiles.filter((f) => f.path !== path) }));
67
+ },
68
+ }));
@@ -0,0 +1,27 @@
1
+ import { create } from "zustand";
2
+ import type { Locale } from "../lib/i18n";
3
+
4
+ interface LocaleState {
5
+ locale: Locale;
6
+ setLocale: (locale: Locale) => void;
7
+ }
8
+
9
+ function getInitialLocale(): Locale {
10
+ if (typeof window === "undefined") return "en";
11
+ const stored = localStorage.getItem("locale");
12
+ if (stored === "en" || stored === "zh") return stored;
13
+ const browserLang = navigator.language.toLowerCase();
14
+ if (browserLang.startsWith("zh")) return "zh";
15
+ return "en";
16
+ }
17
+
18
+ export const useLocaleStore = create<LocaleState>((set) => ({
19
+ locale: getInitialLocale(),
20
+ setLocale: (locale) => {
21
+ localStorage.setItem("locale", locale);
22
+ import("../lib/i18n").then((i18n) => {
23
+ i18n.default.changeLanguage(locale);
24
+ });
25
+ set({ locale });
26
+ },
27
+ }));
@@ -0,0 +1,16 @@
1
+ import { create } from "zustand";
2
+
3
+ interface LogState {
4
+ logs: string[];
5
+ addLog: (message: string) => void;
6
+ }
7
+
8
+ const MAX_LOGS = 100;
9
+
10
+ export const useLogStore = create<LogState>((set) => ({
11
+ logs: [],
12
+ addLog: (message) =>
13
+ set((state) => ({
14
+ logs: [...state.logs.slice(-(MAX_LOGS - 1)), `[${new Date().toISOString().slice(11, 19)}] ${message}`],
15
+ })),
16
+ }));
@@ -0,0 +1,18 @@
1
+ /**
2
+ * Network Panel state — shared open/close state between TopBar button
3
+ * and the AppLayout drawer.
4
+ */
5
+
6
+ import { create } from "zustand";
7
+
8
+ interface NetworkPanelState {
9
+ open: boolean;
10
+ toggle: () => void;
11
+ setOpen: (open: boolean) => void;
12
+ }
13
+
14
+ export const useNetworkPanelStore = create<NetworkPanelState>((set, get) => ({
15
+ open: false,
16
+ toggle: () => set({ open: !get().open }),
17
+ setOpen: (open) => set({ open }),
18
+ }));
@@ -0,0 +1,47 @@
1
+ /**
2
+ * Output Store — 产出物管理(Artifacts)
3
+ */
4
+ import { create } from "zustand";
5
+ import { apiClient } from "../lib/api-client";
6
+ import { useLogStore } from "./use-log-store";
7
+
8
+ export interface Output {
9
+ id: string;
10
+ name: string;
11
+ kind: "file" | "snippet" | "link";
12
+ path?: string;
13
+ content?: string;
14
+ size?: number;
15
+ taskId?: string;
16
+ createdAt: number;
17
+ }
18
+
19
+ interface OutputState {
20
+ outputs: Output[];
21
+ fetchOutputs: () => Promise<void>;
22
+ addOutput: (name: string, kind: Output["kind"], path?: string, taskId?: string) => Promise<void>;
23
+ removeOutput: (id: string) => Promise<void>;
24
+ }
25
+
26
+ export const useOutputStore = create<OutputState>((set) => ({
27
+ outputs: [],
28
+
29
+ fetchOutputs: async () => {
30
+ try {
31
+ const res = await apiClient.call("output.list", {});
32
+ set({ outputs: res.outputs });
33
+ } catch (err) {
34
+ useLogStore.getState().addLog("error", "Failed to fetch outputs", { error: err });
35
+ }
36
+ },
37
+
38
+ addOutput: async (name, kind, path, taskId) => {
39
+ const res = await apiClient.call("output.add", { name, kind, path, taskId });
40
+ set((s) => ({ outputs: [res.output, ...s.outputs] }));
41
+ },
42
+
43
+ removeOutput: async (id) => {
44
+ await apiClient.call("output.remove", { id });
45
+ set((s) => ({ outputs: s.outputs.filter((o) => o.id !== id) }));
46
+ },
47
+ }));
@@ -0,0 +1,97 @@
1
+ /**
2
+ * Preview Store — 内嵌浏览器状态管理(Code Tab)
3
+ */
4
+ import { create } from 'zustand';
5
+ import { apiClient } from '../lib/api-client';
6
+ import { useLogStore } from './use-log-store';
7
+
8
+ export type NavState = 'idle' | 'loading' | 'ready' | 'error';
9
+
10
+ export interface PreviewTab {
11
+ id: string;
12
+ url: string;
13
+ title: string;
14
+ state: NavState;
15
+ canGoBack: boolean;
16
+ canGoForward: boolean;
17
+ }
18
+
19
+ interface PreviewState {
20
+ currentTab: PreviewTab | null;
21
+ useIframe: boolean;
22
+ navState: NavState;
23
+ history: string[];
24
+ openUrl: (url: string) => Promise<void>;
25
+ navigate: (action: 'back' | 'forward' | 'reload') => Promise<void>;
26
+ closeTab: () => Promise<void>;
27
+ loadHistory: () => Promise<void>;
28
+ }
29
+
30
+ export const usePreviewStore = create<PreviewState>((set, get) => ({
31
+ currentTab: null,
32
+ useIframe: true,
33
+ navState: 'idle',
34
+ history: [],
35
+
36
+ openUrl: async (url) => {
37
+ const fullUrl = url.startsWith('http') ? url : `http://${url}`;
38
+ set({ navState: 'loading' });
39
+
40
+ try {
41
+ const res = await apiClient.call('preview.open', { url: fullUrl });
42
+
43
+ // 订阅导航状态事件(subId 暂不存储,订阅会自动管理)
44
+ await apiClient.subscribe(
45
+ 'preview.navState',
46
+ (tab: PreviewTab) => {
47
+ if (tab.id === get().currentTab?.id) {
48
+ set({ currentTab: tab, navState: tab.state });
49
+ }
50
+ },
51
+ {},
52
+ );
53
+
54
+ set({
55
+ currentTab: { ...res.tab, state: res.tab.state },
56
+ useIframe: res.useIframe,
57
+ navState: res.tab.state,
58
+ });
59
+ } catch (err) {
60
+ useLogStore.getState().addLog('error', 'Failed to open URL', { error: err });
61
+ set({ navState: 'error' });
62
+ }
63
+ },
64
+
65
+ navigate: async (action) => {
66
+ const tab = get().currentTab;
67
+ if (!tab) return;
68
+ set({ navState: 'loading' });
69
+ try {
70
+ await apiClient.call('preview.navigate', { tabId: tab.id, action });
71
+ } catch (err) {
72
+ useLogStore.getState().addLog('error', 'Navigation failed', { error: err });
73
+ set({ navState: 'error' });
74
+ }
75
+ },
76
+
77
+ closeTab: async () => {
78
+ const tab = get().currentTab;
79
+ if (tab) {
80
+ try {
81
+ await apiClient.call('preview.close', { tabId: tab.id });
82
+ } catch {
83
+ /* ignore */
84
+ }
85
+ }
86
+ set({ currentTab: null, navState: 'idle' });
87
+ },
88
+
89
+ loadHistory: async () => {
90
+ try {
91
+ const res = await apiClient.call('preview.history', {});
92
+ set({ history: res.urls });
93
+ } catch {
94
+ /* ignore */
95
+ }
96
+ },
97
+ }));