@alivault/pico 0.12.0 → 0.13.0

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 (311) hide show
  1. package/.output/nitro.json +1 -1
  2. package/.output/public/assets/addon-fit-CMMXLNsg.js +1 -0
  3. package/.output/public/assets/{index-DQLwecED.js → index-CW1T9dk-.js} +11 -10
  4. package/.output/public/assets/routes-2LboeUx4.js +2194 -0
  5. package/.output/public/assets/styles-B1ITYh-r.css +2 -0
  6. package/.output/public/assets/xterm-CCMwI7sy.js +36 -0
  7. package/.output/server/_libs/lucide-react.mjs +20 -1
  8. package/.output/server/_libs/xterm__addon-fit.mjs +44 -0
  9. package/.output/server/_libs/xterm__xterm.mjs +10234 -0
  10. package/.output/server/_ssr/{router-D36lUq7O.mjs → router-l__gf5Y7.mjs} +703 -170
  11. package/.output/server/_ssr/{routes-9nZumcXQ.mjs → routes-CuWg5WTI.mjs} +159 -18
  12. package/.output/server/_ssr/{routes-C8qT6Oju.mjs → routes-DySm_W8N.mjs} +1659 -561
  13. package/.output/server/_ssr/ssr.mjs +2 -2
  14. package/.output/server/{_tanstack-start-manifest_v-Cd2ypHmK.mjs → _tanstack-start-manifest_v-CVoKKKc_.mjs} +5 -4
  15. package/.output/server/index.mjs +583 -569
  16. package/.output/server/node_modules/node-pty/LICENSE +69 -0
  17. package/.output/server/node_modules/node-pty/README.md +165 -0
  18. package/.output/server/node_modules/node-pty/binding.gyp +111 -0
  19. package/.output/server/node_modules/node-pty/build/Makefile +369 -0
  20. package/.output/server/node_modules/node-pty/build/Release/pty.node +0 -0
  21. package/.output/server/node_modules/node-pty/build/binding.Makefile +6 -0
  22. package/.output/server/node_modules/node-pty/build/pty.target.mk +168 -0
  23. package/.output/server/node_modules/node-pty/deps/.editorconfig +2 -0
  24. package/.output/server/node_modules/node-pty/deps/winpty/.drone.yml +17 -0
  25. package/.output/server/node_modules/node-pty/deps/winpty/.gitattributes +19 -0
  26. package/.output/server/node_modules/node-pty/deps/winpty/LICENSE +21 -0
  27. package/.output/server/node_modules/node-pty/deps/winpty/Makefile +166 -0
  28. package/.output/server/node_modules/node-pty/deps/winpty/README.md +151 -0
  29. package/.output/server/node_modules/node-pty/deps/winpty/RELEASES.md +280 -0
  30. package/.output/server/node_modules/node-pty/deps/winpty/VERSION.txt +1 -0
  31. package/.output/server/node_modules/node-pty/deps/winpty/configure +167 -0
  32. package/.output/server/node_modules/node-pty/deps/winpty/misc/BufferResizeTests.cc +90 -0
  33. package/.output/server/node_modules/node-pty/deps/winpty/misc/ChangeScreenBuffer.cc +53 -0
  34. package/.output/server/node_modules/node-pty/deps/winpty/misc/ClearConsole.cc +72 -0
  35. package/.output/server/node_modules/node-pty/deps/winpty/misc/ConinMode.cc +117 -0
  36. package/.output/server/node_modules/node-pty/deps/winpty/misc/ConinMode.ps1 +116 -0
  37. package/.output/server/node_modules/node-pty/deps/winpty/misc/ConoutMode.cc +113 -0
  38. package/.output/server/node_modules/node-pty/deps/winpty/misc/DebugClient.py +42 -0
  39. package/.output/server/node_modules/node-pty/deps/winpty/misc/DebugServer.py +63 -0
  40. package/.output/server/node_modules/node-pty/deps/winpty/misc/DumpLines.py +5 -0
  41. package/.output/server/node_modules/node-pty/deps/winpty/misc/EnableExtendedFlags.txt +46 -0
  42. package/.output/server/node_modules/node-pty/deps/winpty/misc/Font-Report-June2016/CP437-Consolas.txt +528 -0
  43. package/.output/server/node_modules/node-pty/deps/winpty/misc/Font-Report-June2016/CP437-Lucida.txt +633 -0
  44. package/.output/server/node_modules/node-pty/deps/winpty/misc/Font-Report-June2016/CP932.txt +630 -0
  45. package/.output/server/node_modules/node-pty/deps/winpty/misc/Font-Report-June2016/CP936.txt +630 -0
  46. package/.output/server/node_modules/node-pty/deps/winpty/misc/Font-Report-June2016/CP949.txt +630 -0
  47. package/.output/server/node_modules/node-pty/deps/winpty/misc/Font-Report-June2016/CP950.txt +630 -0
  48. package/.output/server/node_modules/node-pty/deps/winpty/misc/Font-Report-June2016/MinimumWindowWidths.txt +16 -0
  49. package/.output/server/node_modules/node-pty/deps/winpty/misc/Font-Report-June2016/Results.txt +4 -0
  50. package/.output/server/node_modules/node-pty/deps/winpty/misc/Font-Report-June2016/Windows10SetFontBugginess.txt +144 -0
  51. package/.output/server/node_modules/node-pty/deps/winpty/misc/FontSurvey.cc +100 -0
  52. package/.output/server/node_modules/node-pty/deps/winpty/misc/FormatChar.h +21 -0
  53. package/.output/server/node_modules/node-pty/deps/winpty/misc/FreezePerfTest.cc +62 -0
  54. package/.output/server/node_modules/node-pty/deps/winpty/misc/GetCh.cc +20 -0
  55. package/.output/server/node_modules/node-pty/deps/winpty/misc/GetConsolePos.cc +41 -0
  56. package/.output/server/node_modules/node-pty/deps/winpty/misc/GetFont.cc +261 -0
  57. package/.output/server/node_modules/node-pty/deps/winpty/misc/IdentifyConsoleWindow.ps1 +51 -0
  58. package/.output/server/node_modules/node-pty/deps/winpty/misc/IsNewConsole.cc +87 -0
  59. package/.output/server/node_modules/node-pty/deps/winpty/misc/MouseInputNotes.txt +90 -0
  60. package/.output/server/node_modules/node-pty/deps/winpty/misc/MoveConsoleWindow.cc +34 -0
  61. package/.output/server/node_modules/node-pty/deps/winpty/misc/Notes.txt +219 -0
  62. package/.output/server/node_modules/node-pty/deps/winpty/misc/OSVersion.cc +27 -0
  63. package/.output/server/node_modules/node-pty/deps/winpty/misc/ScreenBufferFreezeInactive.cc +101 -0
  64. package/.output/server/node_modules/node-pty/deps/winpty/misc/ScreenBufferTest.cc +671 -0
  65. package/.output/server/node_modules/node-pty/deps/winpty/misc/ScreenBufferTest2.cc +151 -0
  66. package/.output/server/node_modules/node-pty/deps/winpty/misc/SelectAllTest.cc +45 -0
  67. package/.output/server/node_modules/node-pty/deps/winpty/misc/SetBufferSize.cc +32 -0
  68. package/.output/server/node_modules/node-pty/deps/winpty/misc/SetCursorPos.cc +10 -0
  69. package/.output/server/node_modules/node-pty/deps/winpty/misc/SetFont.cc +145 -0
  70. package/.output/server/node_modules/node-pty/deps/winpty/misc/SetWindowRect.cc +36 -0
  71. package/.output/server/node_modules/node-pty/deps/winpty/misc/ShowArgv.cc +12 -0
  72. package/.output/server/node_modules/node-pty/deps/winpty/misc/ShowConsoleInput.cc +40 -0
  73. package/.output/server/node_modules/node-pty/deps/winpty/misc/Spew.py +5 -0
  74. package/.output/server/node_modules/node-pty/deps/winpty/misc/TestUtil.cc +172 -0
  75. package/.output/server/node_modules/node-pty/deps/winpty/misc/UnicodeDoubleWidthTest.cc +102 -0
  76. package/.output/server/node_modules/node-pty/deps/winpty/misc/UnicodeWideTest1.cc +246 -0
  77. package/.output/server/node_modules/node-pty/deps/winpty/misc/UnicodeWideTest2.cc +130 -0
  78. package/.output/server/node_modules/node-pty/deps/winpty/misc/UnixEcho.cc +89 -0
  79. package/.output/server/node_modules/node-pty/deps/winpty/misc/Utf16Echo.cc +46 -0
  80. package/.output/server/node_modules/node-pty/deps/winpty/misc/VeryLargeRead.cc +122 -0
  81. package/.output/server/node_modules/node-pty/deps/winpty/misc/VkEscapeTest.cc +56 -0
  82. package/.output/server/node_modules/node-pty/deps/winpty/misc/Win10ResizeWhileFrozen.cc +52 -0
  83. package/.output/server/node_modules/node-pty/deps/winpty/misc/Win10WrapTest1.cc +57 -0
  84. package/.output/server/node_modules/node-pty/deps/winpty/misc/Win10WrapTest2.cc +30 -0
  85. package/.output/server/node_modules/node-pty/deps/winpty/misc/Win32Echo1.cc +26 -0
  86. package/.output/server/node_modules/node-pty/deps/winpty/misc/Win32Echo2.cc +19 -0
  87. package/.output/server/node_modules/node-pty/deps/winpty/misc/Win32Test1.cc +46 -0
  88. package/.output/server/node_modules/node-pty/deps/winpty/misc/Win32Test2.cc +70 -0
  89. package/.output/server/node_modules/node-pty/deps/winpty/misc/Win32Test3.cc +78 -0
  90. package/.output/server/node_modules/node-pty/deps/winpty/misc/Win32Write1.cc +44 -0
  91. package/.output/server/node_modules/node-pty/deps/winpty/misc/WindowsBugCrashReader.cc +27 -0
  92. package/.output/server/node_modules/node-pty/deps/winpty/misc/WriteConsole.cc +106 -0
  93. package/.output/server/node_modules/node-pty/deps/winpty/misc/build32.sh +9 -0
  94. package/.output/server/node_modules/node-pty/deps/winpty/misc/build64.sh +9 -0
  95. package/.output/server/node_modules/node-pty/deps/winpty/misc/color-test.sh +212 -0
  96. package/.output/server/node_modules/node-pty/deps/winpty/misc/font-notes.txt +300 -0
  97. package/.output/server/node_modules/node-pty/deps/winpty/misc/winbug-15048.cc +201 -0
  98. package/.output/server/node_modules/node-pty/deps/winpty/ship/build-pty4j-libpty.bat +36 -0
  99. package/.output/server/node_modules/node-pty/deps/winpty/ship/common_ship.py +53 -0
  100. package/.output/server/node_modules/node-pty/deps/winpty/ship/make_msvc_package.py +165 -0
  101. package/.output/server/node_modules/node-pty/deps/winpty/ship/ship.py +108 -0
  102. package/.output/server/node_modules/node-pty/deps/winpty/src/agent/Agent.cc +613 -0
  103. package/.output/server/node_modules/node-pty/deps/winpty/src/agent/Agent.h +103 -0
  104. package/.output/server/node_modules/node-pty/deps/winpty/src/agent/AgentCreateDesktop.cc +84 -0
  105. package/.output/server/node_modules/node-pty/deps/winpty/src/agent/AgentCreateDesktop.h +28 -0
  106. package/.output/server/node_modules/node-pty/deps/winpty/src/agent/ConsoleFont.cc +632 -0
  107. package/.output/server/node_modules/node-pty/deps/winpty/src/agent/ConsoleFont.h +28 -0
  108. package/.output/server/node_modules/node-pty/deps/winpty/src/agent/ConsoleInput.cc +852 -0
  109. package/.output/server/node_modules/node-pty/deps/winpty/src/agent/ConsoleInput.h +109 -0
  110. package/.output/server/node_modules/node-pty/deps/winpty/src/agent/ConsoleInputReencoding.cc +121 -0
  111. package/.output/server/node_modules/node-pty/deps/winpty/src/agent/ConsoleInputReencoding.h +36 -0
  112. package/.output/server/node_modules/node-pty/deps/winpty/src/agent/ConsoleLine.cc +152 -0
  113. package/.output/server/node_modules/node-pty/deps/winpty/src/agent/ConsoleLine.h +41 -0
  114. package/.output/server/node_modules/node-pty/deps/winpty/src/agent/Coord.h +87 -0
  115. package/.output/server/node_modules/node-pty/deps/winpty/src/agent/DebugShowInput.cc +239 -0
  116. package/.output/server/node_modules/node-pty/deps/winpty/src/agent/DebugShowInput.h +32 -0
  117. package/.output/server/node_modules/node-pty/deps/winpty/src/agent/DefaultInputMap.cc +422 -0
  118. package/.output/server/node_modules/node-pty/deps/winpty/src/agent/DefaultInputMap.h +28 -0
  119. package/.output/server/node_modules/node-pty/deps/winpty/src/agent/DsrSender.h +30 -0
  120. package/.output/server/node_modules/node-pty/deps/winpty/src/agent/EventLoop.cc +99 -0
  121. package/.output/server/node_modules/node-pty/deps/winpty/src/agent/EventLoop.h +47 -0
  122. package/.output/server/node_modules/node-pty/deps/winpty/src/agent/InputMap.cc +246 -0
  123. package/.output/server/node_modules/node-pty/deps/winpty/src/agent/InputMap.h +114 -0
  124. package/.output/server/node_modules/node-pty/deps/winpty/src/agent/LargeConsoleRead.cc +71 -0
  125. package/.output/server/node_modules/node-pty/deps/winpty/src/agent/LargeConsoleRead.h +68 -0
  126. package/.output/server/node_modules/node-pty/deps/winpty/src/agent/NamedPipe.cc +378 -0
  127. package/.output/server/node_modules/node-pty/deps/winpty/src/agent/NamedPipe.h +125 -0
  128. package/.output/server/node_modules/node-pty/deps/winpty/src/agent/Scraper.cc +699 -0
  129. package/.output/server/node_modules/node-pty/deps/winpty/src/agent/Scraper.h +103 -0
  130. package/.output/server/node_modules/node-pty/deps/winpty/src/agent/SimplePool.h +75 -0
  131. package/.output/server/node_modules/node-pty/deps/winpty/src/agent/SmallRect.h +143 -0
  132. package/.output/server/node_modules/node-pty/deps/winpty/src/agent/Terminal.cc +535 -0
  133. package/.output/server/node_modules/node-pty/deps/winpty/src/agent/Terminal.h +69 -0
  134. package/.output/server/node_modules/node-pty/deps/winpty/src/agent/UnicodeEncoding.h +157 -0
  135. package/.output/server/node_modules/node-pty/deps/winpty/src/agent/UnicodeEncodingTest.cc +189 -0
  136. package/.output/server/node_modules/node-pty/deps/winpty/src/agent/Win32Console.cc +107 -0
  137. package/.output/server/node_modules/node-pty/deps/winpty/src/agent/Win32Console.h +67 -0
  138. package/.output/server/node_modules/node-pty/deps/winpty/src/agent/Win32ConsoleBuffer.cc +193 -0
  139. package/.output/server/node_modules/node-pty/deps/winpty/src/agent/Win32ConsoleBuffer.h +99 -0
  140. package/.output/server/node_modules/node-pty/deps/winpty/src/agent/main.cc +114 -0
  141. package/.output/server/node_modules/node-pty/deps/winpty/src/agent/subdir.mk +61 -0
  142. package/.output/server/node_modules/node-pty/deps/winpty/src/configurations.gypi +60 -0
  143. package/.output/server/node_modules/node-pty/deps/winpty/src/debugserver/DebugServer.cc +117 -0
  144. package/.output/server/node_modules/node-pty/deps/winpty/src/debugserver/subdir.mk +41 -0
  145. package/.output/server/node_modules/node-pty/deps/winpty/src/include/winpty.h +242 -0
  146. package/.output/server/node_modules/node-pty/deps/winpty/src/include/winpty_constants.h +131 -0
  147. package/.output/server/node_modules/node-pty/deps/winpty/src/libwinpty/AgentLocation.cc +75 -0
  148. package/.output/server/node_modules/node-pty/deps/winpty/src/libwinpty/AgentLocation.h +28 -0
  149. package/.output/server/node_modules/node-pty/deps/winpty/src/libwinpty/LibWinptyException.h +54 -0
  150. package/.output/server/node_modules/node-pty/deps/winpty/src/libwinpty/WinptyInternal.h +72 -0
  151. package/.output/server/node_modules/node-pty/deps/winpty/src/libwinpty/subdir.mk +46 -0
  152. package/.output/server/node_modules/node-pty/deps/winpty/src/libwinpty/winpty.cc +970 -0
  153. package/.output/server/node_modules/node-pty/deps/winpty/src/shared/AgentMsg.h +38 -0
  154. package/.output/server/node_modules/node-pty/deps/winpty/src/shared/BackgroundDesktop.cc +122 -0
  155. package/.output/server/node_modules/node-pty/deps/winpty/src/shared/BackgroundDesktop.h +73 -0
  156. package/.output/server/node_modules/node-pty/deps/winpty/src/shared/Buffer.cc +103 -0
  157. package/.output/server/node_modules/node-pty/deps/winpty/src/shared/Buffer.h +102 -0
  158. package/.output/server/node_modules/node-pty/deps/winpty/src/shared/DebugClient.cc +187 -0
  159. package/.output/server/node_modules/node-pty/deps/winpty/src/shared/DebugClient.h +38 -0
  160. package/.output/server/node_modules/node-pty/deps/winpty/src/shared/GenRandom.cc +138 -0
  161. package/.output/server/node_modules/node-pty/deps/winpty/src/shared/GenRandom.h +55 -0
  162. package/.output/server/node_modules/node-pty/deps/winpty/src/shared/GetCommitHash.bat +13 -0
  163. package/.output/server/node_modules/node-pty/deps/winpty/src/shared/Mutex.h +54 -0
  164. package/.output/server/node_modules/node-pty/deps/winpty/src/shared/OsModule.h +63 -0
  165. package/.output/server/node_modules/node-pty/deps/winpty/src/shared/OwnedHandle.cc +36 -0
  166. package/.output/server/node_modules/node-pty/deps/winpty/src/shared/OwnedHandle.h +45 -0
  167. package/.output/server/node_modules/node-pty/deps/winpty/src/shared/PrecompiledHeader.h +43 -0
  168. package/.output/server/node_modules/node-pty/deps/winpty/src/shared/StringBuilder.h +227 -0
  169. package/.output/server/node_modules/node-pty/deps/winpty/src/shared/StringBuilderTest.cc +114 -0
  170. package/.output/server/node_modules/node-pty/deps/winpty/src/shared/StringUtil.cc +55 -0
  171. package/.output/server/node_modules/node-pty/deps/winpty/src/shared/StringUtil.h +80 -0
  172. package/.output/server/node_modules/node-pty/deps/winpty/src/shared/TimeMeasurement.h +63 -0
  173. package/.output/server/node_modules/node-pty/deps/winpty/src/shared/UnixCtrlChars.h +45 -0
  174. package/.output/server/node_modules/node-pty/deps/winpty/src/shared/UpdateGenVersion.bat +20 -0
  175. package/.output/server/node_modules/node-pty/deps/winpty/src/shared/WindowsSecurity.cc +460 -0
  176. package/.output/server/node_modules/node-pty/deps/winpty/src/shared/WindowsSecurity.h +104 -0
  177. package/.output/server/node_modules/node-pty/deps/winpty/src/shared/WindowsVersion.cc +252 -0
  178. package/.output/server/node_modules/node-pty/deps/winpty/src/shared/WindowsVersion.h +29 -0
  179. package/.output/server/node_modules/node-pty/deps/winpty/src/shared/WinptyAssert.cc +55 -0
  180. package/.output/server/node_modules/node-pty/deps/winpty/src/shared/WinptyAssert.h +64 -0
  181. package/.output/server/node_modules/node-pty/deps/winpty/src/shared/WinptyException.cc +57 -0
  182. package/.output/server/node_modules/node-pty/deps/winpty/src/shared/WinptyException.h +43 -0
  183. package/.output/server/node_modules/node-pty/deps/winpty/src/shared/WinptyVersion.cc +42 -0
  184. package/.output/server/node_modules/node-pty/deps/winpty/src/shared/WinptyVersion.h +27 -0
  185. package/.output/server/node_modules/node-pty/deps/winpty/src/shared/winpty_snprintf.h +99 -0
  186. package/.output/server/node_modules/node-pty/deps/winpty/src/subdir.mk +5 -0
  187. package/.output/server/node_modules/node-pty/deps/winpty/src/tests/subdir.mk +28 -0
  188. package/.output/server/node_modules/node-pty/deps/winpty/src/tests/trivial_test.cc +158 -0
  189. package/.output/server/node_modules/node-pty/deps/winpty/src/unix-adapter/InputHandler.cc +114 -0
  190. package/.output/server/node_modules/node-pty/deps/winpty/src/unix-adapter/InputHandler.h +56 -0
  191. package/.output/server/node_modules/node-pty/deps/winpty/src/unix-adapter/OutputHandler.cc +80 -0
  192. package/.output/server/node_modules/node-pty/deps/winpty/src/unix-adapter/OutputHandler.h +53 -0
  193. package/.output/server/node_modules/node-pty/deps/winpty/src/unix-adapter/Util.cc +86 -0
  194. package/.output/server/node_modules/node-pty/deps/winpty/src/unix-adapter/Util.h +31 -0
  195. package/.output/server/node_modules/node-pty/deps/winpty/src/unix-adapter/WakeupFd.cc +70 -0
  196. package/.output/server/node_modules/node-pty/deps/winpty/src/unix-adapter/WakeupFd.h +42 -0
  197. package/.output/server/node_modules/node-pty/deps/winpty/src/unix-adapter/main.cc +729 -0
  198. package/.output/server/node_modules/node-pty/deps/winpty/src/unix-adapter/subdir.mk +41 -0
  199. package/.output/server/node_modules/node-pty/deps/winpty/src/winpty.gyp +234 -0
  200. package/.output/server/node_modules/node-pty/deps/winpty/vcbuild.bat +83 -0
  201. package/.output/server/node_modules/node-pty/lib/conpty_console_list_agent.js +16 -0
  202. package/.output/server/node_modules/node-pty/lib/conpty_console_list_agent.js.map +1 -0
  203. package/.output/server/node_modules/node-pty/lib/eventEmitter2.js +47 -0
  204. package/.output/server/node_modules/node-pty/lib/eventEmitter2.js.map +1 -0
  205. package/.output/server/node_modules/node-pty/lib/eventEmitter2.test.js +30 -0
  206. package/.output/server/node_modules/node-pty/lib/eventEmitter2.test.js.map +1 -0
  207. package/.output/server/node_modules/node-pty/lib/index.js +52 -0
  208. package/.output/server/node_modules/node-pty/lib/index.js.map +1 -0
  209. package/.output/server/node_modules/node-pty/lib/interfaces.js +7 -0
  210. package/.output/server/node_modules/node-pty/lib/interfaces.js.map +1 -0
  211. package/.output/server/node_modules/node-pty/lib/shared/conout.js +11 -0
  212. package/.output/server/node_modules/node-pty/lib/shared/conout.js.map +1 -0
  213. package/.output/server/node_modules/node-pty/lib/terminal.js +190 -0
  214. package/.output/server/node_modules/node-pty/lib/terminal.js.map +1 -0
  215. package/.output/server/node_modules/node-pty/lib/terminal.test.js +139 -0
  216. package/.output/server/node_modules/node-pty/lib/terminal.test.js.map +1 -0
  217. package/.output/server/node_modules/node-pty/lib/testUtils.test.js +28 -0
  218. package/.output/server/node_modules/node-pty/lib/testUtils.test.js.map +1 -0
  219. package/.output/server/node_modules/node-pty/lib/types.js +7 -0
  220. package/.output/server/node_modules/node-pty/lib/types.js.map +1 -0
  221. package/.output/server/node_modules/node-pty/lib/unixTerminal.js +346 -0
  222. package/.output/server/node_modules/node-pty/lib/unixTerminal.js.map +1 -0
  223. package/.output/server/node_modules/node-pty/lib/unixTerminal.test.js +351 -0
  224. package/.output/server/node_modules/node-pty/lib/unixTerminal.test.js.map +1 -0
  225. package/.output/server/node_modules/node-pty/lib/utils.js +39 -0
  226. package/.output/server/node_modules/node-pty/lib/utils.js.map +1 -0
  227. package/.output/server/node_modules/node-pty/lib/windowsConoutConnection.js +125 -0
  228. package/.output/server/node_modules/node-pty/lib/windowsConoutConnection.js.map +1 -0
  229. package/.output/server/node_modules/node-pty/lib/windowsPtyAgent.js +320 -0
  230. package/.output/server/node_modules/node-pty/lib/windowsPtyAgent.js.map +1 -0
  231. package/.output/server/node_modules/node-pty/lib/windowsPtyAgent.test.js +90 -0
  232. package/.output/server/node_modules/node-pty/lib/windowsPtyAgent.test.js.map +1 -0
  233. package/.output/server/node_modules/node-pty/lib/windowsTerminal.js +199 -0
  234. package/.output/server/node_modules/node-pty/lib/windowsTerminal.js.map +1 -0
  235. package/.output/server/node_modules/node-pty/lib/windowsTerminal.test.js +219 -0
  236. package/.output/server/node_modules/node-pty/lib/windowsTerminal.test.js.map +1 -0
  237. package/.output/server/node_modules/node-pty/lib/worker/conoutSocketWorker.js +22 -0
  238. package/.output/server/node_modules/node-pty/lib/worker/conoutSocketWorker.js.map +1 -0
  239. package/.output/server/node_modules/node-pty/package.json +64 -0
  240. package/.output/server/node_modules/node-pty/prebuilds/darwin-arm64/pty.node +0 -0
  241. package/.output/server/node_modules/node-pty/prebuilds/darwin-arm64/spawn-helper +0 -0
  242. package/.output/server/node_modules/node-pty/prebuilds/darwin-x64/pty.node +0 -0
  243. package/.output/server/node_modules/node-pty/prebuilds/darwin-x64/spawn-helper +0 -0
  244. package/.output/server/node_modules/node-pty/prebuilds/win32-arm64/conpty/OpenConsole.exe +0 -0
  245. package/.output/server/node_modules/node-pty/prebuilds/win32-arm64/conpty/conpty.dll +0 -0
  246. package/.output/server/node_modules/node-pty/prebuilds/win32-arm64/conpty.node +0 -0
  247. package/.output/server/node_modules/node-pty/prebuilds/win32-arm64/conpty.pdb +0 -0
  248. package/.output/server/node_modules/node-pty/prebuilds/win32-arm64/conpty_console_list.node +0 -0
  249. package/.output/server/node_modules/node-pty/prebuilds/win32-arm64/conpty_console_list.pdb +0 -0
  250. package/.output/server/node_modules/node-pty/prebuilds/win32-arm64/pty.node +0 -0
  251. package/.output/server/node_modules/node-pty/prebuilds/win32-arm64/pty.pdb +0 -0
  252. package/.output/server/node_modules/node-pty/prebuilds/win32-arm64/winpty-agent.exe +0 -0
  253. package/.output/server/node_modules/node-pty/prebuilds/win32-arm64/winpty-agent.pdb +0 -0
  254. package/.output/server/node_modules/node-pty/prebuilds/win32-arm64/winpty.dll +0 -0
  255. package/.output/server/node_modules/node-pty/prebuilds/win32-arm64/winpty.pdb +0 -0
  256. package/.output/server/node_modules/node-pty/prebuilds/win32-x64/conpty/OpenConsole.exe +0 -0
  257. package/.output/server/node_modules/node-pty/prebuilds/win32-x64/conpty/conpty.dll +0 -0
  258. package/.output/server/node_modules/node-pty/prebuilds/win32-x64/conpty.node +0 -0
  259. package/.output/server/node_modules/node-pty/prebuilds/win32-x64/conpty.pdb +0 -0
  260. package/.output/server/node_modules/node-pty/prebuilds/win32-x64/conpty_console_list.node +0 -0
  261. package/.output/server/node_modules/node-pty/prebuilds/win32-x64/conpty_console_list.pdb +0 -0
  262. package/.output/server/node_modules/node-pty/prebuilds/win32-x64/pty.node +0 -0
  263. package/.output/server/node_modules/node-pty/prebuilds/win32-x64/pty.pdb +0 -0
  264. package/.output/server/node_modules/node-pty/prebuilds/win32-x64/winpty-agent.exe +0 -0
  265. package/.output/server/node_modules/node-pty/prebuilds/win32-x64/winpty-agent.pdb +0 -0
  266. package/.output/server/node_modules/node-pty/prebuilds/win32-x64/winpty.dll +0 -0
  267. package/.output/server/node_modules/node-pty/prebuilds/win32-x64/winpty.pdb +0 -0
  268. package/.output/server/node_modules/node-pty/scripts/gen-compile-commands.js +8 -0
  269. package/.output/server/node_modules/node-pty/scripts/increment-version.js +54 -0
  270. package/.output/server/node_modules/node-pty/scripts/post-install.js +80 -0
  271. package/.output/server/node_modules/node-pty/scripts/prebuild.js +34 -0
  272. package/.output/server/node_modules/node-pty/src/conpty_console_list_agent.ts +15 -0
  273. package/.output/server/node_modules/node-pty/src/eventEmitter2.test.ts +30 -0
  274. package/.output/server/node_modules/node-pty/src/eventEmitter2.ts +48 -0
  275. package/.output/server/node_modules/node-pty/src/index.ts +52 -0
  276. package/.output/server/node_modules/node-pty/src/interfaces.ts +130 -0
  277. package/.output/server/node_modules/node-pty/src/native.d.ts +54 -0
  278. package/.output/server/node_modules/node-pty/src/shared/conout.ts +15 -0
  279. package/.output/server/node_modules/node-pty/src/terminal.test.ts +119 -0
  280. package/.output/server/node_modules/node-pty/src/terminal.ts +211 -0
  281. package/.output/server/node_modules/node-pty/src/testUtils.test.ts +23 -0
  282. package/.output/server/node_modules/node-pty/src/tsconfig.json +22 -0
  283. package/.output/server/node_modules/node-pty/src/types.ts +15 -0
  284. package/.output/server/node_modules/node-pty/src/unix/pty.cc +799 -0
  285. package/.output/server/node_modules/node-pty/src/unix/spawn-helper.cc +23 -0
  286. package/.output/server/node_modules/node-pty/src/unixTerminal.test.ts +367 -0
  287. package/.output/server/node_modules/node-pty/src/unixTerminal.ts +388 -0
  288. package/.output/server/node_modules/node-pty/src/utils.ts +29 -0
  289. package/.output/server/node_modules/node-pty/src/win/conpty.cc +583 -0
  290. package/.output/server/node_modules/node-pty/src/win/conpty.h +41 -0
  291. package/.output/server/node_modules/node-pty/src/win/conpty_console_list.cc +44 -0
  292. package/.output/server/node_modules/node-pty/src/win/path_util.cc +95 -0
  293. package/.output/server/node_modules/node-pty/src/win/path_util.h +26 -0
  294. package/.output/server/node_modules/node-pty/src/win/winpty.cc +333 -0
  295. package/.output/server/node_modules/node-pty/src/windowsConoutConnection.ts +82 -0
  296. package/.output/server/node_modules/node-pty/src/windowsPtyAgent.test.ts +94 -0
  297. package/.output/server/node_modules/node-pty/src/windowsPtyAgent.ts +321 -0
  298. package/.output/server/node_modules/node-pty/src/windowsTerminal.test.ts +229 -0
  299. package/.output/server/node_modules/node-pty/src/windowsTerminal.ts +203 -0
  300. package/.output/server/node_modules/node-pty/src/worker/conoutSocketWorker.ts +22 -0
  301. package/.output/server/node_modules/node-pty/third_party/conpty/1.23.251008001/win10-arm64/OpenConsole.exe +0 -0
  302. package/.output/server/node_modules/node-pty/third_party/conpty/1.23.251008001/win10-arm64/conpty.dll +0 -0
  303. package/.output/server/node_modules/node-pty/third_party/conpty/1.23.251008001/win10-x64/OpenConsole.exe +0 -0
  304. package/.output/server/node_modules/node-pty/third_party/conpty/1.23.251008001/win10-x64/conpty.dll +0 -0
  305. package/.output/server/node_modules/node-pty/typings/node-pty.d.ts +211 -0
  306. package/.output/server/package.json +1 -0
  307. package/package.json +11 -4
  308. package/.output/public/assets/routes-DynLhCvj.js +0 -2195
  309. package/.output/public/assets/styles-DJ9ubgcc.css +0 -2
  310. package/scripts/patch-react-jsx-dev-runtime.mjs +0 -93
  311. /package/.output/public/assets/{wasm-CEUrWlVz.js → wasm-D_MQmneJ.js} +0 -0
