trident-git 0.2.0 → 0.2.1

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 (310) hide show
  1. package/.next/BUILD_ID +1 -0
  2. package/.next/app-path-routes-manifest.json +23 -0
  3. package/.next/build-manifest.json +20 -0
  4. package/.next/export-marker.json +6 -0
  5. package/.next/images-manifest.json +73 -0
  6. package/.next/next-minimal-server.js.nft.json +1 -0
  7. package/.next/next-server.js.nft.json +1 -0
  8. package/.next/package.json +1 -0
  9. package/.next/prerender-manifest.json +282 -0
  10. package/.next/react-loadable-manifest.json +1 -0
  11. package/.next/required-server-files.js +330 -0
  12. package/.next/required-server-files.json +330 -0
  13. package/.next/routes-manifest.json +170 -0
  14. package/.next/server/app/_global-error/page.js +3 -0
  15. package/.next/server/app/_global-error/page.js.nft.json +1 -0
  16. package/.next/server/app/_global-error/page_client-reference-manifest.js +1 -0
  17. package/.next/server/app/_global-error.html +2 -0
  18. package/.next/server/app/_global-error.meta +16 -0
  19. package/.next/server/app/_global-error.rsc +12 -0
  20. package/.next/server/app/_global-error.segments/_full.segment.rsc +12 -0
  21. package/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +5 -0
  22. package/.next/server/app/_global-error.segments/_global-error.segment.rsc +4 -0
  23. package/.next/server/app/_global-error.segments/_head.segment.rsc +5 -0
  24. package/.next/server/app/_global-error.segments/_index.segment.rsc +4 -0
  25. package/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -0
  26. package/.next/server/app/_not-found/page.js +2 -0
  27. package/.next/server/app/_not-found/page.js.nft.json +1 -0
  28. package/.next/server/app/_not-found/page_client-reference-manifest.js +1 -0
  29. package/.next/server/app/_not-found.html +1 -0
  30. package/.next/server/app/_not-found.meta +16 -0
  31. package/.next/server/app/_not-found.rsc +19 -0
  32. package/.next/server/app/_not-found.segments/_full.segment.rsc +19 -0
  33. package/.next/server/app/_not-found.segments/_head.segment.rsc +6 -0
  34. package/.next/server/app/_not-found.segments/_index.segment.rsc +8 -0
  35. package/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +5 -0
  36. package/.next/server/app/_not-found.segments/_not-found.segment.rsc +4 -0
  37. package/.next/server/app/_not-found.segments/_tree.segment.rsc +6 -0
  38. package/.next/server/app/api/credentials/route.js +1 -0
  39. package/.next/server/app/api/credentials/route.js.nft.json +1 -0
  40. package/.next/server/app/api/credentials/route_client-reference-manifest.js +1 -0
  41. package/.next/server/app/api/custom-scripts/route.js +3 -0
  42. package/.next/server/app/api/custom-scripts/route.js.nft.json +1 -0
  43. package/.next/server/app/api/custom-scripts/route_client-reference-manifest.js +1 -0
  44. package/.next/server/app/api/fs/route.js +1 -0
  45. package/.next/server/app/api/fs/route.js.nft.json +1 -0
  46. package/.next/server/app/api/fs/route_client-reference-manifest.js +1 -0
  47. package/.next/server/app/api/git/action/route.js +1 -0
  48. package/.next/server/app/api/git/action/route.js.nft.json +1 -0
  49. package/.next/server/app/api/git/action/route_client-reference-manifest.js +1 -0
  50. package/.next/server/app/api/git/branches/route.js +1 -0
  51. package/.next/server/app/api/git/branches/route.js.nft.json +1 -0
  52. package/.next/server/app/api/git/branches/route_client-reference-manifest.js +1 -0
  53. package/.next/server/app/api/git/diff/route.js +1 -0
  54. package/.next/server/app/api/git/diff/route.js.nft.json +1 -0
  55. package/.next/server/app/api/git/diff/route_client-reference-manifest.js +1 -0
  56. package/.next/server/app/api/git/log/route.js +1 -0
  57. package/.next/server/app/api/git/log/route.js.nft.json +1 -0
  58. package/.next/server/app/api/git/log/route_client-reference-manifest.js +1 -0
  59. package/.next/server/app/api/git/status/route.js +1 -0
  60. package/.next/server/app/api/git/status/route.js.nft.json +1 -0
  61. package/.next/server/app/api/git/status/route_client-reference-manifest.js +1 -0
  62. package/.next/server/app/api/repos/route.js +1 -0
  63. package/.next/server/app/api/repos/route.js.nft.json +1 -0
  64. package/.next/server/app/api/repos/route_client-reference-manifest.js +1 -0
  65. package/.next/server/app/api/settings/route.js +1 -0
  66. package/.next/server/app/api/settings/route.js.nft.json +1 -0
  67. package/.next/server/app/api/settings/route_client-reference-manifest.js +1 -0
  68. package/.next/server/app/credentials/page.js +4 -0
  69. package/.next/server/app/credentials/page.js.nft.json +1 -0
  70. package/.next/server/app/credentials/page_client-reference-manifest.js +1 -0
  71. package/.next/server/app/credentials.html +1 -0
  72. package/.next/server/app/credentials.meta +15 -0
  73. package/.next/server/app/credentials.rsc +23 -0
  74. package/.next/server/app/credentials.segments/_full.segment.rsc +23 -0
  75. package/.next/server/app/credentials.segments/_head.segment.rsc +6 -0
  76. package/.next/server/app/credentials.segments/_index.segment.rsc +8 -0
  77. package/.next/server/app/credentials.segments/_tree.segment.rsc +6 -0
  78. package/.next/server/app/credentials.segments/credentials/__PAGE__.segment.rsc +9 -0
  79. package/.next/server/app/credentials.segments/credentials.segment.rsc +4 -0
  80. package/.next/server/app/icon.png/route.js +1 -0
  81. package/.next/server/app/icon.png/route.js.nft.json +1 -0
  82. package/.next/server/app/icon.png.meta +1 -0
  83. package/.next/server/app/index.html +1 -0
  84. package/.next/server/app/index.meta +14 -0
  85. package/.next/server/app/index.rsc +20 -0
  86. package/.next/server/app/index.segments/__PAGE__.segment.rsc +6 -0
  87. package/.next/server/app/index.segments/_full.segment.rsc +20 -0
  88. package/.next/server/app/index.segments/_head.segment.rsc +6 -0
  89. package/.next/server/app/index.segments/_index.segment.rsc +8 -0
  90. package/.next/server/app/index.segments/_tree.segment.rsc +6 -0
  91. package/.next/server/app/page.js +2 -0
  92. package/.next/server/app/page.js.nft.json +1 -0
  93. package/.next/server/app/page_client-reference-manifest.js +1 -0
  94. package/.next/server/app/workspace/changes/page.js +4 -0
  95. package/.next/server/app/workspace/changes/page.js.nft.json +1 -0
  96. package/.next/server/app/workspace/changes/page_client-reference-manifest.js +1 -0
  97. package/.next/server/app/workspace/changes.html +1 -0
  98. package/.next/server/app/workspace/changes.meta +16 -0
  99. package/.next/server/app/workspace/changes.rsc +26 -0
  100. package/.next/server/app/workspace/changes.segments/_full.segment.rsc +26 -0
  101. package/.next/server/app/workspace/changes.segments/_head.segment.rsc +6 -0
  102. package/.next/server/app/workspace/changes.segments/_index.segment.rsc +8 -0
  103. package/.next/server/app/workspace/changes.segments/_tree.segment.rsc +7 -0
  104. package/.next/server/app/workspace/changes.segments/workspace/changes/__PAGE__.segment.rsc +10 -0
  105. package/.next/server/app/workspace/changes.segments/workspace/changes.segment.rsc +4 -0
  106. package/.next/server/app/workspace/changes.segments/workspace.segment.rsc +7 -0
  107. package/.next/server/app/workspace/custom-scripts/page.js +2 -0
  108. package/.next/server/app/workspace/custom-scripts/page.js.nft.json +1 -0
  109. package/.next/server/app/workspace/custom-scripts/page_client-reference-manifest.js +1 -0
  110. package/.next/server/app/workspace/custom-scripts.html +1 -0
  111. package/.next/server/app/workspace/custom-scripts.meta +16 -0
  112. package/.next/server/app/workspace/custom-scripts.rsc +25 -0
  113. package/.next/server/app/workspace/custom-scripts.segments/_full.segment.rsc +25 -0
  114. package/.next/server/app/workspace/custom-scripts.segments/_head.segment.rsc +6 -0
  115. package/.next/server/app/workspace/custom-scripts.segments/_index.segment.rsc +8 -0
  116. package/.next/server/app/workspace/custom-scripts.segments/_tree.segment.rsc +6 -0
  117. package/.next/server/app/workspace/custom-scripts.segments/workspace/custom-scripts/__PAGE__.segment.rsc +9 -0
  118. package/.next/server/app/workspace/custom-scripts.segments/workspace/custom-scripts.segment.rsc +4 -0
  119. package/.next/server/app/workspace/custom-scripts.segments/workspace.segment.rsc +7 -0
  120. package/.next/server/app/workspace/history/page.js +2 -0
  121. package/.next/server/app/workspace/history/page.js.nft.json +1 -0
  122. package/.next/server/app/workspace/history/page_client-reference-manifest.js +1 -0
  123. package/.next/server/app/workspace/history.html +1 -0
  124. package/.next/server/app/workspace/history.meta +16 -0
  125. package/.next/server/app/workspace/history.rsc +26 -0
  126. package/.next/server/app/workspace/history.segments/_full.segment.rsc +26 -0
  127. package/.next/server/app/workspace/history.segments/_head.segment.rsc +6 -0
  128. package/.next/server/app/workspace/history.segments/_index.segment.rsc +8 -0
  129. package/.next/server/app/workspace/history.segments/_tree.segment.rsc +7 -0
  130. package/.next/server/app/workspace/history.segments/workspace/history/__PAGE__.segment.rsc +10 -0
  131. package/.next/server/app/workspace/history.segments/workspace/history.segment.rsc +4 -0
  132. package/.next/server/app/workspace/history.segments/workspace.segment.rsc +7 -0
  133. package/.next/server/app/workspace/page.js +2 -0
  134. package/.next/server/app/workspace/page.js.nft.json +1 -0
  135. package/.next/server/app/workspace/page_client-reference-manifest.js +1 -0
  136. package/.next/server/app/workspace/settings/page.js +2 -0
  137. package/.next/server/app/workspace/settings/page.js.nft.json +1 -0
  138. package/.next/server/app/workspace/settings/page_client-reference-manifest.js +1 -0
  139. package/.next/server/app/workspace/settings.html +1 -0
  140. package/.next/server/app/workspace/settings.meta +16 -0
  141. package/.next/server/app/workspace/settings.rsc +25 -0
  142. package/.next/server/app/workspace/settings.segments/_full.segment.rsc +25 -0
  143. package/.next/server/app/workspace/settings.segments/_head.segment.rsc +6 -0
  144. package/.next/server/app/workspace/settings.segments/_index.segment.rsc +8 -0
  145. package/.next/server/app/workspace/settings.segments/_tree.segment.rsc +6 -0
  146. package/.next/server/app/workspace/settings.segments/workspace/settings/__PAGE__.segment.rsc +9 -0
  147. package/.next/server/app/workspace/settings.segments/workspace/settings.segment.rsc +4 -0
  148. package/.next/server/app/workspace/settings.segments/workspace.segment.rsc +7 -0
  149. package/.next/server/app/workspace/stashes/page.js +2 -0
  150. package/.next/server/app/workspace/stashes/page.js.nft.json +1 -0
  151. package/.next/server/app/workspace/stashes/page_client-reference-manifest.js +1 -0
  152. package/.next/server/app/workspace/stashes.html +1 -0
  153. package/.next/server/app/workspace/stashes.meta +16 -0
  154. package/.next/server/app/workspace/stashes.rsc +26 -0
  155. package/.next/server/app/workspace/stashes.segments/_full.segment.rsc +26 -0
  156. package/.next/server/app/workspace/stashes.segments/_head.segment.rsc +6 -0
  157. package/.next/server/app/workspace/stashes.segments/_index.segment.rsc +8 -0
  158. package/.next/server/app/workspace/stashes.segments/_tree.segment.rsc +7 -0
  159. package/.next/server/app/workspace/stashes.segments/workspace/stashes/__PAGE__.segment.rsc +10 -0
  160. package/.next/server/app/workspace/stashes.segments/workspace/stashes.segment.rsc +4 -0
  161. package/.next/server/app/workspace/stashes.segments/workspace.segment.rsc +7 -0
  162. package/.next/server/app/workspace.html +1 -0
  163. package/.next/server/app/workspace.meta +15 -0
  164. package/.next/server/app/workspace.rsc +26 -0
  165. package/.next/server/app/workspace.segments/_full.segment.rsc +26 -0
  166. package/.next/server/app/workspace.segments/_head.segment.rsc +6 -0
  167. package/.next/server/app/workspace.segments/_index.segment.rsc +8 -0
  168. package/.next/server/app/workspace.segments/_tree.segment.rsc +7 -0
  169. package/.next/server/app/workspace.segments/workspace/__PAGE__.segment.rsc +10 -0
  170. package/.next/server/app/workspace.segments/workspace.segment.rsc +7 -0
  171. package/.next/server/app-paths-manifest.json +23 -0
  172. package/.next/server/chunks/116.js +1 -0
  173. package/.next/server/chunks/170.js +1 -0
  174. package/.next/server/chunks/309.js +6 -0
  175. package/.next/server/chunks/356.js +1 -0
  176. package/.next/server/chunks/392.js +1 -0
  177. package/.next/server/chunks/443.js +37 -0
  178. package/.next/server/chunks/445.js +22 -0
  179. package/.next/server/chunks/471.js +13 -0
  180. package/.next/server/chunks/557.js +5 -0
  181. package/.next/server/chunks/680.js +1 -0
  182. package/.next/server/chunks/739.js +5 -0
  183. package/.next/server/chunks/781.js +1 -0
  184. package/.next/server/chunks/813.js +1 -0
  185. package/.next/server/chunks/816.js +13 -0
  186. package/.next/server/functions-config-manifest.json +6 -0
  187. package/.next/server/interception-route-rewrite-manifest.js +1 -0
  188. package/.next/server/middleware-build-manifest.js +1 -0
  189. package/.next/server/middleware-manifest.json +6 -0
  190. package/.next/server/middleware-react-loadable-manifest.js +1 -0
  191. package/.next/server/next-font-manifest.js +1 -0
  192. package/.next/server/next-font-manifest.json +1 -0
  193. package/.next/server/pages/404.html +1 -0
  194. package/.next/server/pages/500.html +2 -0
  195. package/.next/server/pages-manifest.json +4 -0
  196. package/.next/server/server-reference-manifest.js +1 -0
  197. package/.next/server/server-reference-manifest.json +1 -0
  198. package/.next/server/webpack-runtime.js +1 -0
  199. package/.next/static/chunks/131-6237c77d708182b1.js +1 -0
  200. package/.next/static/chunks/147-d4cb3e03218d75df.js +6 -0
  201. package/.next/static/chunks/176-b068120641913226.js +1 -0
  202. package/.next/static/chunks/200-3664e65687e716a2.js +1 -0
  203. package/.next/static/chunks/26-d93e82c9c09ff972.js +1 -0
  204. package/.next/static/chunks/4bd1b696-096d35a2bd1da3af.js +1 -0
  205. package/.next/static/chunks/500-5d45efbf86b8d36e.js +1 -0
  206. package/.next/static/chunks/772-b39107b40c31b6b6.js +3 -0
  207. package/.next/static/chunks/785-e6d755147ff87934.js +1 -0
  208. package/.next/static/chunks/790-3e8fa6e595d97940.js +6 -0
  209. package/.next/static/chunks/794-37dad9bbc14b04b8.js +2 -0
  210. package/.next/static/chunks/841-4d865378216d36eb.js +13 -0
  211. package/.next/static/chunks/app/_global-error/page-9eb8a19109bbd711.js +1 -0
  212. package/.next/static/chunks/app/_not-found/page-5bea07380837254f.js +1 -0
  213. package/.next/static/chunks/app/api/credentials/route-9eb8a19109bbd711.js +1 -0
  214. package/.next/static/chunks/app/api/custom-scripts/route-9eb8a19109bbd711.js +1 -0
  215. package/.next/static/chunks/app/api/fs/route-9eb8a19109bbd711.js +1 -0
  216. package/.next/static/chunks/app/api/git/action/route-9eb8a19109bbd711.js +1 -0
  217. package/.next/static/chunks/app/api/git/branches/route-9eb8a19109bbd711.js +1 -0
  218. package/.next/static/chunks/app/api/git/diff/route-9eb8a19109bbd711.js +1 -0
  219. package/.next/static/chunks/app/api/git/log/route-9eb8a19109bbd711.js +1 -0
  220. package/.next/static/chunks/app/api/git/status/route-9eb8a19109bbd711.js +1 -0
  221. package/.next/static/chunks/app/api/repos/route-9eb8a19109bbd711.js +1 -0
  222. package/.next/static/chunks/app/api/settings/route-9eb8a19109bbd711.js +1 -0
  223. package/.next/static/chunks/app/credentials/page-23c1807ad9f68a40.js +1 -0
  224. package/.next/static/chunks/app/layout-a06241c385e50ae0.js +1 -0
  225. package/.next/static/chunks/app/page-c8404790f5191fd7.js +1 -0
  226. package/.next/static/chunks/app/workspace/changes/page-8cca17f80e66f534.js +3 -0
  227. package/.next/static/chunks/app/workspace/custom-scripts/page-ee24b9dd7779244f.js +1 -0
  228. package/.next/static/chunks/app/workspace/history/page-b7017fea285c804c.js +1 -0
  229. package/.next/static/chunks/app/workspace/layout-cb86c00fdcd8db08.js +1 -0
  230. package/.next/static/chunks/app/workspace/page-21211acc90c08509.js +1 -0
  231. package/.next/static/chunks/app/workspace/settings/page-7ccb58ece31492e9.js +1 -0
  232. package/.next/static/chunks/app/workspace/stashes/page-0e1b8304c4b0f4f3.js +1 -0
  233. package/.next/static/chunks/framework-75892d61b920805f.js +1 -0
  234. package/.next/static/chunks/main-app-221e1b945693a142.js +1 -0
  235. package/.next/static/chunks/main-f1d55fffd3cc9809.js +5 -0
  236. package/.next/static/chunks/next/dist/client/components/builtin/app-error-9eb8a19109bbd711.js +1 -0
  237. package/.next/static/chunks/next/dist/client/components/builtin/forbidden-9eb8a19109bbd711.js +1 -0
  238. package/.next/static/chunks/next/dist/client/components/builtin/global-error-f2a2c324f3740ed8.js +1 -0
  239. package/.next/static/chunks/next/dist/client/components/builtin/not-found-9eb8a19109bbd711.js +1 -0
  240. package/.next/static/chunks/next/dist/client/components/builtin/unauthorized-9eb8a19109bbd711.js +1 -0
  241. package/.next/static/chunks/polyfills-42372ed130431b0a.js +1 -0
  242. package/.next/static/chunks/webpack-7dec27dc3bfe207a.js +1 -0
  243. package/.next/static/css/36968b90b2339533.css +1 -0
  244. package/.next/static/css/c56c0e57397a3a60.css +1 -0
  245. package/.next/static/css/de70bee13400563f.css +1 -0
  246. package/.next/static/i5Fy6fyWcq44ejS8h_y1c/_buildManifest.js +1 -0
  247. package/.next/static/i5Fy6fyWcq44ejS8h_y1c/_ssgManifest.js +1 -0
  248. package/.next/static/media/4cf2300e9c8272f7-s.p.woff2 +0 -0
  249. package/.next/static/media/747892c23ea88013-s.woff2 +0 -0
  250. package/.next/static/media/8d697b304b401681-s.woff2 +0 -0
  251. package/.next/static/media/93f479601ee12b01-s.p.woff2 +0 -0
  252. package/.next/static/media/9610d9e46709d722-s.woff2 +0 -0
  253. package/.next/static/media/ba015fad6dcf6784-s.woff2 +0 -0
  254. package/README.md +2 -0
  255. package/bin/trident-git.mjs +9 -11
  256. package/package.json +7 -5
  257. package/eslint.config.mjs +0 -18
  258. package/postcss.config.mjs +0 -7
  259. package/src/app/api/credentials/route.ts +0 -113
  260. package/src/app/api/custom-scripts/route.ts +0 -203
  261. package/src/app/api/fs/route.ts +0 -75
  262. package/src/app/api/git/action/route.ts +0 -383
  263. package/src/app/api/git/branches/route.ts +0 -20
  264. package/src/app/api/git/diff/route.ts +0 -104
  265. package/src/app/api/git/log/route.ts +0 -28
  266. package/src/app/api/git/status/route.ts +0 -28
  267. package/src/app/api/repos/route.ts +0 -84
  268. package/src/app/api/settings/route.ts +0 -37
  269. package/src/app/credentials/page.tsx +0 -408
  270. package/src/app/globals.css +0 -109
  271. package/src/app/layout.tsx +0 -38
  272. package/src/app/page.tsx +0 -10
  273. package/src/app/providers.tsx +0 -21
  274. package/src/app/workspace/changes/page.tsx +0 -27
  275. package/src/app/workspace/custom-scripts/page.tsx +0 -247
  276. package/src/app/workspace/history/page.tsx +0 -27
  277. package/src/app/workspace/layout.tsx +0 -26
  278. package/src/app/workspace/page.tsx +0 -27
  279. package/src/app/workspace/settings/page.tsx +0 -233
  280. package/src/app/workspace/stashes/page.tsx +0 -395
  281. package/src/components/command-palette.tsx +0 -178
  282. package/src/components/context-menu.tsx +0 -200
  283. package/src/components/fs-browser.tsx +0 -154
  284. package/src/components/git/diff-view.tsx +0 -137
  285. package/src/components/git/git-graph.tsx +0 -489
  286. package/src/components/git/grouped-diff-viewer.tsx +0 -332
  287. package/src/components/git/history-view.tsx +0 -4862
  288. package/src/components/git/image-diff-view.tsx +0 -342
  289. package/src/components/git/status-view.tsx +0 -597
  290. package/src/components/home-settings-modal.tsx +0 -192
  291. package/src/components/layout/sidebar.tsx +0 -256
  292. package/src/components/repo-list.tsx +0 -206
  293. package/src/components/theme-toggle.tsx +0 -37
  294. package/src/components/toaster.tsx +0 -36
  295. package/src/components/workspace-repo-open-tracker.tsx +0 -39
  296. package/src/hooks/use-credentials.ts +0 -123
  297. package/src/hooks/use-escape-dismiss.ts +0 -72
  298. package/src/hooks/use-git.ts +0 -448
  299. package/src/hooks/use-toast.ts +0 -280
  300. package/src/hooks/use-workspace-title.ts +0 -23
  301. package/src/lib/api-utils.ts +0 -24
  302. package/src/lib/branch-colors.ts +0 -98
  303. package/src/lib/credentials.ts +0 -404
  304. package/src/lib/git.ts +0 -1510
  305. package/src/lib/graph-utils.ts +0 -253
  306. package/src/lib/store.ts +0 -145
  307. package/src/lib/types.ts +0 -95
  308. package/src/lib/utils.ts +0 -266
  309. package/tsconfig.json +0 -34
  310. /package/{src/app/icon.png → .next/server/app/icon.png.body} +0 -0
