@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,160 @@
1
+ import { describe, it, expect, vi, beforeEach } from "vitest";
2
+
3
+ vi.mock("../../lib/api-client", () => ({
4
+ apiClient: {
5
+ call: vi.fn(),
6
+ subscribe: vi.fn(() => "sub-id"),
7
+ unsubscribe: vi.fn(),
8
+ },
9
+ }));
10
+
11
+ describe("useFeedStore", () => {
12
+ beforeEach(async () => {
13
+ vi.resetModules();
14
+ });
15
+
16
+ it("should have correct initial state", async () => {
17
+ const { useFeedStore } = await import("../use-feed-store");
18
+ const state = useFeedStore.getState();
19
+ expect(state.posts).toEqual([]);
20
+ expect(state.author).toBe("alice");
21
+ expect(state.category).toBe("tech");
22
+ expect(state.content).toBe("");
23
+ expect(state.loading).toBe(false);
24
+ });
25
+
26
+ it("should set author", async () => {
27
+ const { useFeedStore } = await import("../use-feed-store");
28
+ useFeedStore.getState().setAuthor("bob");
29
+ expect(useFeedStore.getState().author).toBe("bob");
30
+ });
31
+
32
+ it("should set category", async () => {
33
+ const { useFeedStore } = await import("../use-feed-store");
34
+ useFeedStore.getState().setCategory("news");
35
+ expect(useFeedStore.getState().category).toBe("news");
36
+ });
37
+
38
+ it("should set content", async () => {
39
+ const { useFeedStore } = await import("../use-feed-store");
40
+ useFeedStore.getState().setContent("hello world");
41
+ expect(useFeedStore.getState().content).toBe("hello world");
42
+ });
43
+
44
+ it("should create post and clear content", async () => {
45
+ const { apiClient } = await import("../../lib/api-client");
46
+ const { useFeedStore } = await import("../use-feed-store");
47
+ (apiClient.call as ReturnType<typeof vi.fn>).mockResolvedValueOnce({ id: "post-1" });
48
+ useFeedStore.getState().setContent(" new post ");
49
+
50
+ await useFeedStore.getState().createPost();
51
+ expect(apiClient.call).toHaveBeenCalledWith("feed.post", {
52
+ content: "new post",
53
+ category: "tech",
54
+ author: "alice",
55
+ });
56
+ expect(useFeedStore.getState().content).toBe("");
57
+ expect(useFeedStore.getState().loading).toBe(false);
58
+ });
59
+
60
+ it("should not create post with empty content", async () => {
61
+ const { apiClient } = await import("../../lib/api-client");
62
+ const { useFeedStore } = await import("../use-feed-store");
63
+ useFeedStore.setState({ content: " " });
64
+ (apiClient.call as ReturnType<typeof vi.fn>).mockClear();
65
+
66
+ await useFeedStore.getState().createPost();
67
+ expect(apiClient.call).not.toHaveBeenCalled();
68
+ });
69
+
70
+ it("should set loading to false on createPost error", async () => {
71
+ const { apiClient } = await import("../../lib/api-client");
72
+ const { useFeedStore } = await import("../use-feed-store");
73
+ (apiClient.call as ReturnType<typeof vi.fn>).mockRejectedValueOnce(new Error("fail"));
74
+ useFeedStore.getState().setContent("content");
75
+
76
+ await useFeedStore.getState().createPost();
77
+ expect(useFeedStore.getState().loading).toBe(false);
78
+ expect(useFeedStore.getState().content).toBe("content");
79
+ });
80
+
81
+ it("should load posts", async () => {
82
+ const { apiClient } = await import("../../lib/api-client");
83
+ const { useFeedStore } = await import("../use-feed-store");
84
+ const posts = [
85
+ { id: "1", content: "hello", category: "tech", author: "alice", timestamp: Date.now() },
86
+ ];
87
+ (apiClient.call as ReturnType<typeof vi.fn>).mockResolvedValueOnce({ posts });
88
+
89
+ await useFeedStore.getState().loadPosts();
90
+ expect(useFeedStore.getState().posts).toHaveLength(1);
91
+ });
92
+
93
+ it("should add a post locally", async () => {
94
+ const { useFeedStore } = await import("../use-feed-store");
95
+ const post = {
96
+ id: "p1",
97
+ content: "hi",
98
+ category: "tech" as const,
99
+ author: "alice",
100
+ timestamp: Date.now(),
101
+ };
102
+ useFeedStore.getState().addPost(post);
103
+ expect(useFeedStore.getState().posts).toHaveLength(1);
104
+ expect(useFeedStore.getState().posts[0].id).toBe("p1");
105
+ });
106
+ });
107
+
108
+ describe("useEventStreamStore", () => {
109
+ beforeEach(async () => {
110
+ vi.resetModules();
111
+ });
112
+
113
+ it("should have correct initial state", async () => {
114
+ const { useEventStreamStore } = await import("../use-feed-store");
115
+ const state = useEventStreamStore.getState();
116
+ expect(state.entries).toEqual([]);
117
+ expect(state.activeEventType).toBe("feed.update");
118
+ expect(state.activeFilter).toBe("");
119
+ expect(state.subscriptionId).toBeNull();
120
+ expect(state.subscribed).toBe(false);
121
+ });
122
+
123
+ it("should set active event type", async () => {
124
+ const { useEventStreamStore } = await import("../use-feed-store");
125
+ useEventStreamStore.getState().setActiveEventType("chat.message");
126
+ expect(useEventStreamStore.getState().activeEventType).toBe("chat.message");
127
+ });
128
+
129
+ it("should set active filter", async () => {
130
+ const { useEventStreamStore } = await import("../use-feed-store");
131
+ useEventStreamStore.getState().setActiveFilter('{"key":"val"}');
132
+ expect(useEventStreamStore.getState().activeFilter).toBe('{"key":"val"}');
133
+ });
134
+
135
+ it("should subscribe and set state", async () => {
136
+ const { useEventStreamStore } = await import("../use-feed-store");
137
+ await useEventStreamStore.getState().handleSubscribe();
138
+ expect(useEventStreamStore.getState().subscribed).toBe(true);
139
+ expect(useEventStreamStore.getState().subscriptionId).toBe("sub-id");
140
+ });
141
+
142
+ it("should unsubscribe and clear state", async () => {
143
+ const { apiClient } = await import("../../lib/api-client");
144
+ const { useEventStreamStore } = await import("../use-feed-store");
145
+ await useEventStreamStore.getState().handleSubscribe();
146
+
147
+ useEventStreamStore.getState().handleUnsubscribe();
148
+ expect(apiClient.unsubscribe).toHaveBeenCalledWith("sub-id");
149
+ expect(useEventStreamStore.getState().subscribed).toBe(false);
150
+ expect(useEventStreamStore.getState().subscriptionId).toBeNull();
151
+ });
152
+
153
+ it("should add entries keeping max 50", async () => {
154
+ const { useEventStreamStore } = await import("../use-feed-store");
155
+ for (let i = 0; i < 55; i++) {
156
+ useEventStreamStore.getState().addEntry("feed.update", { i }, {});
157
+ }
158
+ expect(useEventStreamStore.getState().entries.length).toBeLessThanOrEqual(50);
159
+ });
160
+ });
@@ -0,0 +1,313 @@
1
+ import { describe, it, expect, vi, beforeEach } from "vitest";
2
+
3
+ vi.mock("../../lib/api-client", () => ({
4
+ apiClient: { call: vi.fn() },
5
+ }));
6
+
7
+ vi.mock("../use-log-store", () => ({
8
+ useLogStore: { getState: () => ({ addLog: vi.fn() }) },
9
+ }));
10
+
11
+ describe("useGitStore", () => {
12
+ beforeEach(async () => {
13
+ vi.resetModules();
14
+ });
15
+
16
+ it("should have correct initial state", async () => {
17
+ const { useGitStore } = await import("../use-git-store");
18
+ const state = useGitStore.getState();
19
+ expect(state.branch).toBe("");
20
+ expect(state.ahead).toBe(0);
21
+ expect(state.behind).toBe(0);
22
+ expect(state.staged).toEqual([]);
23
+ expect(state.changed).toEqual([]);
24
+ expect(state.untracked).toEqual([]);
25
+ expect(state.commits).toEqual([]);
26
+ expect(state.loadingCommits).toBe(false);
27
+ expect(state.currentDiff).toBeNull();
28
+ expect(state.loadingDiff).toBe(false);
29
+ expect(state.expandedCommits).toEqual(new Set());
30
+ expect(state.commitFiles).toEqual({});
31
+ expect(state.loadingCommitFiles).toEqual(new Set());
32
+ expect(state.branches).toEqual([]);
33
+ expect(state.loadingBranches).toBe(false);
34
+ expect(state.worktrees).toEqual([]);
35
+ expect(state.loadingAction).toBeNull();
36
+ });
37
+
38
+ it("should fetch status", async () => {
39
+ const { apiClient } = await import("../../lib/api-client");
40
+ const { useGitStore } = await import("../use-git-store");
41
+ (apiClient.call as ReturnType<typeof vi.fn>).mockResolvedValueOnce({
42
+ branch: "main",
43
+ ahead: 2,
44
+ behind: 1,
45
+ staged: [{ path: "a.ts", status: "modified" }],
46
+ changed: [{ path: "b.ts", status: "added" }],
47
+ untracked: ["c.ts"],
48
+ });
49
+
50
+ await useGitStore.getState().fetchStatus("/repo");
51
+ const state = useGitStore.getState();
52
+ expect(state.branch).toBe("main");
53
+ expect(state.ahead).toBe(2);
54
+ expect(state.behind).toBe(1);
55
+ expect(state.staged).toHaveLength(1);
56
+ expect(state.untracked).toEqual(["c.ts"]);
57
+ });
58
+
59
+ it("should fetch diff", async () => {
60
+ const { apiClient } = await import("../../lib/api-client");
61
+ const { useGitStore } = await import("../use-git-store");
62
+ (apiClient.call as ReturnType<typeof vi.fn>).mockResolvedValueOnce({
63
+ filePath: "a.ts",
64
+ diff: "+hello",
65
+ oldContent: "",
66
+ newContent: "hello",
67
+ });
68
+
69
+ await useGitStore.getState().fetchDiff("/repo", "a.ts");
70
+ const state = useGitStore.getState();
71
+ expect(state.currentDiff).not.toBeNull();
72
+ expect(state.currentDiff!.filePath).toBe("a.ts");
73
+ expect(state.loadingDiff).toBe(false);
74
+ });
75
+
76
+ it("should set loadingDiff on fetchDiff error", async () => {
77
+ const { apiClient } = await import("../../lib/api-client");
78
+ const { useGitStore } = await import("../use-git-store");
79
+ (apiClient.call as ReturnType<typeof vi.fn>).mockRejectedValueOnce(new Error("fail"));
80
+
81
+ await useGitStore.getState().fetchDiff("/repo", "a.ts");
82
+ expect(useGitStore.getState().loadingDiff).toBe(false);
83
+ });
84
+
85
+ it("should clear diff", async () => {
86
+ const { useGitStore } = await import("../use-git-store");
87
+ useGitStore.setState({
88
+ currentDiff: { filePath: "a.ts", diff: "", oldContent: "", newContent: "" },
89
+ });
90
+ useGitStore.getState().clearDiff();
91
+ expect(useGitStore.getState().currentDiff).toBeNull();
92
+ });
93
+
94
+ it("should fetch log", async () => {
95
+ const { apiClient } = await import("../../lib/api-client");
96
+ const { useGitStore } = await import("../use-git-store");
97
+ const commits = [
98
+ { hash: "abc123", shortHash: "abc1234", message: "init", author: "user", date: "2024-01-01" },
99
+ ];
100
+ (apiClient.call as ReturnType<typeof vi.fn>).mockResolvedValueOnce({ commits });
101
+
102
+ await useGitStore.getState().fetchLog("/repo");
103
+ expect(useGitStore.getState().commits).toHaveLength(1);
104
+ expect(useGitStore.getState().loadingCommits).toBe(false);
105
+ });
106
+
107
+ it("should set loadingCommits false on fetchLog error", async () => {
108
+ const { apiClient } = await import("../../lib/api-client");
109
+ const { useGitStore } = await import("../use-git-store");
110
+ (apiClient.call as ReturnType<typeof vi.fn>).mockRejectedValueOnce(new Error("fail"));
111
+
112
+ await useGitStore.getState().fetchLog("/repo");
113
+ expect(useGitStore.getState().loadingCommits).toBe(false);
114
+ });
115
+
116
+ it("should toggle commit expand and load files", async () => {
117
+ const { apiClient } = await import("../../lib/api-client");
118
+ const { useGitStore } = await import("../use-git-store");
119
+ (apiClient.call as ReturnType<typeof vi.fn>).mockResolvedValueOnce({
120
+ files: [{ path: "a.ts", status: "modified" }],
121
+ });
122
+
123
+ await useGitStore.getState().toggleCommitExpand("/repo", "abc123");
124
+ const state = useGitStore.getState();
125
+ expect(state.expandedCommits.has("abc123")).toBe(true);
126
+ expect(state.commitFiles["abc123"]).toHaveLength(1);
127
+ });
128
+
129
+ it("should collapse expanded commit on toggle", async () => {
130
+ const { useGitStore } = await import("../use-git-store");
131
+ useGitStore.setState({ expandedCommits: new Set(["abc123"]) });
132
+
133
+ await useGitStore.getState().toggleCommitExpand("/repo", "abc123");
134
+ expect(useGitStore.getState().expandedCommits.has("abc123")).toBe(false);
135
+ });
136
+
137
+ it("should fetch branches", async () => {
138
+ const { apiClient } = await import("../../lib/api-client");
139
+ const { useGitStore } = await import("../use-git-store");
140
+ (apiClient.call as ReturnType<typeof vi.fn>).mockResolvedValueOnce({
141
+ branches: [
142
+ { name: "main", isCurrent: true, isRemote: false },
143
+ { name: "dev", isCurrent: false, isRemote: false },
144
+ ],
145
+ });
146
+
147
+ await useGitStore.getState().fetchBranches("/repo");
148
+ expect(useGitStore.getState().branches).toHaveLength(2);
149
+ expect(useGitStore.getState().loadingBranches).toBe(false);
150
+ });
151
+
152
+ it("should checkout and refresh", async () => {
153
+ const { apiClient } = await import("../../lib/api-client");
154
+ const { useGitStore } = await import("../use-git-store");
155
+ const callMock = apiClient.call as ReturnType<typeof vi.fn>;
156
+ callMock.mockResolvedValueOnce(undefined);
157
+ callMock.mockResolvedValueOnce({
158
+ branch: "dev",
159
+ ahead: 0,
160
+ behind: 0,
161
+ staged: [],
162
+ changed: [],
163
+ untracked: [],
164
+ });
165
+
166
+ await useGitStore.getState().checkout("/repo", "dev");
167
+ expect(callMock).toHaveBeenCalledWith("git.checkout", { repoPath: "/repo", branch: "dev" });
168
+ expect(useGitStore.getState().loadingAction).toBeNull();
169
+ });
170
+
171
+ it("should stage files and refresh", async () => {
172
+ const { apiClient } = await import("../../lib/api-client");
173
+ const { useGitStore } = await import("../use-git-store");
174
+ const callMock = apiClient.call as ReturnType<typeof vi.fn>;
175
+ callMock.mockResolvedValueOnce(undefined);
176
+ callMock.mockResolvedValueOnce({
177
+ branch: "main",
178
+ ahead: 0,
179
+ behind: 0,
180
+ staged: [],
181
+ changed: [],
182
+ untracked: [],
183
+ });
184
+
185
+ await useGitStore.getState().stageFiles("/repo", ["a.ts"]);
186
+ expect(callMock).toHaveBeenCalledWith("git.add", { repoPath: "/repo", paths: ["a.ts"] });
187
+ expect(useGitStore.getState().loadingAction).toBeNull();
188
+ });
189
+
190
+ it("should unstage files and refresh", async () => {
191
+ const { apiClient } = await import("../../lib/api-client");
192
+ const { useGitStore } = await import("../use-git-store");
193
+ const callMock = apiClient.call as ReturnType<typeof vi.fn>;
194
+ callMock.mockResolvedValueOnce(undefined);
195
+ callMock.mockResolvedValueOnce({
196
+ branch: "main",
197
+ ahead: 0,
198
+ behind: 0,
199
+ staged: [],
200
+ changed: [],
201
+ untracked: [],
202
+ });
203
+
204
+ await useGitStore.getState().unstageFiles("/repo", ["a.ts"]);
205
+ expect(callMock).toHaveBeenCalledWith("git.reset", { repoPath: "/repo", paths: ["a.ts"] });
206
+ expect(useGitStore.getState().loadingAction).toBeNull();
207
+ });
208
+
209
+ it("should commit and refresh", async () => {
210
+ const { apiClient } = await import("../../lib/api-client");
211
+ const { useGitStore } = await import("../use-git-store");
212
+ const callMock = apiClient.call as ReturnType<typeof vi.fn>;
213
+ callMock.mockResolvedValueOnce({ shortHash: "abc1234" });
214
+ callMock.mockResolvedValueOnce({
215
+ branch: "main",
216
+ ahead: 0,
217
+ behind: 0,
218
+ staged: [],
219
+ changed: [],
220
+ untracked: [],
221
+ });
222
+
223
+ await useGitStore.getState().commit("/repo", "init commit");
224
+ expect(callMock).toHaveBeenCalledWith("git.commit", {
225
+ repoPath: "/repo",
226
+ message: "init commit",
227
+ });
228
+ expect(useGitStore.getState().loadingAction).toBeNull();
229
+ });
230
+
231
+ it("should push and refresh", async () => {
232
+ const { apiClient } = await import("../../lib/api-client");
233
+ const { useGitStore } = await import("../use-git-store");
234
+ const callMock = apiClient.call as ReturnType<typeof vi.fn>;
235
+ callMock.mockResolvedValueOnce(undefined);
236
+ callMock.mockResolvedValueOnce({
237
+ branch: "main",
238
+ ahead: 0,
239
+ behind: 0,
240
+ staged: [],
241
+ changed: [],
242
+ untracked: [],
243
+ });
244
+
245
+ await useGitStore.getState().push("/repo");
246
+ expect(callMock).toHaveBeenCalledWith("git.push", { repoPath: "/repo" });
247
+ expect(useGitStore.getState().loadingAction).toBeNull();
248
+ });
249
+
250
+ it("should pull and refresh", async () => {
251
+ const { apiClient } = await import("../../lib/api-client");
252
+ const { useGitStore } = await import("../use-git-store");
253
+ const callMock = apiClient.call as ReturnType<typeof vi.fn>;
254
+ callMock.mockResolvedValueOnce(undefined);
255
+ callMock.mockResolvedValueOnce({
256
+ branch: "main",
257
+ ahead: 0,
258
+ behind: 0,
259
+ staged: [],
260
+ changed: [],
261
+ untracked: [],
262
+ });
263
+
264
+ await useGitStore.getState().pull("/repo");
265
+ expect(callMock).toHaveBeenCalledWith("git.pull", { repoPath: "/repo" });
266
+ expect(useGitStore.getState().loadingAction).toBeNull();
267
+ });
268
+
269
+ it("should fetch worktrees", async () => {
270
+ const { apiClient } = await import("../../lib/api-client");
271
+ const { useGitStore } = await import("../use-git-store");
272
+ (apiClient.call as ReturnType<typeof vi.fn>).mockResolvedValueOnce({
273
+ worktrees: [
274
+ { path: "/repo", branch: "main", isMain: true },
275
+ { path: "/repo-wt", branch: "dev", isMain: false },
276
+ ],
277
+ });
278
+
279
+ await useGitStore.getState().fetchWorktrees("/repo");
280
+ expect(useGitStore.getState().worktrees).toHaveLength(2);
281
+ });
282
+
283
+ it("should fetch commit file diff", async () => {
284
+ const { apiClient } = await import("../../lib/api-client");
285
+ const { useGitStore } = await import("../use-git-store");
286
+ (apiClient.call as ReturnType<typeof vi.fn>).mockResolvedValueOnce({
287
+ filePath: "a.ts",
288
+ diff: "+x",
289
+ oldContent: "",
290
+ newContent: "x",
291
+ });
292
+
293
+ await useGitStore.getState().fetchCommitFileDiff("/repo", "abc123", "a.ts");
294
+ expect(useGitStore.getState().currentDiff!.filePath).toBe("a.ts");
295
+ expect(useGitStore.getState().loadingDiff).toBe(false);
296
+ });
297
+
298
+ it("should refresh by calling fetchStatus", async () => {
299
+ const { apiClient } = await import("../../lib/api-client");
300
+ const { useGitStore } = await import("../use-git-store");
301
+ (apiClient.call as ReturnType<typeof vi.fn>).mockResolvedValueOnce({
302
+ branch: "main",
303
+ ahead: 0,
304
+ behind: 0,
305
+ staged: [],
306
+ changed: [],
307
+ untracked: [],
308
+ });
309
+
310
+ await useGitStore.getState().refresh("/repo");
311
+ expect(apiClient.call).toHaveBeenCalledWith("git.status", { repoPath: "/repo" });
312
+ });
313
+ });
@@ -0,0 +1,115 @@
1
+ import { describe, it, expect, vi, beforeEach, afterEach } from "vitest";
2
+
3
+ describe("useNotificationStore", () => {
4
+ beforeEach(async () => {
5
+ vi.resetModules();
6
+ vi.useFakeTimers();
7
+ });
8
+
9
+ afterEach(() => {
10
+ vi.useRealTimers();
11
+ });
12
+
13
+ it("should have correct initial state", async () => {
14
+ const { useNotificationStore } = await import("../use-notification-store");
15
+ const state = useNotificationStore.getState();
16
+ expect(state.notifications).toEqual([]);
17
+ expect(state.panelOpen).toBe(false);
18
+ });
19
+
20
+ it("should push a notification", async () => {
21
+ const { useNotificationStore } = await import("../use-notification-store");
22
+ useNotificationStore.getState().push({ message: "hello", level: "info" });
23
+ const state = useNotificationStore.getState();
24
+ expect(state.notifications).toHaveLength(1);
25
+ expect(state.notifications[0].message).toBe("hello");
26
+ expect(state.notifications[0].level).toBe("info");
27
+ expect(state.notifications[0].read).toBe(false);
28
+ expect(state.notifications[0].id).toMatch(/^notif-/);
29
+ });
30
+
31
+ it("should push notification with sessionId", async () => {
32
+ const { useNotificationStore } = await import("../use-notification-store");
33
+ useNotificationStore.getState().push({ message: "test", level: "warning", sessionId: "s1" });
34
+ expect(useNotificationStore.getState().notifications[0].sessionId).toBe("s1");
35
+ });
36
+
37
+ it("should auto-dismiss info notifications after 5s", async () => {
38
+ const { useNotificationStore } = await import("../use-notification-store");
39
+ useNotificationStore.getState().push({ message: "temp", level: "info" });
40
+ expect(useNotificationStore.getState().notifications).toHaveLength(1);
41
+
42
+ vi.advanceTimersByTime(5000);
43
+ expect(useNotificationStore.getState().notifications).toHaveLength(0);
44
+ });
45
+
46
+ it("should not auto-dismiss warning notifications", async () => {
47
+ const { useNotificationStore } = await import("../use-notification-store");
48
+ useNotificationStore.getState().push({ message: "warn", level: "warning" });
49
+
50
+ vi.advanceTimersByTime(10000);
51
+ expect(useNotificationStore.getState().notifications).toHaveLength(1);
52
+ });
53
+
54
+ it("should not auto-dismiss error notifications", async () => {
55
+ const { useNotificationStore } = await import("../use-notification-store");
56
+ useNotificationStore.getState().push({ message: "err", level: "error" });
57
+
58
+ vi.advanceTimersByTime(10000);
59
+ expect(useNotificationStore.getState().notifications).toHaveLength(1);
60
+ });
61
+
62
+ it("should mark a notification as read", async () => {
63
+ const { useNotificationStore } = await import("../use-notification-store");
64
+ useNotificationStore.getState().push({ message: "test", level: "warning" });
65
+ const id = useNotificationStore.getState().notifications[0].id;
66
+
67
+ useNotificationStore.getState().markRead(id);
68
+ expect(useNotificationStore.getState().notifications[0].read).toBe(true);
69
+ });
70
+
71
+ it("should mark all notifications as read", async () => {
72
+ const { useNotificationStore } = await import("../use-notification-store");
73
+ useNotificationStore.getState().push({ message: "a", level: "warning" });
74
+ useNotificationStore.getState().push({ message: "b", level: "error" });
75
+
76
+ useNotificationStore.getState().markAllRead();
77
+ const state = useNotificationStore.getState();
78
+ expect(state.notifications.every((n) => n.read)).toBe(true);
79
+ });
80
+
81
+ it("should dismiss a notification", async () => {
82
+ const { useNotificationStore } = await import("../use-notification-store");
83
+ useNotificationStore.getState().push({ message: "bye", level: "warning" });
84
+ const id = useNotificationStore.getState().notifications[0].id;
85
+
86
+ useNotificationStore.getState().dismiss(id);
87
+ expect(useNotificationStore.getState().notifications).toHaveLength(0);
88
+ });
89
+
90
+ it("should clear all notifications", async () => {
91
+ const { useNotificationStore } = await import("../use-notification-store");
92
+ useNotificationStore.getState().push({ message: "a", level: "error" });
93
+ useNotificationStore.getState().push({ message: "b", level: "error" });
94
+
95
+ useNotificationStore.getState().clearAll();
96
+ expect(useNotificationStore.getState().notifications).toHaveLength(0);
97
+ });
98
+
99
+ it("should toggle panel", async () => {
100
+ const { useNotificationStore } = await import("../use-notification-store");
101
+ expect(useNotificationStore.getState().panelOpen).toBe(false);
102
+ useNotificationStore.getState().togglePanel();
103
+ expect(useNotificationStore.getState().panelOpen).toBe(true);
104
+ useNotificationStore.getState().togglePanel();
105
+ expect(useNotificationStore.getState().panelOpen).toBe(false);
106
+ });
107
+
108
+ it("should set panel open explicitly", async () => {
109
+ const { useNotificationStore } = await import("../use-notification-store");
110
+ useNotificationStore.getState().setPanelOpen(true);
111
+ expect(useNotificationStore.getState().panelOpen).toBe(true);
112
+ useNotificationStore.getState().setPanelOpen(false);
113
+ expect(useNotificationStore.getState().panelOpen).toBe(false);
114
+ });
115
+ });