@@ -1,8 +1,8 @@
1
1
  import { i as __toESM } from "../_runtime.mjs";
2
2
  import { $ as require_react, A as TabsTab, B as Input, C as MenuPositioner, D as MenuGroupLabel, E as MenuItem, F as DialogPopup, H as useRender, I as DialogDescription$1, L as DialogClose$1, M as DialogTitle$1, N as DialogRoot, O as MenuGroup, P as DialogPortal$1, Q as require_jsx_runtime, R as DialogBackdrop, S as MenuRoot, T as MenuPopup, V as Button, Y as mergeProps, _ as AccordionItem$1, a as ContextMenuRoot, b as MenuTrigger, c as CollapsibleRoot, d as PopoverPortal, f as PopoverTrigger$1, g as AccordionHeader, h as AccordionTrigger$1, i as ContextMenuTrigger$1, j as TabsRoot, k as TabsList$1, l as PopoverPopup, m as AccordionPanel, n as ToggleGroup, o as CollapsiblePanel, p as PopoverRoot, r as Toggle, s as CollapsibleTrigger$1, t as AlertDialogRoot, u as PopoverPositioner, v as AccordionRoot, w as MenuPortal, x as MenuSubmenuRoot, y as MenuSubmenuTrigger, z as Separator } from "../_libs/@base-ui/react+[...].mjs";
3
3
  import { t as cva } from "../_libs/class-variance-authority+clsx.mjs";
4
- import { A as FolderPlus, B as ChevronsDownUp, C as ListEnd, D as GitBranch, E as GitPullRequestArrow, F as Download, G as ArrowUp, H as ChevronRight, I as Copy, K as ArrowUpToLine, L as Command, M as ExternalLink, N as Ellipsis, O as GitCommitHorizontal, P as EllipsisVertical, S as ListStart, T as ImagePlus, U as ChevronDown, W as Check, X as ArrowDown, Y as ArrowLeft, a as TriangleAlert, b as Minimize2, c as Square, d as Search, f as Plus, g as PanelLeftClose, h as PanelLeftOpen, i as Undo2, j as FileDiff, k as Folder, l as SquarePen, m as PanelLeft, n as WandSparkles, o as Trash2, p as PanelRight, q as ArrowUpRight, r as Upload, s as Tag, t as X, u as Settings2, v as OctagonX, x as Maximize2, y as Minus, z as ChevronsUpDown } from "../_libs/lucide-react.mjs";
5
- import { $ as normalizeThemeFamily, A as Tooltip$1, C as SESSION_DONE_SOUND_ENABLED_STORAGE_KEY, Ct as themeFamilyDescription, D as THEME_FAMILIES, Dt as themeFamilySupportsColorMode, E as THEME_COLOR_MODE_STORAGE_KEY, Et as themeFamilyLabel, F as appliedThemeClass, H as createInitialSessionState, I as appliedThemeClassColorMode, K as flattenTree, N as TooltipTrigger, O as THEME_STORAGE_KEY, Ot as thinkingSummaryText, P as VIEWER_CONTEXT_STORAGE_KEY, Q as normalizeThemeColorMode, R as buildItemsFromSync, S as SESSION_DONE_DESKTOP_NOTIFICATIONS_ENABLED_STORAGE_KEY, St as themeColorModeLabel, T as Spinner, Tt as themeFamilyKeywords, V as createContextId, X as normalizeSessionSelectionKeys, Y as normalizePromptImage, Z as normalizeStoredDirectoryList, _ as RIGHT_SIDEBAR_HISTORY_HEIGHT_STORAGE_KEY, _t as rememberStoredPromptDraft, a as COLLAPSED_DIRECTORIES_STORAGE_KEY, at as readStoredCollapsedDirectories, b as RIGHT_SIDEBAR_WIDTHS_STORAGE_KEY, bt as sameContextUsage, c as Kbd, ct as readStoredPinnedSessionKeys, d as PICO_DIFF_SHIKI_THEMES, dt as readStoredRightSidebarOpen, et as previewUrlForImage, f as PICO_SHIKI_VARIABLE_DEFAULTS, ft as readStoredSessionDoneDesktopNotificationsEnabled, g as RIGHT_SIDEBAR_FILE_TREE_WIDTH_STORAGE_KEY, gt as readStoredThemeColorMode, h as RIGHT_SIDEBAR_ACTIVE_TAB_STORAGE_KEY, ht as readStoredTheme, i as CENTER_MESSAGES_STORAGE_KEY, it as readStoredCenterMessages, j as TooltipContent, k as TitleTooltip, l as KeyboardShortcutContent, lt as readStoredPromptDraft, m as RECENT_DIRECTORIES_STORAGE_KEY, mt as readStoredSidebarDirectories, n as APPLIED_THEME_STORAGE_KEY, nt as promptDraftKeyMatchesOwner, o as DRAFT_DIRECTORY_STORAGE_KEY, ot as readStoredDraftDirectory, p as PINNED_SESSIONS_STORAGE_KEY, pt as readStoredSessionDoneSoundEnabled, q as getSessionTitle, r as AUTO_SCROLL_ENABLED_STORAGE_KEY, rt as readStoredAutoScrollEnabled, s as HIDE_TOOL_BLOCKS_STORAGE_KEY, st as readStoredHideToolBlocks, tt as promptDraftKey, ut as readStoredRecentDirectories, v as RIGHT_SIDEBAR_HISTORY_TAB_STORAGE_KEY, vt as safeLocalStorageGetItem, w as SIDEBAR_DIRECTORIES_STORAGE_KEY, wt as themeFamilyFixedMode, x as Route, xt as sessionListEntryKey, y as RIGHT_SIDEBAR_OPEN_STORAGE_KEY, yt as safeLocalStorageSetItem, z as cn } from "./routes-9nZumcXQ.mjs";
4
+ import { A as Folder, B as ChevronsUpDown, C as ListStart, D as GitPullRequestArrow, E as ImagePlus, F as EllipsisVertical, G as Check, I as Download, J as ArrowUpRight, K as ArrowUp, L as Copy, M as FileDiff, N as ExternalLink, O as GitBranch, P as Ellipsis, R as Command, S as Maximize2, U as ChevronRight, V as ChevronsDownUp, W as ChevronDown, X as ArrowLeft, Z as ArrowDown, _ as PanelLeftClose, a as TriangleAlert, b as Minus, c as Square, d as Settings2, f as Search, g as PanelLeftOpen, h as PanelLeft, i as Undo2, j as FolderPlus, k as GitCommitHorizontal, l as SquareTerminal, m as PanelRight, n as WandSparkles, o as Trash2, p as Plus, q as ArrowUpToLine, r as Upload, s as Tag, t as X, u as SquarePen, w as ListEnd, x as Minimize2, y as OctagonX } from "../_libs/lucide-react.mjs";
5
+ import { $ as normalizeSessionSelectionKeys, A as THEME_STORAGE_KEY, At as themeFamilyDescription, B as buildItemsFromSync, C as SESSION_DONE_SOUND_ENABLED_STORAGE_KEY, Ct as rememberStoredPromptDraft, D as TERMINAL_PANEL_OPEN_STORAGE_KEY, Dt as sameContextUsage, E as TERMINAL_PANEL_HEIGHT_STORAGE_KEY, Et as safeLocalStorageSetItem, F as TooltipTrigger, Ft as thinkingSummaryText, G as exploreShellCommandNameFromTool, I as VIEWER_CONTEXT_STORAGE_KEY, It as toolArgsAreIncompleteJsonObject, L as appliedThemeClass, Lt as toolCategoryFromTool, M as Tooltip$1, Mt as themeFamilyKeywords, N as TooltipContent, Nt as themeFamilyLabel, O as THEME_COLOR_MODE_STORAGE_KEY, Ot as sessionListEntryKey, Pt as themeFamilySupportsColorMode, Q as normalizePromptImage, R as appliedThemeClassColorMode, Rt as toolCouldBecomeExploreFromTool, S as SESSION_DONE_DESKTOP_NOTIFICATIONS_ENABLED_STORAGE_KEY, St as readStoredThemeColorMode, T as Spinner, Tt as safeLocalStorageGetItem, U as createContextId, V as cn, W as createInitialSessionState, X as getSessionTitle, Y as flattenTree, _ as RIGHT_SIDEBAR_HISTORY_HEIGHT_STORAGE_KEY, _t as readStoredSessionDoneSoundEnabled, a as COLLAPSED_DIRECTORIES_STORAGE_KEY, at as promptDraftKeyMatchesOwner, b as RIGHT_SIDEBAR_WIDTHS_STORAGE_KEY, bt as readStoredTerminalTabs, c as Kbd, ct as readStoredCenterMessages, d as PICO_DIFF_SHIKI_THEMES, dt as readStoredHideToolBlocks, et as normalizeStoredDirectoryList, f as PICO_SHIKI_VARIABLE_DEFAULTS, ft as readStoredPinnedSessionKeys, g as RIGHT_SIDEBAR_FILE_TREE_WIDTH_STORAGE_KEY, gt as readStoredSessionDoneDesktopNotificationsEnabled, h as RIGHT_SIDEBAR_ACTIVE_TAB_STORAGE_KEY, ht as readStoredRightSidebarOpen, i as CENTER_MESSAGES_STORAGE_KEY, it as promptDraftKey, j as TitleTooltip, jt as themeFamilyFixedMode, k as THEME_FAMILIES, kt as themeColorModeLabel, l as KeyboardShortcutContent, lt as readStoredCollapsedDirectories, m as RECENT_DIRECTORIES_STORAGE_KEY, mt as readStoredRecentDirectories, n as APPLIED_THEME_STORAGE_KEY, nt as normalizeThemeFamily, o as DRAFT_DIRECTORY_STORAGE_KEY, ot as rawShellCommandTextFromTool, p as PINNED_SESSIONS_STORAGE_KEY, pt as readStoredPromptDraft, r as AUTO_SCROLL_ENABLED_STORAGE_KEY, rt as previewUrlForImage, s as HIDE_TOOL_BLOCKS_STORAGE_KEY, st as readStoredAutoScrollEnabled, tt as normalizeThemeColorMode, ut as readStoredDraftDirectory, v as RIGHT_SIDEBAR_HISTORY_TAB_STORAGE_KEY, vt as readStoredSidebarDirectories, w as SIDEBAR_DIRECTORIES_STORAGE_KEY, wt as rememberStoredTerminalTabs, x as Route, xt as readStoredTheme, y as RIGHT_SIDEBAR_OPEN_STORAGE_KEY, yt as readStoredTerminalPanelOpen } from "./routes-CuWg5WTI.mjs";
6
6
  import { i as useQuery, n as useIsMutating, o as useQueryClient, r as useIsFetching, t as useMutation } from "../_libs/tanstack__react-query.mjs";
7
7
  import { t as useHotkeys } from "../_libs/tanstack__react-hotkeys.mjs";
8
8
  import { a as matchesKeyboardEvent, i as batch, o as formatForDisplay, r as createStore } from "../_libs/@tanstack/hotkeys+[...].mjs";
@@ -22,7 +22,7 @@ import { t as remarkGfm } from "../_libs/remark-gfm.mjs";
22
22
  import { a as getBuiltInSpriteSheet, i as getBuiltInFileIconColor, n as FileTree, r as createFileTreeIconResolver, t as useFileTree } from "../_libs/@pierre/trees+[...].mjs";
23
23
  import { a as closestCenter, c as useDraggable, d as useSensors, f as CSS$1, i as PointerSensor, n as DragOverlay, t as DndContext, u as useSensor } from "../_libs/@dnd-kit/core+[...].mjs";
24
24
  import { a as verticalListSortingStrategy, i as useSortable, n as arrayMove, r as horizontalListSortingStrategy, t as SortableContext } from "../_libs/dnd-kit__sortable.mjs";
25
- //#region node_modules/.nitro/vite/services/ssr/assets/routes-C8qT6Oju.js
25
+ //#region node_modules/.nitro/vite/services/ssr/assets/routes-DySm_W8N.js
26
26
  var import_react = /* @__PURE__ */ __toESM(require_react());
27
27
  var import_jsx_runtime = require_jsx_runtime();
28
28
  var MOBILE_BREAKPOINT = 768;
@@ -503,8 +503,9 @@ function SidebarProvider({ defaultOpen = true, open: openProp, onOpenChange: set
503
503
  window.addEventListener("keydown", handleKeyDown);
504
504
  return () => window.removeEventListener("keydown", handleKeyDown);
505
505
  }, [toggleSidebarRef]);