@@ -1,395 +0,0 @@
1
- 'use client';
2
-
3
- import { useSearchParams } from 'next/navigation';
4
- import { Suspense, useState, useEffect } from 'react';
5
- import { useWorkspaceTitle } from '@/hooks/use-workspace-title';
6
- import { useGitStashes, useGitAction, useStashFiles, useStashFileDiff } from '@/hooks/use-git';
7
- import { cn, getChangedLineCountFromDiff, isFileBinary, isImageFile } from '@/lib/utils';
8
- import { useTheme } from 'next-themes';
9
- import { GroupedDiffViewer } from '@/components/git/grouped-diff-viewer';
10
- import { ImageDiffView } from '@/components/git/image-diff-view';
11
-
12
- function StashDiffView({ repoPath, stashIndex, filePath }: { repoPath: string; stashIndex: number; filePath: string }) {
13
- const { data, isLoading } = useStashFileDiff(repoPath, stashIndex, filePath);
14
- const { resolvedTheme } = useTheme();
15
- const storageKey = 'git-web:diff-view-split';
16
-
17
- const [splitView, setSplitView] = useState(() => {
18
- if (typeof window === 'undefined') return true;
19
- try {
20
- const stored = localStorage.getItem(storageKey);
21
- return stored !== null ? JSON.parse(stored) : true;
22
- } catch {
23
- return true;
24
- }
25
- });
26
-
27
- const [renderAnyway, setRenderAnyway] = useState(false);
28
-
29
- // Reset renderAnyway when file or stash changes
30
- useEffect(() => {
31
- setRenderAnyway(false);
32
- }, [filePath, stashIndex]);
33
-
34
- if (isLoading) {
35
- return <div className="flex items-center justify-center p-8 h-full"><span className="loading loading-spinner text-base-content/50"></span></div>;
36
- }
37
-
38
- if (!data) {
39
- return (
40
- <div className="flex items-center justify-center h-full opacity-50">
41
- No diff available
42
- </div>
43
- );
44
- }
45
-
46
- if (isImageFile(filePath)) {
47
- return (
48
- <div className="flex flex-col h-full bg-base-100">
49
- <div className="flex items-center justify-between px-4 h-[57px] border-b border-base-300 shrink-0 bg-base-100">
50
- <span className="text-sm font-mono truncate max-w-[70%]" title={filePath}>{filePath}</span>
51
- </div>
52
- <div className="flex-1 overflow-auto">
53
- <ImageDiffView filePath={filePath} imageDiff={data.imageDiff} />
54
- </div>
55
- </div>
56
- );
57
- }
58
-
59
- const isBinary = isFileBinary(filePath, data.left, data.right);
60
-
61
- if (isBinary) {
62
- return (
63
- <div className="flex flex-col h-full bg-base-100">
64
- <div className="flex items-center justify-between px-4 h-[57px] border-b border-base-300 shrink-0 bg-base-100">
65
- <span className="text-sm font-mono truncate max-w-[70%]" title={filePath}>{filePath}</span>
66
- </div>
67
- <div className="flex-1 flex items-center justify-center opacity-50">
68
- Binary file - diff not available
69
- </div>
70
- </div>
71
- );
72
- }
73
-
74
- // Large file protection
75
- const MAX_DIFF_SIZE = 100 * 1024; // 100KB
76
- const MAX_DIFF_LINES = 3000;
77
-
78
- const diffContent = data.diff || '';
79
- const contentSize = diffContent.length;
80
- const lineCount = getChangedLineCountFromDiff(diffContent);
81
-
82
- const isLargeDiff = (contentSize > MAX_DIFF_SIZE || lineCount > MAX_DIFF_LINES);
83
-
84
- return (
85
- <div className="flex flex-col h-full bg-base-100">
86
- <div className="flex items-center justify-between px-4 h-[57px] border-b border-base-300 shrink-0 bg-base-100">
87
- <span className="text-sm font-mono truncate max-w-[70%]" title={filePath}>{filePath}</span>
88
- <div className="flex items-center gap-2">
89
- <label htmlFor="split-view-stash" className="text-[10px] uppercase tracking-wider font-bold cursor-pointer opacity-70">Split View</label>
90
- <input
91
- type="checkbox"
92
- id="split-view-stash"
93
- checked={splitView}
94
- onChange={(e) => {
95
- setSplitView(e.target.checked);
96
- try {
97
- localStorage.setItem(storageKey, JSON.stringify(e.target.checked));
98
- } catch { }
99
- }}
100
- className="toggle toggle-xs toggle-primary"
101
- />
102
- </div>
103
- </div>
104
- <div className="flex-1 overflow-auto diff-viewer-wrapper">
105
- {isLargeDiff && !renderAnyway ? (
106
- <div className="flex flex-col items-center justify-center h-full gap-4 text-center p-4">
107
- <i className="iconoir-warning-triangle text-[32px] text-warning" aria-hidden="true" />
108
- <div className="space-y-2">
109
- <h3 className="font-bold text-lg">Large Diff Detected</h3>
110
- <p className="opacity-70">
111
- This diff is large ({Math.round(contentSize / 1024)}KB, ~{lineCount} changed lines) and may freeze your browser if rendered.
112
- </p>
113
- </div>
114
- <button className="btn btn-outline" onClick={() => setRenderAnyway(true)}>
115
- Show Diff Anyway
116
- </button>
117
- </div>
118
- ) : (
119
- <GroupedDiffViewer
120
- oldValue={data.left || ''}
121
- newValue={data.right || ''}
122
- splitView={splitView}
123
- useDarkTheme={resolvedTheme === 'dark'}
124
- />
125
- )}
126
- </div>
127
- </div>
128
- );
129
- }
130
-
131
- function StashesContent() {
132
- const searchParams = useSearchParams();
133
- const repoPath = searchParams.get('path');
134
- const { data: stashes, isLoading, isError, error, refetch } = useGitStashes(repoPath);
135
- const action = useGitAction();
136
- const [selectedStashIndex, setSelectedStashIndex] = useState<number | null>(null);
137
- const [selectedFile, setSelectedFile] = useState<string | null>(null);
138
- const [expandedStashes, setExpandedStashes] = useState<Set<number>>(new Set());
139
-
140
- const { data: stashFiles, isLoading: filesLoading } = useStashFiles(repoPath, selectedStashIndex);
141
-
142
- useWorkspaceTitle(repoPath, 'Stashes');
143
-
144
- if (!repoPath) {
145
- return <div className="p-8">No repository path specified.</div>;
146
- }
147
-
148
- if (isLoading) {
149
- return (
150
- <div className="flex items-center justify-center h-64">
151
- <span className="loading loading-spinner text-base-content/50"></span>
152
- </div>
153
- );
154
- }
155
-
156
- if (isError) {
157
- return (
158
- <div className="flex items-center justify-center h-64 flex-col gap-4">
159
- <p className="text-error font-medium">Error Loading Stashes</p>
160
- <p className="text-sm opacity-70">{(error as Error)?.message || 'An unknown error occurred'}</p>
161
- <button onClick={() => refetch()} className="btn btn-outline btn-sm">
162
- <i className="iconoir-refresh-circle text-[16px] mr-1" aria-hidden="true" />
163
- Try Again
164
- </button>
165
- </div>
166
- );
167
- }
168
-
169
- const handleApply = async (index: number) => {
170
- await action.mutateAsync({ repoPath, action: 'stash-apply', data: { index } });
171
- refetch();
172
- };
173
-
174
- const handlePop = async (index: number) => {
175
- await action.mutateAsync({ repoPath, action: 'stash-pop', data: { index } });
176
- setSelectedStashIndex(null);
177
- setSelectedFile(null);
178
- refetch();
179
- };
180
-
181
- const handleDrop = async (index: number) => {
182
- await action.mutateAsync({ repoPath, action: 'stash-drop', data: { index } });
183
- if (selectedStashIndex === index) {
184
- setSelectedStashIndex(null);
185
- setSelectedFile(null);
186
- }
187
- refetch();
188
- };
189
-
190
- const toggleStashExpanded = (index: number) => {
191
- const newExpanded = new Set(expandedStashes);
192
- if (newExpanded.has(index)) {
193
- newExpanded.delete(index);
194
- } else {
195
- newExpanded.add(index);
196
- }
197
- setExpandedStashes(newExpanded);
198
- setSelectedStashIndex(index);
199
- setSelectedFile(null);
200
- };
201
-
202
- const formatDate = (dateString: string) => {
203
- try {
204
- const date = new Date(dateString);
205
- const now = new Date();
206
- const diffMs = now.getTime() - date.getTime();
207
- const diffMins = Math.floor(diffMs / 60000);
208
- const diffHours = Math.floor(diffMs / 3600000);
209
- const diffDays = Math.floor(diffMs / 86400000);
210
-
211
- if (diffMins < 1) return 'just now';
212
- if (diffMins < 60) return `${diffMins} minute${diffMins === 1 ? '' : 's'} ago`;
213
- if (diffHours < 24) return `${diffHours} hour${diffHours === 1 ? '' : 's'} ago`;
214
- if (diffDays < 30) return `${diffDays} day${diffDays === 1 ? '' : 's'} ago`;
215
- return date.toLocaleDateString();
216
- } catch {
217
- return dateString;
218
- }
219
- };
220
-
221
- const getStatusColor = (status: string) => {
222
- switch (status) {
223
- case 'A': return 'text-success';
224
- case 'D': return 'text-error';
225
- case 'M': return 'text-warning';
226
- default: return 'opacity-50';
227
- }
228
- };
229
-
230
- const getStatusLabel = (status: string) => {
231
- switch (status) {
232
- case 'A': return 'Added';
233
- case 'D': return 'Deleted';
234
- case 'M': return 'Modified';
235
- default: return status;
236
- }
237
- };
238
-
239
- return (
240
- <div className="flex h-full overflow-hidden">
241
- {/* Left Panel: Stash List */}
242
- <div className="w-64 border-r border-base-300 flex flex-col bg-base-200/30">
243
- <div className="h-[57px] px-4 border-b border-base-300 flex items-center justify-between bg-base-100">
244
- <h1 className="font-bold text-lg">Stashes</h1>
245
- <button className="btn btn-ghost btn-sm btn-square" onClick={() => refetch()} disabled={action.isPending} title="Refresh">
246
- {action.isPending ? <span className="loading loading-spinner loading-xs"></span> : <i className="iconoir-refresh-circle text-[16px]" aria-hidden="true" />}
247
- </button>
248
- </div>
249
-
250
- <div className="flex-1 overflow-y-auto overflow-x-hidden">
251
- {!stashes || stashes.length === 0 ? (
252
- <div className="flex-1 flex flex-col items-center justify-center opacity-50 h-64">
253
- <div className="p-8 rounded-full bg-base-200 mb-4 text-4xl">
254
- 📦
255
- </div>
256
- <p className="text-sm font-bold">No stashes</p>
257
- <p className="text-xs mt-1">Stash changes from the Changes page</p>
258
- </div>
259
- ) : (
260
- <div className="p-2">
261
- {stashes.map((stash) => {
262
- const isExpanded = expandedStashes.has(stash.index);
263
- const isSelected = selectedStashIndex === stash.index;
264
-
265
- return (
266
- <div key={stash.hash} className="mb-1">
267
- <div
268
- className={cn(
269
- "p-2 rounded-md cursor-pointer transition-colors",
270
- isSelected ? "bg-base-200" : "hover:bg-base-200/50"
271
- )}
272
- onClick={() => toggleStashExpanded(stash.index)}
273
- >
274
- <div className="flex flex-col gap-1">
275
- {/* Row 1: ID and Buttons */}
276
- <div className="flex items-center justify-between">
277
- <div className="flex items-center gap-2">
278
- <div className="opacity-50 text-[10px]">
279
- {isExpanded ? '▼' : '▶'}
280
- </div>
281
- <span className="text-[10px] font-mono bg-base-300 px-1 py-0.5 rounded opacity-70">
282
- stash@{'{' + stash.index + '}'}
283
- </span>
284
- </div>
285
- <div className="flex items-center gap-1 shrink-0">
286
- <div className="tooltip tooltip-left before:whitespace-normal before:max-w-[120px]" data-tip="Apply stash (keep)">
287
- <button
288
- className="btn btn-ghost btn-xs btn-square"
289
- onClick={(e) => { e.stopPropagation(); handleApply(stash.index); }}
290
- disabled={action.isPending}
291
- >
292
- <i className="iconoir-play text-[16px]" aria-hidden="true" />
293
- </button>
294
- </div>
295
- <div className="tooltip tooltip-left before:whitespace-normal before:max-w-[120px]" data-tip="Pop stash (apply and delete)">
296
- <button
297
- className="btn btn-ghost btn-xs btn-square"
298
- onClick={(e) => { e.stopPropagation(); handlePop(stash.index); }}
299
- disabled={action.isPending}
300
- >
301
- <i className="iconoir-u-turn-arrow-left text-[16px]" aria-hidden="true" />
302
- </button>
303
- </div>
304
- <div className="tooltip tooltip-left before:whitespace-normal before:max-w-[120px]" data-tip="Delete stash">
305
- <button
306
- className="btn btn-ghost btn-xs btn-square text-error hover:bg-error/10"
307
- onClick={(e) => { e.stopPropagation(); handleDrop(stash.index); }}
308
- disabled={action.isPending}
309
- >
310
- <i className="iconoir-trash text-[16px]" aria-hidden="true" />
311
- </button>
312
- </div>
313
- </div>
314
- </div>
315
-
316
- {/* Row 2: Message */}
317
- <div className="tooltip tooltip-bottom before:whitespace-normal before:max-w-[200px] w-full block text-left" data-tip={stash.message}>
318
- <p className="text-xs font-bold truncate opacity-90 w-full">
319
- {stash.message}
320
- </p>
321
- </div>
322
-
323
- {/* Row 3: Metadata */}
324
- <div className="text-[10px] opacity-50">
325
- {formatDate(stash.date)}
326
- </div>
327
- </div>
328
- </div>
329
-
330
- {/* Files list when expanded */}
331
- {isExpanded && isSelected && (
332
- <div className="ml-6 mt-1 space-y-0.5">
333
- {filesLoading ? (
334
- <div className="flex items-center gap-2 px-2 py-1 text-xs opacity-50">
335
- <span className="loading loading-spinner loading-xs"></span>
336
- Loading files...
337
- </div>
338
- ) : stashFiles && stashFiles.length > 0 ? (
339
- stashFiles.map((file) => (
340
- <div
341
- key={file.path}
342
- className={cn(
343
- "flex items-center gap-2 px-2 py-1 rounded cursor-pointer transition-colors text-xs",
344
- selectedFile === file.path ? "bg-primary/10 text-primary font-bold" : "hover:bg-base-200/50"
345
- )}
346
- onClick={(e) => { e.stopPropagation(); setSelectedFile(file.path); }}
347
- >
348
- <i className="iconoir-page text-[14px] opacity-50" aria-hidden="true" />
349
- <span className="font-mono truncate flex-1" title={file.path}>{file.path}</span>
350
- <span className={cn("text-[10px] uppercase font-bold", getStatusColor(file.status))} title={getStatusLabel(file.status)}>
351
- {file.status}
352
- </span>
353
- </div>
354
- ))
355
- ) : (
356
- <div className="px-2 py-1 text-xs opacity-50 italic">
357
- No files in stash
358
- </div>
359
- )}
360
- </div>
361
- )}
362
- </div>
363
- );
364
- })}
365
- </div>
366
- )}
367
- </div>
368
- </div>
369
-
370
- {/* Right Panel: Diff View */}
371
- <div className="flex-1 flex flex-col bg-base-100 overflow-hidden">
372
- {selectedStashIndex !== null && selectedFile ? (
373
- <StashDiffView repoPath={repoPath} stashIndex={selectedStashIndex} filePath={selectedFile} />
374
- ) : (
375
- <div className="flex-1 flex flex-col items-center justify-center opacity-50">
376
- <div className="p-8 rounded-full bg-base-200 mb-4 text-4xl">
377
- 📦
378
- </div>
379
- <p className="text-sm font-bold">
380
- {selectedStashIndex !== null ? 'Select a file to view changes' : 'Select a stash to view files'}
381
- </p>
382
- </div>
383
- )}
384
- </div>
385
- </div>
386
- );
387
- }
388
-
389
- export default function WorkspaceStashesPage() {
390
- return (
391
- <Suspense fallback={<div className="flex items-center justify-center h-full"><span className="loading loading-spinner"></span></div>}>
392
- <StashesContent />
393
- </Suspense>
394
- );
395
- }
@@ -1,178 +0,0 @@
1
- 'use client';
2
-
3
- import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
4
- import { useRouter } from 'next/navigation';
5
- import { Repository } from '@/lib/types';
6
- import { useRepositories } from '@/hooks/use-git';
7
- import { cn, getRepositoryDisplayName } from '@/lib/utils';
8
- import { useEscapeDismiss } from '@/hooks/use-escape-dismiss';
9
-
10
- function sortByLastOpenedDesc(a: Repository, b: Repository) {
11
- return new Date(b.lastOpenedAt || 0).getTime() - new Date(a.lastOpenedAt || 0).getTime();
12
- }
13
-
14
- export function CommandPalette() {
15
- const router = useRouter();
16
- const { data: repositories } = useRepositories();
17
- const [isOpen, setIsOpen] = useState(false);
18
- const [query, setQuery] = useState('');
19
- const [selectedIndex, setSelectedIndex] = useState(0);
20
- const inputRef = useRef<HTMLInputElement>(null);
21
-
22
- const recentRepositories = useMemo(
23
- () =>
24
- (repositories || [])
25
- .filter((repo) => Boolean(repo.lastOpenedAt))
26
- .sort(sortByLastOpenedDesc)
27
- .slice(0, 5),
28
- [repositories]
29
- );
30
-
31
- const filteredRepositories = useMemo(() => {
32
- const keyword = query.trim().toLowerCase();
33
- if (!keyword) return recentRepositories;
34
-
35
- return recentRepositories.filter((repo) => {
36
- const name = getRepositoryDisplayName(repo).toLowerCase();
37
- const repoPath = repo.path.toLowerCase();
38
- return name.includes(keyword) || repoPath.includes(keyword);
39
- });
40
- }, [query, recentRepositories]);
41
-
42
- const closePalette = useCallback(() => {
43
- setIsOpen(false);
44
- setQuery('');
45
- setSelectedIndex(0);
46
- }, []);
47
-
48
- const activeIndex =
49
- filteredRepositories.length === 0
50
- ? -1
51
- : Math.min(selectedIndex, filteredRepositories.length - 1);
52
-
53
- const openRepository = useCallback(
54
- (repoPath: string) => {
55
- closePalette();
56
- router.push(`/workspace?path=${encodeURIComponent(repoPath)}`);
57
- },
58
- [closePalette, router]
59
- );
60
-
61
- useEffect(() => {
62
- const onGlobalKeyDown = (event: KeyboardEvent) => {
63
- const isCommandOpenShortcut = (event.metaKey || event.ctrlKey) && event.key.toLowerCase() === 'k';
64
- if (isCommandOpenShortcut) {
65
- event.preventDefault();
66
- setIsOpen(true);
67
- }
68
- };
69
-
70
- window.addEventListener('keydown', onGlobalKeyDown);
71
- return () => window.removeEventListener('keydown', onGlobalKeyDown);
72
- }, []);
73
-
74
- useEscapeDismiss(isOpen, closePalette);
75
-
76
- useEffect(() => {
77
- if (!isOpen) return;
78
- requestAnimationFrame(() => inputRef.current?.focus());
79
- }, [isOpen]);
80
-
81
- if (!isOpen) return null;
82
-
83
- return (
84
- <div
85
- className="fixed inset-0 z-50 bg-black/40 px-4 py-16"
86
- onMouseDown={closePalette}
87
- role="dialog"
88
- aria-modal="true"
89
- aria-label="Command palette"
90
- >
91
- <div
92
- className="mx-auto w-full max-w-2xl overflow-hidden rounded-xl border border-base-300 bg-base-100 shadow-2xl"
93
- onMouseDown={(event) => event.stopPropagation()}
94
- >
95
- <div className="border-b border-base-300 p-3">
96
- <input
97
- ref={inputRef}
98
- type="text"
99
- value={query}
100
- onChange={(event) => {
101
- setQuery(event.target.value);
102
- setSelectedIndex(0);
103
- }}
104
- onKeyDown={(event) => {
105
- if (event.key === 'ArrowDown') {
106
- event.preventDefault();
107
- if (filteredRepositories.length > 0) {
108
- setSelectedIndex((current) => (current + 1) % filteredRepositories.length);
109
- }
110
- return;
111
- }
112
-
113
- if (event.key === 'ArrowUp') {
114
- event.preventDefault();
115
- if (filteredRepositories.length > 0) {
116
- setSelectedIndex((current) =>
117
- current <= 0 ? filteredRepositories.length - 1 : current - 1
118
- );
119
- }
120
- return;
121
- }
122
-
123
- if (event.key === 'Enter') {
124
- event.preventDefault();
125
- const selectedRepo = activeIndex >= 0 ? filteredRepositories[activeIndex] : null;
126
- if (selectedRepo) {
127
- openRepository(selectedRepo.path);
128
- }
129
- }
130
- }}
131
- placeholder="Type a command..."
132
- className="input input-ghost w-full text-base focus:outline-none"
133
- />
134
- </div>
135
-
136
- <div className="max-h-80 overflow-y-auto py-2">
137
- <div className="px-4 pb-2 text-xs font-semibold uppercase tracking-wider opacity-60">
138
- Recent repositories
139
- </div>
140
-
141
- {recentRepositories.length === 0 && (
142
- <div className="px-4 py-6 text-sm opacity-60">No recently opened repositories yet.</div>
143
- )}
144
-
145
- {recentRepositories.length > 0 && filteredRepositories.length === 0 && (
146
- <div className="px-4 py-6 text-sm opacity-60">No matching repositories.</div>
147
- )}
148
-
149
- {filteredRepositories.map((repo, index) => {
150
- const repoDisplayName = getRepositoryDisplayName(repo);
151
- return (
152
- <button
153
- key={repo.path}
154
- type="button"
155
- className={cn(
156
- 'flex w-full items-center justify-between gap-4 px-4 py-3 text-left',
157
- activeIndex === index ? 'bg-base-200' : 'hover:bg-base-200/70'
158
- )}
159
- onMouseEnter={() => setSelectedIndex(index)}
160
- onClick={() => openRepository(repo.path)}
161
- >
162
- <div className="min-w-0">
163
- <div className="truncate text-sm font-medium">{repoDisplayName}</div>
164
- <div className="truncate text-xs opacity-65">{repo.path}</div>
165
- </div>
166
- <span className="text-xs opacity-60">Open</span>
167
- </button>
168
- );
169
- })}
170
- </div>
171
-
172
- <div className="border-t border-base-300 px-4 py-2 text-xs opacity-60">
173
- Use ↑ ↓ to navigate, Enter to open, Esc to close.
174
- </div>
175
- </div>
176
- </div>
177
- );
178
- }