506
+ const state = open ? "expanded" : "collapsed";
506
507
  const contextValue = {
507
- state: open ? "expanded" : "collapsed",
508
+ state,
508
509
  open,
509
510
  setOpen,
510
511
  isMobile,
@@ -522,6 +523,7 @@ function SidebarProvider({ defaultOpen = true, open: openProp, onOpenChange: set
522
523
  value: contextValue,
523
524
  children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
524
525
  "data-slot": "sidebar-wrapper",
526
+ "data-sidebar-state": state,
525
527
  style: {
526
528
  ...style,
527
529
  "--sidebar-width": sidebarWidth,
@@ -10618,10 +10620,9 @@ var PICO_DIFF_COLOR_CSS = `
10618
10620
  --diffs-bg-deletion-emphasis-override: color-mix(in lab, var(--diffs-bg) 42%, var(--diffs-deletion-base));
10619
10621
  }
10620
10622
 
10621
- @media (max-width: 640px) {
10622
- [data-annotation-content] {
10623
- inline-size: min(100%, calc(100vw - var(--diffs-column-number-width, 0px) - 1rem));
10624
- }
10623
+ [data-overflow="wrap"] [data-annotation-content] {
10624
+ inline-size: var(--diffs-column-content-width, auto);
10625
+ max-inline-size: min(100%, calc(100vw - 1rem));
10625
10626
  }
10626
10627
 
10627
10628
  [data-line-type="change-addition"]:is(
@@ -10813,22 +10814,7 @@ function formatToolArgString(value) {
10813
10814
  return trimmed;
10814
10815
  }
10815
10816
  function rawShellCommandText(block) {
10816
- if (block.type !== "tool" || block.name !== "bash") return "";
10817
- if (typeof block.args === "string") {
10818
- const trimmed = block.args.trim();
10819
- if (!trimmed) return "";
10820
- try {
10821
- const parsed = JSON.parse(trimmed);
10822
- if (parsed && typeof parsed === "object" && "command" in parsed) {
10823
- const command = parsed.command;
10824
- return typeof command === "string" ? command.trim() : trimmed;
10825
- }
10826
- } catch {
10827
- return trimmed;
10828
- }
10829
- return trimmed;
10830
- }
10831
- return getToolArgText(normalizeToolArgs(block.args), "command") || toolCommandPreview(block);
10817
+ return block.type === "tool" ? rawShellCommandTextFromTool(block.name, block.args) : "";
10832
10818
  }
10833
10819
  function toolCallText(block) {
10834
10820
  if (block.type !== "tool") return "";
@@ -10862,64 +10848,12 @@ function toolOutputText(block) {
10862
10848
  if (block.isError) return "Tool failed with no output.";
10863
10849
  return "No output available.";
10864
10850
  }
10865
- var EXPLORE_TOOL_NAMES = new Set([
10866
- "read",
10867
- "grep",
10868
- "glob",
10869
- "find",
10870
- "ls",
10871
- "list",
10872
- "rg"
10873
- ]);
10874
- var EXPLORE_SHELL_COMMAND_NAMES = new Set([
10875
- "find",
10876
- "grep",
10877
- "ls",
10878
- "rg"
10879
- ]);
10880
- var SKIPPABLE_SHELL_COMMAND_NAMES = new Set([
10881
- "cd",
10882
- "export",
10883
- "pwd",
10884
- "set"
10885
- ]);
10886
- var SHELL_COMMAND_WRAPPER_NAMES = new Set([
10887
- "command",
10888
- "env",
10889
- "noglob",
10890
- "sudo",
10891
- "time"
10892
- ]);
10893
- function shellCommandNameFromSegment(segment) {
10894
- let text = segment.trim();
10895
- while (text) {
10896
- const assignmentMatch = text.match(/^[A-Za-z_][A-Za-z0-9_]*=\S+\s+/);
10897
- if (assignmentMatch) {
10898
- text = text.slice(assignmentMatch[0].length).trimStart();
10899
- continue;
10900
- }
10901
- const commandMatch = text.match(/^([./A-Za-z0-9_-]+)\b/);
10902
- if (!commandMatch) return "";
10903
- const commandPath = commandMatch[1] || "";
10904
- const commandName = pathBaseName$1(commandPath);
10905
- if (!SHELL_COMMAND_WRAPPER_NAMES.has(commandName)) return commandName;
10906
- text = text.slice(commandPath.length).trimStart();
10907
- }
10908
- return "";
10909
- }
10910
10851
  function exploreShellCommandName(block) {
10911
- if (block.type !== "tool" || block.name !== "bash") return "";
10912
- const segments = rawShellCommandText(block).split(/(?:&&|\|\||;)/);
10913
- for (const segment of segments) {
10914
- const commandName = shellCommandNameFromSegment(segment);
10915
- if (!commandName || SKIPPABLE_SHELL_COMMAND_NAMES.has(commandName)) continue;
10916
- return EXPLORE_SHELL_COMMAND_NAMES.has(commandName) ? commandName : "";
10917
- }
10918
- return "";
10852
+ return block.type === "tool" ? exploreShellCommandNameFromTool(block.name, block.args) : "";
10919
10853
  }
10920
10854
  function matchesExploreToolBlock(block) {
10921
10855
  if (block.type !== "tool") return false;
10922
- return block.category === "explore" || EXPLORE_TOOL_NAMES.has(block.name || "") || Boolean(exploreShellCommandName(block));
10856
+ return block.category === "explore" || toolCategoryFromTool(block.name, block.args) === "explore";
10923
10857
  }
10924
10858
  function isExploreToolBlock(block) {
10925
10859
  return matchesExploreToolBlock(block);
@@ -10927,7 +10861,10 @@ function isExploreToolBlock(block) {
10927
10861
  function isPendingUnclassifiedToolBlock(block) {
10928
10862
  if (block.type !== "tool" || !block.running || matchesExploreToolBlock(block)) return false;
10929
10863
  if (!block.name) return true;
10930
- if (block.name === "bash") return !rawShellCommandText(block);
10864
+ if (block.name === "bash") {
10865
+ const category = toolCategoryFromTool(block.name, block.args);
10866
+ return !rawShellCommandText(block) || category !== "explore" && (toolArgsAreIncompleteJsonObject(block.args) || toolCouldBecomeExploreFromTool(block.name, block.args));
10867
+ }
10931
10868
  return false;
10932
10869
  }
10933
10870
  function getToolArgNumber(args, key) {
@@ -11725,7 +11662,7 @@ function PlainToolOutput({ isError = false, text }) {
11725
11662
  children: text
11726
11663
  });
11727
11664
  }
11728
- function scrollDistanceFromBottom(element) {
11665
+ function scrollDistanceFromBottom$1(element) {
11729
11666
  return element.scrollHeight - element.clientHeight - element.scrollTop;
11730
11667
  }
11731
11668
  function scrollElementToBottom(element) {
@@ -11749,7 +11686,7 @@ function useRunningToolAutoScroll({ contentKey, enabled }) {
11749
11686
  }, [contentKey, enabled]);
11750
11687
  const handleScroll = (event) => {
11751
11688
  if (!enabled) return;
11752
- autoScrollRef.current = scrollDistanceFromBottom(event.currentTarget) < 1;
11689
+ autoScrollRef.current = scrollDistanceFromBottom$1(event.currentTarget) < 1;
11753
11690
  };
11754
11691
  return {
11755
11692
  onScroll: handleScroll,
@@ -12055,53 +11992,67 @@ var UserMessageCard = import_react.memo(function UserMessageCard({ item }) {
12055
11992
  const selectedImage = selectedImageIndex === null ? null : item.images[selectedImageIndex] ?? null;
12056
11993
  return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
12057
11994
  className: "mr-2 ml-auto w-fit max-w-[80%]",
12058
- children: [/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
12059
- className: "relative rounded-3xl bg-primary px-5 py-2.5 text-primary-foreground before:absolute before:-right-1.75 before:bottom-0 before:h-5.5 before:w-4 before:rounded-bl-[16px_14px] before:bg-primary before:content-[''] after:absolute after:-right-6.5 after:bottom-0 after:h-6.25 after:w-6.5 after:rounded-bl-[10px] after:bg-background after:content-['']",
12060
- children: [
12061
- labelText ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
12062
- className: "mb-2 flex items-center gap-2",
12063
- children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Badge, {
12064
- variant: "outline",
12065
- className: "border-primary-foreground/35 bg-primary-foreground/10 text-primary-foreground",
12066
- children: labelText
12067
- })
12068
- }) : null,
12069
- item.text ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
12070
- className: "text-sm break-words whitespace-pre-wrap text-primary-foreground",
12071
- children: item.text
12072
- }) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
12073
- className: "text-sm text-primary-foreground/80",
12074
- children: "Image prompt"
12075
- }),
12076
- item.images.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
12077
- className: "mt-3 flex flex-wrap gap-3",
12078
- children: item.images.map((image, index) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("button", {
12079
- type: "button",
12080
- className: "max-w-full cursor-zoom-in overflow-hidden rounded-lg border border-primary-foreground/20 bg-primary-foreground/10 p-0 transition hover:opacity-90 focus-visible:ring-2 focus-visible:ring-primary-foreground/70 focus-visible:ring-offset-2 focus-visible:ring-offset-primary focus-visible:outline-none",
12081
- onClick: () => setSelectedImageIndex(index),
12082
- children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)("img", {
12083
- src: image.previewUrl,
12084
- alt: "Prompt upload",
12085
- className: "block h-28 max-w-full object-cover"
12086
- }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
12087
- className: "sr-only",
12088
- children: "Open prompt upload"
12089
- })]
12090
- }, `${promptImageKey(image)}:${index}`))
12091
- }) : null
12092
- ]
12093
- }), selectedImage ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ImageLightbox, {
12094
- open: true,
12095
- onOpenChange: (open) => {
12096
- if (!open) setSelectedImageIndex(null);
12097
- },
12098
- imageSrc: selectedImage.previewUrl,
12099
- imageAlt: "Prompt upload",
12100
- title: "Prompt upload preview",
12101
- description: "Expanded preview of an image attached to a user message."
12102
- }) : null]
11995
+ children: [
11996
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
11997
+ className: "relative rounded-3xl bg-primary px-5 py-2.5 text-primary-foreground before:absolute before:-right-1.75 before:bottom-0 before:h-5.5 before:w-4 before:rounded-bl-[16px_14px] before:bg-primary before:content-[''] after:absolute after:-right-6.5 after:bottom-0 after:h-6.25 after:w-6.5 after:rounded-bl-[10px] after:bg-background after:content-['']",
11998
+ children: [
11999
+ labelText ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
12000
+ className: "mb-2 flex items-center gap-2",
12001
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Badge, {
12002
+ variant: "outline",
12003
+ className: "border-primary-foreground/35 bg-primary-foreground/10 text-primary-foreground",
12004
+ children: labelText
12005
+ })
12006
+ }) : null,
12007
+ item.text ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
12008
+ className: "text-sm break-words whitespace-pre-wrap text-primary-foreground",
12009
+ children: item.text
12010
+ }) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
12011
+ className: "text-sm text-primary-foreground/80",
12012
+ children: "Image prompt"
12013
+ }),
12014
+ item.images.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
12015
+ className: "mt-3 flex flex-wrap gap-3",
12016
+ children: item.images.map((image, index) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("button", {
12017
+ type: "button",
12018
+ className: "max-w-full cursor-zoom-in overflow-hidden rounded-lg border border-primary-foreground/20 bg-primary-foreground/10 p-0 transition hover:opacity-90 focus-visible:ring-2 focus-visible:ring-primary-foreground/70 focus-visible:ring-offset-2 focus-visible:ring-offset-primary focus-visible:outline-none",
12019
+ onClick: () => setSelectedImageIndex(index),
12020
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)("img", {
12021
+ src: image.previewUrl,
12022
+ alt: "Prompt upload",
12023
+ className: "block h-28 max-w-full object-cover"
12024
+ }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
12025
+ className: "sr-only",
12026
+ children: "Open prompt upload"
12027
+ })]
12028
+ }, `${promptImageKey(image)}:${index}`))
12029
+ }) : null
12030
+ ]
12031
+ }),
12032
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(UserMessageFooter, { copyText: item.text }),
12033
+ selectedImage ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ImageLightbox, {
12034
+ open: true,
12035
+ onOpenChange: (open) => {
12036
+ if (!open) setSelectedImageIndex(null);
12037
+ },
12038
+ imageSrc: selectedImage.previewUrl,
12039
+ imageAlt: "Prompt upload",
12040
+ title: "Prompt upload preview",
12041
+ description: "Expanded preview of an image attached to a user message."
12042
+ }) : null
12043
+ ]
12103
12044
  });
12104
12045
  });
12046
+ function UserMessageFooter({ copyText }) {
12047
+ if (!copyText.trim()) return null;
12048
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
12049
+ className: "mt-1 flex justify-end gap-1.5 text-xs font-medium text-muted-foreground",
12050
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(MessageFooterCopyButton, {
12051
+ idleLabel: "Copy user message",
12052
+ text: copyText
12053
+ })
12054
+ });
12055
+ }
12105
12056
  function assistantBlockIsVisible({ block, hideThinking, hideToolBlocks }) {
12106
12057
  switch (block.type) {
12107
12058
  case "text": return Boolean(block.text.trim());
@@ -12200,7 +12151,7 @@ function AssistantBlockGroupsView({ anchorBlockKey, store, streaming }) {
12200
12151
  streaming
12201
12152
  }, descriptor.key)) });
12202
12153
  }
12203
- function AssistantMessageFooterCopyButton({ text }) {
12154
+ function MessageFooterCopyButton({ className, idleLabel, text }) {
12204
12155
  const [copyState, setCopyState] = import_react.useState("idle");
12205
12156
  const canCopy = Boolean(text.trim());
12206
12157
  const copyMessage = async () => {
@@ -12214,13 +12165,13 @@ function AssistantMessageFooterCopyButton({ text }) {
12214
12165
  window.setTimeout(() => setCopyState("idle"), 1400);
12215
12166
  }
12216
12167
  };
12217
- const label = copyState === "copied" ? "Copied" : copyState === "error" ? "Retry copy" : "Copy assistant message";
12168
+ const label = copyState === "copied" ? "Copied" : copyState === "error" ? "Retry copy" : idleLabel;
12218
12169
  return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(TitleTooltip, {
12219
12170
  title: label,
12220
12171
  children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Button$1, {
12221
12172
  variant: "ghost",
12222
12173
  size: "icon-xs",
12223
- className: "-ml-2 text-muted-foreground",
12174
+ className: cn("text-muted-foreground", className),
12224
12175
  disabled: !canCopy,
12225
12176
  "aria-label": label,
12226
12177
  onClick: copyMessage,
@@ -12234,7 +12185,11 @@ function AssistantMessageFooter({ copyText, modelLabel, streaming }) {
12234
12185
  if (!showCopyButton && !modelLabel) return null;
12235
12186
  return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
12236
12187
  className: "-mt-2 flex items-center gap-1.5 text-xs font-medium text-muted-foreground",
12237
- children: [showCopyButton ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(AssistantMessageFooterCopyButton, { text: copyText }) : null, modelLabel ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
12188
+ children: [showCopyButton ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(MessageFooterCopyButton, {
12189
+ className: "-ml-2",
12190
+ idleLabel: "Copy assistant message",
12191
+ text: copyText
12192
+ }) : null, modelLabel ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
12238
12193
  className: "truncate",
12239
12194
  children: modelLabel
12240
12195
  }) : null]
@@ -12264,7 +12219,7 @@ function AssistantMessagesStoreCard({ className, hideFooter = false, store }) {
12264
12219
  store
12265
12220
  ]);
12266
12221
  const shellSnapshot = import_react.useSyncExternalStore(shellStore.subscribe, shellStore.getSnapshot, shellStore.getSnapshot);
12267
- const showFooter = Boolean(!hideFooter && !shellSnapshot.streaming && shellSnapshot.hasFooter);
12222
+ const showFooter = Boolean(!hideFooter && !shellSnapshot.working && shellSnapshot.hasFooter);
12268
12223
  if (!shellSnapshot.hasBlocks && !showFooter) return null;
12269
12224
  return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
12270
12225
  className: cn("flex flex-col gap-4", className),
@@ -14593,6 +14548,78 @@ function useAppShellMessageScroll({ autoScrollEnabled, isSessionViewLoading, ses
14593
14548
  syncAfterConversationChange
14594
14549
  };
14595
14550
  }
14551
+ var DEFAULT_SCROLL_GRADIENT_MAX_HEIGHT = 4;
14552
+ var EMPTY_SCROLL_GRADIENT_STATE = {
14553
+ bottomHeight: 0,
14554
+ topHeight: 0
14555
+ };
14556
+ function scrollDistanceFromBottom(element) {
14557
+ return Math.max(0, element.scrollHeight - element.scrollTop - element.clientHeight);
14558
+ }
14559
+ function scrollGradientHeight(distanceFromEdge, maxHeight) {
14560
+ return Math.min(maxHeight, Math.max(0, distanceFromEdge));
14561
+ }
14562
+ function scrollGradientState(element, maxHeight) {
14563
+ if (!element) return EMPTY_SCROLL_GRADIENT_STATE;
14564
+ return {
14565
+ bottomHeight: scrollGradientHeight(scrollDistanceFromBottom(element), maxHeight),
14566
+ topHeight: scrollGradientHeight(element.scrollTop, maxHeight)
14567
+ };
14568
+ }
14569
+ function setScrollGradientState(setState, nextState) {
14570
+ setState((currentState) => currentState.bottomHeight === nextState.bottomHeight && currentState.topHeight === nextState.topHeight ? currentState : nextState);
14571
+ }
14572
+ function useScrollGradients({ disabled = false, maxHeight = DEFAULT_SCROLL_GRADIENT_MAX_HEIGHT } = {}) {
14573
+ const elementRef = import_react.useRef(null);
14574
+ const [state, setState] = import_react.useState(EMPTY_SCROLL_GRADIENT_STATE);
14575
+ const syncScrollGradients = import_react.useCallback((element = elementRef.current) => {
14576
+ setScrollGradientState(setState, disabled ? EMPTY_SCROLL_GRADIENT_STATE : scrollGradientState(element, maxHeight));
14577
+ }, [disabled, maxHeight]);
14578
+ const setScrollElement = import_react.useCallback((element) => {
14579
+ elementRef.current = element;
14580
+ syncScrollGradients(element);
14581
+ }, [syncScrollGradients]);
14582
+ const onScroll = import_react.useCallback((event) => {
14583
+ syncScrollGradients(event.currentTarget);
14584
+ }, [syncScrollGradients]);
14585
+ import_react.useLayoutEffect(() => {
14586
+ const element = elementRef.current;
14587
+ syncScrollGradients(element);
14588
+ if (!element || typeof ResizeObserver === "undefined") return;
14589
+ let animationFrame = 0;
14590
+ const scheduleSync = () => {
14591
+ window.cancelAnimationFrame(animationFrame);
14592
+ animationFrame = window.requestAnimationFrame(() => {
14593
+ animationFrame = 0;
14594
+ syncScrollGradients(elementRef.current);
14595
+ });
14596
+ };
14597
+ const resizeObserver = new ResizeObserver(scheduleSync);
14598
+ resizeObserver.observe(element);
14599
+ const content = element.firstElementChild;
14600
+ if (content instanceof HTMLElement) resizeObserver.observe(content);
14601
+ return () => {
14602
+ window.cancelAnimationFrame(animationFrame);
14603
+ resizeObserver.disconnect();
14604
+ };
14605
+ }, [syncScrollGradients]);
14606
+ return {
14607
+ bottomHeight: state.bottomHeight,
14608
+ onScroll,
14609
+ setScrollElement,
14610
+ syncScrollGradients,
14611
+ topHeight: state.topHeight
14612
+ };
14613
+ }
14614
+ function ScrollGradientOverlays({ bottomClassName, bottomHeight, className, topClassName, topHeight }) {
14615
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
14616
+ className: cn("pointer-events-none absolute inset-x-0 top-0 z-10 bg-gradient-to-b from-black/10 to-transparent transition-[height,opacity] duration-150", topHeight > 0 ? "opacity-100" : "opacity-0", className, topClassName),
14617
+ style: { height: topHeight }
14618
+ }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
14619
+ className: cn("pointer-events-none absolute inset-x-0 bottom-0 z-10 bg-gradient-to-t from-black/10 to-transparent transition-[height,opacity] duration-150", bottomHeight > 0 ? "opacity-100" : "opacity-0", className, bottomClassName),
14620
+ style: { height: bottomHeight }
14621
+ })] });
14622
+ }
14596
14623
  function ConversationLatestMessageButton({ conversationItemsStore, draft, onClick, scrollStateStore }) {
14597
14624
  const hasMessages = useConversationHasMessages(conversationItemsStore);
14598
14625
  const isDisabled = useMessageScrollValue(scrollStateStore, (snapshot) => draft || !hasMessages || snapshot.isMessagesNearBottom);
@@ -14659,6 +14686,11 @@ function AppShellConversationFrame({ autoScrollEnabled, children, centerMessages
14659
14686
  isSessionViewLoading,
14660
14687
  sessionState
14661
14688
  });
14689
+ const { bottomHeight: conversationScrollBottomGradientHeight, onScroll: onConversationScrollGradientScroll, setScrollElement: setConversationScrollGradientElement, topHeight: conversationScrollTopGradientHeight } = useScrollGradients({ disabled: isSessionViewLoading });
14690
+ const setMessagesScrollAreaElement = import_react.useCallback((element) => {
14691
+ messagesScrollAreaRef.current = element;
14692
+ setConversationScrollGradientElement(element);
14693
+ }, [messagesScrollAreaRef, setConversationScrollGradientElement]);
14662
14694
  import_react.useImperativeHandle(ref, () => ({
14663
14695
  jumpToNextMessage,
14664
14696
  jumpToPreviousMessage,
@@ -14672,39 +14704,47 @@ function AppShellConversationFrame({ autoScrollEnabled, children, centerMessages
14672
14704
  ]);
14673
14705
  return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
14674
14706
  className: "relative min-h-0 flex-1",
14675
- children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
14676
- ref: messagesScrollAreaRef,
14677
- "data-conversation-viewport": "true",
14678
- tabIndex: 0,
14679
- role: "region",
14680
- "aria-label": "Conversation messages",
14681
- className: "h-full overflow-x-hidden overflow-y-auto overscroll-contain px-4 outline-none [overflow-anchor:none]",
14682
- children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
14683
- ref: messagesContentRef,
14684
- className: "flex min-h-full flex-col",
14685
- children: [
14686
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ConversationScrollRevisionObserver, {
14687
- conversationItemsStore,
14688
- disabled: isSessionViewLoading,
14689
- onRevisionChange: syncAfterConversationChange
14690
- }),
14691
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ConversationContentChangeContext.Provider, {
14692
- value: syncAfterConversationChange,
14693
- children
14694
- }),
14695
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { ref: bottomRef })
14696
- ]
14697
- })
14698
- }), !isSessionViewLoading ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(ConversationLatestMessageButton, {
14699
- conversationItemsStore,
14700
- draft: sessionState.draft,
14701
- onClick: scrollConversationToBottom,
14702
- scrollStateStore
14703
- }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ConversationPreviousMessageButton, {
14704
- centerMessages,
14705
- onClick: jumpToPreviousMessage,
14706
- scrollStateStore
14707
- })] }) : null]
14707
+ children: [
14708
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
14709
+ ref: setMessagesScrollAreaElement,
14710
+ "data-conversation-viewport": "true",
14711
+ tabIndex: 0,
14712
+ role: "region",
14713
+ "aria-label": "Conversation messages",
14714
+ className: "h-full overflow-x-hidden overflow-y-auto overscroll-contain px-4 outline-none [overflow-anchor:none]",
14715
+ onScroll: onConversationScrollGradientScroll,
14716
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
14717
+ ref: messagesContentRef,
14718
+ className: "flex min-h-full flex-col",
14719
+ children: [
14720
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ConversationScrollRevisionObserver, {
14721
+ conversationItemsStore,
14722
+ disabled: isSessionViewLoading,
14723
+ onRevisionChange: syncAfterConversationChange
14724
+ }),
14725
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ConversationContentChangeContext.Provider, {
14726
+ value: syncAfterConversationChange,
14727
+ children
14728
+ }),
14729
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { ref: bottomRef })
14730
+ ]
14731
+ })
14732
+ }),
14733
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ScrollGradientOverlays, {
14734
+ bottomHeight: conversationScrollBottomGradientHeight,
14735
+ topHeight: conversationScrollTopGradientHeight
14736
+ }),
14737
+ !isSessionViewLoading ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(ConversationLatestMessageButton, {
14738
+ conversationItemsStore,
14739
+ draft: sessionState.draft,
14740
+ onClick: scrollConversationToBottom,
14741
+ scrollStateStore
14742
+ }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ConversationPreviousMessageButton, {
14743
+ centerMessages,
14744
+ onClick: jumpToPreviousMessage,
14745
+ scrollStateStore
14746
+ })] }) : null
14747
+ ]
14708
14748
  });
14709
14749
  }
14710
14750
  function AppShellWorkingIndicatorLabel({ fallbackLabel, hiddenThinkingPreviewStore, useHiddenThinkingPreview }) {
@@ -16762,6 +16802,7 @@ function GitRepositorySummary({ viewerContextId, cwd, active, onBranchClick }) {
16762
16802
  }
16763
16803
  function GitPanelToolbar({ viewerContextId, cwd, active }) {
16764
16804
  const queryClient = useQueryClient();
16805
+ const isMobile = useIsMobile();
16765
16806
  const normalizedCwd = normalizeCwd(cwd);
16766
16807
  const [branchDialogOpen, setBranchDialogOpen] = import_react.useState(false);
16767
16808
  const [commitDialogOpen, setCommitDialogOpen] = import_react.useState(false);
@@ -16875,6 +16916,7 @@ function GitPanelToolbar({ viewerContextId, cwd, active }) {
16875
16916
  kbd: formatShortcutLabel("Control+C"),
16876
16917
  children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Button$1, {
16877
16918
  variant: "outline",
16919
+ size: isMobile ? "default" : "sm",
16878
16920
  onClick: () => {
16879
16921
  setCommitDialogOpen(true);
16880
16922
  },
@@ -16886,6 +16928,7 @@ function GitPanelToolbar({ viewerContextId, cwd, active }) {
16886
16928
  kbd: formatShortcutLabel("Control+P"),
16887
16929
  children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Button$1, {
16888
16930
  variant: "outline",
16931
+ size: isMobile ? "default" : "sm",
16889
16932
  disabled: gitActionBusy,
16890
16933
  onClick: () => {
16891
16934
  gitActionMutation.mutate("push");
@@ -16898,6 +16941,7 @@ function GitPanelToolbar({ viewerContextId, cwd, active }) {
16898
16941
  kbd: formatShortcutLabel("Control+Shift+P"),
16899
16942
  children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Button$1, {
16900
16943
  variant: "outline",
16944
+ size: isMobile ? "default" : "sm",
16901
16945
  disabled: gitActionBusy,
16902
16946
  onClick: () => {
16903
16947
  gitActionMutation.mutate("force-push");
@@ -16910,6 +16954,7 @@ function GitPanelToolbar({ viewerContextId, cwd, active }) {
16910
16954
  kbd: formatShortcutLabel("Alt+P"),
16911
16955
  children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Button$1, {
16912
16956
  variant: "outline",
16957
+ size: isMobile ? "default" : "sm",
16913
16958
  disabled: gitActionBusy,
16914
16959
  onClick: () => {
16915
16960
  gitActionMutation.mutate("pull");
@@ -17018,26 +17063,6 @@ function AlertDialogCancel({ className, variant = "outline", size = "default", .
17018
17063
  ...props
17019
17064
  });
17020
17065
  }
17021
- function setDerivedScrollState(setState, nextState) {
17022
- setState((current) => Object.is(current, nextState) ? current : nextState);
17023
- }
17024
- function hasScrolledContent(scrollElement) {
17025
- return Boolean(scrollElement && scrollElement.scrollTop > 0);
17026
- }
17027
- function hasRemainingScrollContent(scrollElement) {
17028
- if (!scrollElement) return false;
17029
- return scrollElement.scrollHeight - scrollElement.clientHeight - scrollElement.scrollTop > 1;
17030
- }
17031
- function getStuckScrollTriggerValue({ getValue, scrollElement, selector }) {
17032
- if (!scrollElement || scrollElement.scrollTop <= 0) return "";
17033
- const containerTop = scrollElement.getBoundingClientRect().top;
17034
- const triggers = Array.from(scrollElement.querySelectorAll(selector));
17035
- for (const trigger of triggers) {
17036
- const rect = trigger.getBoundingClientRect();
17037
- if (rect.top <= containerTop + 1 && rect.bottom > containerTop + 1) return getValue(trigger);
17038
- }
17039
- return "";
17040
- }
17041
17066
  function GitSection({ title, meta, controls, className, bodyClassName, children }) {
17042
17067
  return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("section", {
17043
17068
  className: cn("overflow-hidden rounded-xl border border-border/80 bg-card/50", className),
@@ -17081,10 +17106,7 @@ var initialGitCommitRowState = {
17081
17106
  formDialog: null,
17082
17107
  confirmDialog: null,
17083
17108
  contextMenuOpen: false,
17084
- contextMenuKey: 0,
17085
- scrollValue: "",
17086
- commitBodyHeaderShadowed: false,
17087
- commitBodyFooterShadowed: false
17109
+ contextMenuKey: 0
17088
17110
  };
17089
17111
  function gitCommitRowReducer(state, action) {
17090
17112
  switch (action.type) {
@@ -17132,18 +17154,10 @@ function gitCommitRowReducer(state, action) {
17132
17154
  contextMenuOpen: false,
17133
17155
  contextMenuKey: state.contextMenuKey + 1
17134
17156
  };
17135
- case "set-scroll-shadows":
17136
- if (state.scrollValue === action.value && state.commitBodyHeaderShadowed === action.headerShadowed && state.commitBodyFooterShadowed === action.footerShadowed) return state;
17137
- return {
17138
- ...state,
17139
- scrollValue: action.value,
17140
- commitBodyHeaderShadowed: action.headerShadowed,
17141
- commitBodyFooterShadowed: action.footerShadowed
17142
- };
17143
17157
  }
17144
17158
  }
17145
17159
  function gitGraphLaneColor(index, active = false) {
17146
- if (active) return "#f87171";
17160
+ if (active) return "var(--warning)";
17147
17161
  return GIT_GRAPH_LANE_COLORS[index % GIT_GRAPH_LANE_COLORS.length];
17148
17162
  }
17149
17163
  var GitCommitGraphBranch = class {
@@ -17768,8 +17782,8 @@ function useGitCommitRowView({ viewerContextId, cwd, graphWidth, line, value, on
17768
17782
  const [rowState, dispatchRow] = import_react.useReducer(gitCommitRowReducer, initialGitCommitRowState);
17769
17783
  const hashCopiedResetRef = import_react.useRef(void 0);
17770
17784
  const messageCopiedResetRef = import_react.useRef(void 0);
17771
- const commitBodyScrollRef = import_react.useRef(null);
17772
17785
  const contextMenuActionsRef = import_react.useRef(null);
17786
+ const { bottomHeight: commitBodyBottomGradientHeight, onScroll: onCommitBodyGradientScroll, setScrollElement: setCommitBodyGradientElement, topHeight: commitBodyTopGradientHeight } = useScrollGradients();
17773
17787
  const title = parsed.subject.trim();
17774
17788
  const time = formatGitCommitDetailTime(parsed.relativeDate);
17775
17789
  const fullTime = formatGitCommitFullDate(parsed.fullDate);
@@ -17778,19 +17792,6 @@ function useGitCommitRowView({ viewerContextId, cwd, graphWidth, line, value, on
17778
17792
  const shortHash = parsed.hash || parsed.fullHash.slice(0, 7);
17779
17793
  const canRunCommitAction = Boolean(viewerContextId && cwd && parsed.fullHash);
17780
17794
  const { hashCopied, messageCopied, formDialog, confirmDialog, contextMenuOpen, contextMenuKey } = rowState;
17781
- const commitBodyHeaderShadowed = rowState.scrollValue === value && rowState.commitBodyHeaderShadowed;
17782
- const commitBodyFooterShadowed = rowState.scrollValue === value && rowState.commitBodyFooterShadowed;
17783
- const updateCommitBodyScrollShadows = (scrollElement) => {
17784
- dispatchRow({
17785
- type: "set-scroll-shadows",
17786
- value,
17787
- headerShadowed: hasScrolledContent(scrollElement),
17788
- footerShadowed: hasRemainingScrollContent(scrollElement)
17789
- });
17790
- };
17791
- import_react.useLayoutEffect(() => {
17792
- updateCommitBodyScrollShadows(commitBodyScrollRef.current);
17793
- });
17794
17795
  import_react.useEffect(() => {
17795
17796
  return () => {
17796
17797
  if (typeof hashCopiedResetRef.current === "number") window.clearTimeout(hashCopiedResetRef.current);
@@ -17957,7 +17958,7 @@ function useGitCommitRowView({ viewerContextId, cwd, graphWidth, line, value, on
17957
17958
  });
17958
17959
  };
17959
17960
  if (!parsed.hash && !parsed.subject) return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
17960
- className: "flex h-8 max-w-full min-w-0 items-center font-mono text-[13px] leading-5",
17961
+ className: "flex h-8 max-w-full min-w-0 items-center text-[13px] leading-5",
17961
17962
  children: parsed.subject ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
17962
17963
  className: "min-w-0 flex-1 truncate text-foreground",
17963
17964
  children: parsed.subject
@@ -17965,7 +17966,7 @@ function useGitCommitRowView({ viewerContextId, cwd, graphWidth, line, value, on
17965
17966
  });
17966
17967
  const trigger = /* @__PURE__ */ (0, import_jsx_runtime.jsx)(AccordionTrigger, {
17967
17968
  headerClassName: "min-w-0",
17968
- className: cn("h-8 min-h-8 w-full items-center gap-2 rounded-none px-1.5 py-0 font-mono text-[13px] leading-5 font-normal transition-[background-color,box-shadow,color] hover:no-underline focus-visible:ring-0 aria-expanded:bg-muted/50 **:data-[slot=accordion-trigger-icon]:size-4"),
17969
+ className: cn("h-8 min-h-8 w-full items-center gap-2 rounded-none px-1.5 py-0 text-[13px] leading-5 font-normal transition-[background-color,box-shadow,color] hover:no-underline focus-visible:ring-0 aria-expanded:bg-muted/50 **:data-[slot=accordion-trigger-icon]:size-4"),
17969
17970
  children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
17970
17971
  className: "min-w-0 flex-1 truncate pr-8 text-foreground",
17971
17972
  style: { paddingLeft: graphWidth },
@@ -18107,122 +18108,119 @@ function useGitCommitRowView({ viewerContextId, cwd, graphWidth, line, value, on
18107
18108
  }),
18108
18109
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(AccordionContent, {
18109
18110
  className: "relative h-[200px] border-b border-border/70 bg-muted/50 p-0",
18110
- children: [
18111
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
18112
- ref: commitBodyScrollRef,
18113
- className: "h-full overflow-y-auto pr-2 pb-2",
18114
- onScroll: (event) => {
18115
- updateCommitBodyScrollShadows(event.currentTarget);
18116
- },
18117
- children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
18118
- className: "grid content-start gap-1.5 pt-1 font-mono text-xs leading-4",
18119
- style: { paddingLeft: graphWidth },
18120
- children: [
18121
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
18122
- className: "flex min-w-0 flex-wrap items-center gap-1.5 font-sans",
18123
- children: [/* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Button$1, {
18111
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
18112
+ ref: setCommitBodyGradientElement,
18113
+ className: "h-full overflow-y-auto pr-2 pb-2",
18114
+ onScroll: onCommitBodyGradientScroll,
18115
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
18116
+ className: "grid content-start gap-1.5 pt-1 text-xs leading-4",
18117
+ style: { paddingLeft: graphWidth },
18118
+ children: [
18119
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
18120
+ className: "flex min-w-0 flex-wrap items-center gap-1.5 font-sans",
18121
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Button$1, {
18122
+ type: "button",
18123
+ size: "xs",
18124
+ variant: "outline",
18125
+ disabled: !canRunCommitAction,
18126
+ onClick: () => openDiff("commit"),
18127
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(FileDiff, {}), "View diff"]
18128
+ }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(GitCommitActionsMenu, { ...commitActionProps })]
18129
+ }),
18130
+ parsed.author || time || parsed.fullHash ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
18131
+ className: "flex max-w-full flex-wrap items-center gap-x-2 gap-y-1 text-muted-foreground",
18132
+ children: [
18133
+ parsed.author ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: parsed.author }) : null,
18134
+ parsed.author && time ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
18135
+ "aria-hidden": true,
18136
+ children: "·"
18137
+ }) : null,
18138
+ time ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(TitleTooltip, {
18139
+ title: fullTime,
18140
+ side: "top",
18141
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: time })
18142
+ }) : null,
18143
+ (parsed.author || time) && parsed.fullHash ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
18144
+ "aria-hidden": true,
18145
+ children: "·"
18146
+ }) : null,
18147
+ parsed.fullHash ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", {
18148
+ className: "inline-flex min-w-0 items-center gap-1.5",
18149
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
18150
+ className: "min-w-0 truncate",
18151
+ children: shortHash
18152
+ }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(TitleTooltip, {
18153
+ title: hashCopied ? "Copied" : "Copy full commit hash",
18154
+ side: "top",
18155
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", {
18156
+ type: "button",
18157
+ "aria-label": "Copy full commit hash",
18158
+ className: "inline-flex size-5 shrink-0 items-center justify-center rounded-sm text-muted-foreground transition-colors hover:bg-muted hover:text-foreground",
18159
+ onClick: (event) => {
18160
+ event.preventDefault();
18161
+ event.stopPropagation();
18162
+ copyFullHashInline();
18163
+ },
18164
+ children: hashCopied ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Check, { className: "size-3.5 text-emerald-500" }) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Copy, { className: "size-3.5" })
18165
+ })
18166
+ })]
18167
+ }) : null
18168
+ ]
18169
+ }) : null,
18170
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
18171
+ className: "max-w-full break-words whitespace-normal text-foreground",
18172
+ children: [title || "No commit message", title ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(TitleTooltip, {
18173
+ title: messageCopied ? "Copied" : "Copy commit message",
18174
+ side: "top",
18175
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", {
18124
18176
  type: "button",
18125
- size: "xs",
18126
- variant: "outline",
18127
- disabled: !canRunCommitAction,
18128
- onClick: () => openDiff("commit"),
18129
- children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(FileDiff, {}), "View diff"]
18130
- }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(GitCommitActionsMenu, { ...commitActionProps })]
18131
- }),
18132
- parsed.author || time || parsed.fullHash ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
18133
- className: "flex max-w-full flex-wrap items-center gap-x-2 gap-y-1 text-muted-foreground",
18177
+ "aria-label": "Copy commit message",
18178
+ className: "ml-1 inline-flex size-4 items-center justify-center rounded-sm align-middle text-muted-foreground transition-colors hover:bg-muted hover:text-foreground",
18179
+ onClick: (event) => {
18180
+ event.preventDefault();
18181
+ event.stopPropagation();
18182
+ copyCommitMessageInline();
18183
+ },
18184
+ children: messageCopied ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Check, { className: "size-3.5 text-emerald-500" }) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Copy, { className: "size-3.5" })
18185
+ })
18186
+ }) : null]
18187
+ }),
18188
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
18189
+ className: "flex min-w-0 flex-wrap items-center gap-x-3 gap-y-1",
18190
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", {
18191
+ className: "text-emerald-500",
18134
18192
  children: [
18135
- parsed.author ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: parsed.author }) : null,
18136
- parsed.author && time ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
18137
- "aria-hidden": true,
18138
- children: "·"
18139
- }) : null,
18140
- time ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(TitleTooltip, {
18141
- title: fullTime,
18142
- side: "top",
18143
- children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: time })
18144
- }) : null,
18145
- (parsed.author || time) && parsed.fullHash ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
18146
- "aria-hidden": true,
18147
- children: "·"
18148
- }) : null,
18149
- parsed.fullHash ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", {
18150
- className: "inline-flex min-w-0 items-center gap-1.5",
18151
- children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
18152
- className: "min-w-0 truncate",
18153
- children: shortHash
18154
- }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(TitleTooltip, {
18155
- title: hashCopied ? "Copied" : "Copy full commit hash",
18156
- side: "top",
18157
- children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", {
18158
- type: "button",
18159
- "aria-label": "Copy full commit hash",
18160
- className: "inline-flex size-5 shrink-0 items-center justify-center rounded-sm text-muted-foreground transition-colors hover:bg-muted hover:text-foreground",
18161
- onClick: (event) => {
18162
- event.preventDefault();
18163
- event.stopPropagation();
18164
- copyFullHashInline();
18165
- },
18166
- children: hashCopied ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Check, { className: "size-3.5 text-emerald-500" }) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Copy, { className: "size-3.5" })
18167
- })
18168
- })]
18169
- }) : null
18193
+ "+",
18194
+ insertions,
18195
+ " addition",
18196
+ insertions === 1 ? "" : "s"
18170
18197
  ]
18171
- }) : null,
18172
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
18173
- className: "max-w-full break-words whitespace-normal text-foreground",
18174
- children: [title || "No commit message", title ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(TitleTooltip, {
18175
- title: messageCopied ? "Copied" : "Copy commit message",
18176
- side: "top",
18177
- children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", {
18178
- type: "button",
18179
- "aria-label": "Copy commit message",
18180
- className: "ml-1 inline-flex size-4 items-center justify-center rounded-sm align-middle text-muted-foreground transition-colors hover:bg-muted hover:text-foreground",
18181
- onClick: (event) => {
18182
- event.preventDefault();
18183
- event.stopPropagation();
18184
- copyCommitMessageInline();
18185
- },
18186
- children: messageCopied ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Check, { className: "size-3.5 text-emerald-500" }) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Copy, { className: "size-3.5" })
18187
- })
18188
- }) : null]
18189
- }),
18190
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
18191
- className: "flex min-w-0 flex-wrap items-center gap-x-3 gap-y-1",
18192
- children: [/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", {
18193
- className: "text-emerald-500",
18194
- children: [
18195
- "+",
18196
- insertions,
18197
- " addition",
18198
- insertions === 1 ? "" : "s"
18199
- ]
18200
- }), /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", {
18201
- className: "text-red-400",
18202
- children: [
18203
- "-",
18204
- deletions,
18205
- " deletion",
18206
- deletions === 1 ? "" : "s"
18207
- ]
18208
- })]
18209
- }),
18210
- parsed.fullHash ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(GitCommitFilesList, {
18211
- viewerContextId,
18212
- cwd,
18213
- commit: parsed.fullHash,
18214
- shortHash,
18215
- previousShortHash: parsed.parents[0]?.slice(0, 7) || "",
18216
- subject: title,
18217
- disabled: !canRunCommitAction,
18218
- onOpenCommitDiff
18219
- }) : null
18220
- ]
18221
- })
18222
- }),
18223
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: cn("pointer-events-none absolute inset-x-0 top-0 h-1 bg-gradient-to-b from-black/10 to-transparent transition-opacity duration-150", commitBodyHeaderShadowed ? "opacity-100" : "opacity-0") }),
18224
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: cn("pointer-events-none absolute inset-x-0 bottom-0 h-1 bg-gradient-to-t from-black/10 to-transparent transition-opacity duration-150", commitBodyFooterShadowed ? "opacity-100" : "opacity-0") })
18225
- ]
18198
+ }), /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", {
18199
+ className: "text-red-400",
18200
+ children: [
18201
+ "-",
18202
+ deletions,
18203
+ " deletion",
18204
+ deletions === 1 ? "" : "s"
18205
+ ]
18206
+ })]
18207
+ }),
18208
+ parsed.fullHash ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(GitCommitFilesList, {
18209
+ viewerContextId,
18210
+ cwd,
18211
+ commit: parsed.fullHash,
18212
+ shortHash,
18213
+ previousShortHash: parsed.parents[0]?.slice(0, 7) || "",
18214
+ subject: title,
18215
+ disabled: !canRunCommitAction,
18216
+ onOpenCommitDiff
18217
+ }) : null
18218
+ ]
18219
+ })
18220
+ }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ScrollGradientOverlays, {
18221
+ bottomHeight: commitBodyBottomGradientHeight,
18222
+ topHeight: commitBodyTopGradientHeight
18223
+ })]
18226
18224
  }),
18227
18225
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Dialog$1, {
18228
18226
  open: formDialog !== null,
@@ -18473,6 +18471,22 @@ function GitCommitsSection({ viewerContextId, cwd, active, onOpenCommitDiff, onR
18473
18471
  children: content
18474
18472
  });
18475
18473
  }
18474
+ function setDerivedScrollState(setState, nextState) {
18475
+ setState((current) => Object.is(current, nextState) ? current : nextState);
18476
+ }
18477
+ function hasScrolledContent(scrollElement) {
18478
+ return Boolean(scrollElement && scrollElement.scrollTop > 0);
18479
+ }
18480
+ function getStuckScrollTriggerValue({ getValue, scrollElement, selector }) {
18481
+ if (!scrollElement || scrollElement.scrollTop <= 0) return "";
18482
+ const containerTop = scrollElement.getBoundingClientRect().top;
18483
+ const triggers = Array.from(scrollElement.querySelectorAll(selector));
18484
+ for (const trigger of triggers) {
18485
+ const rect = trigger.getBoundingClientRect();
18486
+ if (rect.top <= containerTop + 1 && rect.bottom > containerTop + 1) return getValue(trigger);
18487
+ }
18488
+ return "";
18489
+ }
18476
18490
  function GitFileStatus({ status }) {
18477
18491
  const [indexCharacter, worktreeCharacter] = gitFileStatusCharacters(status);
18478
18492
  return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
@@ -18576,7 +18590,7 @@ function ReviewDiffPendingLineComment({ comment }) {
18576
18590
  comment.onSubmit(normalizedText);
18577
18591
  };
18578
18592
  return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("form", {
18579
- className: "m-2 w-[min(38rem,calc(100vw_-_5rem))] max-w-[calc(100%_-_1rem)] rounded-lg border border-border/70 bg-background p-2 shadow-sm",
18593
+ className: "m-2 w-[calc(100%_-_1rem)] max-w-[38rem] rounded-lg border border-border/70 bg-background p-2 shadow-sm",
18580
18594
  onPointerDown: (event) => event.stopPropagation(),
18581
18595
  onSubmit: (event) => {
18582
18596
  event.preventDefault();
@@ -18776,6 +18790,11 @@ function useFileReviewContentView({ viewerContextId, cwd, active, diffLineCommen
18776
18790
  });
18777
18791
  };
18778
18792
  const changesScrollRef = import_react.useRef(null);
18793
+ const { bottomHeight: changesScrollBottomGradientHeight, onScroll: onChangesScrollGradientScroll, setScrollElement: setChangesScrollGradientElement, topHeight: changesScrollTopGradientHeight } = useScrollGradients();
18794
+ const setChangesScrollElement = import_react.useCallback((element) => {
18795
+ changesScrollRef.current = element;
18796
+ setChangesScrollGradientElement(element);
18797
+ }, [setChangesScrollGradientElement]);
18779
18798
  const reviewContentRef = import_react.useRef(null);
18780
18799
  const historyPanelRef = import_react.useRef(null);
18781
18800
  const historyScrollRef = import_react.useRef(null);
@@ -18974,7 +18993,7 @@ function useFileReviewContentView({ viewerContextId, cwd, active, diffLineCommen
18974
18993
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(DropdownMenu, { children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(DropdownMenuTrigger, {
18975
18994
  render: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Button$1, {
18976
18995
  variant: "outline",
18977
- size: "icon",
18996
+ size: isMobile ? "icon" : "icon-sm",
18978
18997
  "aria-label": "Diff actions",
18979
18998
  disabled: bulkActionDisabled
18980
18999
  }),
@@ -19012,7 +19031,7 @@ function useFileReviewContentView({ viewerContextId, cwd, active, diffLineCommen
19012
19031
  title: hasOpenFile ? "Collapse all" : "Expand all",
19013
19032
  children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Button$1, {
19014
19033
  variant: "outline",
19015
- size: "icon",
19034
+ size: isMobile ? "icon" : "icon-sm",
19016
19035
  "aria-label": hasOpenFile ? "Collapse all" : "Expand all",
19017
19036
  disabled: changedFiles.length === 0,
19018
19037
  onClick: toggleAll,
@@ -19021,6 +19040,7 @@ function useFileReviewContentView({ viewerContextId, cwd, active, diffLineCommen
19021
19040
  }),
19022
19041
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ToggleGroup$1, {
19023
19042
  variant: "outline",
19043
+ size: isMobile ? "default" : "sm",
19024
19044
  value: [diffStyle],
19025
19045
  onValueChange: (values) => {
19026
19046
  const value = values[0];
@@ -19037,54 +19057,61 @@ function useFileReviewContentView({ viewerContextId, cwd, active, diffLineCommen
19037
19057
  ]
19038
19058
  })]
19039
19059
  }),
19040
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
19041
- ref: changesScrollRef,
19042
- className: "min-h-0 flex-1 overflow-auto",
19043
- onScroll: (event) => {
19044
- updateStickyReviewFileHeader(event.currentTarget);
19045
- },
19046
- children: !normalizedCwd ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(GitSectionNote, {
19047
- className: "px-3 py-2.5",
19048
- children: "No directory selected."
19049
- }) : !viewerContextId ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(GitSectionNote, {
19050
- className: "px-3 py-2.5",
19051
- children: "Waiting for viewer context…"
19052
- }) : filesQuery.isPending && typeof files === "undefined" ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(GitSectionNote, {
19053
- className: "px-3 py-2.5",
19054
- children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Spinner, {}), " Loading changes…"]
19055
- }) : filesQuery.error ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(GitSectionNote, {
19056
- tone: "destructive",
19057
- className: "px-3 py-2.5",
19058
- children: getErrorMessage(filesQuery.error, "Failed to load changes")
19059
- }) : files === null ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(GitSectionNote, {
19060
- className: "px-3 py-2.5",
19061
- children: "No git repository detected."
19062
- }) : changedFiles.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Accordion$1, {
19063
- multiple: true,
19064
- value: openFiles,
19065
- onValueChange: (nextOpenFiles) => {
19066
- dispatch({
19067
- type: "openFilesChanged",
19068
- openFiles: nextOpenFiles
19069
- });
19060
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
19061
+ className: "relative min-h-0 flex-1",
19062
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
19063
+ ref: setChangesScrollElement,
19064
+ className: "h-full overflow-auto",
19065
+ onScroll: (event) => {
19066
+ updateStickyReviewFileHeader(event.currentTarget);
19067
+ onChangesScrollGradientScroll(event);
19070
19068
  },
19071
- className: "border-b border-border/80",
19072
- children: changedFiles.map((file) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ReviewFileAccordionItem, {
19073
- viewerContextId,
19074
- cwd: normalizedCwd,
19075
- active,
19076
- diffLineComments,
19077
- diffStyle,
19078
- file,
19079
- open: openFiles.includes(reviewFileValue(file)),
19080
- stuck: stickyReviewFileValue === reviewFileValue(file),
19081
- onAddDiffLineComment,
19082
- onOpenFile
19083
- }, reviewFileValue(file)))
19084
- }) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)(GitSectionNote, {
19085
- className: "px-3 py-2.5",
19086
- children: "Working tree clean."
19087
- })
19069
+ children: !normalizedCwd ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(GitSectionNote, {
19070
+ className: "px-3 py-2.5",
19071
+ children: "No directory selected."
19072
+ }) : !viewerContextId ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(GitSectionNote, {
19073
+ className: "px-3 py-2.5",
19074
+ children: "Waiting for viewer context…"
19075
+ }) : filesQuery.isPending && typeof files === "undefined" ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(GitSectionNote, {
19076
+ className: "px-3 py-2.5",
19077
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Spinner, {}), " Loading changes…"]
19078
+ }) : filesQuery.error ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(GitSectionNote, {
19079
+ tone: "destructive",
19080
+ className: "px-3 py-2.5",
19081
+ children: getErrorMessage(filesQuery.error, "Failed to load changes")
19082
+ }) : files === null ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(GitSectionNote, {
19083
+ className: "px-3 py-2.5",
19084
+ children: "No git repository detected."
19085
+ }) : changedFiles.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Accordion$1, {
19086
+ multiple: true,
19087
+ value: openFiles,
19088
+ onValueChange: (nextOpenFiles) => {
19089
+ dispatch({
19090
+ type: "openFilesChanged",
19091
+ openFiles: nextOpenFiles
19092
+ });
19093
+ },
19094
+ className: "border-b border-border/80",
19095
+ children: changedFiles.map((file) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ReviewFileAccordionItem, {
19096
+ viewerContextId,
19097
+ cwd: normalizedCwd,
19098
+ active,
19099
+ diffLineComments,
19100
+ diffStyle,
19101
+ file,
19102
+ open: openFiles.includes(reviewFileValue(file)),
19103
+ stuck: stickyReviewFileValue === reviewFileValue(file),
19104
+ onAddDiffLineComment,
19105
+ onOpenFile
19106
+ }, reviewFileValue(file)))
19107
+ }) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)(GitSectionNote, {
19108
+ className: "px-3 py-2.5",
19109
+ children: "Working tree clean."
19110
+ })
19111
+ }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ScrollGradientOverlays, {
19112
+ bottomHeight: changesScrollBottomGradientHeight,
19113
+ topHeight: changesScrollTopGradientHeight
19114
+ })]
19088
19115
  }),
19089
19116
  showEmbeddedHistory && normalizedCwd && viewerContextId && files !== null && !filesQuery.error ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
19090
19117
  ref: historyPanelRef,
@@ -19235,6 +19262,7 @@ function ReviewFileAccordionItem({ viewerContextId, cwd, active, diffLineComment
19235
19262
  } : {};
19236
19263
  const themeOptions = usePicoDiffThemeOptions();
19237
19264
  const canStage = gitFileCanStage(file);
19265
+ const canUnstage = gitFileCanUnstage(file);
19238
19266
  const invalidateChangedFileQueries = async () => {
19239
19267
  await invalidateGitQueries({
19240
19268
  queryClient,
@@ -19243,26 +19271,27 @@ function ReviewFileAccordionItem({ viewerContextId, cwd, active, diffLineComment
19243
19271
  });
19244
19272
  };
19245
19273
  const stageMutation = useMutation({
19246
- mutationFn: async () => await fetchJson(buildRequestUrl("/api/git-stage", { contextId: viewerContextId }), {
19274
+ mutationFn: async (action) => await fetchJson(buildRequestUrl("/api/git-stage", { contextId: viewerContextId }), {
19247
19275
  method: "POST",
19248
19276
  headers: { "content-type": "application/json" },
19249
19277
  body: JSON.stringify({
19278
+ action,
19250
19279
  cwd: normalizedCwd,
19251
19280
  path: file.path,
19252
19281
  previousPath: file.previousPath
19253
19282
  })
19254
19283
  }),
19255
- onSuccess: async () => {
19284
+ onSuccess: async (_response, action) => {
19256
19285
  await queryClient.invalidateQueries({
19257
19286
  queryKey: picoQueryKeys.gitStatus(viewerContextId, normalizedCwd),
19258
19287
  exact: true,
19259
19288
  refetchType: "active"
19260
19289
  });
19261
19290
  await invalidateChangedFileQueries();
19262
- toast.success("Staged changes", { description: file.path });
19291
+ toast.success(action === "unstage" ? "Unstaged changes" : "Staged changes", { description: file.path });
19263
19292
  },
19264
- onError: (error) => {
19265
- toast.error(getErrorMessage(error, "Failed to stage changes"));
19293
+ onError: (error, action) => {
19294
+ toast.error(getErrorMessage(error, action === "unstage" ? "Failed to unstage changes" : "Failed to stage changes"));
19266
19295
  }
19267
19296
  });
19268
19297
  const discardMutation = useMutation({
@@ -19299,6 +19328,9 @@ function ReviewFileAccordionItem({ viewerContextId, cwd, active, diffLineComment
19299
19328
  oldContent,
19300
19329
  newContent
19301
19330
  ]);
19331
+ const gitActionDisabled = actionPending || !viewerContextId || !normalizedCwd;
19332
+ const stagePending = stageMutation.isPending && stageMutation.variables === "stage";
19333
+ const unstagePending = stageMutation.isPending && stageMutation.variables === "unstage";
19302
19334
  const openFile = (event) => {
19303
19335
  event?.stopPropagation();
19304
19336
  onOpenFile?.(file.path, { pin: true });
@@ -19309,50 +19341,81 @@ function ReviewFileAccordionItem({ viewerContextId, cwd, active, diffLineComment
19309
19341
  };
19310
19342
  const stageFileChanges = (event) => {
19311
19343
  event?.stopPropagation();
19312
- stageMutation.mutate();
19344
+ stageMutation.mutate("stage");
19313
19345
  };
19314
- return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(AccordionItem, {
19315
- value,
19316
- className: "border-border/70",
19317
- children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(AccordionHeader, {
19318
- className: cn("group/review-file-header sticky top-0 z-20 flex bg-background transition-shadow", stuck && "shadow-sm"),
19319
- children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(AccordionTrigger$1, {
19320
- "data-review-file-trigger": true,
19321
- "data-review-file-value": value,
19322
- className: "group/review-file-trigger relative flex min-h-10 min-w-0 flex-1 items-center justify-between gap-3 rounded-none border border-transparent bg-background px-3 py-2 text-left font-mono text-[13px] font-medium transition-all outline-none hover:no-underline focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring/50 aria-disabled:pointer-events-none aria-disabled:opacity-50 **:data-[slot=accordion-trigger-icon]:ml-auto **:data-[slot=accordion-trigger-icon]:size-4 **:data-[slot=accordion-trigger-icon]:text-muted-foreground",
19346
+ const unstageFileChanges = (event) => {
19347
+ event?.stopPropagation();
19348
+ stageMutation.mutate("unstage");
19349
+ };
19350
+ const trigger = /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(AccordionTrigger$1, {
19351
+ "data-review-file-trigger": true,
19352
+ "data-review-file-value": value,
19353
+ className: "group/review-file-trigger relative flex min-h-10 min-w-0 flex-1 items-center justify-between gap-3 rounded-none border border-transparent bg-background px-3 py-2 text-left text-[13px] font-medium transition-all outline-none hover:no-underline focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring/50 aria-disabled:pointer-events-none aria-disabled:opacity-50 **:data-[slot=accordion-trigger-icon]:ml-auto **:data-[slot=accordion-trigger-icon]:size-4 **:data-[slot=accordion-trigger-icon]:text-muted-foreground",
19354
+ children: [
19355
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", {
19356
+ className: "grid min-w-0 flex-1 grid-cols-[auto_auto_minmax(0,1fr)_auto] items-center gap-2",
19323
19357
  children: [
19324
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", {
19325
- className: "grid min-w-0 flex-1 grid-cols-[auto_auto_minmax(0,1fr)_auto] items-center gap-2",
19326
- children: [
19327
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(GitFileStatus, { status: file.status }),
19328
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ProjectFileTypeIcon, { path: file.path }),
19358
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(GitFileStatus, { status: file.status }),
19359
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ProjectFileTypeIcon, { path: file.path }),
19360
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
19361
+ className: "min-w-0 truncate text-left",
19362
+ children: file.previousPath ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
19329
19363
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
19330
- className: "min-w-0 truncate text-left",
19331
- children: file.previousPath ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
19332
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
19333
- className: "text-muted-foreground",
19334
- children: file.previousPath
19335
- }),
19336
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
19337
- className: "text-muted-foreground/70",
19338
- children: " → "
19339
- }),
19340
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: file.path })
19341
- ] }) : file.path
19364
+ className: "text-muted-foreground",
19365
+ children: file.previousPath
19342
19366
  }),
19343
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(GitFileDiff, { file })
19344
- ]
19345
- }),
19346
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ChevronRight, {
19347
- "data-slot": "accordion-trigger-icon",
19348
- className: "pointer-events-none size-4 shrink-0 group-aria-expanded/review-file-trigger:hidden"
19367
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
19368
+ className: "text-muted-foreground/70",
19369
+ children: " → "
19370
+ }),
19371
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: file.path })
19372
+ ] }) : file.path
19349
19373
  }),
19350
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ChevronDown, {
19351
- "data-slot": "accordion-trigger-icon",
19352
- className: "pointer-events-none hidden size-4 shrink-0 group-aria-expanded/review-file-trigger:inline"
19353
- })
19374
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(GitFileDiff, { file })
19354
19375
  ]
19376
+ }),
19377
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ChevronRight, {
19378
+ "data-slot": "accordion-trigger-icon",
19379
+ className: "pointer-events-none size-4 shrink-0 group-aria-expanded/review-file-trigger:hidden"
19380
+ }),
19381
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ChevronDown, {
19382
+ "data-slot": "accordion-trigger-icon",
19383
+ className: "pointer-events-none hidden size-4 shrink-0 group-aria-expanded/review-file-trigger:inline"
19355
19384
  })
19385
+ ]
19386
+ });
19387
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(AccordionItem, {
19388
+ value,
19389
+ className: "border-border/70",
19390
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(AccordionHeader, {
19391
+ className: cn("group/review-file-header sticky top-0 z-20 flex bg-background transition-shadow", stuck && "shadow-sm"),
19392
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(ContextMenu$1, { children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(ContextMenuTrigger, { render: trigger }), /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(ContextMenuContent, {
19393
+ className: "w-48",
19394
+ children: [
19395
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(ContextMenuItem, {
19396
+ disabled: !onOpenFile,
19397
+ onClick: openFile,
19398
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(ArrowUpRight, {}), "Open file"]
19399
+ }),
19400
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ContextMenuSeparator, {}),
19401
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(ContextMenuItem, {
19402
+ disabled: gitActionDisabled || !canStage,
19403
+ onClick: stageFileChanges,
19404
+ children: [stagePending ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Spinner, { size: "xs" }) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Plus, {}), "Stage changes"]
19405
+ }),
19406
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(ContextMenuItem, {
19407
+ disabled: gitActionDisabled || !canUnstage,
19408
+ onClick: unstageFileChanges,
19409
+ children: [unstagePending ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Spinner, { size: "xs" }) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Minus, {}), "Unstage changes"]
19410
+ }),
19411
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(ContextMenuItem, {
19412
+ variant: "destructive",
19413
+ disabled: gitActionDisabled,
19414
+ onClick: discardFileChanges,
19415
+ children: [discardMutation.isPending ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Spinner, { size: "xs" }) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Undo2, {}), "Discard changes"]
19416
+ })
19417
+ ]
19418
+ })] })
19356
19419
  }), /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(AccordionContent, {
19357
19420
  className: "bg-background p-0",
19358
19421
  children: [/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
@@ -19597,7 +19660,7 @@ function assignClassName(element, className) {
19597
19660
  }
19598
19661
  function renderPendingCommitDiffLineComment(pendingComment) {
19599
19662
  const form = document.createElement("form");
19600
- assignClassName(form, "m-2 w-[min(38rem,calc(100vw_-_5rem))] max-w-[calc(100%_-_1rem)] rounded-lg border border-border/70 bg-background p-2 shadow-sm");
19663
+ assignClassName(form, "m-2 w-[calc(100%_-_1rem)] max-w-[38rem] rounded-lg border border-border/70 bg-background p-2 shadow-sm");
19601
19664
  const header = document.createElement("div");
19602
19665
  assignClassName(header, "mb-2 flex min-w-0 items-center justify-between gap-2 text-xs text-muted-foreground");
19603
19666
  const label = document.createElement("span");
@@ -19740,7 +19803,7 @@ function CommitFileDiffAccordionItem({ comments, diffStyle, fileDiff, index, onS
19740
19803
  children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(AccordionTrigger$1, {
19741
19804
  "data-commit-file-trigger": true,
19742
19805
  "data-commit-file-value": value,
19743
- className: "group/commit-file-trigger relative flex min-h-10 min-w-0 flex-1 items-center justify-between gap-3 rounded-none border border-transparent bg-background px-3 py-2 text-left font-mono text-[13px] font-medium transition-all outline-none hover:no-underline focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring/50 aria-disabled:pointer-events-none aria-disabled:opacity-50 **:data-[slot=accordion-trigger-icon]:ml-auto **:data-[slot=accordion-trigger-icon]:size-4 **:data-[slot=accordion-trigger-icon]:text-muted-foreground",
19806
+ className: "group/commit-file-trigger relative flex min-h-10 min-w-0 flex-1 items-center justify-between gap-3 rounded-none border border-transparent bg-background px-3 py-2 text-left text-[13px] font-medium transition-all outline-none hover:no-underline focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring/50 aria-disabled:pointer-events-none aria-disabled:opacity-50 **:data-[slot=accordion-trigger-icon]:ml-auto **:data-[slot=accordion-trigger-icon]:size-4 **:data-[slot=accordion-trigger-icon]:text-muted-foreground",
19744
19807
  children: [
19745
19808
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", {
19746
19809
  className: "grid min-w-0 flex-1 grid-cols-[minmax(0,1fr)_auto] items-baseline gap-3",
@@ -19878,8 +19941,14 @@ function CommitDiffHeaderTitle({ tab }) {
19878
19941
  function CommitDiffContent({ viewerContextId, cwd, active, diffLineComments, onAddDiffLineComment, tab }) {
19879
19942
  const [diffStyle, setDiffStyle] = import_react.useState("unified");
19880
19943
  const [stickyCommitFileValue, setStickyCommitFileValue] = import_react.useState("");
19944
+ const isMobile = useIsMobile();
19881
19945
  const [commentTarget, setCommentTarget] = import_react.useState(null);
19882
19946
  const diffScrollRef = import_react.useRef(null);
19947
+ const { bottomHeight: diffScrollBottomGradientHeight, onScroll: onDiffScrollGradientScroll, setScrollElement: setDiffScrollGradientElement, topHeight: diffScrollTopGradientHeight } = useScrollGradients();
19948
+ const setDiffScrollElement = import_react.useCallback((element) => {
19949
+ diffScrollRef.current = element;
19950
+ setDiffScrollGradientElement(element);
19951
+ }, [setDiffScrollGradientElement]);
19883
19952
  const updateStickyCommitFileHeader = (scrollElement) => {
19884
19953
  setDerivedScrollState(setStickyCommitFileValue, getStuckScrollTriggerValue({
19885
19954
  getValue: (trigger) => trigger.dataset.commitFileValue || "",
@@ -19945,6 +20014,7 @@ function CommitDiffContent({ viewerContextId, cwd, active, diffLineComments, onA
19945
20014
  className: "flex min-h-10 shrink-0 items-center justify-between gap-2 border-b border-border/70 bg-background p-2",
19946
20015
  children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(CommitDiffHeaderTitle, { tab }), /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(ToggleGroup$1, {
19947
20016
  variant: "outline",
20017
+ size: isMobile ? "default" : "sm",
19948
20018
  value: [diffStyle],
19949
20019
  onValueChange: (values) => {
19950
20020
  const value = values[0];
@@ -19958,41 +20028,48 @@ function CommitDiffContent({ viewerContextId, cwd, active, diffLineComments, onA
19958
20028
  children: "Split"
19959
20029
  })]
19960
20030
  })]
19961
- }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
19962
- ref: diffScrollRef,
19963
- className: "min-h-0 flex-1 overflow-auto",
19964
- onScroll: (event) => {
19965
- updateStickyCommitFileHeader(event.currentTarget);
19966
- },
19967
- children: diffQuery.isPending && !diffQuery.data ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
19968
- className: "p-4",
19969
- children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(GitSectionNote, { children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Spinner, {}), " Loading commit diff…"] })
19970
- }) : diffQuery.error ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
19971
- className: "p-4",
19972
- children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(GitSectionNote, {
19973
- tone: "destructive",
19974
- children: getErrorMessage(diffQuery.error, "Failed to load commit diff")
20031
+ }), /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
20032
+ className: "relative min-h-0 flex-1",
20033
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
20034
+ ref: setDiffScrollElement,
20035
+ className: "h-full overflow-auto",
20036
+ onScroll: (event) => {
20037
+ updateStickyCommitFileHeader(event.currentTarget);
20038
+ onDiffScrollGradientScroll(event);
20039
+ },
20040
+ children: diffQuery.isPending && !diffQuery.data ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
20041
+ className: "p-4",
20042
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(GitSectionNote, { children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Spinner, {}), " Loading commit diff…"] })
20043
+ }) : diffQuery.error ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
20044
+ className: "p-4",
20045
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(GitSectionNote, {
20046
+ tone: "destructive",
20047
+ children: getErrorMessage(diffQuery.error, "Failed to load commit diff")
20048
+ })
20049
+ }) : diffQuery.data?.patch && tab.path ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(CommitSingleFileDiff, {
20050
+ comments: commentsForTab.filter((comment) => {
20051
+ if (!tab.path) return true;
20052
+ return comment.path === tab.path || comment.path === tab.previousPath;
20053
+ }),
20054
+ diffStyle,
20055
+ onStartComment: startLineComment,
20056
+ patch: diffQuery.data.patch,
20057
+ pendingComment
20058
+ }) : diffQuery.data?.patch ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(CommitPatchDiffs, {
20059
+ comments: commentsForTab,
20060
+ diffStyle,
20061
+ onStartComment: startLineComment,
20062
+ patch: diffQuery.data.patch,
20063
+ pendingComment,
20064
+ stuckFileValue: stickyCommitFileValue
20065
+ }, `${diffQuery.data.commit}:${diffQuery.data.mode}`) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
20066
+ className: "p-4",
20067
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(GitSectionNote, { children: "No line changes." })
19975
20068
  })
19976
- }) : diffQuery.data?.patch && tab.path ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(CommitSingleFileDiff, {
19977
- comments: commentsForTab.filter((comment) => {
19978
- if (!tab.path) return true;
19979
- return comment.path === tab.path || comment.path === tab.previousPath;
19980
- }),
19981
- diffStyle,
19982
- onStartComment: startLineComment,
19983
- patch: diffQuery.data.patch,
19984
- pendingComment
19985
- }) : diffQuery.data?.patch ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(CommitPatchDiffs, {
19986
- comments: commentsForTab,
19987
- diffStyle,
19988
- onStartComment: startLineComment,
19989
- patch: diffQuery.data.patch,
19990
- pendingComment,
19991
- stuckFileValue: stickyCommitFileValue
19992
- }, `${diffQuery.data.commit}:${diffQuery.data.mode}`) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
19993
- className: "p-4",
19994
- children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(GitSectionNote, { children: "No line changes." })
19995
- })
20069
+ }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ScrollGradientOverlays, {
20070
+ bottomHeight: diffScrollBottomGradientHeight,
20071
+ topHeight: diffScrollTopGradientHeight
20072
+ })]
19996
20073
  })]
19997
20074
  });
19998
20075
  }
@@ -20249,6 +20326,701 @@ function RightSidebar({ viewerContextId, cwd, active, activeFilePath = "", activ
20249
20326
  })]
20250
20327
  });
20251
20328
  }
20329
+ var TERMINAL_ANSI_THEMES = {
20330
+ dark: {
20331
+ black: "#484f58",
20332
+ red: "#ff7b72",
20333
+ green: "#3fb950",
20334
+ yellow: "#d29922",
20335
+ blue: "#58a6ff",
20336
+ magenta: "#bc8cff",
20337
+ cyan: "#39c5cf",
20338
+ white: "#b1bac4",
20339
+ brightBlack: "#6e7681",
20340
+ brightRed: "#ffa198",
20341
+ brightGreen: "#56d364",
20342
+ brightYellow: "#e3b341",
20343
+ brightBlue: "#79c0ff",
20344
+ brightMagenta: "#d2a8ff",
20345
+ brightCyan: "#56d4dd",
20346
+ brightWhite: "#f0f6fc"
20347
+ },
20348
+ light: {
20349
+ black: "#24292f",
20350
+ red: "#cf222e",
20351
+ green: "#116329",
20352
+ yellow: "#953800",
20353
+ blue: "#0969da",
20354
+ magenta: "#8250df",
20355
+ cyan: "#1b7c83",
20356
+ white: "#6e7781",
20357
+ brightBlack: "#57606a",
20358
+ brightRed: "#a40e26",
20359
+ brightGreen: "#1a7f37",
20360
+ brightYellow: "#9a6700",
20361
+ brightBlue: "#218bff",
20362
+ brightMagenta: "#a475f9",
20363
+ brightCyan: "#3192aa",
20364
+ brightWhite: "#24292f"
20365
+ }
20366
+ };
20367
+ var terminalTabCounter = 0;
20368
+ function createTerminalTabId() {
20369
+ if (typeof crypto !== "undefined" && typeof crypto.randomUUID === "function") return `terminal-tab-${crypto.randomUUID()}`;
20370
+ terminalTabCounter += 1;
20371
+ return `terminal-tab-${Date.now()}-${terminalTabCounter}`;
20372
+ }
20373
+ function createTerminalTab(id = createTerminalTabId()) {
20374
+ return {
20375
+ id,
20376
+ status: { type: "idle" },
20377
+ terminalId: null
20378
+ };
20379
+ }
20380
+ function createTerminalTabsState(scopeKey) {
20381
+ const storedTabs = readStoredTerminalTabs(scopeKey);
20382
+ const tabs = storedTabs.tabs.map((id) => createTerminalTab(id));
20383
+ return {
20384
+ activeTabId: storedTabs.activeTabId ?? tabs[0]?.id ?? null,
20385
+ scopeKey,
20386
+ tabs
20387
+ };
20388
+ }
20389
+ function rememberTerminalTabsState(state) {
20390
+ rememberStoredTerminalTabs(state.scopeKey, {
20391
+ activeTabId: state.activeTabId,
20392
+ tabs: state.tabs.map((tab) => tab.id)
20393
+ });
20394
+ }
20395
+ function terminalStatusText(status) {
20396
+ switch (status.type) {
20397
+ case "idle": return "Idle";
20398
+ case "connecting": return "Connecting…";
20399
+ case "connected": return status.shell ? `Running ${status.shell}` : "Connected";
20400
+ case "exited": return `Exited ${status.exitCode}`;
20401
+ case "error": return "Terminal error";
20402
+ }
20403
+ }
20404
+ function terminalStatusDetail(status) {
20405
+ switch (status.type) {
20406
+ case "idle": return "This terminal has not started yet.";
20407
+ case "connecting": return "Pico is connecting to the terminal process.";
20408
+ case "connected": return status.shell ? `The terminal is connected and running ${status.shell}.` : "The terminal is connected.";
20409
+ case "exited": return typeof status.signal === "number" ? `The terminal process exited with code ${status.exitCode} and signal ${status.signal}.` : `The terminal process exited with code ${status.exitCode}.`;
20410
+ case "error": return status.message;
20411
+ }
20412
+ }
20413
+ function terminalTabLabel(tab, index) {
20414
+ if (tab.status.type === "connected" && tab.status.shell) return `${index + 1}: ${tab.status.shell}`;
20415
+ return `Terminal ${index + 1}`;
20416
+ }
20417
+ function statusDotClassName(status) {
20418
+ switch (status.type) {
20419
+ case "connected": return "bg-success";
20420
+ case "connecting": return "bg-warning";
20421
+ case "error":
20422
+ case "exited": return "bg-destructive";
20423
+ case "idle": return "bg-muted-foreground/50";
20424
+ }
20425
+ }
20426
+ function cssVariable(name, fallback) {
20427
+ return window.getComputedStyle(document.documentElement).getPropertyValue(name).trim() || fallback;
20428
+ }
20429
+ function resolveTerminalThemeMode() {
20430
+ for (const className of document.documentElement.classList) {
20431
+ const mode = appliedThemeClassColorMode(className);
20432
+ if (mode) return mode;
20433
+ }
20434
+ return window.matchMedia("(prefers-color-scheme: dark)").matches ? "dark" : "light";
20435
+ }
20436
+ function terminalAnsiTheme() {
20437
+ const fallback = TERMINAL_ANSI_THEMES[resolveTerminalThemeMode()];
20438
+ return {
20439
+ black: cssVariable("--terminal-ansi-black", fallback.black),
20440
+ red: cssVariable("--terminal-ansi-red", fallback.red),
20441
+ green: cssVariable("--terminal-ansi-green", fallback.green),
20442
+ yellow: cssVariable("--terminal-ansi-yellow", fallback.yellow),
20443
+ blue: cssVariable("--terminal-ansi-blue", fallback.blue),
20444
+ magenta: cssVariable("--terminal-ansi-magenta", fallback.magenta),
20445
+ cyan: cssVariable("--terminal-ansi-cyan", fallback.cyan),
20446
+ white: cssVariable("--terminal-ansi-white", fallback.white),
20447
+ brightBlack: cssVariable("--terminal-ansi-bright-black", fallback.brightBlack),
20448
+ brightRed: cssVariable("--terminal-ansi-bright-red", fallback.brightRed),
20449
+ brightGreen: cssVariable("--terminal-ansi-bright-green", fallback.brightGreen),
20450
+ brightYellow: cssVariable("--terminal-ansi-bright-yellow", fallback.brightYellow),
20451
+ brightBlue: cssVariable("--terminal-ansi-bright-blue", fallback.brightBlue),
20452
+ brightMagenta: cssVariable("--terminal-ansi-bright-magenta", fallback.brightMagenta),
20453
+ brightCyan: cssVariable("--terminal-ansi-bright-cyan", fallback.brightCyan),
20454
+ brightWhite: cssVariable("--terminal-ansi-bright-white", fallback.brightWhite)
20455
+ };
20456
+ }
20457
+ function createTerminalTheme() {
20458
+ const ansiTheme = terminalAnsiTheme();
20459
+ return {
20460
+ ...ansiTheme,
20461
+ background: cssVariable("--terminal-background", cssVariable("--background", "#111111")),
20462
+ cursor: cssVariable("--terminal-cursor", cssVariable("--primary", "#7aa2f7")),
20463
+ foreground: cssVariable("--terminal-foreground", cssVariable("--foreground", ansiTheme.brightWhite)),
20464
+ selectionBackground: cssVariable("--terminal-selection-background", cssVariable("--code-selection", "#334155"))
20465
+ };
20466
+ }
20467
+ function createTerminalFontFamily() {
20468
+ return cssVariable("--font-mono", "Menlo, ui-monospace, SFMono-Regular, Monaco, Consolas, \"Liberation Mono\", \"Courier New\", monospace");
20469
+ }
20470
+ function errorMessage(error) {
20471
+ if (error instanceof Error && error.message.trim()) return error.message;
20472
+ if (typeof error === "string" && error.trim()) return error;
20473
+ return "Unknown terminal error";
20474
+ }
20475
+ function exitedStatus(event) {
20476
+ return typeof event.signal === "number" && event.signal > 0 ? {
20477
+ type: "exited",
20478
+ exitCode: event.exitCode,
20479
+ signal: event.signal
20480
+ } : {
20481
+ type: "exited",
20482
+ exitCode: event.exitCode
20483
+ };
20484
+ }
20485
+ function isTerminalToggleEvent(event) {
20486
+ return event.ctrlKey && !event.altKey && !event.metaKey && !event.shiftKey && (event.key === "`" || event.code === "Backquote");
20487
+ }
20488
+ function TerminalStatusDot({ status }) {
20489
+ if (status.type === "connecting") return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Spinner, {
20490
+ size: "xs",
20491
+ className: "shrink-0"
20492
+ });
20493
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
20494
+ "aria-hidden": "true",
20495
+ className: cn("size-2 shrink-0 rounded-full", statusDotClassName(status))
20496
+ });
20497
+ }
20498
+ function TerminalStatusDialog({ cwd, index, onOpenChange, open, tab }) {
20499
+ const status = tab?.status ?? { type: "idle" };
20500
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Dialog$1, {
20501
+ open,
20502
+ onOpenChange,
20503
+ focusPromptOnClose: false,
20504
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(DialogContent, {
20505
+ className: "max-w-sm",
20506
+ children: [
20507
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(DialogHeader, { children: [/* @__PURE__ */ (0, import_jsx_runtime.jsxs)(DialogTitle, { children: [tab ? terminalTabLabel(tab, index) : "Terminal", " status"] }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(DialogDescription, { children: terminalStatusDetail(status) })] }),
20508
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("dl", {
20509
+ className: "grid gap-3 text-sm",
20510
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
20511
+ className: "grid gap-1",
20512
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)("dt", {
20513
+ className: "text-xs font-medium text-muted-foreground",
20514
+ children: "Status"
20515
+ }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)("dd", { children: terminalStatusText(status) })]
20516
+ }), /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
20517
+ className: "grid gap-1",
20518
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)("dt", {
20519
+ className: "text-xs font-medium text-muted-foreground",
20520
+ children: "Working directory"
20521
+ }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)("dd", {
20522
+ className: "break-all",
20523
+ children: cwd
20524
+ })]
20525
+ })]
20526
+ }),
20527
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(DialogFooter, { showCloseButton: true })
20528
+ ]
20529
+ })
20530
+ });
20531
+ }
20532
+ function TerminalTabPane({ active, cwd, onStatusChange, onTerminalIdChange, selected, sessionId, tabId, viewerContextId }) {
20533
+ const terminalElementRef = import_react.useRef(null);
20534
+ const terminalRef = import_react.useRef(null);
20535
+ const terminalIdRef = import_react.useRef(null);
20536
+ const activeRef = import_react.useRef(active);
20537
+ const fitAndResizeRef = import_react.useRef(() => {});
20538
+ const onStatusChangeRef = import_react.useRef(onStatusChange);
20539
+ const onTerminalIdChangeRef = import_react.useRef(onTerminalIdChange);
20540
+ const [status, setStatus] = import_react.useState({ type: "idle" });
20541
+ import_react.useLayoutEffect(() => {
20542
+ activeRef.current = active;
20543
+ }, [active]);
20544
+ import_react.useEffect(() => {
20545
+ onStatusChangeRef.current = onStatusChange;
20546
+ }, [onStatusChange]);
20547
+ import_react.useEffect(() => {
20548
+ onTerminalIdChangeRef.current = onTerminalIdChange;
20549
+ }, [onTerminalIdChange]);
20550
+ import_react.useEffect(() => {
20551
+ if (!cwd) {
20552
+ setStatus({ type: "idle" });
20553
+ onStatusChangeRef.current(tabId, { type: "idle" });
20554
+ return;
20555
+ }
20556
+ const terminalElement = terminalElementRef.current;
20557
+ if (!terminalElement) return;
20558
+ let disposed = false;
20559
+ let eventSource = null;
20560
+ let inputDisposable = null;
20561
+ let fitAddon = null;
20562
+ let resizeObserver = null;
20563
+ let inputFlushTimer = null;
20564
+ let resizeFrame = null;
20565
+ let inputBuffer = "";
20566
+ let lastResizeKey = "";
20567
+ const requestOptions = sessionId ? {
20568
+ contextId: viewerContextId,
20569
+ sessionId
20570
+ } : { contextId: viewerContextId };
20571
+ const publishStatus = (nextStatus) => {
20572
+ if (disposed) return;
20573
+ setStatus(nextStatus);
20574
+ onStatusChangeRef.current(tabId, nextStatus);
20575
+ };
20576
+ const postJson = async (path, body) => {
20577
+ return await fetchJson(buildRequestUrl(path, requestOptions), {
20578
+ body: JSON.stringify(body),
20579
+ headers: { "content-type": "application/json" },
20580
+ method: "POST"
20581
+ });
20582
+ };
20583
+ const flushInput = () => {
20584
+ inputFlushTimer = null;
20585
+ const id = terminalIdRef.current;
20586
+ if (!id || !inputBuffer) return;
20587
+ const data = inputBuffer;
20588
+ inputBuffer = "";
20589
+ postJson(`/api/terminal/${encodeURIComponent(id)}/input`, { data }).catch((error) => {
20590
+ if (!disposed) publishStatus({
20591
+ type: "error",
20592
+ message: errorMessage(error)
20593
+ });
20594
+ });
20595
+ };
20596
+ const scheduleInputFlush = () => {
20597
+ if (inputFlushTimer !== null) return;
20598
+ inputFlushTimer = window.setTimeout(flushInput, 12);
20599
+ };
20600
+ const fitAndResize = () => {
20601
+ const terminal = terminalRef.current;
20602
+ if (!terminal || !fitAddon) return;
20603
+ try {
20604
+ fitAddon.fit();
20605
+ } catch {
20606
+ return;
20607
+ }
20608
+ const id = terminalIdRef.current;
20609
+ if (!id) return;
20610
+ const resizeKey = `${terminal.cols}x${terminal.rows}`;
20611
+ if (resizeKey === lastResizeKey) return;
20612
+ lastResizeKey = resizeKey;
20613
+ postJson(`/api/terminal/${encodeURIComponent(id)}/resize`, {
20614
+ cols: terminal.cols,
20615
+ rows: terminal.rows
20616
+ }).catch(() => {});
20617
+ };
20618
+ const scheduleFitAndResize = () => {
20619
+ if (resizeFrame !== null) return;
20620
+ resizeFrame = window.requestAnimationFrame(() => {
20621
+ resizeFrame = null;
20622
+ fitAndResize();
20623
+ });
20624
+ };
20625
+ fitAndResizeRef.current = scheduleFitAndResize;
20626
+ const startTerminal = async () => {
20627
+ try {
20628
+ publishStatus({ type: "connecting" });
20629
+ terminalElement.textContent = "";
20630
+ const [{ Terminal }, { FitAddon }] = await Promise.all([import("../_libs/xterm__xterm.mjs").then((n) => n.t), import("../_libs/xterm__addon-fit.mjs").then((n) => n.t)]);
20631
+ if (disposed) return;
20632
+ const terminal = new Terminal({
20633
+ allowTransparency: true,
20634
+ convertEol: false,
20635
+ cursorBlink: true,
20636
+ fontFamily: createTerminalFontFamily(),
20637
+ fontSize: 12,
20638
+ letterSpacing: 0,
20639
+ lineHeight: 1.2,
20640
+ scrollback: 5e3,
20641
+ theme: createTerminalTheme()
20642
+ });
20643
+ fitAddon = new FitAddon();
20644
+ terminal.loadAddon(fitAddon);
20645
+ terminal.open(terminalElement);
20646
+ terminalRef.current = terminal;
20647
+ inputDisposable = terminal.onData((data) => {
20648
+ if (!activeRef.current) return;
20649
+ inputBuffer += data;
20650
+ scheduleInputFlush();
20651
+ });
20652
+ scheduleFitAndResize();
20653
+ const terminalResponse = await postJson("/api/terminal", {
20654
+ clientKey: tabId,
20655
+ cols: terminal.cols,
20656
+ rows: terminal.rows
20657
+ });
20658
+ if (disposed) return;
20659
+ terminalIdRef.current = terminalResponse.id;
20660
+ onTerminalIdChangeRef.current(tabId, terminalResponse.id);
20661
+ publishStatus({
20662
+ type: "connected",
20663
+ shell: terminalResponse.shell
20664
+ });
20665
+ eventSource = new EventSource(buildRequestUrl(`/api/terminal/${encodeURIComponent(terminalResponse.id)}/events`, requestOptions));
20666
+ eventSource.onmessage = (message) => {
20667
+ if (disposed) return;
20668
+ const event = JSON.parse(message.data);
20669
+ if (event.type === "output") {
20670
+ terminal.write(event.data);
20671
+ return;
20672
+ }
20673
+ if (event.type === "ready") {
20674
+ publishStatus({
20675
+ type: "connected",
20676
+ shell: event.shell
20677
+ });
20678
+ return;
20679
+ }
20680
+ if (event.type === "exit") {
20681
+ publishStatus(exitedStatus(event));
20682
+ return;
20683
+ }
20684
+ if (event.type === "error") publishStatus({
20685
+ type: "error",
20686
+ message: event.error
20687
+ });
20688
+ };
20689
+ eventSource.onerror = () => {
20690
+ if (!disposed && terminalIdRef.current) setStatus((current) => {
20691
+ if (current.type === "connected") return current;
20692
+ const nextStatus = {
20693
+ type: "error",
20694
+ message: "Terminal connection interrupted."
20695
+ };
20696
+ onStatusChangeRef.current(tabId, nextStatus);
20697
+ return nextStatus;
20698
+ });
20699
+ };
20700
+ resizeObserver = new ResizeObserver(scheduleFitAndResize);
20701
+ resizeObserver.observe(terminalElement);
20702
+ scheduleFitAndResize();
20703
+ } catch (error) {
20704
+ if (disposed) return;
20705
+ const message = errorMessage(error);
20706
+ publishStatus({
20707
+ type: "error",
20708
+ message
20709
+ });
20710
+ terminalRef.current?.writeln(`\r\n${message}`);
20711
+ }
20712
+ };
20713
+ startTerminal();
20714
+ return () => {
20715
+ disposed = true;
20716
+ if (inputFlushTimer !== null) window.clearTimeout(inputFlushTimer);
20717
+ if (resizeFrame !== null) window.cancelAnimationFrame(resizeFrame);
20718
+ flushInput();
20719
+ resizeObserver?.disconnect();
20720
+ eventSource?.close();
20721
+ inputDisposable?.dispose();
20722
+ terminalRef.current?.dispose();
20723
+ terminalRef.current = null;
20724
+ terminalIdRef.current = null;
20725
+ fitAndResizeRef.current = () => {};
20726
+ };
20727
+ }, [
20728
+ cwd,
20729
+ sessionId,
20730
+ tabId,
20731
+ viewerContextId
20732
+ ]);
20733
+ import_react.useEffect(() => {
20734
+ const applyTheme = () => {
20735
+ const terminal = terminalRef.current;
20736
+ if (!terminal) return;
20737
+ terminal.options.theme = createTerminalTheme();
20738
+ terminal.options.fontFamily = createTerminalFontFamily();
20739
+ };
20740
+ const observer = new MutationObserver(applyTheme);
20741
+ observer.observe(document.documentElement, {
20742
+ attributeFilter: ["class", "data-pico-theme-mode"],
20743
+ attributes: true
20744
+ });
20745
+ applyTheme();
20746
+ return () => observer.disconnect();
20747
+ }, []);
20748
+ import_react.useEffect(() => {
20749
+ if (!active) {
20750
+ terminalRef.current?.blur();
20751
+ return;
20752
+ }
20753
+ const frame = window.requestAnimationFrame(() => {
20754
+ fitAndResizeRef.current();
20755
+ terminalRef.current?.focus();
20756
+ });
20757
+ return () => window.cancelAnimationFrame(frame);
20758
+ }, [active, status]);
20759
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
20760
+ "aria-hidden": !selected,
20761
+ "data-terminal-tab": tabId,
20762
+ className: cn("absolute inset-0 min-h-0 w-full overflow-hidden bg-background", selected ? "z-10 opacity-100" : "pointer-events-none z-0 opacity-0"),
20763
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
20764
+ ref: terminalElementRef,
20765
+ className: "h-full min-h-0 w-full"
20766
+ })
20767
+ });
20768
+ }
20769
+ function TerminalPanel({ active, className, cwd, onClose, sessionId, showCloseButton = true, viewerContextId }) {
20770
+ const normalizedCwd = cwd?.trim() || "";
20771
+ const scopeKey = `${viewerContextId}:${sessionId ?? "no-session"}:${normalizedCwd}`;
20772
+ const [tabsState, setTabsState] = import_react.useState(() => createTerminalTabsState(scopeKey));
20773
+ const [statusDialogTabId, setStatusDialogTabId] = import_react.useState(null);
20774
+ const tabs = tabsState.scopeKey === scopeKey ? tabsState.tabs : [];
20775
+ const selectedTabId = tabsState.scopeKey === scopeKey ? tabsState.activeTabId ?? tabs[0]?.id ?? null : null;
20776
+ import_react.useEffect(() => {
20777
+ if (tabsState.scopeKey === scopeKey) return;
20778
+ setTabsState(createTerminalTabsState(scopeKey));
20779
+ }, [scopeKey, tabsState.scopeKey]);
20780
+ import_react.useEffect(() => {
20781
+ if (!active || !normalizedCwd || tabs.length > 0 || tabsState.scopeKey !== scopeKey) return;
20782
+ const tab = createTerminalTab();
20783
+ const nextState = {
20784
+ activeTabId: tab.id,
20785
+ scopeKey,
20786
+ tabs: [tab]
20787
+ };
20788
+ rememberTerminalTabsState(nextState);
20789
+ setTabsState(nextState);
20790
+ }, [
20791
+ active,
20792
+ normalizedCwd,
20793
+ scopeKey,
20794
+ tabs.length,
20795
+ tabsState.scopeKey
20796
+ ]);
20797
+ const activeStatus = tabs.find((tab) => tab.id === selectedTabId)?.status ?? { type: "idle" };
20798
+ const activeError = activeStatus.type === "error" ? activeStatus.message : void 0;
20799
+ const statusDialogTab = tabs.find((tab) => tab.id === statusDialogTabId);
20800
+ const statusDialogTabIndex = statusDialogTab ? tabs.indexOf(statusDialogTab) : 0;
20801
+ const requestOptions = sessionId ? {
20802
+ contextId: viewerContextId,
20803
+ sessionId
20804
+ } : { contextId: viewerContextId };
20805
+ const addTerminalTab = () => {
20806
+ if (!normalizedCwd) return;
20807
+ const tab = createTerminalTab();
20808
+ setTabsState((current) => {
20809
+ const nextState = current.scopeKey !== scopeKey ? {
20810
+ activeTabId: tab.id,
20811
+ scopeKey,
20812
+ tabs: [tab]
20813
+ } : {
20814
+ ...current,
20815
+ activeTabId: tab.id,
20816
+ tabs: [...current.tabs, tab]
20817
+ };
20818
+ rememberTerminalTabsState(nextState);
20819
+ return nextState;
20820
+ });
20821
+ };
20822
+ const closeServerTerminal = (terminalId) => {
20823
+ if (!terminalId) return;
20824
+ fetchJson(buildRequestUrl(`/api/terminal/${encodeURIComponent(terminalId)}/close`, requestOptions), { method: "POST" }).catch(() => {});
20825
+ };
20826
+ const closeTerminalTab = (tabId) => {
20827
+ if (tabs.length <= 0) return;
20828
+ closeServerTerminal(tabs.find((tab) => tab.id === tabId)?.terminalId ?? null);
20829
+ setTabsState((current) => {
20830
+ if (current.scopeKey !== scopeKey || current.tabs.length <= 0) return current;
20831
+ const closingIndex = current.tabs.findIndex((tab) => tab.id === tabId);
20832
+ const nextTabs = current.tabs.filter((tab) => tab.id !== tabId);
20833
+ const nextActiveTabId = current.activeTabId === tabId ? nextTabs[Math.min(Math.max(closingIndex, 0), nextTabs.length - 1)]?.id ?? null : current.activeTabId;
20834
+ const nextState = {
20835
+ ...current,
20836
+ activeTabId: nextActiveTabId,
20837
+ tabs: nextTabs
20838
+ };
20839
+ rememberTerminalTabsState(nextState);
20840
+ return nextState;
20841
+ });
20842
+ };
20843
+ const updateTabStatus = (tabId, status) => {
20844
+ setTabsState((current) => {
20845
+ if (current.scopeKey !== scopeKey) return current;
20846
+ return {
20847
+ ...current,
20848
+ tabs: current.tabs.map((tab) => tab.id === tabId ? {
20849
+ ...tab,
20850
+ status
20851
+ } : tab)
20852
+ };
20853
+ });
20854
+ };
20855
+ const updateTabTerminalId = (tabId, terminalId) => {
20856
+ setTabsState((current) => {
20857
+ if (current.scopeKey !== scopeKey) return current;
20858
+ return {
20859
+ ...current,
20860
+ tabs: current.tabs.map((tab) => tab.id === tabId ? {
20861
+ ...tab,
20862
+ terminalId
20863
+ } : tab)
20864
+ };
20865
+ });
20866
+ };
20867
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("section", {
20868
+ "data-terminal-panel": "true",
20869
+ className: cn("pico-terminal-panel flex h-full min-h-0 w-full min-w-0 flex-col bg-background", className),
20870
+ onKeyDownCapture: (event) => {
20871
+ if (!isTerminalToggleEvent(event) || !onClose) return;
20872
+ event.preventDefault();
20873
+ event.stopPropagation();
20874
+ event.nativeEvent.stopImmediatePropagation();
20875
+ onClose();
20876
+ },
20877
+ children: [
20878
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
20879
+ className: "flex min-h-10 shrink-0 items-center gap-2 border-b border-border/70 px-2",
20880
+ children: [
20881
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
20882
+ className: "hidden max-w-56 min-w-0 flex-col gap-0 px-1 sm:flex",
20883
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
20884
+ className: "truncate text-xs font-medium",
20885
+ children: "Terminal"
20886
+ }), /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
20887
+ className: "flex min-w-0 items-center gap-1 truncate text-[11px] text-muted-foreground",
20888
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(TerminalStatusDot, { status: activeStatus }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
20889
+ className: "truncate",
20890
+ children: normalizedCwd ? formatDisplayPath$1(normalizedCwd) : "No cwd"
20891
+ })]
20892
+ })]
20893
+ }),
20894
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
20895
+ className: "flex min-w-0 flex-1 items-center gap-1 overflow-x-auto py-1",
20896
+ children: [tabs.map((tab, index) => {
20897
+ const selected = tab.id === selectedTabId;
20898
+ const label = terminalTabLabel(tab, index);
20899
+ const statusText = terminalStatusText(tab.status);
20900
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
20901
+ className: cn("group flex h-7 max-w-48 shrink-0 items-center overflow-hidden rounded-md border text-xs", selected ? "border-border bg-muted text-foreground" : "border-transparent bg-transparent text-muted-foreground hover:bg-muted/60 hover:text-foreground"),
20902
+ children: [
20903
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(TitleTooltip, {
20904
+ title: `${label} · ${statusText}`,
20905
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", {
20906
+ type: "button",
20907
+ className: "flex h-full w-7 shrink-0 items-center justify-center outline-hidden focus-visible:ring-1 focus-visible:ring-ring",
20908
+ "aria-label": `Show ${label} status`,
20909
+ onClick: (event) => {
20910
+ event.stopPropagation();
20911
+ setStatusDialogTabId(tab.id);
20912
+ },
20913
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(TerminalStatusDot, { status: tab.status })
20914
+ })
20915
+ }),
20916
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(TitleTooltip, {
20917
+ title: `${label} · ${statusText}`,
20918
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", {
20919
+ type: "button",
20920
+ className: "flex h-full min-w-0 flex-1 items-center px-1 text-left outline-hidden focus-visible:ring-1 focus-visible:ring-ring",
20921
+ onClick: () => {
20922
+ setTabsState((current) => {
20923
+ if (current.scopeKey !== scopeKey) return current;
20924
+ const nextState = {
20925
+ ...current,
20926
+ activeTabId: tab.id
20927
+ };
20928
+ rememberTerminalTabsState(nextState);
20929
+ return nextState;
20930
+ });
20931
+ },
20932
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
20933
+ className: "truncate",
20934
+ children: label
20935
+ })
20936
+ })
20937
+ }),
20938
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(TitleTooltip, {
20939
+ title: `Close ${label}`,
20940
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", {
20941
+ type: "button",
20942
+ className: "flex h-full w-6 shrink-0 items-center justify-center text-muted-foreground opacity-70 outline-hidden group-hover:opacity-100 hover:bg-muted hover:text-foreground focus-visible:ring-1 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-30",
20943
+ "aria-label": `Close ${label}`,
20944
+ onClick: (event) => {
20945
+ event.stopPropagation();
20946
+ closeTerminalTab(tab.id);
20947
+ },
20948
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(X, { className: "size-3" })
20949
+ })
20950
+ })
20951
+ ]
20952
+ }, tab.id);
20953
+ }), normalizedCwd ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(TitleTooltip, {
20954
+ title: "New terminal tab",
20955
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Button$1, {
20956
+ size: "icon",
20957
+ variant: "ghost",
20958
+ className: "size-7 shrink-0",
20959
+ "aria-label": "New terminal tab",
20960
+ onClick: addTerminalTab,
20961
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Plus, { className: "size-4" })
20962
+ })
20963
+ }) : null]
20964
+ }),
20965
+ activeError ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(TitleTooltip, {
20966
+ title: activeError,
20967
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
20968
+ className: "hidden max-w-48 truncate text-[11px] text-destructive sm:inline",
20969
+ children: activeError
20970
+ })
20971
+ }) : null,
20972
+ onClose && showCloseButton ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(TitleTooltip, {
20973
+ title: "Hide terminal",
20974
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Button$1, {
20975
+ size: "icon",
20976
+ variant: "ghost",
20977
+ className: "size-8 shrink-0",
20978
+ "aria-label": "Hide terminal",
20979
+ onClick: onClose,
20980
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(X, {})
20981
+ })
20982
+ }) : null
20983
+ ]
20984
+ }),
20985
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(TerminalStatusDialog, {
20986
+ cwd: normalizedCwd,
20987
+ index: statusDialogTabIndex,
20988
+ open: statusDialogTab !== void 0,
20989
+ tab: statusDialogTab,
20990
+ onOpenChange: (open) => {
20991
+ if (!open) setStatusDialogTabId(null);
20992
+ }
20993
+ }),
20994
+ normalizedCwd ? tabs.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
20995
+ className: "relative min-h-0 flex-1 overflow-hidden bg-background",
20996
+ children: tabs.map((tab) => {
20997
+ const selected = tab.id === selectedTabId;
20998
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(TerminalTabPane, {
20999
+ active: active && selected,
21000
+ cwd: normalizedCwd,
21001
+ onStatusChange: updateTabStatus,
21002
+ onTerminalIdChange: updateTabTerminalId,
21003
+ selected,
21004
+ sessionId,
21005
+ tabId: tab.id,
21006
+ viewerContextId
21007
+ }, tab.id);
21008
+ })
21009
+ }) : /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
21010
+ className: "flex min-h-0 flex-1 flex-col items-center justify-center gap-3 p-6 text-sm text-muted-foreground",
21011
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { children: "No terminal tabs are open." }), /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Button$1, {
21012
+ size: "sm",
21013
+ variant: "outline",
21014
+ onClick: addTerminalTab,
21015
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Plus, { className: "size-4" }), "New terminal tab"]
21016
+ })]
21017
+ }) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
21018
+ className: "flex min-h-0 flex-1 items-center justify-center p-6 text-sm text-muted-foreground",
21019
+ children: "Open a session before starting a terminal."
21020
+ })
21021
+ ]
21022
+ });
21023
+ }
20252
21024
  function readStoredRightSidebarActiveTab() {
20253
21025
  const stored = safeLocalStorageGetItem(RIGHT_SIDEBAR_ACTIVE_TAB_STORAGE_KEY);
20254
21026
  return stored === "files" || stored === "review" ? stored : "review";
@@ -20363,22 +21135,33 @@ function reorderRightSidebarFiles(store, paths) {
20363
21135
  fileTabs: uniquePaths
20364
21136
  }));
20365
21137
  }
21138
+ function shallowTerminalSession(left, right) {
21139
+ return left.cwd === right.cwd && left.sessionId === right.sessionId;
21140
+ }
20366
21141
  function AppShellTabsList({ sessionStore, viewerContextId }) {
20367
21142
  const cwd = useSelector(sessionStore, (sessionState) => sessionState.cwd);
20368
21143
  return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(TabsList, {
20369
21144
  className: "w-full gap-2 rounded-none border-b border-border/70 bg-background p-2 group-data-horizontal/tabs:h-auto md:hidden",
20370
- children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(TabsTrigger, {
20371
- value: "session",
20372
- className: "h-8 data-active:bg-muted dark:data-active:bg-muted",
20373
- children: "Session"
20374
- }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(TabsTrigger, {
20375
- value: "git",
20376
- className: "h-8 data-active:bg-muted dark:data-active:bg-muted",
20377
- children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(GitTabStatusText, {
20378
- viewerContextId,
20379
- cwd
21145
+ children: [
21146
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(TabsTrigger, {
21147
+ value: "session",
21148
+ className: "h-8 data-active:bg-muted dark:data-active:bg-muted",
21149
+ children: "Session"
21150
+ }),
21151
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(TabsTrigger, {
21152
+ value: "git",
21153
+ className: "h-8 data-active:bg-muted dark:data-active:bg-muted",
21154
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(GitTabStatusText, {
21155
+ viewerContextId,
21156
+ cwd
21157
+ })
21158
+ }),
21159
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(TabsTrigger, {
21160
+ value: "terminal",
21161
+ className: "h-8 data-active:bg-muted dark:data-active:bg-muted",
21162
+ children: "Terminal"
20380
21163
  })
20381
- })]
21164
+ ]
20382
21165
  });
20383
21166
  }
20384
21167
  var AppShellGitPanelController = import_react.memo(function AppShellGitPanelController({ active, composerDiffLineComments, onAddDiffLineComment, onCloseAllFiles, onCloseFile, onCloseFilesToRight, onCloseOtherFiles, onOpenFile, onReorderFiles, rightSidebarStore, sessionStore, viewerContextId }) {
@@ -20457,6 +21240,10 @@ var DESKTOP_DEFAULT_GIT_PANEL_WIDTH = 320;
20457
21240
  var DESKTOP_MIN_SESSION_WIDTH = 320;
20458
21241
  var DESKTOP_MIN_SIDE_PANEL_WIDTH = 260;
20459
21242
  var DESKTOP_MAX_STORED_SIDE_PANEL_WIDTH = 1600;
21243
+ var DESKTOP_DEFAULT_TERMINAL_PANEL_HEIGHT = 280;
21244
+ var DESKTOP_MIN_TERMINAL_PANEL_HEIGHT = 160;
21245
+ var DESKTOP_MIN_TOP_PANEL_HEIGHT = 220;
21246
+ var DESKTOP_MAX_STORED_TERMINAL_PANEL_HEIGHT = 1200;
20460
21247
  function defaultDesktopSidePanelWidths() {
20461
21248
  return {
20462
21249
  fileViewWidth: DESKTOP_DEFAULT_FILE_VIEW_WIDTH,
@@ -20491,6 +21278,18 @@ function storeDesktopSidePanelWidths(widths) {
20491
21278
  function sameDesktopSidePanelWidths(left, right) {
20492
21279
  return left.fileViewWidth === right.fileViewWidth && left.gitPanelWidth === right.gitPanelWidth;
20493
21280
  }
21281
+ function clampStoredDesktopTerminalPanelHeight(value) {
21282
+ if (value == null || value === "") return DESKTOP_DEFAULT_TERMINAL_PANEL_HEIGHT;
21283
+ const height = typeof value === "number" ? value : Number(value);
21284
+ if (!Number.isFinite(height)) return DESKTOP_DEFAULT_TERMINAL_PANEL_HEIGHT;
21285
+ return Math.round(Math.min(DESKTOP_MAX_STORED_TERMINAL_PANEL_HEIGHT, Math.max(DESKTOP_MIN_TERMINAL_PANEL_HEIGHT, height)));
21286
+ }
21287
+ function readStoredDesktopTerminalPanelHeight() {
21288
+ return clampStoredDesktopTerminalPanelHeight(safeLocalStorageGetItem(TERMINAL_PANEL_HEIGHT_STORAGE_KEY));
21289
+ }
21290
+ function storeDesktopTerminalPanelHeight(height) {
21291
+ safeLocalStorageSetItem(TERMINAL_PANEL_HEIGHT_STORAGE_KEY, String(clampStoredDesktopTerminalPanelHeight(height)));
21292
+ }
20494
21293
  function clampDesktopPanelSize(value, min, max) {
20495
21294
  if (!Number.isFinite(value)) return min;
20496
21295
  if (max <= min) return Math.max(0, max);
@@ -20614,21 +21413,106 @@ function AppShellDesktopResizeHandle({ label, max, min, onResize, onResizeEnd, o
20614
21413
  }
20615
21414
  });
20616
21415
  }
21416
+ function AppShellDesktopVerticalResizeHandle({ label, max, min, onResize, onResizeEnd, onResizeStart, size }) {
21417
+ const propsRef = useLatestRef({
21418
+ max,
21419
+ min,
21420
+ onResize,
21421
+ onResizeEnd,
21422
+ onResizeStart,
21423
+ size
21424
+ });
21425
+ const [verticalResizeCursor, setVerticalResizeCursor] = import_react.useState("row-resize");
21426
+ const [resizeTargetMinimumSize, setResizeTargetMinimumSize] = import_react.useState(10);
21427
+ import_react.useEffect(() => {
21428
+ const updateResizeTarget = () => {
21429
+ setResizeTargetMinimumSize(getSidebarResizeTargetMinimumSize());
21430
+ };
21431
+ const coarsePointerQuery = window.matchMedia("(pointer:coarse)");
21432
+ setVerticalResizeCursor(getSidebarVerticalResizeCursor());
21433
+ updateResizeTarget();
21434
+ coarsePointerQuery.addEventListener("change", updateResizeTarget);
21435
+ return () => {
21436
+ coarsePointerQuery.removeEventListener("change", updateResizeTarget);
21437
+ };
21438
+ }, []);
21439
+ const verticalResizeCursorClass = verticalResizeCursor === "ns-resize" ? "cursor-ns-resize" : "cursor-row-resize";
21440
+ const resizeTo = (nextSize) => {
21441
+ const current = propsRef.current;
21442
+ current.onResize(clampDesktopPanelSize(nextSize, current.min, current.max));
21443
+ };
21444
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
21445
+ role: "separator",
21446
+ "aria-label": label,
21447
+ "aria-orientation": "horizontal",
21448
+ tabIndex: 0,
21449
+ style: {
21450
+ "--resize-target-height": `${resizeTargetMinimumSize}px`,
21451
+ cursor: verticalResizeCursor
21452
+ },
21453
+ className: `absolute inset-x-0 top-0 z-20 h-(--resize-target-height) -translate-y-1/2 touch-none bg-transparent outline-hidden after:absolute after:inset-x-0 after:top-1/2 after:h-px after:bg-transparent focus-visible:ring-1 focus-visible:ring-ring focus-visible:ring-offset-1 ${resizeRailPrimaryInteractiveClass} ${verticalResizeCursorClass}`,
21454
+ onKeyDown: (event) => {
21455
+ if (event.key !== "ArrowUp" && event.key !== "ArrowDown") return;
21456
+ event.preventDefault();
21457
+ const delta = event.shiftKey ? 48 : 16;
21458
+ resizeTo(propsRef.current.size + (event.key === "ArrowUp" ? delta : -delta));
21459
+ },
21460
+ onPointerDown: (event) => {
21461
+ if (event.button !== 0) return;
21462
+ event.preventDefault();
21463
+ const current = propsRef.current;
21464
+ const startY = event.clientY;
21465
+ const startSize = current.size;
21466
+ const cursor = getSidebarVerticalResizeCursor();
21467
+ const previousCursor = document.body.style.cursor;
21468
+ const previousUserSelect = document.body.style.userSelect;
21469
+ const cleanupGlobalResizeCursor = installGlobalResizeCursor(cursor);
21470
+ current.onResizeStart?.();
21471
+ Object.assign(document.body.style, {
21472
+ userSelect: "none",
21473
+ cursor
21474
+ });
21475
+ const handlePointerMove = (moveEvent) => {
21476
+ resizeTo(startSize + startY - moveEvent.clientY);
21477
+ };
21478
+ const handlePointerUp = () => {
21479
+ cleanupGlobalResizeCursor();
21480
+ Object.assign(document.body.style, {
21481
+ userSelect: previousUserSelect,
21482
+ cursor: previousCursor
21483
+ });
21484
+ document.removeEventListener("pointermove", handlePointerMove);
21485
+ document.removeEventListener("pointerup", handlePointerUp);
21486
+ document.removeEventListener("pointercancel", handlePointerUp);
21487
+ propsRef.current.onResizeEnd?.();
21488
+ };
21489
+ document.addEventListener("pointermove", handlePointerMove);
21490
+ document.addEventListener("pointerup", handlePointerUp);
21491
+ document.addEventListener("pointercancel", handlePointerUp);
21492
+ }
21493
+ });
21494
+ }
20617
21495
  function AppShellTabsController(props) {
20618
21496
  return useAppShellTabsControllerView(props);
20619
21497
  }
20620
- function useAppShellTabsControllerView({ actionsRef, appUiStore, awaitingFirstTurn, composerDiffLineComments, composerPanelRef, contextUsageStore, conversationFrameRef, conversationItemsStore, defaultNewSessionDirectory, displaySettingsStore, fileInputRef, gitPanelOpen, hiddenThinkingPreviewStore, isSessionViewLoading, isSubmitting, isMobile, newSessionDirectoryOptions, onCancelCompaction, onCreateSession, onOpenAddDirectoryDialog, onCloseAllFileViewTabs, onCloseFileViewTab, onCloseFileViewTabsToRight, onCloseOtherFileViewTabs, onAddDiffLineComment, onOpenFileViewTab, onReorderFileViewTabs, onValueChange, rightSidebarStore, sessionStore, store, viewerContextId, workingStateStore }) {
21498
+ function useAppShellTabsControllerView({ actionsRef, appUiStore, awaitingFirstTurn, composerDiffLineComments, composerPanelRef, contextUsageStore, conversationFrameRef, conversationItemsStore, defaultNewSessionDirectory, displaySettingsStore, fileInputRef, gitPanelOpen, terminalPanelOpen, hiddenThinkingPreviewStore, isSessionViewLoading, isSubmitting, isMobile, newSessionDirectoryOptions, onCancelCompaction, onCreateSession, onOpenAddDirectoryDialog, onCloseAllFileViewTabs, onCloseFileViewTab, onCloseFileViewTabsToRight, onCloseOtherFileViewTabs, onCloseTerminalPanel, onAddDiffLineComment, onOpenFileViewTab, onReorderFileViewTabs, onValueChange, rightSidebarStore, sessionStore, store, viewerContextId, workingStateStore }) {
20621
21499
  const currentTab = useSelector(appUiStore, (state) => state.currentTab);
20622
21500
  const isDraftSession = useSelector(sessionStore, (sessionState) => sessionState.draft);
21501
+ const terminalSession = useSelector(sessionStore, (sessionState) => ({
21502
+ cwd: sessionState.cwd,
21503
+ sessionId: sessionState.sessionId
21504
+ }), { compare: shallowTerminalSession });
20623
21505
  const showTabsList = isMobile || !isDraftSession || isSessionViewLoading;
20624
- const sessionVisibleClassName = currentTab === "git" ? "hidden min-h-0 flex-1 flex-col md:flex" : "flex min-h-0 flex-1 flex-col";
21506
+ const sessionVisibleClassName = currentTab === "session" ? "flex min-h-0 flex-1 flex-col" : "hidden min-h-0 flex-1 flex-col md:flex";
20625
21507
  const mobileGitClassName = currentTab === "git" ? "min-h-0 flex-1 overflow-hidden md:hidden" : "hidden";
21508
+ const mobileTerminalClassName = currentTab === "terminal" ? "min-h-0 flex-1 overflow-hidden md:hidden" : "hidden";
20626
21509
  const rightSidebarHasVisibleFiles = useSelector(rightSidebarStore, selectRightSidebarHasVisibleFiles);
20627
21510
  const desktopGitPanelOpen = !isMobile && gitPanelOpen;
20628
21511
  const desktopFileViewOpen = !isMobile && gitPanelOpen && rightSidebarHasVisibleFiles;
20629
21512
  const desktopSideWorkspaceOpen = desktopFileViewOpen || desktopGitPanelOpen;
20630
21513
  const desktopLayoutRef = import_react.useRef(null);
20631
21514
  const [desktopLayoutWidth, setDesktopLayoutWidth] = import_react.useState(0);
21515
+ const [desktopLayoutHeight, setDesktopLayoutHeight] = import_react.useState(0);
20632
21516
  const [desktopSidePanelWidths, setDesktopSidePanelWidthsState] = import_react.useState(() => defaultDesktopSidePanelWidths());
20633
21517
  const desktopSidePanelWidthsLoadedRef = import_react.useRef(false);
20634
21518
  const desktopSidePanelWidthsRef = import_react.useRef(desktopSidePanelWidths);
@@ -20656,12 +21540,30 @@ function useAppShellTabsControllerView({ actionsRef, appUiStore, awaitingFirstTu
20656
21540
  }));
20657
21541
  };
20658
21542
  const [desktopGitPanelMounted, setDesktopGitPanelMounted] = import_react.useState(desktopGitPanelOpen);
21543
+ const [desktopTerminalPanelHeight, setDesktopTerminalPanelHeightState] = import_react.useState(() => DESKTOP_DEFAULT_TERMINAL_PANEL_HEIGHT);
21544
+ const desktopTerminalPanelHeightLoadedRef = import_react.useRef(false);
21545
+ const desktopTerminalPanelHeightRef = import_react.useRef(desktopTerminalPanelHeight);
21546
+ desktopTerminalPanelHeightRef.current = desktopTerminalPanelHeight;
21547
+ const [desktopTerminalPanelMounted, setDesktopTerminalPanelMounted] = import_react.useState(!isMobile && terminalPanelOpen);
20659
21548
  const [desktopPanelResizing, setDesktopPanelResizing] = import_react.useState(false);
21549
+ const [desktopTerminalResizing, setDesktopTerminalResizing] = import_react.useState(false);
21550
+ const setDesktopTerminalPanelHeight = (action) => {
21551
+ const current = desktopTerminalPanelHeightRef.current;
21552
+ const next = clampStoredDesktopTerminalPanelHeight(applyStoreAction(current, action));
21553
+ if (current === next) return;
21554
+ desktopTerminalPanelHeightRef.current = next;
21555
+ setDesktopTerminalPanelHeightState(next);
21556
+ if (desktopTerminalPanelHeightLoadedRef.current) storeDesktopTerminalPanelHeight(next);
21557
+ };
20660
21558
  import_react.useEffect(() => {
20661
21559
  const storedWidths = readStoredDesktopSidePanelWidths();
21560
+ const storedTerminalPanelHeight = readStoredDesktopTerminalPanelHeight();
20662
21561
  desktopSidePanelWidthsLoadedRef.current = true;
21562
+ desktopTerminalPanelHeightLoadedRef.current = true;
20663
21563
  desktopSidePanelWidthsRef.current = storedWidths;
21564
+ desktopTerminalPanelHeightRef.current = storedTerminalPanelHeight;
20664
21565
  setDesktopSidePanelWidthsState((current) => sameDesktopSidePanelWidths(current, storedWidths) ? current : storedWidths);
21566
+ setDesktopTerminalPanelHeightState((current) => current === storedTerminalPanelHeight ? current : storedTerminalPanelHeight);
20665
21567
  }, []);
20666
21568
  const sessionPane = /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
20667
21569
  className: "flex h-full min-h-0 min-w-0 flex-1 flex-col overflow-hidden",
@@ -20689,7 +21591,7 @@ function useAppShellTabsControllerView({ actionsRef, appUiStore, awaitingFirstTu
20689
21591
  viewerContextId,
20690
21592
  workingStateStore
20691
21593
  })
20692
- }), isMobile ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
21594
+ }), isMobile ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
20693
21595
  className: mobileGitClassName,
20694
21596
  children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(AppShellGitPanelController, {
20695
21597
  viewerContextId,
@@ -20705,32 +21607,50 @@ function useAppShellTabsControllerView({ actionsRef, appUiStore, awaitingFirstTu
20705
21607
  onOpenFile: onOpenFileViewTab,
20706
21608
  onReorderFiles: onReorderFileViewTabs
20707
21609
  })
20708
- }) : null]
21610
+ }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
21611
+ className: mobileTerminalClassName,
21612
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(TerminalPanel, {
21613
+ active: currentTab === "terminal",
21614
+ cwd: terminalSession.cwd,
21615
+ onClose: onCloseTerminalPanel,
21616
+ sessionId: terminalSession.sessionId,
21617
+ showCloseButton: false,
21618
+ viewerContextId
21619
+ })
21620
+ })] }) : null]
20709
21621
  });
20710
21622
  import_react.useLayoutEffect(() => {
20711
21623
  if (isMobile) {
20712
21624
  setDesktopGitPanelMounted(false);
21625
+ setDesktopTerminalPanelMounted(false);
20713
21626
  return;
20714
21627
  }
20715
21628
  if (desktopSideWorkspaceOpen) setDesktopGitPanelMounted(true);
20716
- }, [desktopSideWorkspaceOpen, isMobile]);
21629
+ if (terminalPanelOpen) setDesktopTerminalPanelMounted(true);
21630
+ }, [
21631
+ desktopSideWorkspaceOpen,
21632
+ isMobile,
21633
+ terminalPanelOpen
21634
+ ]);
20717
21635
  import_react.useLayoutEffect(() => {
20718
21636
  if (isMobile) return;
20719
21637
  const element = desktopLayoutRef.current;
20720
21638
  if (!element) return;
20721
- const updateWidth = () => {
21639
+ const updateSize = () => {
20722
21640
  setDesktopLayoutWidth(element.clientWidth);
21641
+ setDesktopLayoutHeight(element.clientHeight);
20723
21642
  };
20724
- updateWidth();
21643
+ updateSize();
20725
21644
  if (typeof ResizeObserver === "undefined") {
20726
- window.addEventListener("resize", updateWidth);
20727
- return () => window.removeEventListener("resize", updateWidth);
21645
+ window.addEventListener("resize", updateSize);
21646
+ return () => window.removeEventListener("resize", updateSize);
20728
21647
  }
20729
- const observer = new ResizeObserver(updateWidth);
21648
+ const observer = new ResizeObserver(updateSize);
20730
21649
  observer.observe(element);
20731
21650
  return () => observer.disconnect();
20732
21651
  }, [isMobile]);
20733
21652
  const desktopAvailableWidth = desktopLayoutWidth || (typeof window === "undefined" ? 1200 : window.innerWidth);
21653
+ const desktopAvailableHeight = desktopLayoutHeight || (typeof window === "undefined" ? 800 : window.innerHeight);
20734
21654
  const desktopGitPanelRendered = desktopSideWorkspaceOpen || desktopGitPanelMounted || desktopGitPanelOpen;
20735
21655
  const desktopFittedWidths = fitDesktopSidePanelWidths({
20736
21656
  availableWidth: desktopAvailableWidth,
@@ -20741,14 +21661,25 @@ function useAppShellTabsControllerView({ actionsRef, appUiStore, awaitingFirstTu
20741
21661
  });
20742
21662
  const desktopSideWorkspaceRendered = desktopSideWorkspaceOpen || desktopGitPanelMounted;
20743
21663
  const desktopSideWorkspaceWidth = desktopSideWorkspaceOpen ? desktopFittedWidths.sideWidth : 0;
20744
- const desktopTransitionClassName = !desktopPanelResizing ? "transition-[width] duration-200 ease-linear" : "";
20745
- const desktopPanelGroupClassName = "relative flex h-full min-h-0 w-full flex-1 overflow-hidden";
21664
+ const desktopTerminalOpen = !isMobile && terminalPanelOpen;
21665
+ const desktopTerminalRendered = desktopTerminalOpen || desktopTerminalPanelMounted;
21666
+ const desktopTerminalMaxHeight = Math.max(DESKTOP_MIN_TERMINAL_PANEL_HEIGHT, desktopAvailableHeight - DESKTOP_MIN_TOP_PANEL_HEIGHT);
21667
+ const desktopFittedTerminalPanelHeight = clampDesktopPanelSize(desktopTerminalPanelHeight, Math.min(DESKTOP_MIN_TERMINAL_PANEL_HEIGHT, desktopTerminalMaxHeight), desktopTerminalMaxHeight);
21668
+ const desktopTerminalVisibleHeight = desktopTerminalOpen ? desktopFittedTerminalPanelHeight : 0;
21669
+ const desktopTransitionClassName = !desktopPanelResizing && !desktopTerminalResizing ? "transition-[width,height] duration-200 ease-linear" : "";
21670
+ const desktopPanelGroupClassName = "relative flex min-h-0 w-full shrink-0 overflow-hidden";
20746
21671
  const desktopResizeStart = () => {
20747
21672
  setDesktopPanelResizing(true);
20748
21673
  };
20749
21674
  const desktopResizeEnd = () => {
20750
21675
  setDesktopPanelResizing(false);
20751
21676
  };
21677
+ const desktopTerminalResizeStart = () => {
21678
+ setDesktopTerminalResizing(true);
21679
+ };
21680
+ const desktopTerminalResizeEnd = () => {
21681
+ setDesktopTerminalResizing(false);
21682
+ };
20752
21683
  const desktopSideWorkspaceMaxWidth = Math.max(0, desktopAvailableWidth - DESKTOP_MIN_SESSION_WIDTH);
20753
21684
  const setDesktopSideWorkspaceWidth = (nextSize) => {
20754
21685
  if (!desktopFileViewOpen) {
@@ -20757,6 +21688,9 @@ function useAppShellTabsControllerView({ actionsRef, appUiStore, awaitingFirstTu
20757
21688
  }
20758
21689
  setDesktopFileViewWidth(Math.max(DESKTOP_MIN_SIDE_PANEL_WIDTH, nextSize - desktopGitPanelWidth));
20759
21690
  };
21691
+ const setDesktopTerminalHeight = (nextSize) => {
21692
+ setDesktopTerminalPanelHeight(clampDesktopPanelSize(nextSize, Math.min(DESKTOP_MIN_TERMINAL_PANEL_HEIGHT, desktopTerminalMaxHeight), desktopTerminalMaxHeight));
21693
+ };
20760
21694
  return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Tabs$1, {
20761
21695
  value: currentTab,
20762
21696
  onValueChange,
@@ -20769,54 +21703,83 @@ function useAppShellTabsControllerView({ actionsRef, appUiStore, awaitingFirstTu
20769
21703
  children: sessionPane
20770
21704
  }) : /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
20771
21705
  ref: desktopLayoutRef,
20772
- className: desktopPanelGroupClassName,
20773
- children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
20774
- "data-desktop-panel": "session",
20775
- className: `h-full min-h-0 min-w-0 shrink-0 overflow-hidden ${desktopTransitionClassName}`,
20776
- style: { width: desktopSideWorkspaceOpen ? `calc(100% - ${desktopSideWorkspaceWidth}px)` : "100%" },
20777
- children: sessionPane
20778
- }), desktopSideWorkspaceRendered ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("aside", {
20779
- "aria-label": "Desktop side workspace",
20780
- "aria-hidden": !desktopSideWorkspaceOpen,
20781
- "data-state": desktopSideWorkspaceOpen ? "open" : "closed",
20782
- "data-desktop-panel": "side-workspace",
20783
- className: `flex h-full min-h-0 min-w-0 shrink-0 overflow-visible bg-background data-[state=closed]:pointer-events-none ${desktopTransitionClassName}`,
20784
- style: { width: `${desktopSideWorkspaceWidth}px` },
20785
- children: desktopGitPanelRendered ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
20786
- "data-desktop-panel": "git",
20787
- className: `relative h-full min-h-0 min-w-0 shrink-0 overflow-visible ${desktopTransitionClassName}`,
21706
+ className: "flex min-h-0 flex-1 flex-col overflow-hidden",
21707
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
21708
+ className: desktopPanelGroupClassName,
21709
+ style: { height: desktopTerminalOpen ? `calc(100% - ${desktopTerminalVisibleHeight}px)` : "100%" },
21710
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
21711
+ "data-desktop-panel": "session",
21712
+ className: `h-full min-h-0 min-w-0 shrink-0 overflow-hidden max-md:w-full! ${desktopTransitionClassName}`,
21713
+ style: { width: desktopSideWorkspaceOpen ? `calc(100% - ${desktopSideWorkspaceWidth}px)` : "100%" },
21714
+ children: sessionPane
21715
+ }), desktopSideWorkspaceRendered ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("aside", {
21716
+ "aria-label": "Desktop side workspace",
21717
+ "aria-hidden": !desktopSideWorkspaceOpen,
21718
+ "data-state": desktopSideWorkspaceOpen ? "open" : "closed",
21719
+ "data-desktop-panel": "side-workspace",
21720
+ className: `hidden h-full min-h-0 min-w-0 shrink-0 overflow-visible bg-background data-[state=closed]:pointer-events-none md:flex ${desktopTransitionClassName}`,
20788
21721
  style: { width: `${desktopSideWorkspaceWidth}px` },
20789
- children: [desktopGitPanelOpen ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(AppShellDesktopResizeHandle, {
20790
- label: "Resize right sidebar",
20791
- min: Math.min(DESKTOP_MIN_SIDE_PANEL_WIDTH, desktopSideWorkspaceMaxWidth),
20792
- max: desktopSideWorkspaceMaxWidth,
20793
- size: desktopSideWorkspaceWidth,
20794
- onResize: setDesktopSideWorkspaceWidth,
20795
- onResizeStart: desktopResizeStart,
20796
- onResizeEnd: desktopResizeEnd
20797
- }) : null, /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
20798
- className: "h-full min-h-0 min-w-0 overflow-hidden",
20799
- children: desktopGitPanelRendered ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(AppShellDesktopGitPanel, {
20800
- viewerContextId,
20801
- sessionStore,
20802
- active: desktopGitPanelOpen,
20803
- composerDiffLineComments,
20804
- rightSidebarStore,
20805
- onAddDiffLineComment,
20806
- onCloseAllFiles: onCloseAllFileViewTabs,
20807
- onCloseFile: onCloseFileViewTab,
20808
- onCloseFilesToRight: onCloseFileViewTabsToRight,
20809
- onCloseOtherFiles: onCloseOtherFileViewTabs,
20810
- onOpenFile: onOpenFileViewTab,
20811
- onReorderFiles: onReorderFileViewTabs
20812
- }) : null
20813
- })]
20814
- }) : null
21722
+ children: desktopGitPanelRendered ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
21723
+ "data-desktop-panel": "git",
21724
+ className: `relative h-full min-h-0 min-w-0 shrink-0 overflow-visible ${desktopTransitionClassName}`,
21725
+ style: { width: `${desktopSideWorkspaceWidth}px` },
21726
+ children: [desktopGitPanelOpen ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(AppShellDesktopResizeHandle, {
21727
+ label: "Resize right sidebar",
21728
+ min: Math.min(DESKTOP_MIN_SIDE_PANEL_WIDTH, desktopSideWorkspaceMaxWidth),
21729
+ max: desktopSideWorkspaceMaxWidth,
21730
+ size: desktopSideWorkspaceWidth,
21731
+ onResize: setDesktopSideWorkspaceWidth,
21732
+ onResizeStart: desktopResizeStart,
21733
+ onResizeEnd: desktopResizeEnd
21734
+ }) : null, /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
21735
+ className: "h-full min-h-0 min-w-0 overflow-hidden",
21736
+ children: desktopGitPanelRendered ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(AppShellDesktopGitPanel, {
21737
+ viewerContextId,
21738
+ sessionStore,
21739
+ active: desktopGitPanelOpen,
21740
+ composerDiffLineComments,
21741
+ rightSidebarStore,
21742
+ onAddDiffLineComment,
21743
+ onCloseAllFiles: onCloseAllFileViewTabs,
21744
+ onCloseFile: onCloseFileViewTab,
21745
+ onCloseFilesToRight: onCloseFileViewTabsToRight,
21746
+ onCloseOtherFiles: onCloseOtherFileViewTabs,
21747
+ onOpenFile: onOpenFileViewTab,
21748
+ onReorderFiles: onReorderFileViewTabs
21749
+ }) : null
21750
+ })]
21751
+ }) : null
21752
+ }) : null]
21753
+ }), desktopTerminalRendered ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("aside", {
21754
+ "aria-label": "Terminal",
21755
+ "aria-hidden": !desktopTerminalOpen,
21756
+ "data-state": desktopTerminalOpen ? "open" : "closed",
21757
+ "data-desktop-panel": "terminal",
21758
+ className: `relative hidden min-h-0 w-full shrink-0 overflow-visible border-t border-border/70 bg-background data-[state=closed]:pointer-events-none data-[state=closed]:border-transparent md:flex ${desktopTransitionClassName}`,
21759
+ style: { height: `${desktopTerminalVisibleHeight}px` },
21760
+ children: [desktopTerminalOpen ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(AppShellDesktopVerticalResizeHandle, {
21761
+ label: "Resize terminal",
21762
+ min: Math.min(DESKTOP_MIN_TERMINAL_PANEL_HEIGHT, desktopTerminalMaxHeight),
21763
+ max: desktopTerminalMaxHeight,
21764
+ size: desktopTerminalVisibleHeight,
21765
+ onResize: setDesktopTerminalHeight,
21766
+ onResizeStart: desktopTerminalResizeStart,
21767
+ onResizeEnd: desktopTerminalResizeEnd
21768
+ }) : null, /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
21769
+ className: "h-full min-h-0 w-full min-w-0 overflow-hidden",
21770
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(TerminalPanel, {
21771
+ active: desktopTerminalOpen,
21772
+ cwd: terminalSession.cwd,
21773
+ onClose: onCloseTerminalPanel,
21774
+ sessionId: terminalSession.sessionId,
21775
+ viewerContextId
21776
+ })
21777
+ })]
20815
21778
  }) : null]
20816
21779
  })]
20817
21780
  });
20818
21781
  }
20819
- var AppShellSessionHeader = import_react.memo(function AppShellSessionHeader({ actionsRef, defaultNewSessionDirectory, displaySessionCwd, gitPanelOpen, loadingDisplaySessionTitle, currentSessionPinned, displaySettingsStore, isSessionViewLoading, newSessionDirectoryOptions, onToggleGitPanel, sessionStore, viewerContextId }) {
21782
+ var AppShellSessionHeader = import_react.memo(function AppShellSessionHeader({ actionsRef, defaultNewSessionDirectory, displaySessionCwd, gitPanelOpen, terminalPanelOpen, loadingDisplaySessionTitle, currentSessionPinned, displaySettingsStore, isSessionViewLoading, newSessionDirectoryOptions, onToggleGitPanel, onToggleTerminalPanel, sessionStore, viewerContextId }) {
20820
21783
  const sessionHeaderState = useSelector(sessionStore, (sessionState) => ({
20821
21784
  firstMessage: sessionState.firstMessage,
20822
21785
  hideThinkingBlock: sessionState.hideThinkingBlock,
@@ -20825,8 +21788,6 @@ var AppShellSessionHeader = import_react.memo(function AppShellSessionHeader({ a
20825
21788
  sessionStreaming: sessionState.streaming
20826
21789
  }), { compare: shallowRecordEqual });
20827
21790
  const hideToolBlocks = useSelector(displaySettingsStore, (settings) => settings.hideToolBlocks);
20828
- const { isMobile: sidebarIsMobile, openMobile: sidebarOpenMobile, state: sidebarState } = useSidebar();
20829
- const showCollapsedNewSessionButton = !(sidebarIsMobile ? sidebarOpenMobile : sidebarState === "expanded");
20830
21791
  const displaySessionTitle = isSessionViewLoading ? loadingDisplaySessionTitle : getCurrentSessionTitleFromState(sessionHeaderState);
20831
21792
  const moveSessionDirectoryOptions = newSessionDirectoryOptions.filter((option) => option.path !== displaySessionCwd);
20832
21793
  return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
@@ -20835,25 +21796,26 @@ var AppShellSessionHeader = import_react.memo(function AppShellSessionHeader({ a
20835
21796
  className: "relative flex w-full items-center gap-1",
20836
21797
  children: [
20837
21798
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(SidebarTrigger, { className: "shrink-0" }),
20838
- showCollapsedNewSessionButton ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(TitleTooltip, {
21799
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(TitleTooltip, {
20839
21800
  title: defaultNewSessionDirectory ? `Create a new session in ${formatDisplayPath$1(defaultNewSessionDirectory)}` : "Create a new session",
20840
21801
  kbd: formatShortcutLabel("Control+N"),
20841
21802
  children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Button$1, {
20842
21803
  size: "icon",
20843
21804
  variant: "ghost",
20844
- className: "shrink-0",
21805
+ className: "shrink-0 md:group-data-[sidebar-state=expanded]/sidebar-wrapper:hidden",
20845
21806
  "aria-label": "Create a new session",
20846
21807
  onClick: () => {
20847
21808
  actionsRef.current.createSession();
20848
21809
  },
20849
21810
  children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(SquarePen, {})
20850
21811
  })
20851
- }) : null,
21812
+ }),
20852
21813
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
20853
21814
  className: "absolute left-1/2 flex w-max max-w-[calc(100%-4rem)] -translate-x-1/2 flex-col items-center justify-center gap-0 text-center",
20854
21815
  children: [/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
20855
21816
  className: "flex max-w-full min-w-0 items-center justify-center gap-1.5",
20856
21817
  children: [!isSessionViewLoading && sessionHeaderState.sessionStreaming ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Spinner, {
21818
+ size: "xs",
20857
21819
  className: "shrink-0",
20858
21820
  "aria-label": "Session streaming"
20859
21821
  }) : null, /* @__PURE__ */ (0, import_jsx_runtime.jsx)(TitleTooltip, {
@@ -20864,7 +21826,7 @@ var AppShellSessionHeader = import_react.memo(function AppShellSessionHeader({ a
20864
21826
  })
20865
21827
  })]
20866
21828
  }), /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
20867
- className: "flex max-w-full min-w-0 items-center justify-center gap-x-3",
21829
+ className: "flex max-w-full min-w-0 items-center justify-center gap-x-2",
20868
21830
  children: [displaySessionCwd ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
20869
21831
  className: "inline-flex min-w-0 items-center text-xs text-muted-foreground",
20870
21832
  children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
@@ -21002,6 +21964,19 @@ var AppShellSessionHeader = import_react.memo(function AppShellSessionHeader({ a
21002
21964
  })
21003
21965
  ]
21004
21966
  })] }),
21967
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(TitleTooltip, {
21968
+ title: "Toggle terminal",
21969
+ kbd: formatShortcutLabel("Control+`"),
21970
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Button$1, {
21971
+ size: "icon",
21972
+ variant: "ghost",
21973
+ className: "hidden md:inline-flex",
21974
+ "aria-pressed": terminalPanelOpen,
21975
+ "aria-label": "Toggle terminal panel",
21976
+ onClick: onToggleTerminalPanel,
21977
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(SquareTerminal, {})
21978
+ })
21979
+ }),
21005
21980
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(TitleTooltip, {
21006
21981
  title: "Toggle right sidebar",
21007
21982
  kbd: formatShortcutLabel("Control+\\"),
@@ -21347,7 +22322,10 @@ function SidebarSessionItem({ entryKey, directorySessionsStore, activeSessionSto
21347
22322
  },
21348
22323
  children: [entry.streaming ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
21349
22324
  className: "absolute top-2.5 left-2 flex size-4 items-center justify-center",
21350
- children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Spinner, { "aria-label": "Session streaming" })
22325
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Spinner, {
22326
+ size: "xs",
22327
+ "aria-label": "Session streaming"
22328
+ })
21351
22329
  }) : showUnread ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
21352
22330
  className: "absolute top-2.5 left-2 flex size-4 items-center justify-center",
21353
22331
  children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
@@ -21524,7 +22502,10 @@ var DirectorySessionGroup = import_react.memo(function DirectorySessionGroup({ d
21524
22502
  children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(DirectoryPathLabel, { path: directory })
21525
22503
  }),
21526
22504
  !searchActive && !overlay ? collapsed ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ChevronRight, { className: "size-4 shrink-0 opacity-0 transition-opacity group-hover/directory-label:opacity-100 group-focus-visible/directory-label:opacity-100" }) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ChevronDown, { className: "size-4 shrink-0 opacity-0 transition-opacity group-hover/directory-label:opacity-100 group-focus-visible/directory-label:opacity-100" }) : null,
21527
- showHeaderLoadingSpinner ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Spinner, { className: "shrink-0" }) : null
22505
+ showHeaderLoadingSpinner ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Spinner, {
22506
+ size: "xs",
22507
+ className: "shrink-0"
22508
+ }) : null
21528
22509
  ]
21529
22510
  }),
21530
22511
  onCreateSessionInDirectory && !overlay && !isMobile ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(TitleTooltip, {
@@ -21594,7 +22575,7 @@ var DirectorySessionGroup = import_react.memo(function DirectorySessionGroup({ d
21594
22575
  ]
21595
22576
  })] }) : directoryHeader, !overlay ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(CollapsibleContent, { children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(SidebarGroupContent, { children: showLoadingState ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
21596
22577
  className: "flex items-center gap-2 p-2 text-sm text-sidebar-foreground/70",
21597
- children: [showLoadingSpinner ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Spinner, {}) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
22578
+ children: [showLoadingSpinner ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Spinner, { size: "xs" }) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
21598
22579
  className: "size-4",
21599
22580
  "aria-hidden": "true"
21600
22581
  }), "Loading sessions…"]
@@ -23027,7 +24008,11 @@ function createLocalPendingId() {
23027
24008
  if (typeof crypto !== "undefined" && typeof crypto.randomUUID === "function") return `optimistic:${crypto.randomUUID()}`;
23028
24009
  return `optimistic:${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 10)}`;
23029
24010
  }
23030
- function buildCurrentSessionRequestUrl(path, { contextId, sessionState, fallbackSessionId }) {
24011
+ function buildCurrentSessionRequestUrl(path, { contextId, sessionState, fallbackSessionId, targetSessionKey }) {
24012
+ if (targetSessionKey) return buildRequestUrl(path, {
24013
+ contextId,
24014
+ searchParams: { sessionKey: targetSessionKey }
24015
+ });
23031
24016
  const sessionKey = sessionState.sessionKey && !sessionState.sessionKey.startsWith("optimistic:") ? sessionState.sessionKey : void 0;
23032
24017
  const shouldUseSessionKey = Boolean(sessionKey && (sessionState.draft || !sessionState.sessionId));
23033
24018
  return buildRequestUrl(path, {
@@ -23088,13 +24073,14 @@ function movePendingComposerMessage(messages, pendingId, direction) {
23088
24073
  function useAppShellPromptMutations({ viewerContextId, activeSessionId, defaultNewSessionDirectory, sessionStore, sessionStateRef, draftSessionLoadingOwnerKey, pendingDraftPrompt, pendingDraftFollowUps, awaitingFirstTurn, pendingMessages, composerDiffLineCommentsRef, composerImagesRef, composerTextRef, composerSkillRef, replaceComposerDraft, lastSyncedEditorTextRef, rememberRecentDirectory, prefetchDirectorySessionsIndex, addOptimisticUserMessage, removeOptimisticUserMessage, addOptimisticSidebarSession, removeOptimisticSidebarSession, updateOptimisticSidebarSessionPreview, setSidebarDirectories, setStoredDraftDirectory, setDraftSessionLoadingOwnerKey, setPendingDraftPrompt, setPendingDraftFollowUps, setPendingMessages, setAwaitingFirstTurn, setIsSubmitting, setComposerDiffLineComments, setComposerImages }) {
23089
24074
  const queryClient = useQueryClient();
23090
24075
  const draftSessionLoadingOwnerKeyRef = import_react.useRef(draftSessionLoadingOwnerKey);
24076
+ const pendingNewSessionPromptTargetRef = import_react.useRef(null);
24077
+ const newSessionRequestNonceRef = import_react.useRef(0);
23091
24078
  const pendingDraftPromptRef = import_react.useRef(pendingDraftPrompt);
23092
24079
  const pendingDraftFollowUpsRef = import_react.useRef(pendingDraftFollowUps);
23093
24080
  const pendingMessagesRef = import_react.useRef(pendingMessages);
23094
24081
  const awaitingFirstTurnRef = import_react.useRef(awaitingFirstTurn);
23095
24082
  const awaitingFirstTurnAssistantOutputSignatureRef = import_react.useRef(null);
23096
24083
  const sessionSnapshot = usePromptMutationSessionSnapshot(sessionStore);
23097
- const sessionStreamingRef = import_react.useRef(sessionSnapshot.streaming);
23098
24084
  const flushPendingDraftPromptRef = import_react.useRef(async () => false);
23099
24085
  import_react.useEffect(() => {
23100
24086
  draftSessionLoadingOwnerKeyRef.current = draftSessionLoadingOwnerKey;
@@ -23111,9 +24097,6 @@ function useAppShellPromptMutations({ viewerContextId, activeSessionId, defaultN
23111
24097
  import_react.useEffect(() => {
23112
24098
  awaitingFirstTurnRef.current = awaitingFirstTurn;
23113
24099
  }, [awaitingFirstTurn]);
23114
- import_react.useEffect(() => {
23115
- sessionStreamingRef.current = sessionSnapshot.streaming;
23116
- }, [sessionSnapshot.streaming]);
23117
24100
  const updatePendingMessages = import_react.useCallback((updater) => {
23118
24101
  setPendingMessages((current) => {
23119
24102
  const next = updater(current);
@@ -23168,6 +24151,7 @@ function useAppShellPromptMutations({ viewerContextId, activeSessionId, defaultN
23168
24151
  removeOptimisticUserMessage(nextPrompt.optimisticId);
23169
24152
  removeOptimisticSidebarSession(nextPrompt.optimisticSidebarSessionId);
23170
24153
  for (const followUp of pendingDraftFollowUpsRef.current) removeOptimisticUserMessage(followUp.optimisticId);
24154
+ pendingNewSessionPromptTargetRef.current = null;
23171
24155
  pendingDraftPromptRef.current = null;
23172
24156
  pendingDraftFollowUpsRef.current = [];
23173
24157
  setPendingDraftPrompt(null);
@@ -23240,17 +24224,32 @@ function useAppShellPromptMutations({ viewerContextId, activeSessionId, defaultN
23240
24224
  const ownerKey = promptDraftKey({ cwd: nextCwd });
23241
24225
  draftSessionLoadingOwnerKeyRef.current = ownerKey;
23242
24226
  setDraftSessionLoadingOwnerKey(ownerKey);
24227
+ awaitingFirstTurnAssistantOutputSignatureRef.current = null;
24228
+ awaitingFirstTurnRef.current = false;
24229
+ setAwaitingFirstTurn(false);
24230
+ pendingMessagesRef.current = [];
24231
+ setPendingMessages((current) => current.length === 0 ? current : []);
24232
+ const requestNonce = newSessionRequestNonceRef.current + 1;
24233
+ newSessionRequestNonceRef.current = requestNonce;
23243
24234
  try {
23244
- await createSessionRequest({ cwd: nextCwd });
24235
+ const response = await createSessionRequest({ cwd: nextCwd });
24236
+ if (newSessionRequestNonceRef.current !== requestNonce) return true;
24237
+ pendingNewSessionPromptTargetRef.current = {
24238
+ ownerKey,
24239
+ sessionKey: response.sessionKey,
24240
+ cwd: response.cwd
24241
+ };
23245
24242
  if (draftSessionLoadingOwnerKeyRef.current === ownerKey) {
23246
24243
  draftSessionLoadingOwnerKeyRef.current = null;
23247
24244
  setDraftSessionLoadingOwnerKey((current) => current === ownerKey ? null : current);
23248
24245
  }
23249
- if (pendingDraftPromptRef.current?.ownerKey === ownerKey) flushPendingDraftPromptRef.current(ownerKey);
24246
+ if (pendingDraftPromptRef.current?.ownerKey === ownerKey) flushPendingDraftPromptRef.current(ownerKey, response.sessionKey);
23250
24247
  return true;
23251
24248
  } catch (error) {
24249
+ if (newSessionRequestNonceRef.current !== requestNonce) return true;
23252
24250
  if (draftSessionLoadingOwnerKeyRef.current === ownerKey) draftSessionLoadingOwnerKeyRef.current = null;
23253
24251
  setDraftSessionLoadingOwnerKey((current) => current === ownerKey ? null : current);
24252
+ if (pendingNewSessionPromptTargetRef.current?.ownerKey === ownerKey) pendingNewSessionPromptTargetRef.current = null;
23254
24253
  restorePendingDraftPrompt(ownerKey);
23255
24254
  toast.error(error instanceof Error ? error.message : "Failed to create session");
23256
24255
  return false;
@@ -23261,12 +24260,14 @@ function useAppShellPromptMutations({ viewerContextId, activeSessionId, defaultN
23261
24260
  rememberRecentDirectory,
23262
24261
  restorePendingDraftPrompt,
23263
24262
  sessionStateRef,
24263
+ setAwaitingFirstTurn,
23264
24264
  setDraftSessionLoadingOwnerKey,
24265
+ setPendingMessages,
23265
24266
  setStoredDraftDirectory,
23266
24267
  viewerContextId
23267
24268
  ]);
23268
- const queuePendingDraftPrompt = import_react.useCallback((streamingBehavior) => {
23269
- const ownerKey = draftSessionLoadingOwnerKeyRef.current;
24269
+ const queuePendingDraftPrompt = import_react.useCallback((streamingBehavior, ownerKeyOverride) => {
24270
+ const ownerKey = ownerKeyOverride || draftSessionLoadingOwnerKeyRef.current;
23270
24271
  if (!ownerKey) return false;
23271
24272
  const message = buildComposerPromptMessage({
23272
24273
  text: composerTextRef.current,
@@ -23282,6 +24283,7 @@ function useAppShellPromptMutations({ viewerContextId, activeSessionId, defaultN
23282
24283
  ownerKey,
23283
24284
  message,
23284
24285
  images,
24286
+ streamingBehavior,
23285
24287
  optimisticId: addOptimisticUserMessage({
23286
24288
  message,
23287
24289
  images,
@@ -23332,12 +24334,13 @@ function useAppShellPromptMutations({ viewerContextId, activeSessionId, defaultN
23332
24334
  updateOptimisticSidebarSessionPreview
23333
24335
  ]);
23334
24336
  const promptMutation = useMutation({
23335
- mutationFn: async ({ message, images, streamingBehavior, pendingId, thinkingLevel }) => {
24337
+ mutationFn: async ({ message, images, streamingBehavior, pendingId, thinkingLevel, targetSessionKey, draftTarget }) => {
23336
24338
  if (!viewerContextId) throw new Error("Viewer context unavailable");
23337
24339
  return await fetchJson(buildCurrentSessionRequestUrl("/api/prompt", {
23338
24340
  contextId: viewerContextId,
23339
24341
  sessionState: sessionStateRef.current,
23340
- fallbackSessionId: activeSessionId
24342
+ fallbackSessionId: activeSessionId,
24343
+ targetSessionKey
23341
24344
  }), {
23342
24345
  method: "POST",
23343
24346
  headers: { "content-type": "application/json" },
@@ -23346,7 +24349,9 @@ function useAppShellPromptMutations({ viewerContextId, activeSessionId, defaultN
23346
24349
  images,
23347
24350
  streamingBehavior,
23348
24351
  pendingId,
23349
- thinkingLevel
24352
+ thinkingLevel,
24353
+ draftOwnerKey: draftTarget?.ownerKey,
24354
+ draftCwd: draftTarget?.cwd
23350
24355
  })
23351
24356
  });
23352
24357
  },
@@ -23367,13 +24372,29 @@ function useAppShellPromptMutations({ viewerContextId, activeSessionId, defaultN
23367
24372
  diffLineComments: submittedDiffLineComments
23368
24373
  }).trim();
23369
24374
  if (!message && composerImagesRef.current.length === 0) return false;
23370
- const treatAsQueuedPrompt = options?.forceFirstPrompt ? false : Boolean(sessionStreamingRef.current || awaitingFirstTurnRef.current);
24375
+ const currentSessionState = sessionStateRef.current;
24376
+ const pendingNewSessionPromptTarget = pendingNewSessionPromptTargetRef.current;
24377
+ const currentDraftOwnerKey = currentSessionState.draft ? promptDraftKey(currentSessionState) : "";
24378
+ const implicitTargetSessionKey = !options?.targetSessionKey && currentSessionState.draft && pendingNewSessionPromptTarget && promptDraftKeyMatchesOwner(currentDraftOwnerKey, pendingNewSessionPromptTarget.ownerKey) ? pendingNewSessionPromptTarget.sessionKey : void 0;
24379
+ const targetSessionKey = options?.targetSessionKey || implicitTargetSessionKey;
24380
+ const targetDraft = currentSessionState.draft ? {
24381
+ ownerKey: options?.targetDraftOwnerKey || (targetSessionKey && pendingNewSessionPromptTarget?.sessionKey === targetSessionKey ? pendingNewSessionPromptTarget.ownerKey : currentDraftOwnerKey),
24382
+ cwd: options?.targetDraftCwd || (targetSessionKey && pendingNewSessionPromptTarget?.sessionKey === targetSessionKey ? pendingNewSessionPromptTarget.cwd || currentSessionState.cwd : currentSessionState.cwd)
24383
+ } : void 0;
24384
+ if (currentSessionState.draft && currentSessionState.sessionKey?.startsWith("optimistic:") && !targetSessionKey) {
24385
+ if (!draftSessionLoadingOwnerKeyRef.current && currentDraftOwnerKey) {
24386
+ draftSessionLoadingOwnerKeyRef.current = currentDraftOwnerKey;
24387
+ setDraftSessionLoadingOwnerKey(currentDraftOwnerKey);
24388
+ }
24389
+ return queuePendingDraftPrompt(streamingBehavior, currentDraftOwnerKey);
24390
+ }
24391
+ const treatAsQueuedPrompt = options?.forceFirstPrompt ? false : Boolean(currentSessionState.streaming || awaitingFirstTurnRef.current);
23371
24392
  const normalizedStreamingBehavior = treatAsQueuedPrompt ? normalizeQueuedStreamingBehavior(streamingBehavior) : void 0;
23372
24393
  const submittedImages = composerImagesRef.current.map((image) => ({ ...image }));
23373
24394
  const sidebarPreview = promptSidebarPreview(message, submittedImages);
23374
24395
  const queuedPendingId = treatAsQueuedPrompt ? createLocalPendingId() : void 0;
23375
24396
  if (!treatAsQueuedPrompt) {
23376
- awaitingFirstTurnAssistantOutputSignatureRef.current = sessionAssistantOutputSignature(sessionStateRef.current.items);
24397
+ awaitingFirstTurnAssistantOutputSignatureRef.current = sessionAssistantOutputSignature(currentSessionState.items);
23377
24398
  awaitingFirstTurnRef.current = true;
23378
24399
  setAwaitingFirstTurn(true);
23379
24400
  }
@@ -23382,7 +24403,7 @@ function useAppShellPromptMutations({ viewerContextId, activeSessionId, defaultN
23382
24403
  images: submittedImages,
23383
24404
  queued: false
23384
24405
  }));
23385
- const optimisticSidebarSessionId = options?.optimisticSidebarSessionId ?? (!treatAsQueuedPrompt && sessionStateRef.current.draft ? addOptimisticSidebarSession({ lastMessagePreview: sidebarPreview }) : void 0);
24406
+ const optimisticSidebarSessionId = options?.optimisticSidebarSessionId ?? (!treatAsQueuedPrompt && currentSessionState.draft ? addOptimisticSidebarSession({ lastMessagePreview: sidebarPreview }) : void 0);
23386
24407
  updateOptimisticSidebarSessionPreview({
23387
24408
  ...optimisticSidebarSessionId ? { optimisticSidebarSessionId } : {},
23388
24409
  preview: sidebarPreview
@@ -23404,9 +24425,12 @@ function useAppShellPromptMutations({ viewerContextId, activeSessionId, defaultN
23404
24425
  images: submittedImages,
23405
24426
  streamingBehavior: normalizedStreamingBehavior,
23406
24427
  pendingId: queuedPendingId,
23407
- thinkingLevel: sessionStateRef.current.thinkingLevel
24428
+ thinkingLevel: currentSessionState.thinkingLevel,
24429
+ targetSessionKey,
24430
+ draftTarget: targetDraft
23408
24431
  });
23409
24432
  if (queuedPendingId && "queued" in response && (!response.queued || response.canceled)) removeOptimisticPendingMessage(queuedPendingId);
24433
+ if (targetSessionKey && pendingNewSessionPromptTargetRef.current?.sessionKey === targetSessionKey) pendingNewSessionPromptTargetRef.current = null;
23410
24434
  return true;
23411
24435
  } catch (error) {
23412
24436
  if (queuedPendingId) removeOptimisticPendingMessage(queuedPendingId);
@@ -23449,11 +24473,12 @@ function useAppShellPromptMutations({ viewerContextId, activeSessionId, defaultN
23449
24473
  setAwaitingFirstTurn,
23450
24474
  setComposerDiffLineComments,
23451
24475
  setComposerImages,
24476
+ setDraftSessionLoadingOwnerKey,
23452
24477
  setIsSubmitting,
23453
24478
  updateOptimisticSidebarSessionPreview,
23454
24479
  viewerContextId
23455
24480
  ]);
23456
- const flushPendingDraftFollowUps = import_react.useCallback(async () => {
24481
+ const flushPendingDraftFollowUps = import_react.useCallback(async (targetSessionKey, draftTarget) => {
23457
24482
  if (draftSessionLoadingOwnerKeyRef.current) return false;
23458
24483
  const pendingFollowUps = pendingDraftFollowUpsRef.current;
23459
24484
  if (pendingFollowUps.length === 0) return false;
@@ -23474,7 +24499,9 @@ function useAppShellPromptMutations({ viewerContextId, activeSessionId, defaultN
23474
24499
  images: followUp.images,
23475
24500
  streamingBehavior: followUp.streamingBehavior,
23476
24501
  pendingId: followUp.optimisticId,
23477
- thinkingLevel: sessionStateRef.current.thinkingLevel
24502
+ thinkingLevel: sessionStateRef.current.thinkingLevel,
24503
+ targetSessionKey,
24504
+ draftTarget
23478
24505
  });
23479
24506
  } catch (error) {
23480
24507
  for (const unsentFollowUp of followUps.slice(index)) removeOptimisticUserMessage(unsentFollowUp.optimisticId);
@@ -23497,24 +24524,35 @@ function useAppShellPromptMutations({ viewerContextId, activeSessionId, defaultN
23497
24524
  setComposerImages,
23498
24525
  setPendingDraftFollowUps
23499
24526
  ]);
23500
- const flushPendingDraftPrompt = import_react.useCallback(async (ownerKey) => {
24527
+ const flushPendingDraftPrompt = import_react.useCallback(async (ownerKey, targetSessionKey) => {
23501
24528
  const nextPrompt = pendingDraftPromptRef.current;
23502
24529
  if (!nextPrompt || nextPrompt.ownerKey !== ownerKey || draftSessionLoadingOwnerKeyRef.current) return false;
24530
+ const pendingTarget = pendingNewSessionPromptTargetRef.current;
24531
+ const resolvedTargetSessionKey = targetSessionKey || (pendingTarget?.ownerKey === ownerKey ? pendingTarget.sessionKey : void 0);
24532
+ const resolvedDraftTarget = {
24533
+ ownerKey,
24534
+ cwd: pendingTarget?.cwd || sessionStateRef.current.cwd
24535
+ };
23503
24536
  pendingDraftPromptRef.current = null;
23504
24537
  setPendingDraftPrompt(null);
23505
24538
  applyPendingDraftPromptToComposer(nextPrompt);
23506
24539
  if (!await submitPrompt(nextPrompt.streamingBehavior, {
23507
24540
  forceFirstPrompt: true,
23508
24541
  optimisticId: nextPrompt.optimisticId,
23509
- optimisticSidebarSessionId: nextPrompt.optimisticSidebarSessionId
24542
+ optimisticSidebarSessionId: nextPrompt.optimisticSidebarSessionId,
24543
+ targetSessionKey: resolvedTargetSessionKey,
24544
+ targetDraftOwnerKey: resolvedDraftTarget.ownerKey,
24545
+ targetDraftCwd: resolvedDraftTarget.cwd
23510
24546
  })) {
23511
24547
  removeOptimisticSidebarSession(nextPrompt.optimisticSidebarSessionId);
23512
24548
  for (const followUp of pendingDraftFollowUpsRef.current) removeOptimisticUserMessage(followUp.optimisticId);
23513
24549
  pendingDraftFollowUpsRef.current = [];
23514
24550
  setPendingDraftFollowUps([]);
24551
+ if (pendingNewSessionPromptTargetRef.current?.ownerKey === ownerKey) pendingNewSessionPromptTargetRef.current = null;
23515
24552
  return false;
23516
24553
  }
23517
- await flushPendingDraftFollowUps();
24554
+ await flushPendingDraftFollowUps(resolvedTargetSessionKey, resolvedDraftTarget);
24555
+ if (pendingNewSessionPromptTargetRef.current?.ownerKey === ownerKey) pendingNewSessionPromptTargetRef.current = null;
23518
24556
  return true;
23519
24557
  }, [
23520
24558
  applyPendingDraftPromptToComposer,
@@ -23532,9 +24570,15 @@ function useAppShellPromptMutations({ viewerContextId, activeSessionId, defaultN
23532
24570
  if (currentSessionState.sessionKey?.startsWith("optimistic:")) return;
23533
24571
  const currentOwnerKey = promptDraftKey(currentSessionState);
23534
24572
  if (!currentSessionState.draft || !promptDraftKeyMatchesOwner(currentOwnerKey, draftSessionLoadingOwnerKey)) return;
24573
+ const targetSessionKey = currentSessionState.sessionKey?.startsWith("optimistic:") ? void 0 : currentSessionState.sessionKey;
24574
+ if (targetSessionKey) pendingNewSessionPromptTargetRef.current = {
24575
+ ownerKey: draftSessionLoadingOwnerKey,
24576
+ sessionKey: targetSessionKey,
24577
+ cwd: currentSessionState.cwd
24578
+ };
23535
24579
  draftSessionLoadingOwnerKeyRef.current = null;
23536
24580
  setDraftSessionLoadingOwnerKey(null);
23537
- if (pendingDraftPromptRef.current?.ownerKey === draftSessionLoadingOwnerKey) flushPendingDraftPrompt(draftSessionLoadingOwnerKey);
24581
+ if (pendingDraftPromptRef.current?.ownerKey === draftSessionLoadingOwnerKey) flushPendingDraftPrompt(draftSessionLoadingOwnerKey, targetSessionKey);
23538
24582
  }, [
23539
24583
  draftSessionLoadingOwnerKey,
23540
24584
  flushPendingDraftPrompt,
@@ -24941,10 +25985,13 @@ function useAppShellSessionSync({ viewerContextId, sessionId, draftSessionLoadin
24941
25985
  ]);
24942
25986
  }
24943
25987
  var DOUBLE_ESCAPE_INTERVAL_MS = 500;
25988
+ function isTerminalTarget(target) {
25989
+ return target instanceof HTMLElement && target.closest("[data-terminal-panel=\"true\"]") !== null;
25990
+ }
24944
25991
  function isEditableTarget(target) {
24945
25992
  if (!(target instanceof HTMLElement)) return false;
24946
25993
  const tagName = target.tagName.toLowerCase();
24947
- return target.isContentEditable || tagName === "input" || tagName === "textarea" || target.closest("[contenteditable=\"true\"]") !== null;
25994
+ return target.isContentEditable || isTerminalTarget(target) || tagName === "input" || tagName === "textarea" || target.closest("[contenteditable=\"true\"]") !== null;
24948
25995
  }
24949
25996
  function isPromptTextareaTarget(target) {
24950
25997
  return target instanceof HTMLTextAreaElement && target.name === "prompt";
@@ -24984,7 +26031,7 @@ function useAppShellShortcuts({ addDirectoryOpenRef, commandPaletteOpenRef, comp
24984
26031
  };
24985
26032
  const handleEscape = (event) => {
24986
26033
  const context = getShortcutContext(event);
24987
- if (event.repeat || context.modalOpen || event.defaultPrevented) return;
26034
+ if (event.repeat || context.modalOpen || event.defaultPrevented || isTerminalTarget(event.target)) return;
24988
26035
  const now = Date.now();
24989
26036
  if (now - lastEscapeKeyDownAtRef.current <= DOUBLE_ESCAPE_INTERVAL_MS) {
24990
26037
  lastEscapeKeyDownAtRef.current = 0;
@@ -25023,7 +26070,7 @@ function useAppShellShortcuts({ addDirectoryOpenRef, commandPaletteOpenRef, comp
25023
26070
  };
25024
26071
  const handleFocusPrompt = (event) => {
25025
26072
  const context = getShortcutContext(event);
25026
- if (context.blockingModalOpen || event.defaultPrevented) return;
26073
+ if (context.blockingModalOpen || event.defaultPrevented || isTerminalTarget(event.target)) return;
25027
26074
  event.preventDefault();
25028
26075
  closeCommandPaletteForShortcut(context.commandPaletteOpen);
25029
26076
  shortcutActionsRef.current.focusPrompt();
@@ -25050,12 +26097,19 @@ function useAppShellShortcuts({ addDirectoryOpenRef, commandPaletteOpenRef, comp
25050
26097
  const key = event.altKey && event.code.startsWith("Key") ? event.code.slice(3).toLowerCase() : event.key.toLowerCase();
25051
26098
  const context = getShortcutContext(event);
25052
26099
  if (context.blockingModalOpen || event.defaultPrevented) return;
26100
+ if (isTerminalTarget(event.target) && !context.commandPaletteOpen && key !== "`") return;
25053
26101
  if (key === "\\") {
25054
26102
  event.preventDefault();
25055
26103
  closeCommandPaletteForShortcut(context.commandPaletteOpen);
25056
26104
  shortcutActionsRef.current.toggleGitPanel();
25057
26105
  return;
25058
26106
  }
26107
+ if (key === "`") {
26108
+ event.preventDefault();
26109
+ closeCommandPaletteForShortcut(context.commandPaletteOpen);
26110
+ shortcutActionsRef.current.toggleTerminalPanel();
26111
+ return;
26112
+ }
25059
26113
  if (key === "k") {
25060
26114
  event.preventDefault();
25061
26115
  shortcutActionsRef.current.openCommandPalette();
@@ -25227,6 +26281,7 @@ function useAppShellShortcuts({ addDirectoryOpenRef, commandPaletteOpenRef, comp
25227
26281
  })),
25228
26282
  ...[
25229
26283
  { key: "\\" },
26284
+ { key: "`" },
25230
26285
  { key: "K" },
25231
26286
  { key: "P" },
25232
26287
  {
@@ -25295,6 +26350,7 @@ function useAppShellSessionWorkspaceView({ viewerContextId, sessionId, onSelectS
25295
26350
  if (!appUiStoreRef.current) appUiStoreRef.current = createPicoStore({
25296
26351
  currentTab: "session",
25297
26352
  gitPanelOpen: false,
26353
+ terminalPanelOpen: false,
25298
26354
  initialLoadingSessionId: sessionId || null,
25299
26355
  loadingSessionId: null
25300
26356
  }, shallowRecordEqual);
@@ -25310,6 +26366,11 @@ function useAppShellSessionWorkspaceView({ viewerContextId, sessionId, onSelectS
25310
26366
  setStoreField(appUiStore, "gitPanelOpen", nextOpen);
25311
26367
  safeLocalStorageSetItem(RIGHT_SIDEBAR_OPEN_STORAGE_KEY, nextOpen ? "1" : "0");
25312
26368
  }, [appUiStore]);
26369
+ const setTerminalPanelOpen = import_react.useCallback((action) => {
26370
+ const nextOpen = applyStoreAction(appUiStore.state.terminalPanelOpen, action);
26371
+ setStoreField(appUiStore, "terminalPanelOpen", nextOpen);
26372
+ safeLocalStorageSetItem(TERMINAL_PANEL_OPEN_STORAGE_KEY, nextOpen ? "1" : "0");
26373
+ }, [appUiStore]);
25313
26374
  const setLoadingSessionId = import_react.useCallback((action) => {
25314
26375
  setStoreField(appUiStore, "loadingSessionId", action);
25315
26376
  }, [appUiStore]);
@@ -25735,6 +26796,7 @@ function useAppShellSessionWorkspaceView({ viewerContextId, sessionId, onSelectS
25735
26796
  sessionKey: currentSessionState.sessionKey
25736
26797
  }), { compare: shallowRecordEqual });
25737
26798
  const gitPanelOpen = useSelector(appUiStore, (state) => state.gitPanelOpen);
26799
+ const terminalPanelOpen = useSelector(appUiStore, (state) => state.terminalPanelOpen);
25738
26800
  const openFileViewTab = (path, options) => {
25739
26801
  if (!path) return;
25740
26802
  batch(() => {
@@ -25831,7 +26893,7 @@ function useAppShellSessionWorkspaceView({ viewerContextId, sessionId, onSelectS
25831
26893
  } : currentSession : { draft: true }, sessionId);
25832
26894
  setStoreState(appUiStore, (current) => ({
25833
26895
  ...current,
25834
- currentTab: current.currentTab === "git" ? "session" : current.currentTab,
26896
+ currentTab: current.currentTab === "git" || current.currentTab === "terminal" ? "session" : current.currentTab,
25835
26897
  initialLoadingSessionId: sessionId ? current.initialLoadingSessionId : null,
25836
26898
  loadingSessionId: sessionId && sessionStateRef.current.sessionId !== sessionId ? sessionId : null
25837
26899
  }));
@@ -25843,7 +26905,7 @@ function useAppShellSessionWorkspaceView({ viewerContextId, sessionId, onSelectS
25843
26905
  ]);
25844
26906
  import_react.useEffect(() => {
25845
26907
  if (isMobile) return;
25846
- setCurrentTab((tab) => tab === "git" ? "session" : tab);
26908
+ setCurrentTab((tab) => tab === "git" || tab === "terminal" ? "session" : tab);
25847
26909
  }, [isMobile, setCurrentTab]);
25848
26910
  const syncComposerDraft = (value, target = sessionStateRef.current) => {
25849
26911
  const parsed = parseComposerSkillMessage(value);
@@ -25887,6 +26949,7 @@ function useAppShellSessionWorkspaceView({ viewerContextId, sessionId, onSelectS
25887
26949
  const applyStoredClientPreferences = () => {
25888
26950
  batch(() => {
25889
26951
  setGitPanelOpen(readStoredRightSidebarOpen());
26952
+ setTerminalPanelOpen(readStoredTerminalPanelOpen());
25890
26953
  setStoredDraftDirectory(readStoredDraftDirectory() || "");
25891
26954
  setSessionDoneSoundEnabled(readStoredSessionDoneSoundEnabled());
25892
26955
  setSessionDoneDesktopNotificationsEnabled(readStoredSessionDoneDesktopNotificationsEnabled());
@@ -26050,6 +27113,20 @@ function useAppShellSessionWorkspaceView({ viewerContextId, sessionId, onSelectS
26050
27113
  }
26051
27114
  setGitPanelOpen((open) => !open);
26052
27115
  };
27116
+ const toggleTerminalPanel = () => {
27117
+ if (isMobile) {
27118
+ setCurrentTab((tab) => tab === "terminal" ? "session" : "terminal");
27119
+ return;
27120
+ }
27121
+ setTerminalPanelOpen((open) => !open);
27122
+ };
27123
+ const closeTerminalPanel = () => {
27124
+ if (isMobile) {
27125
+ setCurrentTab((tab) => tab === "terminal" ? "session" : tab);
27126
+ return;
27127
+ }
27128
+ setTerminalPanelOpen(false);
27129
+ };
26053
27130
  const openRenameDialog = () => {
26054
27131
  const currentState = sessionStateRef.current;
26055
27132
  if (!currentState.sessionFile) return;
@@ -26528,10 +27605,6 @@ function useAppShellSessionWorkspaceView({ viewerContextId, sessionId, onSelectS
26528
27605
  const createSessionPromise = requestCreateSession(nextCwd);
26529
27606
  handleSelectSession(void 0);
26530
27607
  clearSelectedSidebarSelection();
26531
- if (shouldCloseMobileSidebar) {
26532
- pendingMobileSidebarPromptFocusRef.current = true;
26533
- setOpenMobile(false);
26534
- } else focusPrompt();
26535
27608
  setAwaitingFirstTurn(false);
26536
27609
  setPendingMessages((current) => current.length === 0 ? current : []);
26537
27610
  const nextState = createOptimisticDraftSessionState({
@@ -26542,6 +27615,10 @@ function useAppShellSessionWorkspaceView({ viewerContextId, sessionId, onSelectS
26542
27615
  sessionStateRef.current = nextState;
26543
27616
  conversationItemsStore.setItems(nextState.items);
26544
27617
  setSessionState(nextState);
27618
+ if (shouldCloseMobileSidebar) {
27619
+ pendingMobileSidebarPromptFocusRef.current = true;
27620
+ setOpenMobile(false);
27621
+ } else focusPrompt();
26545
27622
  if (await createSessionPromise) return;
26546
27623
  if (sessionStateRef.current.sessionKey !== optimisticSessionKey) return;
26547
27624
  sessionStateRef.current = previousState;
@@ -26928,7 +28005,8 @@ function useAppShellSessionWorkspaceView({ viewerContextId, sessionId, onSelectS
26928
28005
  hasAvailableModels: sessionStateRef.current.availableModels.length > 0,
26929
28006
  isMobile,
26930
28007
  selectedSidebarSessions,
26931
- sessionFile: sessionState.sessionFile
28008
+ sessionFile: sessionState.sessionFile,
28009
+ terminalPanelOpen
26932
28010
  });
26933
28011
  const buildCommandPaletteCommands = () => {
26934
28012
  const commandState = commandPaletteStateRef.current;
@@ -26979,6 +28057,21 @@ function useAppShellSessionWorkspaceView({ viewerContextId, sessionId, onSelectS
26979
28057
  ],
26980
28058
  onSelect: toggleGitPanel
26981
28059
  },
28060
+ {
28061
+ id: "toggle-terminal",
28062
+ group: "Workspace",
28063
+ title: commandState.isMobile ? "Toggle terminal tab" : commandState.terminalPanelOpen ? "Close terminal" : "Open terminal",
28064
+ description: commandState.isMobile ? "Switch the mobile terminal tab on or off" : "Toggle the bottom terminal panel",
28065
+ shortcut: formatShortcutLabel("Control+`"),
28066
+ keywords: [
28067
+ "terminal",
28068
+ "shell",
28069
+ "console",
28070
+ "bottom",
28071
+ "panel"
28072
+ ],
28073
+ onSelect: toggleTerminalPanel
28074
+ },
26982
28075
  {
26983
28076
  id: "commit-changes",
26984
28077
  group: "Git",
@@ -27343,6 +28436,7 @@ function useAppShellSessionWorkspaceView({ viewerContextId, sessionId, onSelectS
27343
28436
  conversationFrameRef.current?.scrollConversationToTop();
27344
28437
  },
27345
28438
  toggleGitPanel,
28439
+ toggleTerminalPanel,
27346
28440
  toggleHideThinking,
27347
28441
  toggleHideToolBlocks,
27348
28442
  cycleThinkingLevel
@@ -27483,12 +28577,14 @@ function useAppShellSessionWorkspaceView({ viewerContextId, sessionId, onSelectS
27483
28577
  defaultNewSessionDirectory,
27484
28578
  displaySessionCwd,
27485
28579
  gitPanelOpen,
28580
+ terminalPanelOpen,
27486
28581
  loadingDisplaySessionTitle,
27487
28582
  currentSessionPinned,
27488
28583
  displaySettingsStore,
27489
28584
  isSessionViewLoading,
27490
28585
  newSessionDirectoryOptions,
27491
28586
  onToggleGitPanel: toggleGitPanel,
28587
+ onToggleTerminalPanel: toggleTerminalPanel,
27492
28588
  sessionStore,
27493
28589
  viewerContextId
27494
28590
  }),
@@ -27508,6 +28604,7 @@ function useAppShellSessionWorkspaceView({ viewerContextId, sessionId, onSelectS
27508
28604
  displaySettingsStore,
27509
28605
  fileInputRef,
27510
28606
  gitPanelOpen,
28607
+ terminalPanelOpen,
27511
28608
  hiddenThinkingPreviewStore,
27512
28609
  isSessionViewLoading,
27513
28610
  isSubmitting,
@@ -27525,6 +28622,7 @@ function useAppShellSessionWorkspaceView({ viewerContextId, sessionId, onSelectS
27525
28622
  onAddDiffLineComment: addComposerDiffLineComment,
27526
28623
  onOpenFileViewTab: openFileViewTab,
27527
28624
  onReorderFileViewTabs: reorderFileViewTabs,
28625
+ onCloseTerminalPanel: closeTerminalPanel,
27528
28626
  rightSidebarStore,
27529
28627
  sessionStore,
27530
28628
  store: composerStore,