@openhands/agent-canvas 1.0.0-rc.6 → 1.0.0-rc.8

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 (255) hide show
  1. package/README.md +1 -3
  2. package/README.windows.md +2 -2
  3. package/build/assets/{QueryClientProvider-2bFjjuvv.js → QueryClientProvider-XcSjsfpb.js} +1 -1
  4. package/build/assets/{Trans-D1xHkKyr.js → Trans-sy4l4TyQ.js} +1 -1
  5. package/build/assets/{acp-providers-Djbp2WMV.js → acp-providers-BqUvf67Q.js} +1 -1
  6. package/build/assets/{active-backend-context-BiVlte2V.js → active-backend-context-CNF2lPkA.js} +1 -1
  7. package/build/assets/add-backend-modal-BywVr6uq.js +1 -0
  8. package/build/assets/{agent-server-adapter-BD6kvwWb.js → agent-server-adapter-k8veH-1O.js} +1 -1
  9. package/build/assets/{agent-server-conversation-service.api-8AC86rC5.js → agent-server-conversation-service.api-CGbkhsLj.js} +1 -1
  10. package/build/assets/{agent-settings-DycVwGfd.js → agent-settings-DoHXHcoA.js} +1 -1
  11. package/build/assets/{alert-banner-BUqPui_3.js → alert-banner-Bsvys-hT.js} +1 -1
  12. package/build/assets/{analytics-consent-form-modal-CdP5JRph.js → analytics-consent-form-modal-CVhsAREx.js} +1 -1
  13. package/build/assets/{api-key-entry-screen-BnvQM0v5.js → api-key-entry-screen-Dnr5TcV0.js} +1 -1
  14. package/build/assets/{app-settings-FRYUtG-Z.js → app-settings-Bgm2-yOM.js} +1 -1
  15. package/build/assets/{automation-detail-DiIq3Vsh.js → automation-detail-86M_yMqz.js} +1 -1
  16. package/build/assets/{automations-list-DQn7k1Ix.js → automations-list-BWUILrqY.js} +1 -1
  17. package/build/assets/{back-nav-button-B8eULM4t.js → back-nav-button-D20Avel_.js} +1 -1
  18. package/build/assets/{backend-form-modal-DY3My0us.js → backend-form-modal-Dl29wkF5.js} +1 -1
  19. package/build/assets/{backend-synced-settings-badge-BW2WPX-c.js → backend-synced-settings-badge-Dcd4712m.js} +1 -1
  20. package/build/assets/{base-modal-B_MCb3rx.js → base-modal-DvuGYyu1.js} +1 -1
  21. package/build/assets/{brand-button-B6g_UvEG.js → brand-button-9SOzmqPP.js} +1 -1
  22. package/build/assets/{browser-tab-DVHx54yn.js → browser-tab-bveyJhG9.js} +1 -1
  23. package/build/assets/{chat-send-button-Ce9rK8Vd.js → chat-send-button-BhoO7T0s.js} +1 -1
  24. package/build/assets/{checkmark-BQIxLn5W.js → checkmark--JWAesua.js} +1 -1
  25. package/build/assets/{circle-plus-check-toggle-DRwQ4Z7K.js → circle-plus-check-toggle-AO9DWyDX.js} +1 -1
  26. package/build/assets/{close-HybccyBS.js → close--W-tHn1h.js} +1 -1
  27. package/build/assets/{code-tag-DZCEX2zo.js → code-tag-chmctf4w.js} +1 -1
  28. package/build/assets/{combobox-caret-kEa3kTvf.js → combobox-caret-C9BNHfSu.js} +1 -1
  29. package/build/assets/{condenser-settings-BlbT8YRd.js → condenser-settings-DgQ0rsAf.js} +1 -1
  30. package/build/assets/{confirmation-modal-CG_Fgrny.js → confirmation-modal-Cq3eqvqH.js} +1 -1
  31. package/build/assets/{context-menu-list-item-CvJv3-kq.js → context-menu-list-item-BQU0jZPq.js} +1 -1
  32. package/build/assets/conversation-CayU5Lrr.js +19 -0
  33. package/build/assets/conversation-erinyBqT.js +1 -0
  34. package/build/assets/conversation-panel-DiI4q-8-.js +1 -0
  35. package/build/assets/{conversation-service.api-CngSQhPh.js → conversation-service.api-shHWtFwu.js} +1 -1
  36. package/build/assets/{conversation-tab-empty-state-B5eOSEGv.js → conversation-tab-empty-state-C8tztloo.js} +1 -1
  37. package/build/assets/{copy-D6CyDz2n.js → copy-CdKN5rtJ.js} +1 -1
  38. package/build/assets/{declaration-BDfM67Kl.js → declaration-D9ucT0fq.js} +1 -1
  39. package/build/assets/{device-verify-CAevP6_3.js → device-verify-YuGXnpRk.js} +1 -1
  40. package/build/assets/{dropdown-classes-CL8egwz_.js → dropdown-classes-CVp0kVdt.js} +1 -1
  41. package/build/assets/{edit-automation-modal-CwLfhSMN.js → edit-automation-modal-8y9Uctxo.js} +1 -1
  42. package/build/assets/{ellipsis-button-Db-AwbLv.js → ellipsis-button-k7HLwYth.js} +1 -1
  43. package/build/assets/{entry.client-BKDpKmdB.js → entry.client-D_wtryHT.js} +2 -2
  44. package/build/assets/{enum-filter-dropdown-DnYXhc1E.js → enum-filter-dropdown-COqUq3mB.js} +1 -1
  45. package/build/assets/{environment-switch-overlay-BgYKFdf6.js → environment-switch-overlay-wB5LxfYZ.js} +1 -1
  46. package/build/assets/{extensions-hub-CgisT2wb.js → extensions-hub-CoWO3SzQ.js} +1 -1
  47. package/build/assets/{extensions-navigation-nurGkQe5.js → extensions-navigation-D5w2sFQe.js} +1 -1
  48. package/build/assets/{files-tab-DIyAYvp0.js → files-tab-CxdtgkIe.js} +1 -1
  49. package/build/assets/{files-tab-store-RScGdwu8.js → files-tab-store-DSKUhziI.js} +1 -1
  50. package/build/assets/{folder-IChmji2Y.js → folder-DPTBlxWb.js} +1 -1
  51. package/build/assets/home-3joHMYSR.js +1 -0
  52. package/build/assets/{install-server-modal-C8fylTet.js → install-server-modal-_7l1gcjd.js} +1 -1
  53. package/build/assets/{launch-BNiqgTvz.js → launch-fU77utLO.js} +1 -1
  54. package/build/assets/{lesson-plan-BdmMvboE.js → lesson-plan-CHGc-B8Y.js} +1 -1
  55. package/build/assets/{lightbulb-DzViGAuw.js → lightbulb-S616sGEL.js} +1 -1
  56. package/build/assets/{link-external-Dwv3oQAx.js → link-external-ooNztGLp.js} +1 -1
  57. package/build/assets/llm-not-configured-banner-BMGzpBPV.js +27 -0
  58. package/build/assets/llm-settings-BRotm_g6.js +1 -0
  59. package/build/assets/{llm-settings-B92l1Z2N.js → llm-settings-yDgbXP6Q.js} +1 -1
  60. package/build/assets/{loading-spinner-Dtm7y-w5.js → loading-spinner-D7bOU6I7.js} +1 -1
  61. package/build/assets/{manage-backends-modal-qpq2M_oN.js → manage-backends-modal-tfdzTXWU.js} +1 -1
  62. package/build/assets/{manifest-05156141.js → manifest-bbc1374e.js} +1 -1
  63. package/build/assets/{markdown-renderer-Dt0RXhUJ.js → markdown-renderer-z446oW0Z.js} +1 -1
  64. package/build/assets/{mcp-BKdD21xY.js → mcp-Dopstf0t.js} +1 -1
  65. package/build/assets/{messages-Ch8Rd1cx.js → messages-CsFUrAen.js} +1 -1
  66. package/build/assets/{modal-backdrop-DmDLoot0.js → modal-backdrop-_VbcKk5K.js} +1 -1
  67. package/build/assets/{modal-body-BWjhfiSa.js → modal-body-Cd-PtMHr.js} +1 -1
  68. package/build/assets/{modal-classes-DbJbhQCW.js → modal-classes-CNm4ovfm.js} +1 -1
  69. package/build/assets/{modal-close-button-DXN6uYvt.js → modal-close-button-Dbo0NAMt.js} +1 -1
  70. package/build/assets/{model-selector-BMn4D5v6.js → model-selector-sioC8xP4.js} +1 -1
  71. package/build/assets/{navigation-context-BrRCrWhm.js → navigation-context-BpAOh_t5.js} +1 -1
  72. package/build/assets/{navigation-link-qUrH_I5_.js → navigation-link-Dci7Nuyf.js} +1 -1
  73. package/build/assets/{onboarding-B50IeNyy.js → onboarding-CQh50i2z.js} +1 -1
  74. package/build/assets/{openhands-logo-DnfHAq7Y.js → openhands-logo-Dzm-d_V_.js} +1 -1
  75. package/build/assets/{option-service.api-lzN_3s9w.js → option-service.api-DjPvPAtd.js} +1 -1
  76. package/build/assets/{organization-service.api-CNmSPGj4.js → organization-service.api-BNc9Kn6n.js} +1 -1
  77. package/build/assets/{path-utils-CKgIQZAY.js → path-utils-YlkpoImJ.js} +1 -1
  78. package/build/assets/{plan-components-D9fU4FFh.js → plan-components-CutRBi0G.js} +1 -1
  79. package/build/assets/{planner-tab-B7kpsTT8.js → planner-tab-DjIFIQDk.js} +1 -1
  80. package/build/assets/{providers-189W9TJO.js → providers-BiWSYwRT.js} +1 -1
  81. package/build/assets/{query-client-config-B9ZI-1_w.js → query-client-config-Ca3tW1AH.js} +1 -1
  82. package/build/assets/{recommended-automations-launcher-C_RcP2yb.js → recommended-automations-launcher-DCi0RXIj.js} +1 -1
  83. package/build/assets/{root-CFnl2e43.js → root-DrHpnAqU.js} +2 -2
  84. package/build/assets/{root-layout-BQGwrist.js → root-layout-Cf7FC9DC.js} +2 -2
  85. package/build/assets/{runtime-waiting-state-DPjfE32a.js → runtime-waiting-state-8i0Z3A_i.js} +1 -1
  86. package/build/assets/{sdk-section-page-ozvrzW4r.js → sdk-section-page-COlGKv4E.js} +1 -1
  87. package/build/assets/{sdk-settings-schema-Ck6f2ON9.js → sdk-settings-schema-Big50zDH.js} +1 -1
  88. package/build/assets/{search-aDWSdSb7.js → search-C_l_XW4e.js} +1 -1
  89. package/build/assets/{secrets-settings-Bga0bwB1.js → secrets-settings-DYfTc5c8.js} +1 -1
  90. package/build/assets/{settings-DHAhcPm2.js → settings-DRmVFdOc.js} +1 -1
  91. package/build/assets/{settings-dropdown-input-N0FtDFkk.js → settings-dropdown-input-BP-arqjU.js} +1 -1
  92. package/build/assets/{settings-gear-D8ao16bi.js → settings-gear-f-zWpxfI.js} +1 -1
  93. package/build/assets/{settings-index-CJvQH5ls.js → settings-index-B035V2pu.js} +1 -1
  94. package/build/assets/{settings-input-CyWJBrqC.js → settings-input-BM8ANxDb.js} +1 -1
  95. package/build/assets/{settings-list-classes-CYRnRf8r.js → settings-list-classes-B9uo5WES.js} +1 -1
  96. package/build/assets/{settings-modal-BeAlR7Be.js → settings-modal-CeRvS5Bc.js} +1 -1
  97. package/build/assets/{settings-section-header-context-1fN7Xkb3.js → settings-section-header-context-Cid25HM2.js} +1 -1
  98. package/build/assets/{settings-switch-D3aEIphL.js → settings-switch-xa6q4M6j.js} +1 -1
  99. package/build/assets/{shared-conversation-CtRf6CQN.js → shared-conversation-CBbpyPaD.js} +1 -1
  100. package/build/assets/{sidebar-mobile-menu-toggle-DkYaKR6Q.js → sidebar-mobile-menu-toggle-SKcwsEtD.js} +1 -1
  101. package/build/assets/{sidebar-nav-link-CzeuB4So.js → sidebar-nav-link-CPWuQ_c6.js} +1 -1
  102. package/build/assets/{skill-card-pill-row-CvLCLqqD.js → skill-card-pill-row-DeodrwPG.js} +1 -1
  103. package/build/assets/{skills-jKeJCunm.js → skills-BE5HJuGM.js} +1 -1
  104. package/build/assets/{skills-plugins-juwPhlEE.js → skills-plugins-Dc4ec3CW.js} +1 -1
  105. package/build/assets/{skills-settings-BW09n_Px.js → skills-settings-B6HwvA0S.js} +1 -1
  106. package/build/assets/{styled-tooltip-B3wz8m5E.js → styled-tooltip-X61SEwqk.js} +1 -1
  107. package/build/assets/{suspense-BpkWt-39.js → suspense-Bqb0V0q1.js} +1 -1
  108. package/build/assets/{switch-skeleton-Cl_Nh2kt.js → switch-skeleton-BbIWemkK.js} +1 -1
  109. package/build/assets/{task-list-tab-DKnHShLG.js → task-list-tab-BFnyHGsn.js} +1 -1
  110. package/build/assets/{telemetry-Ca_YAGcO.js → telemetry-DCFAqpIA.js} +1 -1
  111. package/build/assets/{terminal-BFi5FOMc.js → terminal-B92mACR5.js} +1 -1
  112. package/build/assets/{terminal-CZH2h1J3.js → terminal-CltMeLv4.js} +1 -1
  113. package/build/assets/{toggle-switch-BzaEofjj.js → toggle-switch-CL_wRiGs.js} +1 -1
  114. package/build/assets/{typography-BFj4IzMj.js → typography-BLSEGjph.js} +1 -1
  115. package/build/assets/{u-check-circle-Cs2GOrZ7.js → u-check-circle-dG_VJqx5.js} +1 -1
  116. package/build/assets/{u-check-circle-half-DGgLKVV1.js → u-check-circle-half-D-5ZPRnd.js} +1 -1
  117. package/build/assets/{u-circuit-DniZPNAV.js → u-circuit-BXoisXOA.js} +1 -1
  118. package/build/assets/{u-edit-BvTiDr33.js → u-edit-DIgADxLH.js} +1 -1
  119. package/build/assets/{use-acp-credential-form-8yHKY_py.js → use-acp-credential-form-LjCIhMSK.js} +1 -1
  120. package/build/assets/{use-active-conversation-CLzls6XV.js → use-active-conversation-B0_rXnqa.js} +1 -1
  121. package/build/assets/{use-agent-settings-schema-BCXxH2N-.js → use-agent-settings-schema-D8x5LJVS.js} +1 -1
  122. package/build/assets/{use-agent-state-BocVPR5S.js → use-agent-state-BuOzLeFN.js} +1 -1
  123. package/build/assets/{use-cloud-current-user-id-BjDwnldw.js → use-cloud-current-user-id-DVZXa_WH.js} +1 -1
  124. package/build/assets/use-config-5zEjywCL.js +1 -0
  125. package/build/assets/{use-create-conversation-CIkPp9u9.js → use-create-conversation-pV2eA-e9.js} +1 -1
  126. package/build/assets/{use-create-secret-Bwy6457V.js → use-create-secret-ClPpLBgD.js} +1 -1
  127. package/build/assets/{use-handle-plan-click-CjNwpDeO.js → use-handle-plan-click-CwN_nNib.js} +1 -1
  128. package/build/assets/use-is-authed-B8eLO-Xq.js +1 -0
  129. package/build/assets/{use-launch-skill-in-chat-QmcJQxUY.js → use-launch-skill-in-chat-BTgXIu3N.js} +1 -1
  130. package/build/assets/{use-llm-profiles-CZD1ALup.js → use-llm-profiles-CWLfWjav.js} +1 -1
  131. package/build/assets/use-runtime-is-ready-Bbory9-s.js +1 -0
  132. package/build/assets/{use-save-settings-BjYPOrtF.js → use-save-settings-Xp8DD-jf.js} +1 -1
  133. package/build/assets/{use-settings-YTDcMvj1.js → use-settings-0SeqaDeW.js} +1 -1
  134. package/build/assets/{use-settings-nav-items-DMit9adK.js → use-settings-nav-items-DVrMNk0I.js} +1 -1
  135. package/build/assets/{use-skills-fh_U4ZGe.js → use-skills-Bv8LXrys.js} +1 -1
  136. package/build/assets/{use-tracking-DJ3Z7ha1.js → use-tracking-BhyV5A9i.js} +1 -1
  137. package/build/assets/{use-user-conversation-Ct93yaXj.js → use-user-conversation-6yFVEXiG.js} +1 -1
  138. package/build/assets/{useMutation-B1gQr_yr.js → useMutation-Dm84WAKR.js} +1 -1
  139. package/build/assets/{useQuery-CbqdITLM.js → useQuery-DGRSm7IT.js} +1 -1
  140. package/build/assets/{useTranslation-C-T8J2U-.js → useTranslation-CL6FTf1b.js} +1 -1
  141. package/build/assets/{utils-CXRkUr73.js → utils-eXk6yerZ.js} +1 -1
  142. package/build/assets/{vendor~conversation-panel~conversation-CkFCLbQW.js → vendor~conversation-panel~conversation-D98u43rl.js} +1 -1
  143. package/build/assets/{vendor~home~conversation-panel~conversation~shared-conversation~planner-tab~files-tab-C1BmvJX4.js → vendor~home~conversation-panel~conversation~shared-conversation~planner-tab~files-tab-Dqpi2xyZ.js} +1 -1
  144. package/build/assets/{vendor~root-layout~home~conversation-panel~conversation~extensions-hub~skills-settings~mcp~~bok0tgtf-RuHTcYaf.js → vendor~root-layout~home~conversation-panel~conversation~extensions-hub~skills-settings~mcp~~bok0tgtf-7eqKdLSQ.js} +1 -1
  145. package/build/assets/{vendor~root-layout~home~conversation-panel~conversation~extensions-hub~skills-settings~mcp~~bok0tgtf-B04AZhMW.js → vendor~root-layout~home~conversation-panel~conversation~extensions-hub~skills-settings~mcp~~bok0tgtf-BP3MGAAY.js} +1 -1
  146. package/build/assets/{vendor~root-layout~home~conversation-panel~conversation~extensions-hub~skills-settings~mcp~~ceijy54y-D7ZsdcLa.js → vendor~root-layout~home~conversation-panel~conversation~extensions-hub~skills-settings~mcp~~ceijy54y-Dkd2wxJA.js} +1 -1
  147. package/build/assets/{vendor~root-layout~home~conversation-panel~conversation~launch~skills-settings~mcp~automati~dou5smdo-DH07jaIY.js → vendor~root-layout~home~conversation-panel~conversation~launch~skills-settings~mcp~automati~dou5smdo-DavKsAvB.js} +186 -40
  148. package/build/assets/{vendor~root-layout~home~mcp~automations-list-COlx7Is6.js → vendor~root-layout~home~mcp~automations-list-D40jgugf.js} +1 -1
  149. package/build/assets/{vendor~root-layout~home~mcp~llm-settings~agent-settings~condenser-settings~verification-set~o7tv66sg-DuiHR4ce.js → vendor~root-layout~home~mcp~llm-settings~agent-settings~condenser-settings~verification-set~o7tv66sg-BIm99UaF.js} +1 -1
  150. package/build/assets/{vendor~root-layout~home~mcp~llm-settings~agent-settings~condenser-settings~verification-set~o7tv66sg-4yr76Evz.js → vendor~root-layout~home~mcp~llm-settings~agent-settings~condenser-settings~verification-set~o7tv66sg-xCM3k6Rd.js} +1 -1
  151. package/build/assets/{verification-settings-CeIuPtF2.js → verification-settings-Be6NxjuT.js} +1 -1
  152. package/build/assets/{x-mark-BMhi11Sg.js → x-mark-C42svtnu.js} +1 -1
  153. package/build/index.html +4 -4
  154. package/build/locales/ar/openhands.json +45 -40
  155. package/build/locales/ca/openhands.json +41 -36
  156. package/build/locales/de/openhands.json +42 -37
  157. package/build/locales/en/openhands.json +6 -1
  158. package/build/locales/es/openhands.json +44 -39
  159. package/build/locales/fr/openhands.json +44 -39
  160. package/build/locales/it/openhands.json +44 -39
  161. package/build/locales/ja/openhands.json +44 -39
  162. package/build/locales/ko-KR/openhands.json +44 -39
  163. package/build/locales/no/openhands.json +44 -39
  164. package/build/locales/pt/openhands.json +44 -39
  165. package/build/locales/tr/openhands.json +43 -38
  166. package/build/locales/uk/openhands.json +44 -39
  167. package/build/locales/zh-CN/openhands.json +44 -39
  168. package/build/locales/zh-TW/openhands.json +44 -39
  169. package/config/defaults.json +9 -4
  170. package/dist/components/features/backends/backend-form-modal.cjs +1 -1
  171. package/dist/components/features/backends/backend-form-modal.cjs.map +1 -1
  172. package/dist/components/features/backends/backend-form-modal.js +1 -1
  173. package/dist/components/features/backends/backend-form-modal.js.map +1 -1
  174. package/dist/components/features/chat/chat-interface.cjs +1 -1
  175. package/dist/components/features/chat/chat-interface.cjs.map +1 -1
  176. package/dist/components/features/chat/chat-interface.js +1 -1
  177. package/dist/components/features/chat/chat-interface.js.map +1 -1
  178. package/dist/components/features/chat/git-control-bar.cjs +1 -1
  179. package/dist/components/features/chat/git-control-bar.cjs.map +1 -1
  180. package/dist/components/features/chat/git-control-bar.js +1 -1
  181. package/dist/components/features/chat/git-control-bar.js.map +1 -1
  182. package/dist/components/features/chat/open-repository-modal.cjs +1 -1
  183. package/dist/components/features/chat/open-repository-modal.cjs.map +1 -1
  184. package/dist/components/features/chat/open-repository-modal.js +0 -3
  185. package/dist/components/features/chat/open-repository-modal.js.map +1 -1
  186. package/dist/components/features/chat/pending-user-messages.cjs +1 -1
  187. package/dist/components/features/chat/pending-user-messages.cjs.map +1 -1
  188. package/dist/components/features/chat/pending-user-messages.js +31 -28
  189. package/dist/components/features/chat/pending-user-messages.js.map +1 -1
  190. package/dist/components/features/home/git-branch-dropdown/git-branch-dropdown.cjs +1 -1
  191. package/dist/components/features/home/git-branch-dropdown/git-branch-dropdown.cjs.map +1 -1
  192. package/dist/components/features/home/git-branch-dropdown/git-branch-dropdown.js +79 -77
  193. package/dist/components/features/home/git-branch-dropdown/git-branch-dropdown.js.map +1 -1
  194. package/dist/components/features/home/git-provider-dropdown/git-provider-dropdown.cjs +1 -1
  195. package/dist/components/features/home/git-provider-dropdown/git-provider-dropdown.cjs.map +1 -1
  196. package/dist/components/features/home/git-provider-dropdown/git-provider-dropdown.js +67 -65
  197. package/dist/components/features/home/git-provider-dropdown/git-provider-dropdown.js.map +1 -1
  198. package/dist/components/features/home/git-repo-dropdown/git-repo-dropdown.cjs +1 -1
  199. package/dist/components/features/home/git-repo-dropdown/git-repo-dropdown.cjs.map +1 -1
  200. package/dist/components/features/home/git-repo-dropdown/git-repo-dropdown.js +2 -2
  201. package/dist/components/features/home/git-repo-dropdown/git-repo-dropdown.js.map +1 -1
  202. package/dist/components/features/settings/settings-navigation.cjs +1 -1
  203. package/dist/components/features/settings/settings-navigation.cjs.map +1 -1
  204. package/dist/components/features/settings/settings-navigation.js +1 -1
  205. package/dist/components/features/settings/settings-navigation.js.map +1 -1
  206. package/dist/components/features/sidebar/sidebar.cjs +1 -1
  207. package/dist/components/features/sidebar/sidebar.cjs.map +1 -1
  208. package/dist/components/features/sidebar/sidebar.js +3 -2
  209. package/dist/components/features/sidebar/sidebar.js.map +1 -1
  210. package/dist/i18n/declaration.cjs +1 -1
  211. package/dist/i18n/declaration.cjs.map +1 -1
  212. package/dist/i18n/declaration.d.ts +6 -1
  213. package/dist/i18n/declaration.js +1 -1
  214. package/dist/i18n/declaration.js.map +1 -1
  215. package/dist/i18n/translation.cjs +3 -3
  216. package/dist/i18n/translation.cjs.map +1 -1
  217. package/dist/i18n/translation.js +612 -527
  218. package/dist/i18n/translation.js.map +1 -1
  219. package/dist/locales/ar/openhands.json +45 -40
  220. package/dist/locales/ca/openhands.json +41 -36
  221. package/dist/locales/de/openhands.json +42 -37
  222. package/dist/locales/en/openhands.json +6 -1
  223. package/dist/locales/es/openhands.json +44 -39
  224. package/dist/locales/fr/openhands.json +44 -39
  225. package/dist/locales/it/openhands.json +44 -39
  226. package/dist/locales/ja/openhands.json +44 -39
  227. package/dist/locales/ko-KR/openhands.json +44 -39
  228. package/dist/locales/no/openhands.json +44 -39
  229. package/dist/locales/pt/openhands.json +44 -39
  230. package/dist/locales/tr/openhands.json +43 -38
  231. package/dist/locales/uk/openhands.json +44 -39
  232. package/dist/locales/zh-CN/openhands.json +44 -39
  233. package/dist/locales/zh-TW/openhands.json +44 -39
  234. package/dist/node_modules/@openhands/extensions/skills/index.cjs +186 -40
  235. package/dist/node_modules/@openhands/extensions/skills/index.cjs.map +1 -1
  236. package/dist/node_modules/@openhands/extensions/skills/index.js +2 -2
  237. package/dist/node_modules/@openhands/extensions/skills/index.js.map +1 -1
  238. package/dist/package.cjs +1 -1
  239. package/dist/package.cjs.map +1 -1
  240. package/dist/package.js +2 -2
  241. package/dist/package.js.map +1 -1
  242. package/package.json +2 -2
  243. package/scripts/check-sdk-version-sync.mjs +6 -6
  244. package/scripts/check-translation-completeness.cjs +149 -59
  245. package/scripts/dev-safe.mjs +1 -1
  246. package/build/assets/add-backend-modal-CSfOPrnr.js +0 -1
  247. package/build/assets/conversation-B7ztPdyS.js +0 -1
  248. package/build/assets/conversation-DZYd8QiZ.js +0 -19
  249. package/build/assets/conversation-panel-CklMXcs6.js +0 -1
  250. package/build/assets/home-iVOrSOWL.js +0 -1
  251. package/build/assets/llm-not-configured-banner-BQgVTo0O.js +0 -27
  252. package/build/assets/llm-settings-ICWFhhjM.js +0 -1
  253. package/build/assets/use-config-C4DX1-nF.js +0 -1
  254. package/build/assets/use-is-authed-7P3xSsit.js +0 -1
  255. package/build/assets/use-runtime-is-ready-B7sbw-q5.js +0 -1
@@ -1 +1 @@
1
- {"version":3,"file":"git-branch-dropdown.js","names":[],"sources":["../../../../../src/components/features/home/git-branch-dropdown/git-branch-dropdown.tsx"],"sourcesContent":["import React, {\n useState,\n useMemo,\n useCallback,\n useRef,\n useEffect,\n} from \"react\";\nimport { useCombobox } from \"downshift\";\nimport { Branch } from \"#/types/git\";\nimport { Provider } from \"#/types/settings\";\nimport { useDebounce } from \"#/hooks/use-debounce\";\nimport { cn } from \"#/utils/utils\";\nimport { formControlFieldClassName } from \"#/utils/form-control-classes\";\nimport { useBranchData } from \"#/hooks/query/use-branch-data\";\n\nimport { ClearButton } from \"../shared/clear-button\";\nimport { ToggleButton } from \"../shared/toggle-button\";\nimport { ErrorMessage } from \"../shared/error-message\";\nimport { BranchDropdownMenu } from \"./branch-dropdown-menu\";\nimport BranchIcon from \"#/icons/u-code-branch.svg?react\";\n\nexport interface GitBranchDropdownProps {\n repository: string | null;\n provider: Provider;\n selectedBranch: Branch | null;\n onBranchSelect: (branch: Branch | null) => void;\n defaultBranch?: string | null;\n placeholder?: string;\n disabled?: boolean;\n className?: string;\n}\n\nexport function GitBranchDropdown({\n repository,\n provider,\n selectedBranch,\n onBranchSelect,\n defaultBranch,\n placeholder = \"Select branch...\",\n disabled = false,\n className,\n}: GitBranchDropdownProps) {\n const [inputValue, setInputValue] = useState(\"\");\n const [userManuallyCleared, setUserManuallyCleared] = useState(false);\n const debouncedInputValue = useDebounce(inputValue, 300);\n const menuRef = useRef<HTMLUListElement>(null);\n\n // Process search input (debounced and filtered)\n const processedSearchInput = useMemo(\n () =>\n debouncedInputValue.trim().length > 0 ? debouncedInputValue.trim() : \"\",\n [debouncedInputValue],\n );\n\n // Use the new branch data hook with default branch prioritization\n const {\n branches: filteredBranches,\n isLoading,\n isError,\n fetchNextPage,\n hasNextPage,\n isFetchingNextPage,\n isSearchLoading,\n } = useBranchData(\n repository,\n provider,\n defaultBranch || null,\n processedSearchInput,\n inputValue,\n selectedBranch,\n );\n\n const error = isError ? new Error(\"Failed to load branches\") : null;\n\n // Handle clear\n const handleClear = useCallback(() => {\n setInputValue(\"\");\n onBranchSelect(null);\n setUserManuallyCleared(true); // Mark that user manually cleared the branch\n }, [onBranchSelect]);\n\n // Handle branch selection\n const handleBranchSelect = useCallback(\n (branch: Branch | null) => {\n onBranchSelect(branch);\n setInputValue(\"\");\n },\n [onBranchSelect],\n );\n\n // Handle menu scroll for infinite loading\n const handleMenuScroll = useCallback(\n (event: React.UIEvent<HTMLUListElement>) => {\n const { scrollTop, scrollHeight, clientHeight } = event.currentTarget;\n if (\n scrollHeight - scrollTop <= clientHeight * 1.5 &&\n hasNextPage &&\n !isFetchingNextPage\n ) {\n fetchNextPage();\n }\n },\n [hasNextPage, isFetchingNextPage, fetchNextPage],\n );\n\n // Downshift configuration\n const {\n isOpen,\n selectedItem,\n highlightedIndex,\n getInputProps,\n getItemProps,\n getMenuProps,\n getToggleButtonProps,\n } = useCombobox({\n items: filteredBranches,\n selectedItem: selectedBranch,\n itemToString: (item) => item?.name || \"\",\n onSelectedItemChange: ({ selectedItem: newSelectedItem }) => {\n handleBranchSelect(newSelectedItem || null);\n },\n inputValue,\n // Override Downshift's default input-click behavior to avoid closing/reopening\n // the menu, which would reset scroll position and break search continuity.\n stateReducer: (state, actionAndChanges) =>\n actionAndChanges.type === useCombobox.stateChangeTypes.InputClick &&\n state.isOpen\n ? { ...actionAndChanges.changes, isOpen: true }\n : actionAndChanges.changes,\n });\n\n // Reset branch selection when repository changes\n useEffect(() => {\n if (repository) {\n onBranchSelect(null);\n setUserManuallyCleared(false); // Reset the manual clear flag when repository changes\n }\n }, [repository, onBranchSelect]);\n\n // Auto-select default branch when branches are loaded and no branch is selected\n // But only if the user hasn't manually cleared the branch\n useEffect(() => {\n if (\n repository &&\n defaultBranch &&\n !selectedBranch &&\n !userManuallyCleared && // Don't auto-select if user manually cleared\n filteredBranches.length > 0 &&\n !isLoading\n ) {\n const defaultBranchObj = filteredBranches.find(\n (branch) => branch.name === defaultBranch,\n );\n\n if (defaultBranchObj) {\n onBranchSelect(defaultBranchObj);\n }\n }\n }, [\n repository,\n defaultBranch,\n selectedBranch,\n userManuallyCleared,\n filteredBranches,\n onBranchSelect,\n isLoading,\n ]);\n\n // Reset input when repository changes\n useEffect(() => {\n setInputValue(\"\");\n }, [repository]);\n\n // Initialize input value when selectedBranch changes (but not when user is typing)\n useEffect(() => {\n if (selectedBranch && !isOpen) {\n setInputValue(selectedBranch.name);\n } else if (!selectedBranch && !isOpen) {\n setInputValue(\"\");\n }\n }, [selectedBranch, isOpen]);\n\n const isLoadingState = isLoading || isSearchLoading || isFetchingNextPage;\n\n return (\n <div className={cn(\"relative\", className)}>\n <div className=\"group relative text-[var(--oh-muted)] hover:text-white\">\n <div className=\"absolute left-2 top-1/2 transform -translate-y-1/2 z-10\">\n {isLoadingState ? (\n <div className=\"animate-spin h-4 w-4 border-2 border-transparent border-t-white rounded-full\" />\n ) : (\n <BranchIcon width={16} height={16} />\n )}\n </div>\n <input\n {...getInputProps({\n disabled: disabled || !repository,\n placeholder,\n className: cn(\n formControlFieldClassName,\n \"text-inherit shadow-none pl-7 pr-16 text-sm font-normal leading-5\",\n \"placeholder:text-[var(--oh-muted)]\",\n \"disabled:cursor-not-allowed disabled:opacity-60\",\n ),\n // Direct onChange for cursor position preservation\n onChange: (e: React.ChangeEvent<HTMLInputElement>) => {\n setInputValue(e.target.value);\n },\n })}\n data-testid=\"git-branch-dropdown-input\"\n />\n\n <div className=\"absolute right-1 top-1/2 transform -translate-y-1/2 flex items-center\">\n {selectedBranch && (\n <ClearButton disabled={disabled} onClear={handleClear} />\n )}\n\n <ToggleButton\n isOpen={isOpen}\n disabled={disabled || !repository}\n getToggleButtonProps={getToggleButtonProps}\n />\n </div>\n </div>\n\n <BranchDropdownMenu\n isOpen={isOpen}\n filteredBranches={filteredBranches}\n inputValue={inputValue}\n highlightedIndex={highlightedIndex}\n selectedItem={selectedItem}\n getMenuProps={getMenuProps}\n getItemProps={getItemProps}\n onScroll={handleMenuScroll}\n menuRef={menuRef}\n />\n\n <ErrorMessage isError={!!error} />\n </div>\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"git-branch-dropdown.js","names":[],"sources":["../../../../../src/components/features/home/git-branch-dropdown/git-branch-dropdown.tsx"],"sourcesContent":["import React, {\n useState,\n useMemo,\n useCallback,\n useRef,\n useEffect,\n} from \"react\";\nimport { useCombobox } from \"downshift\";\nimport { useTranslation } from \"react-i18next\";\nimport { Branch } from \"#/types/git\";\nimport { Provider } from \"#/types/settings\";\nimport { I18nKey } from \"#/i18n/declaration\";\nimport { useDebounce } from \"#/hooks/use-debounce\";\nimport { cn } from \"#/utils/utils\";\nimport { formControlFieldClassName } from \"#/utils/form-control-classes\";\nimport { useBranchData } from \"#/hooks/query/use-branch-data\";\n\nimport { ClearButton } from \"../shared/clear-button\";\nimport { ToggleButton } from \"../shared/toggle-button\";\nimport { ErrorMessage } from \"../shared/error-message\";\nimport { BranchDropdownMenu } from \"./branch-dropdown-menu\";\nimport BranchIcon from \"#/icons/u-code-branch.svg?react\";\n\nexport interface GitBranchDropdownProps {\n repository: string | null;\n provider: Provider;\n selectedBranch: Branch | null;\n onBranchSelect: (branch: Branch | null) => void;\n defaultBranch?: string | null;\n placeholder?: string;\n disabled?: boolean;\n className?: string;\n}\n\nexport function GitBranchDropdown({\n repository,\n provider,\n selectedBranch,\n onBranchSelect,\n defaultBranch,\n placeholder,\n disabled = false,\n className,\n}: GitBranchDropdownProps) {\n const { t } = useTranslation(\"openhands\");\n const [inputValue, setInputValue] = useState(\"\");\n const [userManuallyCleared, setUserManuallyCleared] = useState(false);\n const debouncedInputValue = useDebounce(inputValue, 300);\n const menuRef = useRef<HTMLUListElement>(null);\n\n // Process search input (debounced and filtered)\n const processedSearchInput = useMemo(\n () =>\n debouncedInputValue.trim().length > 0 ? debouncedInputValue.trim() : \"\",\n [debouncedInputValue],\n );\n\n // Use the new branch data hook with default branch prioritization\n const {\n branches: filteredBranches,\n isLoading,\n isError,\n fetchNextPage,\n hasNextPage,\n isFetchingNextPage,\n isSearchLoading,\n } = useBranchData(\n repository,\n provider,\n defaultBranch || null,\n processedSearchInput,\n inputValue,\n selectedBranch,\n );\n\n const error = isError ? new Error(\"Failed to load branches\") : null;\n\n // Handle clear\n const handleClear = useCallback(() => {\n setInputValue(\"\");\n onBranchSelect(null);\n setUserManuallyCleared(true); // Mark that user manually cleared the branch\n }, [onBranchSelect]);\n\n // Handle branch selection\n const handleBranchSelect = useCallback(\n (branch: Branch | null) => {\n onBranchSelect(branch);\n setInputValue(\"\");\n },\n [onBranchSelect],\n );\n\n // Handle menu scroll for infinite loading\n const handleMenuScroll = useCallback(\n (event: React.UIEvent<HTMLUListElement>) => {\n const { scrollTop, scrollHeight, clientHeight } = event.currentTarget;\n if (\n scrollHeight - scrollTop <= clientHeight * 1.5 &&\n hasNextPage &&\n !isFetchingNextPage\n ) {\n fetchNextPage();\n }\n },\n [hasNextPage, isFetchingNextPage, fetchNextPage],\n );\n\n // Downshift configuration\n const {\n isOpen,\n selectedItem,\n highlightedIndex,\n getInputProps,\n getItemProps,\n getMenuProps,\n getToggleButtonProps,\n } = useCombobox({\n items: filteredBranches,\n selectedItem: selectedBranch,\n itemToString: (item) => item?.name || \"\",\n onSelectedItemChange: ({ selectedItem: newSelectedItem }) => {\n handleBranchSelect(newSelectedItem || null);\n },\n inputValue,\n // Override Downshift's default input-click behavior to avoid closing/reopening\n // the menu, which would reset scroll position and break search continuity.\n stateReducer: (state, actionAndChanges) =>\n actionAndChanges.type === useCombobox.stateChangeTypes.InputClick &&\n state.isOpen\n ? { ...actionAndChanges.changes, isOpen: true }\n : actionAndChanges.changes,\n });\n\n // Reset branch selection when repository changes\n useEffect(() => {\n if (repository) {\n onBranchSelect(null);\n setUserManuallyCleared(false); // Reset the manual clear flag when repository changes\n }\n }, [repository, onBranchSelect]);\n\n // Auto-select default branch when branches are loaded and no branch is selected\n // But only if the user hasn't manually cleared the branch\n useEffect(() => {\n if (\n repository &&\n defaultBranch &&\n !selectedBranch &&\n !userManuallyCleared && // Don't auto-select if user manually cleared\n filteredBranches.length > 0 &&\n !isLoading\n ) {\n const defaultBranchObj = filteredBranches.find(\n (branch) => branch.name === defaultBranch,\n );\n\n if (defaultBranchObj) {\n onBranchSelect(defaultBranchObj);\n }\n }\n }, [\n repository,\n defaultBranch,\n selectedBranch,\n userManuallyCleared,\n filteredBranches,\n onBranchSelect,\n isLoading,\n ]);\n\n // Reset input when repository changes\n useEffect(() => {\n setInputValue(\"\");\n }, [repository]);\n\n // Initialize input value when selectedBranch changes (but not when user is typing)\n useEffect(() => {\n if (selectedBranch && !isOpen) {\n setInputValue(selectedBranch.name);\n } else if (!selectedBranch && !isOpen) {\n setInputValue(\"\");\n }\n }, [selectedBranch, isOpen]);\n\n const isLoadingState = isLoading || isSearchLoading || isFetchingNextPage;\n\n return (\n <div className={cn(\"relative\", className)}>\n <div className=\"group relative text-[var(--oh-muted)] hover:text-white\">\n <div className=\"absolute left-2 top-1/2 transform -translate-y-1/2 z-10\">\n {isLoadingState ? (\n <div className=\"animate-spin h-4 w-4 border-2 border-transparent border-t-white rounded-full\" />\n ) : (\n <BranchIcon width={16} height={16} />\n )}\n </div>\n <input\n {...getInputProps({\n disabled: disabled || !repository,\n placeholder:\n placeholder ?? t(I18nKey.COMMON$SELECT_BRANCH_PLACEHOLDER),\n className: cn(\n formControlFieldClassName,\n \"text-inherit shadow-none pl-7 pr-16 text-sm font-normal leading-5\",\n \"placeholder:text-[var(--oh-muted)]\",\n \"disabled:cursor-not-allowed disabled:opacity-60\",\n ),\n // Direct onChange for cursor position preservation\n onChange: (e: React.ChangeEvent<HTMLInputElement>) => {\n setInputValue(e.target.value);\n },\n })}\n data-testid=\"git-branch-dropdown-input\"\n />\n\n <div className=\"absolute right-1 top-1/2 transform -translate-y-1/2 flex items-center\">\n {selectedBranch && (\n <ClearButton disabled={disabled} onClear={handleClear} />\n )}\n\n <ToggleButton\n isOpen={isOpen}\n disabled={disabled || !repository}\n getToggleButtonProps={getToggleButtonProps}\n />\n </div>\n </div>\n\n <BranchDropdownMenu\n isOpen={isOpen}\n filteredBranches={filteredBranches}\n inputValue={inputValue}\n highlightedIndex={highlightedIndex}\n selectedItem={selectedItem}\n getMenuProps={getMenuProps}\n getItemProps={getItemProps}\n onScroll={handleMenuScroll}\n menuRef={menuRef}\n />\n\n <ErrorMessage isError={!!error} />\n </div>\n );\n}\n"],"mappings":"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"}
@@ -1,2 +1,2 @@
1
- const e=require(`../../../../_virtual/_rolldown/runtime.cjs`),t=require(`../../../../utils/utils.cjs`),n=require(`../../../shared/git-provider-icon.cjs`),r=require(`../../../../node_modules/downshift/dist/downshift.esm.cjs`),i=require(`../shared/toggle-button.cjs`),a=require(`../shared/error-message.cjs`),o=require(`../shared/dropdown-item.cjs`),s=require(`../shared/empty-state.cjs`),c=require(`../shared/generic-dropdown-menu.cjs`),l=require(`../shared/loading-spinner.cjs`);let u=require(`react`);u=e.__toESM(u,1);let d=require(`react/jsx-runtime`);function f({providers:e,value:f,placeholder:p=`Select Provider`,className:m,errorMessage:h,disabled:g=!1,isLoading:_=!1,onChange:v,inputClassName:y,toggleButtonClassName:b,itemClassName:x}){let[S,C]=(0,u.useState)(``),[w,T]=(0,u.useState)(f||null),E=e=>{switch(e){case`github`:return`GitHub`;case`gitlab`:return`GitLab`;case`bitbucket`:return`Bitbucket`;case`bitbucket_data_center`:return`Bitbucket Data Center`;case`azure_devops`:return`Azure DevOps`;default:return e.charAt(0).toUpperCase()+e.slice(1)}},D=(0,u.useMemo)(()=>w&&S===E(w)||!S?.trim()?e:e.filter(e=>E(e).toLowerCase().includes(S.toLowerCase())),[e,S,w]),{isOpen:O,getToggleButtonProps:k,getMenuProps:A,getInputProps:j,highlightedIndex:M,getItemProps:N,selectedItem:P}=r.useCombobox({items:D,itemToString:e=>e?E(e):``,selectedItem:w,onSelectedItemChange:({selectedItem:e})=>{T(e||null),v?.(e||null)},onInputValueChange:({inputValue:e})=>{C(e||``)},inputValue:S});(0,u.useEffect)(()=>{f!==w&&T(f||null)},[f,w]),(0,u.useEffect)(()=>{P&&!O?C(E(P)):P||C(``)},[P,O]);let F=(e,t,n,r,i)=>(0,d.jsx)(o.DropdownItem,{item:e,index:t,isSelected:e===r,getItemProps:i,getDisplayText:E,getItemKey:e=>e,isProviderDropdown:!0,itemClassName:x},e),I=e=>(0,d.jsx)(s.EmptyState,{inputValue:e,searchMessage:`No providers found`,emptyMessage:`No providers available`,testId:`git-provider-dropdown-empty`});return(0,d.jsxs)(`div`,{className:t.cn(`relative`,m),children:[(0,d.jsxs)(`div`,{className:`group relative text-[var(--oh-muted)] hover:text-white`,children:[P&&(0,d.jsx)(`div`,{className:`absolute left-2 top-1/2 transform -translate-y-1/2 z-10`,children:(0,d.jsx)(n.GitProviderIcon,{gitProvider:P,className:`min-w-[14px] min-h-[14px] w-[14px] h-[14px]`})}),(0,d.jsx)(`input`,{...j({disabled:g,placeholder:p,readOnly:!0,className:t.cn(`w-29.5 h-6 py-0 border border-[var(--oh-border-input)] rounded shadow-none h-6 min-h-6 max-h-6 `,`text-inherit bg-tertiary placeholder:text-[var(--oh-muted)]`,`focus:outline-none focus:ring-0 focus:border-[var(--oh-border-input)]`,`disabled:bg-tertiary disabled:cursor-not-allowed disabled:opacity-60`,`pl-1.5 pr-[1px] cursor-pointer text-xs font-normal leading-5`,P&&`pl-6`,y)}),"data-testid":`git-provider-dropdown`}),(0,d.jsx)(`div`,{className:`absolute right-0 top-1/2 transform -translate-y-1/2 flex items-center gap-1`,children:(0,d.jsx)(i.ToggleButton,{isOpen:O,disabled:g,getToggleButtonProps:k,iconClassName:b})}),_&&(0,d.jsx)(l.LoadingSpinner,{hasSelection:!!P})]}),(0,d.jsx)(c.GenericDropdownMenu,{isOpen:O,filteredItems:D,inputValue:S,highlightedIndex:M,selectedItem:P,getMenuProps:A,getItemProps:N,renderItem:F,renderEmptyState:I,itemKey:e=>e}),(0,d.jsx)(a.ErrorMessage,{isError:!!h,message:h})]})}exports.GitProviderDropdown=f;
1
+ const e=require(`../../../../_virtual/_rolldown/runtime.cjs`),t=require(`../../../../node_modules/react-i18next/dist/es/useTranslation.cjs`),n=require(`../../../../i18n/declaration.cjs`),r=require(`../../../../utils/utils.cjs`),i=require(`../../../shared/git-provider-icon.cjs`),a=require(`../../../../node_modules/downshift/dist/downshift.esm.cjs`),o=require(`../shared/toggle-button.cjs`),s=require(`../shared/error-message.cjs`),c=require(`../shared/dropdown-item.cjs`),l=require(`../shared/empty-state.cjs`),u=require(`../shared/generic-dropdown-menu.cjs`),d=require(`../shared/loading-spinner.cjs`);let f=require(`react`);f=e.__toESM(f,1);let p=require(`react/jsx-runtime`);function m({providers:e,value:m,placeholder:h,className:g,errorMessage:_,disabled:v=!1,isLoading:y=!1,onChange:b,inputClassName:x,toggleButtonClassName:S,itemClassName:C}){let{t:w}=t.useTranslation(`openhands`),[T,E]=(0,f.useState)(``),[D,O]=(0,f.useState)(m||null),k=e=>{switch(e){case`github`:return`GitHub`;case`gitlab`:return`GitLab`;case`bitbucket`:return`Bitbucket`;case`bitbucket_data_center`:return`Bitbucket Data Center`;case`azure_devops`:return`Azure DevOps`;default:return e.charAt(0).toUpperCase()+e.slice(1)}},A=(0,f.useMemo)(()=>D&&T===k(D)||!T?.trim()?e:e.filter(e=>k(e).toLowerCase().includes(T.toLowerCase())),[e,T,D]),{isOpen:j,getToggleButtonProps:M,getMenuProps:N,getInputProps:P,highlightedIndex:F,getItemProps:I,selectedItem:L}=a.useCombobox({items:A,itemToString:e=>e?k(e):``,selectedItem:D,onSelectedItemChange:({selectedItem:e})=>{O(e||null),b?.(e||null)},onInputValueChange:({inputValue:e})=>{E(e||``)},inputValue:T});(0,f.useEffect)(()=>{m!==D&&O(m||null)},[m,D]),(0,f.useEffect)(()=>{L&&!j?E(k(L)):L||E(``)},[L,j]);let R=(e,t,n,r,i)=>(0,p.jsx)(c.DropdownItem,{item:e,index:t,isSelected:e===r,getItemProps:i,getDisplayText:k,getItemKey:e=>e,isProviderDropdown:!0,itemClassName:C},e),z=e=>(0,p.jsx)(l.EmptyState,{inputValue:e,searchMessage:`No providers found`,emptyMessage:`No providers available`,testId:`git-provider-dropdown-empty`});return(0,p.jsxs)(`div`,{className:r.cn(`relative`,g),children:[(0,p.jsxs)(`div`,{className:`group relative text-[var(--oh-muted)] hover:text-white`,children:[L&&(0,p.jsx)(`div`,{className:`absolute left-2 top-1/2 transform -translate-y-1/2 z-10`,children:(0,p.jsx)(i.GitProviderIcon,{gitProvider:L,className:`min-w-[14px] min-h-[14px] w-[14px] h-[14px]`})}),(0,p.jsx)(`input`,{...P({disabled:v,placeholder:h??w(n.I18nKey.COMMON$SELECT_PROVIDER_PLACEHOLDER),readOnly:!0,className:r.cn(`w-29.5 h-6 py-0 border border-[var(--oh-border-input)] rounded shadow-none h-6 min-h-6 max-h-6 `,`text-inherit bg-tertiary placeholder:text-[var(--oh-muted)]`,`focus:outline-none focus:ring-0 focus:border-[var(--oh-border-input)]`,`disabled:bg-tertiary disabled:cursor-not-allowed disabled:opacity-60`,`pl-1.5 pr-[1px] cursor-pointer text-xs font-normal leading-5`,L&&`pl-6`,x)}),"data-testid":`git-provider-dropdown`}),(0,p.jsx)(`div`,{className:`absolute right-0 top-1/2 transform -translate-y-1/2 flex items-center gap-1`,children:(0,p.jsx)(o.ToggleButton,{isOpen:j,disabled:v,getToggleButtonProps:M,iconClassName:S})}),y&&(0,p.jsx)(d.LoadingSpinner,{hasSelection:!!L})]}),(0,p.jsx)(u.GenericDropdownMenu,{isOpen:j,filteredItems:A,inputValue:T,highlightedIndex:F,selectedItem:L,getMenuProps:N,getItemProps:I,renderItem:R,renderEmptyState:z,itemKey:e=>e}),(0,p.jsx)(s.ErrorMessage,{isError:!!_,message:_})]})}exports.GitProviderDropdown=m;
2
2
  //# sourceMappingURL=git-provider-dropdown.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"git-provider-dropdown.cjs","names":[],"sources":["../../../../../src/components/features/home/git-provider-dropdown/git-provider-dropdown.tsx"],"sourcesContent":["import React, { useState, useMemo, useEffect } from \"react\";\nimport { useCombobox } from \"downshift\";\nimport { Provider } from \"#/types/settings\";\nimport { cn } from \"#/utils/utils\";\nimport { DropdownItem } from \"../shared/dropdown-item\";\nimport { GenericDropdownMenu } from \"../shared/generic-dropdown-menu\";\nimport { ToggleButton } from \"../shared/toggle-button\";\nimport { LoadingSpinner } from \"../shared/loading-spinner\";\nimport { ErrorMessage } from \"../shared/error-message\";\nimport { EmptyState } from \"../shared/empty-state\";\nimport { GitProviderIcon } from \"#/components/shared/git-provider-icon\";\n\nexport interface GitProviderDropdownProps {\n providers: Provider[];\n value?: Provider | null;\n placeholder?: string;\n className?: string;\n errorMessage?: string;\n disabled?: boolean;\n isLoading?: boolean;\n onChange?: (provider: Provider | null) => void;\n inputClassName?: string;\n toggleButtonClassName?: string;\n itemClassName?: string;\n}\n\nexport function GitProviderDropdown({\n providers,\n value,\n placeholder = \"Select Provider\",\n className,\n errorMessage,\n disabled = false,\n isLoading = false,\n onChange,\n inputClassName,\n toggleButtonClassName,\n itemClassName,\n}: GitProviderDropdownProps) {\n const [inputValue, setInputValue] = useState(\"\");\n const [localSelectedItem, setLocalSelectedItem] = useState<Provider | null>(\n value || null,\n );\n\n // Format provider names for display\n const formatProviderName = (provider: Provider): string => {\n switch (provider) {\n case \"github\":\n return \"GitHub\";\n case \"gitlab\":\n return \"GitLab\";\n case \"bitbucket\":\n return \"Bitbucket\";\n case \"bitbucket_data_center\":\n return \"Bitbucket Data Center\";\n case \"azure_devops\":\n return \"Azure DevOps\";\n default:\n // Fallback for any future provider types\n return (\n (provider as string).charAt(0).toUpperCase() +\n (provider as string).slice(1)\n );\n }\n };\n\n // Filter providers based on input value\n const filteredProviders = useMemo(() => {\n // If we have a selected provider and the input matches it exactly, show all providers\n if (\n localSelectedItem &&\n inputValue === formatProviderName(localSelectedItem)\n ) {\n return providers;\n }\n\n // If no input value, show all providers\n if (!inputValue?.trim()) {\n return providers;\n }\n\n // Filter providers based on input\n return providers.filter((provider) =>\n formatProviderName(provider)\n .toLowerCase()\n .includes(inputValue.toLowerCase()),\n );\n }, [providers, inputValue, localSelectedItem]);\n\n const {\n isOpen,\n getToggleButtonProps,\n getMenuProps,\n getInputProps,\n highlightedIndex,\n getItemProps,\n selectedItem,\n } = useCombobox({\n items: filteredProviders,\n itemToString: (item) => (item ? formatProviderName(item) : \"\"),\n selectedItem: localSelectedItem,\n onSelectedItemChange: ({ selectedItem: newSelectedItem }) => {\n setLocalSelectedItem(newSelectedItem || null);\n onChange?.(newSelectedItem || null);\n },\n onInputValueChange: ({ inputValue: newInputValue }) => {\n setInputValue(newInputValue || \"\");\n },\n inputValue,\n });\n\n // Sync with external value prop\n useEffect(() => {\n if (value !== localSelectedItem) {\n setLocalSelectedItem(value || null);\n }\n }, [value, localSelectedItem]);\n\n // Update input value when selection changes (but not when user is typing)\n useEffect(() => {\n if (selectedItem && !isOpen) {\n setInputValue(formatProviderName(selectedItem));\n } else if (!selectedItem) {\n setInputValue(\"\");\n }\n }, [selectedItem, isOpen]);\n\n const renderItem = (\n item: Provider,\n index: number,\n currentHighlightedIndex: number,\n currentSelectedItem: Provider | null,\n currentGetItemProps: any, // eslint-disable-line @typescript-eslint/no-explicit-any\n ) => (\n <DropdownItem\n key={item}\n item={item}\n index={index}\n isSelected={item === currentSelectedItem}\n getItemProps={currentGetItemProps}\n getDisplayText={formatProviderName}\n getItemKey={(provider) => provider}\n isProviderDropdown\n itemClassName={itemClassName}\n />\n );\n\n const renderEmptyState = (currentInputValue: string) => (\n <EmptyState\n inputValue={currentInputValue}\n searchMessage=\"No providers found\"\n emptyMessage=\"No providers available\"\n testId=\"git-provider-dropdown-empty\"\n />\n );\n\n return (\n <div className={cn(\"relative\", className)}>\n <div className=\"group relative text-[var(--oh-muted)] hover:text-white\">\n {/* Provider icon */}\n {selectedItem && (\n <div className=\"absolute left-2 top-1/2 transform -translate-y-1/2 z-10\">\n <GitProviderIcon\n gitProvider={selectedItem}\n className=\"min-w-[14px] min-h-[14px] w-[14px] h-[14px]\"\n />\n </div>\n )}\n\n <input\n {...getInputProps({\n disabled,\n placeholder,\n readOnly: true, // Make it non-searchable like the original\n className: cn(\n \"w-29.5 h-6 py-0 border border-[var(--oh-border-input)] rounded shadow-none h-6 min-h-6 max-h-6 \",\n \"text-inherit bg-tertiary placeholder:text-[var(--oh-muted)]\",\n \"focus:outline-none focus:ring-0 focus:border-[var(--oh-border-input)]\",\n \"disabled:bg-tertiary disabled:cursor-not-allowed disabled:opacity-60\",\n \"pl-1.5 pr-[1px] cursor-pointer text-xs font-normal leading-5\", // Space for toggle button and pointer cursor\n selectedItem && \"pl-6\",\n inputClassName,\n ),\n })}\n data-testid=\"git-provider-dropdown\"\n />\n\n <div className=\"absolute right-0 top-1/2 transform -translate-y-1/2 flex items-center gap-1\">\n <ToggleButton\n isOpen={isOpen}\n disabled={disabled}\n getToggleButtonProps={getToggleButtonProps}\n iconClassName={toggleButtonClassName}\n />\n </div>\n\n {isLoading && <LoadingSpinner hasSelection={!!selectedItem} />}\n </div>\n\n <GenericDropdownMenu\n isOpen={isOpen}\n filteredItems={filteredProviders}\n inputValue={inputValue}\n highlightedIndex={highlightedIndex}\n selectedItem={selectedItem}\n getMenuProps={getMenuProps}\n getItemProps={getItemProps}\n renderItem={renderItem}\n renderEmptyState={renderEmptyState}\n itemKey={(provider) => provider}\n />\n\n <ErrorMessage isError={!!errorMessage} message={errorMessage} />\n </div>\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"git-provider-dropdown.cjs","names":[],"sources":["../../../../../src/components/features/home/git-provider-dropdown/git-provider-dropdown.tsx"],"sourcesContent":["import React, { useState, useMemo, useEffect } from \"react\";\nimport { useCombobox } from \"downshift\";\nimport { useTranslation } from \"react-i18next\";\nimport { Provider } from \"#/types/settings\";\nimport { I18nKey } from \"#/i18n/declaration\";\nimport { cn } from \"#/utils/utils\";\nimport { DropdownItem } from \"../shared/dropdown-item\";\nimport { GenericDropdownMenu } from \"../shared/generic-dropdown-menu\";\nimport { ToggleButton } from \"../shared/toggle-button\";\nimport { LoadingSpinner } from \"../shared/loading-spinner\";\nimport { ErrorMessage } from \"../shared/error-message\";\nimport { EmptyState } from \"../shared/empty-state\";\nimport { GitProviderIcon } from \"#/components/shared/git-provider-icon\";\n\nexport interface GitProviderDropdownProps {\n providers: Provider[];\n value?: Provider | null;\n placeholder?: string;\n className?: string;\n errorMessage?: string;\n disabled?: boolean;\n isLoading?: boolean;\n onChange?: (provider: Provider | null) => void;\n inputClassName?: string;\n toggleButtonClassName?: string;\n itemClassName?: string;\n}\n\nexport function GitProviderDropdown({\n providers,\n value,\n placeholder,\n className,\n errorMessage,\n disabled = false,\n isLoading = false,\n onChange,\n inputClassName,\n toggleButtonClassName,\n itemClassName,\n}: GitProviderDropdownProps) {\n const { t } = useTranslation(\"openhands\");\n const [inputValue, setInputValue] = useState(\"\");\n const [localSelectedItem, setLocalSelectedItem] = useState<Provider | null>(\n value || null,\n );\n\n // Format provider names for display\n const formatProviderName = (provider: Provider): string => {\n switch (provider) {\n case \"github\":\n return \"GitHub\";\n case \"gitlab\":\n return \"GitLab\";\n case \"bitbucket\":\n return \"Bitbucket\";\n case \"bitbucket_data_center\":\n return \"Bitbucket Data Center\";\n case \"azure_devops\":\n return \"Azure DevOps\";\n default:\n // Fallback for any future provider types\n return (\n (provider as string).charAt(0).toUpperCase() +\n (provider as string).slice(1)\n );\n }\n };\n\n // Filter providers based on input value\n const filteredProviders = useMemo(() => {\n // If we have a selected provider and the input matches it exactly, show all providers\n if (\n localSelectedItem &&\n inputValue === formatProviderName(localSelectedItem)\n ) {\n return providers;\n }\n\n // If no input value, show all providers\n if (!inputValue?.trim()) {\n return providers;\n }\n\n // Filter providers based on input\n return providers.filter((provider) =>\n formatProviderName(provider)\n .toLowerCase()\n .includes(inputValue.toLowerCase()),\n );\n }, [providers, inputValue, localSelectedItem]);\n\n const {\n isOpen,\n getToggleButtonProps,\n getMenuProps,\n getInputProps,\n highlightedIndex,\n getItemProps,\n selectedItem,\n } = useCombobox({\n items: filteredProviders,\n itemToString: (item) => (item ? formatProviderName(item) : \"\"),\n selectedItem: localSelectedItem,\n onSelectedItemChange: ({ selectedItem: newSelectedItem }) => {\n setLocalSelectedItem(newSelectedItem || null);\n onChange?.(newSelectedItem || null);\n },\n onInputValueChange: ({ inputValue: newInputValue }) => {\n setInputValue(newInputValue || \"\");\n },\n inputValue,\n });\n\n // Sync with external value prop\n useEffect(() => {\n if (value !== localSelectedItem) {\n setLocalSelectedItem(value || null);\n }\n }, [value, localSelectedItem]);\n\n // Update input value when selection changes (but not when user is typing)\n useEffect(() => {\n if (selectedItem && !isOpen) {\n setInputValue(formatProviderName(selectedItem));\n } else if (!selectedItem) {\n setInputValue(\"\");\n }\n }, [selectedItem, isOpen]);\n\n const renderItem = (\n item: Provider,\n index: number,\n currentHighlightedIndex: number,\n currentSelectedItem: Provider | null,\n currentGetItemProps: any, // eslint-disable-line @typescript-eslint/no-explicit-any\n ) => (\n <DropdownItem\n key={item}\n item={item}\n index={index}\n isSelected={item === currentSelectedItem}\n getItemProps={currentGetItemProps}\n getDisplayText={formatProviderName}\n getItemKey={(provider) => provider}\n isProviderDropdown\n itemClassName={itemClassName}\n />\n );\n\n const renderEmptyState = (currentInputValue: string) => (\n <EmptyState\n inputValue={currentInputValue}\n searchMessage=\"No providers found\"\n emptyMessage=\"No providers available\"\n testId=\"git-provider-dropdown-empty\"\n />\n );\n\n return (\n <div className={cn(\"relative\", className)}>\n <div className=\"group relative text-[var(--oh-muted)] hover:text-white\">\n {/* Provider icon */}\n {selectedItem && (\n <div className=\"absolute left-2 top-1/2 transform -translate-y-1/2 z-10\">\n <GitProviderIcon\n gitProvider={selectedItem}\n className=\"min-w-[14px] min-h-[14px] w-[14px] h-[14px]\"\n />\n </div>\n )}\n\n <input\n {...getInputProps({\n disabled,\n placeholder:\n placeholder ?? t(I18nKey.COMMON$SELECT_PROVIDER_PLACEHOLDER),\n readOnly: true, // Make it non-searchable like the original\n className: cn(\n \"w-29.5 h-6 py-0 border border-[var(--oh-border-input)] rounded shadow-none h-6 min-h-6 max-h-6 \",\n \"text-inherit bg-tertiary placeholder:text-[var(--oh-muted)]\",\n \"focus:outline-none focus:ring-0 focus:border-[var(--oh-border-input)]\",\n \"disabled:bg-tertiary disabled:cursor-not-allowed disabled:opacity-60\",\n \"pl-1.5 pr-[1px] cursor-pointer text-xs font-normal leading-5\", // Space for toggle button and pointer cursor\n selectedItem && \"pl-6\",\n inputClassName,\n ),\n })}\n data-testid=\"git-provider-dropdown\"\n />\n\n <div className=\"absolute right-0 top-1/2 transform -translate-y-1/2 flex items-center gap-1\">\n <ToggleButton\n isOpen={isOpen}\n disabled={disabled}\n getToggleButtonProps={getToggleButtonProps}\n iconClassName={toggleButtonClassName}\n />\n </div>\n\n {isLoading && <LoadingSpinner hasSelection={!!selectedItem} />}\n </div>\n\n <GenericDropdownMenu\n isOpen={isOpen}\n filteredItems={filteredProviders}\n inputValue={inputValue}\n highlightedIndex={highlightedIndex}\n selectedItem={selectedItem}\n getMenuProps={getMenuProps}\n getItemProps={getItemProps}\n renderItem={renderItem}\n renderEmptyState={renderEmptyState}\n itemKey={(provider) => provider}\n />\n\n <ErrorMessage isError={!!errorMessage} message={errorMessage} />\n </div>\n );\n}\n"],"mappings":"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"}
@@ -1,17 +1,19 @@
1
- import { cn as e } from "../../../../utils/utils.js";
2
- import { GitProviderIcon as t } from "../../../shared/git-provider-icon.js";
3
- import { useCombobox as n } from "../../../../node_modules/downshift/dist/downshift.esm.js";
4
- import { ToggleButton as r } from "../shared/toggle-button.js";
5
- import { ErrorMessage as i } from "../shared/error-message.js";
6
- import { DropdownItem as a } from "../shared/dropdown-item.js";
7
- import { EmptyState as o } from "../shared/empty-state.js";
8
- import { GenericDropdownMenu as s } from "../shared/generic-dropdown-menu.js";
9
- import { LoadingSpinner as c } from "../shared/loading-spinner.js";
10
- import { useEffect as l, useMemo as u, useState as d } from "react";
11
- import { jsx as f, jsxs as p } from "react/jsx-runtime";
1
+ import { useTranslation as e } from "../../../../node_modules/react-i18next/dist/es/useTranslation.js";
2
+ import { I18nKey as t } from "../../../../i18n/declaration.js";
3
+ import { cn as n } from "../../../../utils/utils.js";
4
+ import { GitProviderIcon as r } from "../../../shared/git-provider-icon.js";
5
+ import { useCombobox as i } from "../../../../node_modules/downshift/dist/downshift.esm.js";
6
+ import { ToggleButton as a } from "../shared/toggle-button.js";
7
+ import { ErrorMessage as o } from "../shared/error-message.js";
8
+ import { DropdownItem as s } from "../shared/dropdown-item.js";
9
+ import { EmptyState as c } from "../shared/empty-state.js";
10
+ import { GenericDropdownMenu as l } from "../shared/generic-dropdown-menu.js";
11
+ import { LoadingSpinner as u } from "../shared/loading-spinner.js";
12
+ import { useEffect as d, useMemo as f, useState as p } from "react";
13
+ import { jsx as m, jsxs as h } from "react/jsx-runtime";
12
14
  //#region src/components/features/home/git-provider-dropdown/git-provider-dropdown.tsx
13
- function m({ providers: m, value: h, placeholder: g = "Select Provider", className: _, errorMessage: v, disabled: y = !1, isLoading: b = !1, onChange: x, inputClassName: S, toggleButtonClassName: C, itemClassName: w }) {
14
- let [T, E] = d(""), [D, O] = d(h || null), k = (e) => {
15
+ function g({ providers: g, value: _, placeholder: v, className: y, errorMessage: b, disabled: x = !1, isLoading: S = !1, onChange: C, inputClassName: w, toggleButtonClassName: T, itemClassName: E }) {
16
+ let { t: D } = e("openhands"), [O, k] = p(""), [A, j] = p(_ || null), M = (e) => {
15
17
  switch (e) {
16
18
  case "github": return "GitHub";
17
19
  case "gitlab": return "GitLab";
@@ -20,96 +22,96 @@ function m({ providers: m, value: h, placeholder: g = "Select Provider", classNa
20
22
  case "azure_devops": return "Azure DevOps";
21
23
  default: return e.charAt(0).toUpperCase() + e.slice(1);
22
24
  }
23
- }, A = u(() => D && T === k(D) || !T?.trim() ? m : m.filter((e) => k(e).toLowerCase().includes(T.toLowerCase())), [
24
- m,
25
- T,
26
- D
27
- ]), { isOpen: j, getToggleButtonProps: M, getMenuProps: N, getInputProps: P, highlightedIndex: F, getItemProps: I, selectedItem: L } = n({
28
- items: A,
29
- itemToString: (e) => e ? k(e) : "",
30
- selectedItem: D,
25
+ }, N = f(() => A && O === M(A) || !O?.trim() ? g : g.filter((e) => M(e).toLowerCase().includes(O.toLowerCase())), [
26
+ g,
27
+ O,
28
+ A
29
+ ]), { isOpen: P, getToggleButtonProps: F, getMenuProps: I, getInputProps: L, highlightedIndex: R, getItemProps: z, selectedItem: B } = i({
30
+ items: N,
31
+ itemToString: (e) => e ? M(e) : "",
32
+ selectedItem: A,
31
33
  onSelectedItemChange: ({ selectedItem: e }) => {
32
- O(e || null), x?.(e || null);
34
+ j(e || null), C?.(e || null);
33
35
  },
34
36
  onInputValueChange: ({ inputValue: e }) => {
35
- E(e || "");
37
+ k(e || "");
36
38
  },
37
- inputValue: T
39
+ inputValue: O
38
40
  });
39
- l(() => {
40
- h !== D && O(h || null);
41
- }, [h, D]), l(() => {
42
- L && !j ? E(k(L)) : L || E("");
43
- }, [L, j]);
44
- let R = (e, t, n, r, i) => /* @__PURE__ */ f(a, {
41
+ d(() => {
42
+ _ !== A && j(_ || null);
43
+ }, [_, A]), d(() => {
44
+ B && !P ? k(M(B)) : B || k("");
45
+ }, [B, P]);
46
+ let V = (e, t, n, r, i) => /* @__PURE__ */ m(s, {
45
47
  item: e,
46
48
  index: t,
47
49
  isSelected: e === r,
48
50
  getItemProps: i,
49
- getDisplayText: k,
51
+ getDisplayText: M,
50
52
  getItemKey: (e) => e,
51
53
  isProviderDropdown: !0,
52
- itemClassName: w
53
- }, e), z = (e) => /* @__PURE__ */ f(o, {
54
+ itemClassName: E
55
+ }, e), H = (e) => /* @__PURE__ */ m(c, {
54
56
  inputValue: e,
55
57
  searchMessage: "No providers found",
56
58
  emptyMessage: "No providers available",
57
59
  testId: "git-provider-dropdown-empty"
58
60
  });
59
- return /* @__PURE__ */ p("div", {
60
- className: e("relative", _),
61
+ return /* @__PURE__ */ h("div", {
62
+ className: n("relative", y),
61
63
  children: [
62
- /* @__PURE__ */ p("div", {
64
+ /* @__PURE__ */ h("div", {
63
65
  className: "group relative text-[var(--oh-muted)] hover:text-white",
64
66
  children: [
65
- L && /* @__PURE__ */ f("div", {
67
+ B && /* @__PURE__ */ m("div", {
66
68
  className: "absolute left-2 top-1/2 transform -translate-y-1/2 z-10",
67
- children: /* @__PURE__ */ f(t, {
68
- gitProvider: L,
69
+ children: /* @__PURE__ */ m(r, {
70
+ gitProvider: B,
69
71
  className: "min-w-[14px] min-h-[14px] w-[14px] h-[14px]"
70
72
  })
71
73
  }),
72
- /* @__PURE__ */ f("input", {
73
- ...P({
74
- disabled: y,
75
- placeholder: g,
74
+ /* @__PURE__ */ m("input", {
75
+ ...L({
76
+ disabled: x,
77
+ placeholder: v ?? D(t.COMMON$SELECT_PROVIDER_PLACEHOLDER),
76
78
  readOnly: !0,
77
- className: e("w-29.5 h-6 py-0 border border-[var(--oh-border-input)] rounded shadow-none h-6 min-h-6 max-h-6 ", "text-inherit bg-tertiary placeholder:text-[var(--oh-muted)]", "focus:outline-none focus:ring-0 focus:border-[var(--oh-border-input)]", "disabled:bg-tertiary disabled:cursor-not-allowed disabled:opacity-60", "pl-1.5 pr-[1px] cursor-pointer text-xs font-normal leading-5", L && "pl-6", S)
79
+ className: n("w-29.5 h-6 py-0 border border-[var(--oh-border-input)] rounded shadow-none h-6 min-h-6 max-h-6 ", "text-inherit bg-tertiary placeholder:text-[var(--oh-muted)]", "focus:outline-none focus:ring-0 focus:border-[var(--oh-border-input)]", "disabled:bg-tertiary disabled:cursor-not-allowed disabled:opacity-60", "pl-1.5 pr-[1px] cursor-pointer text-xs font-normal leading-5", B && "pl-6", w)
78
80
  }),
79
81
  "data-testid": "git-provider-dropdown"
80
82
  }),
81
- /* @__PURE__ */ f("div", {
83
+ /* @__PURE__ */ m("div", {
82
84
  className: "absolute right-0 top-1/2 transform -translate-y-1/2 flex items-center gap-1",
83
- children: /* @__PURE__ */ f(r, {
84
- isOpen: j,
85
- disabled: y,
86
- getToggleButtonProps: M,
87
- iconClassName: C
85
+ children: /* @__PURE__ */ m(a, {
86
+ isOpen: P,
87
+ disabled: x,
88
+ getToggleButtonProps: F,
89
+ iconClassName: T
88
90
  })
89
91
  }),
90
- b && /* @__PURE__ */ f(c, { hasSelection: !!L })
92
+ S && /* @__PURE__ */ m(u, { hasSelection: !!B })
91
93
  ]
92
94
  }),
93
- /* @__PURE__ */ f(s, {
94
- isOpen: j,
95
- filteredItems: A,
96
- inputValue: T,
97
- highlightedIndex: F,
98
- selectedItem: L,
99
- getMenuProps: N,
100
- getItemProps: I,
101
- renderItem: R,
102
- renderEmptyState: z,
95
+ /* @__PURE__ */ m(l, {
96
+ isOpen: P,
97
+ filteredItems: N,
98
+ inputValue: O,
99
+ highlightedIndex: R,
100
+ selectedItem: B,
101
+ getMenuProps: I,
102
+ getItemProps: z,
103
+ renderItem: V,
104
+ renderEmptyState: H,
103
105
  itemKey: (e) => e
104
106
  }),
105
- /* @__PURE__ */ f(i, {
106
- isError: !!v,
107
- message: v
107
+ /* @__PURE__ */ m(o, {
108
+ isError: !!b,
109
+ message: b
108
110
  })
109
111
  ]
110
112
  });
111
113
  }
112
114
  //#endregion
113
- export { m as GitProviderDropdown };
115
+ export { g as GitProviderDropdown };
114
116
 
115
117
  //# sourceMappingURL=git-provider-dropdown.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"git-provider-dropdown.js","names":[],"sources":["../../../../../src/components/features/home/git-provider-dropdown/git-provider-dropdown.tsx"],"sourcesContent":["import React, { useState, useMemo, useEffect } from \"react\";\nimport { useCombobox } from \"downshift\";\nimport { Provider } from \"#/types/settings\";\nimport { cn } from \"#/utils/utils\";\nimport { DropdownItem } from \"../shared/dropdown-item\";\nimport { GenericDropdownMenu } from \"../shared/generic-dropdown-menu\";\nimport { ToggleButton } from \"../shared/toggle-button\";\nimport { LoadingSpinner } from \"../shared/loading-spinner\";\nimport { ErrorMessage } from \"../shared/error-message\";\nimport { EmptyState } from \"../shared/empty-state\";\nimport { GitProviderIcon } from \"#/components/shared/git-provider-icon\";\n\nexport interface GitProviderDropdownProps {\n providers: Provider[];\n value?: Provider | null;\n placeholder?: string;\n className?: string;\n errorMessage?: string;\n disabled?: boolean;\n isLoading?: boolean;\n onChange?: (provider: Provider | null) => void;\n inputClassName?: string;\n toggleButtonClassName?: string;\n itemClassName?: string;\n}\n\nexport function GitProviderDropdown({\n providers,\n value,\n placeholder = \"Select Provider\",\n className,\n errorMessage,\n disabled = false,\n isLoading = false,\n onChange,\n inputClassName,\n toggleButtonClassName,\n itemClassName,\n}: GitProviderDropdownProps) {\n const [inputValue, setInputValue] = useState(\"\");\n const [localSelectedItem, setLocalSelectedItem] = useState<Provider | null>(\n value || null,\n );\n\n // Format provider names for display\n const formatProviderName = (provider: Provider): string => {\n switch (provider) {\n case \"github\":\n return \"GitHub\";\n case \"gitlab\":\n return \"GitLab\";\n case \"bitbucket\":\n return \"Bitbucket\";\n case \"bitbucket_data_center\":\n return \"Bitbucket Data Center\";\n case \"azure_devops\":\n return \"Azure DevOps\";\n default:\n // Fallback for any future provider types\n return (\n (provider as string).charAt(0).toUpperCase() +\n (provider as string).slice(1)\n );\n }\n };\n\n // Filter providers based on input value\n const filteredProviders = useMemo(() => {\n // If we have a selected provider and the input matches it exactly, show all providers\n if (\n localSelectedItem &&\n inputValue === formatProviderName(localSelectedItem)\n ) {\n return providers;\n }\n\n // If no input value, show all providers\n if (!inputValue?.trim()) {\n return providers;\n }\n\n // Filter providers based on input\n return providers.filter((provider) =>\n formatProviderName(provider)\n .toLowerCase()\n .includes(inputValue.toLowerCase()),\n );\n }, [providers, inputValue, localSelectedItem]);\n\n const {\n isOpen,\n getToggleButtonProps,\n getMenuProps,\n getInputProps,\n highlightedIndex,\n getItemProps,\n selectedItem,\n } = useCombobox({\n items: filteredProviders,\n itemToString: (item) => (item ? formatProviderName(item) : \"\"),\n selectedItem: localSelectedItem,\n onSelectedItemChange: ({ selectedItem: newSelectedItem }) => {\n setLocalSelectedItem(newSelectedItem || null);\n onChange?.(newSelectedItem || null);\n },\n onInputValueChange: ({ inputValue: newInputValue }) => {\n setInputValue(newInputValue || \"\");\n },\n inputValue,\n });\n\n // Sync with external value prop\n useEffect(() => {\n if (value !== localSelectedItem) {\n setLocalSelectedItem(value || null);\n }\n }, [value, localSelectedItem]);\n\n // Update input value when selection changes (but not when user is typing)\n useEffect(() => {\n if (selectedItem && !isOpen) {\n setInputValue(formatProviderName(selectedItem));\n } else if (!selectedItem) {\n setInputValue(\"\");\n }\n }, [selectedItem, isOpen]);\n\n const renderItem = (\n item: Provider,\n index: number,\n currentHighlightedIndex: number,\n currentSelectedItem: Provider | null,\n currentGetItemProps: any, // eslint-disable-line @typescript-eslint/no-explicit-any\n ) => (\n <DropdownItem\n key={item}\n item={item}\n index={index}\n isSelected={item === currentSelectedItem}\n getItemProps={currentGetItemProps}\n getDisplayText={formatProviderName}\n getItemKey={(provider) => provider}\n isProviderDropdown\n itemClassName={itemClassName}\n />\n );\n\n const renderEmptyState = (currentInputValue: string) => (\n <EmptyState\n inputValue={currentInputValue}\n searchMessage=\"No providers found\"\n emptyMessage=\"No providers available\"\n testId=\"git-provider-dropdown-empty\"\n />\n );\n\n return (\n <div className={cn(\"relative\", className)}>\n <div className=\"group relative text-[var(--oh-muted)] hover:text-white\">\n {/* Provider icon */}\n {selectedItem && (\n <div className=\"absolute left-2 top-1/2 transform -translate-y-1/2 z-10\">\n <GitProviderIcon\n gitProvider={selectedItem}\n className=\"min-w-[14px] min-h-[14px] w-[14px] h-[14px]\"\n />\n </div>\n )}\n\n <input\n {...getInputProps({\n disabled,\n placeholder,\n readOnly: true, // Make it non-searchable like the original\n className: cn(\n \"w-29.5 h-6 py-0 border border-[var(--oh-border-input)] rounded shadow-none h-6 min-h-6 max-h-6 \",\n \"text-inherit bg-tertiary placeholder:text-[var(--oh-muted)]\",\n \"focus:outline-none focus:ring-0 focus:border-[var(--oh-border-input)]\",\n \"disabled:bg-tertiary disabled:cursor-not-allowed disabled:opacity-60\",\n \"pl-1.5 pr-[1px] cursor-pointer text-xs font-normal leading-5\", // Space for toggle button and pointer cursor\n selectedItem && \"pl-6\",\n inputClassName,\n ),\n })}\n data-testid=\"git-provider-dropdown\"\n />\n\n <div className=\"absolute right-0 top-1/2 transform -translate-y-1/2 flex items-center gap-1\">\n <ToggleButton\n isOpen={isOpen}\n disabled={disabled}\n getToggleButtonProps={getToggleButtonProps}\n iconClassName={toggleButtonClassName}\n />\n </div>\n\n {isLoading && <LoadingSpinner hasSelection={!!selectedItem} />}\n </div>\n\n <GenericDropdownMenu\n isOpen={isOpen}\n filteredItems={filteredProviders}\n inputValue={inputValue}\n highlightedIndex={highlightedIndex}\n selectedItem={selectedItem}\n getMenuProps={getMenuProps}\n getItemProps={getItemProps}\n renderItem={renderItem}\n renderEmptyState={renderEmptyState}\n itemKey={(provider) => provider}\n />\n\n <ErrorMessage isError={!!errorMessage} message={errorMessage} />\n </div>\n );\n}\n"],"mappings":";;;;;;;;;;;;AA0BA,SAAgB,EAAoB,EAClC,cACA,UACA,iBAAc,mBACd,cACA,iBACA,cAAW,IACX,eAAY,IACZ,aACA,mBACA,0BACA,oBAC2B;CAC3B,IAAM,CAAC,GAAY,KAAiB,EAAS,GAAG,EAC1C,CAAC,GAAmB,KAAwB,EAChD,KAAS,KACV,EAGK,KAAsB,MAA+B;AACzD,UAAQ,GAAR;GACE,KAAK,SACH,QAAO;GACT,KAAK,SACH,QAAO;GACT,KAAK,YACH,QAAO;GACT,KAAK,wBACH,QAAO;GACT,KAAK,eACH,QAAO;GACT,QAEE,QACG,EAAoB,OAAO,EAAE,CAAC,aAAa,GAC3C,EAAoB,MAAM,EAAE;;IAM/B,IAAoB,QAGtB,KACA,MAAe,EAAmB,EAAkB,IAMlD,CAAC,GAAY,MAAM,GACd,IAIF,EAAU,QAAQ,MACvB,EAAmB,EAAS,CACzB,aAAa,CACb,SAAS,EAAW,aAAa,CAAC,CACtC,EACA;EAAC;EAAW;EAAY;EAAkB,CAAC,EAExC,EACJ,WACA,yBACA,iBACA,kBACA,qBACA,iBACA,oBACE,EAAY;EACd,OAAO;EACP,eAAe,MAAU,IAAO,EAAmB,EAAK,GAAG;EAC3D,cAAc;EACd,uBAAuB,EAAE,cAAc,QAAsB;AAE3D,GADA,EAAqB,KAAmB,KAAK,EAC7C,IAAW,KAAmB,KAAK;;EAErC,qBAAqB,EAAE,YAAY,QAAoB;AACrD,KAAc,KAAiB,GAAG;;EAEpC;EACD,CAAC;AAUF,CAPA,QAAgB;AACd,EAAI,MAAU,KACZ,EAAqB,KAAS,KAAK;IAEpC,CAAC,GAAO,EAAkB,CAAC,EAG9B,QAAgB;AACd,EAAI,KAAgB,CAAC,IACnB,EAAc,EAAmB,EAAa,CAAC,GACrC,KACV,EAAc,GAAG;IAElB,CAAC,GAAc,EAAO,CAAC;CAE1B,IAAM,KACJ,GACA,GACA,GACA,GACA,MAEA,kBAAC,GAAD;EAEQ;EACC;EACP,YAAY,MAAS;EACrB,cAAc;EACd,gBAAgB;EAChB,aAAa,MAAa;EAC1B,oBAAA;EACe;EACf,EATK,EASL,EAGE,KAAoB,MACxB,kBAAC,GAAD;EACE,YAAY;EACZ,eAAc;EACd,cAAa;EACb,QAAO;EACP,CAAA;AAGJ,QACE,kBAAC,OAAD;EAAK,WAAW,EAAG,YAAY,EAAU;YAAzC;GACE,kBAAC,OAAD;IAAK,WAAU;cAAf;KAEG,KACC,kBAAC,OAAD;MAAK,WAAU;gBACb,kBAAC,GAAD;OACE,aAAa;OACb,WAAU;OACV,CAAA;MACE,CAAA;KAGR,kBAAC,SAAD;MACE,GAAI,EAAc;OAChB;OACA;OACA,UAAU;OACV,WAAW,EACT,mGACA,+DACA,yEACA,wEACA,gEACA,KAAgB,QAChB,EACD;OACF,CAAC;MACF,eAAY;MACZ,CAAA;KAEF,kBAAC,OAAD;MAAK,WAAU;gBACb,kBAAC,GAAD;OACU;OACE;OACY;OACtB,eAAe;OACf,CAAA;MACE,CAAA;KAEL,KAAa,kBAAC,GAAD,EAAgB,cAAc,CAAC,CAAC,GAAgB,CAAA;KAC1D;;GAEN,kBAAC,GAAD;IACU;IACR,eAAe;IACH;IACM;IACJ;IACA;IACA;IACF;IACM;IAClB,UAAU,MAAa;IACvB,CAAA;GAEF,kBAAC,GAAD;IAAc,SAAS,CAAC,CAAC;IAAc,SAAS;IAAgB,CAAA;GAC5D"}
1
+ {"version":3,"file":"git-provider-dropdown.js","names":[],"sources":["../../../../../src/components/features/home/git-provider-dropdown/git-provider-dropdown.tsx"],"sourcesContent":["import React, { useState, useMemo, useEffect } from \"react\";\nimport { useCombobox } from \"downshift\";\nimport { useTranslation } from \"react-i18next\";\nimport { Provider } from \"#/types/settings\";\nimport { I18nKey } from \"#/i18n/declaration\";\nimport { cn } from \"#/utils/utils\";\nimport { DropdownItem } from \"../shared/dropdown-item\";\nimport { GenericDropdownMenu } from \"../shared/generic-dropdown-menu\";\nimport { ToggleButton } from \"../shared/toggle-button\";\nimport { LoadingSpinner } from \"../shared/loading-spinner\";\nimport { ErrorMessage } from \"../shared/error-message\";\nimport { EmptyState } from \"../shared/empty-state\";\nimport { GitProviderIcon } from \"#/components/shared/git-provider-icon\";\n\nexport interface GitProviderDropdownProps {\n providers: Provider[];\n value?: Provider | null;\n placeholder?: string;\n className?: string;\n errorMessage?: string;\n disabled?: boolean;\n isLoading?: boolean;\n onChange?: (provider: Provider | null) => void;\n inputClassName?: string;\n toggleButtonClassName?: string;\n itemClassName?: string;\n}\n\nexport function GitProviderDropdown({\n providers,\n value,\n placeholder,\n className,\n errorMessage,\n disabled = false,\n isLoading = false,\n onChange,\n inputClassName,\n toggleButtonClassName,\n itemClassName,\n}: GitProviderDropdownProps) {\n const { t } = useTranslation(\"openhands\");\n const [inputValue, setInputValue] = useState(\"\");\n const [localSelectedItem, setLocalSelectedItem] = useState<Provider | null>(\n value || null,\n );\n\n // Format provider names for display\n const formatProviderName = (provider: Provider): string => {\n switch (provider) {\n case \"github\":\n return \"GitHub\";\n case \"gitlab\":\n return \"GitLab\";\n case \"bitbucket\":\n return \"Bitbucket\";\n case \"bitbucket_data_center\":\n return \"Bitbucket Data Center\";\n case \"azure_devops\":\n return \"Azure DevOps\";\n default:\n // Fallback for any future provider types\n return (\n (provider as string).charAt(0).toUpperCase() +\n (provider as string).slice(1)\n );\n }\n };\n\n // Filter providers based on input value\n const filteredProviders = useMemo(() => {\n // If we have a selected provider and the input matches it exactly, show all providers\n if (\n localSelectedItem &&\n inputValue === formatProviderName(localSelectedItem)\n ) {\n return providers;\n }\n\n // If no input value, show all providers\n if (!inputValue?.trim()) {\n return providers;\n }\n\n // Filter providers based on input\n return providers.filter((provider) =>\n formatProviderName(provider)\n .toLowerCase()\n .includes(inputValue.toLowerCase()),\n );\n }, [providers, inputValue, localSelectedItem]);\n\n const {\n isOpen,\n getToggleButtonProps,\n getMenuProps,\n getInputProps,\n highlightedIndex,\n getItemProps,\n selectedItem,\n } = useCombobox({\n items: filteredProviders,\n itemToString: (item) => (item ? formatProviderName(item) : \"\"),\n selectedItem: localSelectedItem,\n onSelectedItemChange: ({ selectedItem: newSelectedItem }) => {\n setLocalSelectedItem(newSelectedItem || null);\n onChange?.(newSelectedItem || null);\n },\n onInputValueChange: ({ inputValue: newInputValue }) => {\n setInputValue(newInputValue || \"\");\n },\n inputValue,\n });\n\n // Sync with external value prop\n useEffect(() => {\n if (value !== localSelectedItem) {\n setLocalSelectedItem(value || null);\n }\n }, [value, localSelectedItem]);\n\n // Update input value when selection changes (but not when user is typing)\n useEffect(() => {\n if (selectedItem && !isOpen) {\n setInputValue(formatProviderName(selectedItem));\n } else if (!selectedItem) {\n setInputValue(\"\");\n }\n }, [selectedItem, isOpen]);\n\n const renderItem = (\n item: Provider,\n index: number,\n currentHighlightedIndex: number,\n currentSelectedItem: Provider | null,\n currentGetItemProps: any, // eslint-disable-line @typescript-eslint/no-explicit-any\n ) => (\n <DropdownItem\n key={item}\n item={item}\n index={index}\n isSelected={item === currentSelectedItem}\n getItemProps={currentGetItemProps}\n getDisplayText={formatProviderName}\n getItemKey={(provider) => provider}\n isProviderDropdown\n itemClassName={itemClassName}\n />\n );\n\n const renderEmptyState = (currentInputValue: string) => (\n <EmptyState\n inputValue={currentInputValue}\n searchMessage=\"No providers found\"\n emptyMessage=\"No providers available\"\n testId=\"git-provider-dropdown-empty\"\n />\n );\n\n return (\n <div className={cn(\"relative\", className)}>\n <div className=\"group relative text-[var(--oh-muted)] hover:text-white\">\n {/* Provider icon */}\n {selectedItem && (\n <div className=\"absolute left-2 top-1/2 transform -translate-y-1/2 z-10\">\n <GitProviderIcon\n gitProvider={selectedItem}\n className=\"min-w-[14px] min-h-[14px] w-[14px] h-[14px]\"\n />\n </div>\n )}\n\n <input\n {...getInputProps({\n disabled,\n placeholder:\n placeholder ?? t(I18nKey.COMMON$SELECT_PROVIDER_PLACEHOLDER),\n readOnly: true, // Make it non-searchable like the original\n className: cn(\n \"w-29.5 h-6 py-0 border border-[var(--oh-border-input)] rounded shadow-none h-6 min-h-6 max-h-6 \",\n \"text-inherit bg-tertiary placeholder:text-[var(--oh-muted)]\",\n \"focus:outline-none focus:ring-0 focus:border-[var(--oh-border-input)]\",\n \"disabled:bg-tertiary disabled:cursor-not-allowed disabled:opacity-60\",\n \"pl-1.5 pr-[1px] cursor-pointer text-xs font-normal leading-5\", // Space for toggle button and pointer cursor\n selectedItem && \"pl-6\",\n inputClassName,\n ),\n })}\n data-testid=\"git-provider-dropdown\"\n />\n\n <div className=\"absolute right-0 top-1/2 transform -translate-y-1/2 flex items-center gap-1\">\n <ToggleButton\n isOpen={isOpen}\n disabled={disabled}\n getToggleButtonProps={getToggleButtonProps}\n iconClassName={toggleButtonClassName}\n />\n </div>\n\n {isLoading && <LoadingSpinner hasSelection={!!selectedItem} />}\n </div>\n\n <GenericDropdownMenu\n isOpen={isOpen}\n filteredItems={filteredProviders}\n inputValue={inputValue}\n highlightedIndex={highlightedIndex}\n selectedItem={selectedItem}\n getMenuProps={getMenuProps}\n getItemProps={getItemProps}\n renderItem={renderItem}\n renderEmptyState={renderEmptyState}\n itemKey={(provider) => provider}\n />\n\n <ErrorMessage isError={!!errorMessage} message={errorMessage} />\n </div>\n );\n}\n"],"mappings":";;;;;;;;;;;;;;AA4BA,SAAgB,EAAoB,EAClC,cACA,UACA,gBACA,cACA,iBACA,cAAW,IACX,eAAY,IACZ,aACA,mBACA,0BACA,oBAC2B;CAC3B,IAAM,EAAE,SAAM,EAAe,YAAY,EACnC,CAAC,GAAY,KAAiB,EAAS,GAAG,EAC1C,CAAC,GAAmB,KAAwB,EAChD,KAAS,KACV,EAGK,KAAsB,MAA+B;AACzD,UAAQ,GAAR;GACE,KAAK,SACH,QAAO;GACT,KAAK,SACH,QAAO;GACT,KAAK,YACH,QAAO;GACT,KAAK,wBACH,QAAO;GACT,KAAK,eACH,QAAO;GACT,QAEE,QACG,EAAoB,OAAO,EAAE,CAAC,aAAa,GAC3C,EAAoB,MAAM,EAAE;;IAM/B,IAAoB,QAGtB,KACA,MAAe,EAAmB,EAAkB,IAMlD,CAAC,GAAY,MAAM,GACd,IAIF,EAAU,QAAQ,MACvB,EAAmB,EAAS,CACzB,aAAa,CACb,SAAS,EAAW,aAAa,CAAC,CACtC,EACA;EAAC;EAAW;EAAY;EAAkB,CAAC,EAExC,EACJ,WACA,yBACA,iBACA,kBACA,qBACA,iBACA,oBACE,EAAY;EACd,OAAO;EACP,eAAe,MAAU,IAAO,EAAmB,EAAK,GAAG;EAC3D,cAAc;EACd,uBAAuB,EAAE,cAAc,QAAsB;AAE3D,GADA,EAAqB,KAAmB,KAAK,EAC7C,IAAW,KAAmB,KAAK;;EAErC,qBAAqB,EAAE,YAAY,QAAoB;AACrD,KAAc,KAAiB,GAAG;;EAEpC;EACD,CAAC;AAUF,CAPA,QAAgB;AACd,EAAI,MAAU,KACZ,EAAqB,KAAS,KAAK;IAEpC,CAAC,GAAO,EAAkB,CAAC,EAG9B,QAAgB;AACd,EAAI,KAAgB,CAAC,IACnB,EAAc,EAAmB,EAAa,CAAC,GACrC,KACV,EAAc,GAAG;IAElB,CAAC,GAAc,EAAO,CAAC;CAE1B,IAAM,KACJ,GACA,GACA,GACA,GACA,MAEA,kBAAC,GAAD;EAEQ;EACC;EACP,YAAY,MAAS;EACrB,cAAc;EACd,gBAAgB;EAChB,aAAa,MAAa;EAC1B,oBAAA;EACe;EACf,EATK,EASL,EAGE,KAAoB,MACxB,kBAAC,GAAD;EACE,YAAY;EACZ,eAAc;EACd,cAAa;EACb,QAAO;EACP,CAAA;AAGJ,QACE,kBAAC,OAAD;EAAK,WAAW,EAAG,YAAY,EAAU;YAAzC;GACE,kBAAC,OAAD;IAAK,WAAU;cAAf;KAEG,KACC,kBAAC,OAAD;MAAK,WAAU;gBACb,kBAAC,GAAD;OACE,aAAa;OACb,WAAU;OACV,CAAA;MACE,CAAA;KAGR,kBAAC,SAAD;MACE,GAAI,EAAc;OAChB;OACA,aACE,KAAe,EAAE,EAAQ,mCAAmC;OAC9D,UAAU;OACV,WAAW,EACT,mGACA,+DACA,yEACA,wEACA,gEACA,KAAgB,QAChB,EACD;OACF,CAAC;MACF,eAAY;MACZ,CAAA;KAEF,kBAAC,OAAD;MAAK,WAAU;gBACb,kBAAC,GAAD;OACU;OACE;OACY;OACtB,eAAe;OACf,CAAA;MACE,CAAA;KAEL,KAAa,kBAAC,GAAD,EAAgB,cAAc,CAAC,CAAC,GAAgB,CAAA;KAC1D;;GAEN,kBAAC,GAAD;IACU;IACR,eAAe;IACH;IACM;IACJ;IACA;IACA;IACF;IACM;IAClB,UAAU,MAAa;IACvB,CAAA;GAEF,kBAAC,GAAD;IAAc,SAAS,CAAC,CAAC;IAAc,SAAS;IAAgB,CAAA;GAC5D"}
@@ -1,2 +1,2 @@
1
- const e=require(`../../../../_virtual/_rolldown/runtime.cjs`),t=require(`../../../../node_modules/react-i18next/dist/es/useTranslation.cjs`),n=require(`../../../../i18n/declaration.cjs`),r=require(`../../../../utils/utils.cjs`),i=require(`../../../../utils/form-control-classes.cjs`),ee=require(`../../../../ui/typography.cjs`),a=require(`../../../../node_modules/downshift/dist/downshift.esm.cjs`),o=require(`../../../../hooks/use-debounce.cjs`),s=require(`../shared/clear-button.cjs`),c=require(`../shared/toggle-button.cjs`),l=require(`../shared/error-message.cjs`),u=require(`../shared/dropdown-item.cjs`),d=require(`../shared/empty-state.cjs`),f=require(`./use-url-search.cjs`),p=require(`./use-repository-data.cjs`),m=require(`../shared/generic-dropdown-menu.cjs`),h=require(`../../../../icons/repo.cjs`),g=require(`../../../../stores/home-store.cjs`);let _=require(`react`);_=e.__toESM(_,1);let v=require(`react/jsx-runtime`);function y({provider:e,value:y,repositoryName:b,placeholder:x=`Search repositories...`,className:S,disabled:C=!1,onChange:w}){let{t:T}=t.useTranslation(`openhands`),{recentRepositories:E}=g.useHomeStore(),[D,O]=(0,_.useState)(``),[k,A]=(0,_.useState)(null),j=o.useDebounce(D,300),M=(0,_.useRef)(null),N=(0,_.useMemo)(()=>{if(j.startsWith(`https://`)){let e=j.match(/https:\/\/[^/]+\/([^/]+\/[^/]+)/);return e?e[1]:j}return j},[j]),{urlSearchResults:P,isUrlSearchLoading:F}=f.useUrlSearch(D,e),{repositories:I,selectedRepository:L,fetchNextPage:R,hasNextPage:z,isLoading:B,isFetchingNextPage:V,isError:H,isSearchLoading:U}=p.useRepositoryData(e,C,N,P,D,y,b),W=L??k,G=(0,_.useMemo)(()=>{let t=E.filter(t=>t.git_provider===e);if(!D?.trim())return t;let n=D.startsWith(`https://`)?N:D;return t.filter(e=>e.full_name.toLowerCase().includes(n.toLowerCase()))},[E,e,D,N]),K=(0,_.useCallback)(e=>{let t=new Set(G.map(e=>e.id)),n=e.filter(e=>!t.has(e.id));return[...G,...n]},[G]),q=(0,_.useMemo)(()=>{let e;if(P.length>0)e=I;else if(D===L?.full_name)e=I;else if(!D?.trim())e=I;else{let t=D.startsWith(`https://`)?N:D;e=I.filter(e=>e.full_name.toLowerCase().includes(t.toLowerCase()))}return K(e)},[I,D,L,P,N,K]),J=(0,_.useCallback)(e=>{A(e),w?.(e||void 0),e&&O(e.full_name)},[w]),Y=(0,_.useCallback)(()=>{A(null),J(null),O(``)},[J]),X=(0,_.useCallback)(e=>{let{scrollTop:t,scrollHeight:n,clientHeight:r}=e.currentTarget;t+r>=n-10&&z&&!V&&R()},[z,V,R]),{isOpen:Z,getToggleButtonProps:Q,getMenuProps:te,getInputProps:ne,highlightedIndex:re,getItemProps:$,selectedItem:ie}=a.useCombobox({items:q,itemToString:e=>e?.full_name||``,selectedItem:k,onSelectedItemChange:({selectedItem:e})=>{J(e)},inputValue:D,stateReducer:(e,t)=>t.type===a.useCombobox.stateChangeTypes.InputClick&&e.isOpen?{...t.changes,isOpen:!0}:t.changes});(0,_.useEffect)(()=>{L?A(L):y===null&&A(null)},[L,y]),(0,_.useEffect)(()=>{Z||O(W?.full_name??``)},[W,Z]);let ae=B||U||V||F,oe=(e,t,n,r,i)=>(0,v.jsx)(u.DropdownItem,{item:e,index:t,isSelected:r?.id===e.id,getItemProps:i,getDisplayText:e=>e.full_name,getItemKey:e=>e.id},e.id),se=e=>(0,v.jsx)(d.EmptyState,{inputValue:e,searchMessage:T(n.I18nKey.HOME$NO_REPOSITORY_FOUND),emptyMessage:T(n.I18nKey.COMMON$NO_REPOSITORY),testId:`git-repo-dropdown-empty`}),ce=(0,_.useMemo)(()=>G.length===0?null:(0,v.jsx)(`div`,{children:(0,v.jsx)(ee.Typography.Text,{className:`text-xs text-content-2 font-semibold leading-4 pl-2`,children:T(n.I18nKey.COMMON$MOST_RECENT)})}),[G,k,$,T]);return(0,v.jsxs)(`div`,{className:r.cn(`relative`,S),children:[(0,v.jsxs)(`div`,{className:`group relative text-[var(--oh-muted)] hover:text-white`,children:[(0,v.jsx)(`div`,{className:`absolute left-2 top-1/2 transform -translate-y-1/2 z-10`,children:ae?(0,v.jsx)(`div`,{className:`animate-spin h-4 w-4 border-2 border-transparent border-t-white rounded-full`}):(0,v.jsx)(h.default,{width:16,height:16})}),(0,v.jsx)(`input`,{...ne({disabled:C,placeholder:x,className:r.cn(i.formControlFieldClassName,`text-inherit shadow-none pl-7 pr-16 text-sm font-normal leading-5`,`placeholder:text-[var(--oh-muted)]`,`disabled:cursor-not-allowed disabled:opacity-60`),onChange:e=>{O(e.target.value)}}),"data-testid":`git-repo-dropdown`}),(0,v.jsxs)(`div`,{className:`absolute right-1 top-1/2 transform -translate-y-1/2 flex items-center`,children:[W&&(0,v.jsx)(s.ClearButton,{disabled:C,onClear:Y}),(0,v.jsx)(c.ToggleButton,{isOpen:Z,disabled:C,getToggleButtonProps:Q})]})]}),(0,v.jsx)(m.GenericDropdownMenu,{isOpen:Z,filteredItems:q,inputValue:D,highlightedIndex:re,selectedItem:ie,getMenuProps:te,getItemProps:$,onScroll:X,menuRef:M,renderItem:oe,renderEmptyState:se,stickyTopItem:ce,testId:`git-repo-dropdown-menu`,numberOfRecentItems:G.length,itemKey:e=>e.id}),(0,v.jsx)(l.ErrorMessage,{isError:H})]})}exports.GitRepoDropdown=y;
1
+ const e=require(`../../../../_virtual/_rolldown/runtime.cjs`),t=require(`../../../../node_modules/react-i18next/dist/es/useTranslation.cjs`),n=require(`../../../../i18n/declaration.cjs`),r=require(`../../../../utils/utils.cjs`),i=require(`../../../../utils/form-control-classes.cjs`),ee=require(`../../../../ui/typography.cjs`),a=require(`../../../../node_modules/downshift/dist/downshift.esm.cjs`),o=require(`../../../../hooks/use-debounce.cjs`),s=require(`../shared/clear-button.cjs`),c=require(`../shared/toggle-button.cjs`),l=require(`../shared/error-message.cjs`),u=require(`../shared/dropdown-item.cjs`),d=require(`../shared/empty-state.cjs`),f=require(`./use-url-search.cjs`),p=require(`./use-repository-data.cjs`),m=require(`../shared/generic-dropdown-menu.cjs`),h=require(`../../../../icons/repo.cjs`),g=require(`../../../../stores/home-store.cjs`);let _=require(`react`);_=e.__toESM(_,1);let v=require(`react/jsx-runtime`);function y({provider:e,value:y,repositoryName:b,placeholder:x,className:S,disabled:C=!1,onChange:w}){let{t:T}=t.useTranslation(`openhands`),{recentRepositories:E}=g.useHomeStore(),[D,O]=(0,_.useState)(``),[k,A]=(0,_.useState)(null),j=o.useDebounce(D,300),M=(0,_.useRef)(null),N=(0,_.useMemo)(()=>{if(j.startsWith(`https://`)){let e=j.match(/https:\/\/[^/]+\/([^/]+\/[^/]+)/);return e?e[1]:j}return j},[j]),{urlSearchResults:P,isUrlSearchLoading:F}=f.useUrlSearch(D,e),{repositories:I,selectedRepository:L,fetchNextPage:R,hasNextPage:z,isLoading:B,isFetchingNextPage:V,isError:H,isSearchLoading:U}=p.useRepositoryData(e,C,N,P,D,y,b),W=L??k,G=(0,_.useMemo)(()=>{let t=E.filter(t=>t.git_provider===e);if(!D?.trim())return t;let n=D.startsWith(`https://`)?N:D;return t.filter(e=>e.full_name.toLowerCase().includes(n.toLowerCase()))},[E,e,D,N]),K=(0,_.useCallback)(e=>{let t=new Set(G.map(e=>e.id)),n=e.filter(e=>!t.has(e.id));return[...G,...n]},[G]),q=(0,_.useMemo)(()=>{let e;if(P.length>0)e=I;else if(D===L?.full_name)e=I;else if(!D?.trim())e=I;else{let t=D.startsWith(`https://`)?N:D;e=I.filter(e=>e.full_name.toLowerCase().includes(t.toLowerCase()))}return K(e)},[I,D,L,P,N,K]),J=(0,_.useCallback)(e=>{A(e),w?.(e||void 0),e&&O(e.full_name)},[w]),Y=(0,_.useCallback)(()=>{A(null),J(null),O(``)},[J]),X=(0,_.useCallback)(e=>{let{scrollTop:t,scrollHeight:n,clientHeight:r}=e.currentTarget;t+r>=n-10&&z&&!V&&R()},[z,V,R]),{isOpen:Z,getToggleButtonProps:Q,getMenuProps:te,getInputProps:ne,highlightedIndex:re,getItemProps:$,selectedItem:ie}=a.useCombobox({items:q,itemToString:e=>e?.full_name||``,selectedItem:k,onSelectedItemChange:({selectedItem:e})=>{J(e)},inputValue:D,stateReducer:(e,t)=>t.type===a.useCombobox.stateChangeTypes.InputClick&&e.isOpen?{...t.changes,isOpen:!0}:t.changes});(0,_.useEffect)(()=>{L?A(L):y===null&&A(null)},[L,y]),(0,_.useEffect)(()=>{Z||O(W?.full_name??``)},[W,Z]);let ae=B||U||V||F,oe=(e,t,n,r,i)=>(0,v.jsx)(u.DropdownItem,{item:e,index:t,isSelected:r?.id===e.id,getItemProps:i,getDisplayText:e=>e.full_name,getItemKey:e=>e.id},e.id),se=e=>(0,v.jsx)(d.EmptyState,{inputValue:e,searchMessage:T(n.I18nKey.HOME$NO_REPOSITORY_FOUND),emptyMessage:T(n.I18nKey.COMMON$NO_REPOSITORY),testId:`git-repo-dropdown-empty`}),ce=(0,_.useMemo)(()=>G.length===0?null:(0,v.jsx)(`div`,{children:(0,v.jsx)(ee.Typography.Text,{className:`text-xs text-content-2 font-semibold leading-4 pl-2`,children:T(n.I18nKey.COMMON$MOST_RECENT)})}),[G,k,$,T]);return(0,v.jsxs)(`div`,{className:r.cn(`relative`,S),children:[(0,v.jsxs)(`div`,{className:`group relative text-[var(--oh-muted)] hover:text-white`,children:[(0,v.jsx)(`div`,{className:`absolute left-2 top-1/2 transform -translate-y-1/2 z-10`,children:ae?(0,v.jsx)(`div`,{className:`animate-spin h-4 w-4 border-2 border-transparent border-t-white rounded-full`}):(0,v.jsx)(h.default,{width:16,height:16})}),(0,v.jsx)(`input`,{...ne({disabled:C,placeholder:x??T(n.I18nKey.COMMON$SEARCH_REPOSITORIES_PLACEHOLDER),className:r.cn(i.formControlFieldClassName,`text-inherit shadow-none pl-7 pr-16 text-sm font-normal leading-5`,`placeholder:text-[var(--oh-muted)]`,`disabled:cursor-not-allowed disabled:opacity-60`),onChange:e=>{O(e.target.value)}}),"data-testid":`git-repo-dropdown`}),(0,v.jsxs)(`div`,{className:`absolute right-1 top-1/2 transform -translate-y-1/2 flex items-center`,children:[W&&(0,v.jsx)(s.ClearButton,{disabled:C,onClear:Y}),(0,v.jsx)(c.ToggleButton,{isOpen:Z,disabled:C,getToggleButtonProps:Q})]})]}),(0,v.jsx)(m.GenericDropdownMenu,{isOpen:Z,filteredItems:q,inputValue:D,highlightedIndex:re,selectedItem:ie,getMenuProps:te,getItemProps:$,onScroll:X,menuRef:M,renderItem:oe,renderEmptyState:se,stickyTopItem:ce,testId:`git-repo-dropdown-menu`,numberOfRecentItems:G.length,itemKey:e=>e.id}),(0,v.jsx)(l.ErrorMessage,{isError:H})]})}exports.GitRepoDropdown=y;
2
2
  //# sourceMappingURL=git-repo-dropdown.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"git-repo-dropdown.cjs","names":[],"sources":["../../../../../src/components/features/home/git-repo-dropdown/git-repo-dropdown.tsx"],"sourcesContent":["import React, {\n useState,\n useMemo,\n useCallback,\n useRef,\n useEffect,\n} from \"react\";\nimport { useCombobox } from \"downshift\";\nimport { useTranslation } from \"react-i18next\";\nimport { Provider } from \"#/types/settings\";\nimport { GitRepository } from \"#/types/git\";\nimport { useDebounce } from \"#/hooks/use-debounce\";\nimport { cn } from \"#/utils/utils\";\nimport { formControlFieldClassName } from \"#/utils/form-control-classes\";\n\nimport { ClearButton } from \"../shared/clear-button\";\nimport { ToggleButton } from \"../shared/toggle-button\";\nimport { ErrorMessage } from \"../shared/error-message\";\nimport { DropdownItem } from \"../shared/dropdown-item\";\nimport { EmptyState } from \"../shared/empty-state\";\nimport { useUrlSearch } from \"./use-url-search\";\nimport { useRepositoryData } from \"./use-repository-data\";\nimport { GenericDropdownMenu } from \"../shared/generic-dropdown-menu\";\nimport { I18nKey } from \"#/i18n/declaration\";\nimport RepoIcon from \"#/icons/repo.svg?react\";\nimport { useHomeStore } from \"#/stores/home-store\";\nimport { Typography } from \"#/ui/typography\";\n\nexport interface GitRepoDropdownProps {\n provider: Provider;\n value?: string | null;\n repositoryName?: string | null;\n placeholder?: string;\n className?: string;\n disabled?: boolean;\n onChange?: (repository?: GitRepository) => void;\n}\n\nexport function GitRepoDropdown({\n provider,\n value,\n repositoryName,\n placeholder = \"Search repositories...\",\n className,\n disabled = false,\n onChange,\n}: GitRepoDropdownProps) {\n const { t } = useTranslation(\"openhands\");\n const { recentRepositories: storedRecentRepositories } = useHomeStore();\n const [inputValue, setInputValue] = useState(\"\");\n const [localSelectedItem, setLocalSelectedItem] =\n useState<GitRepository | null>(null);\n const debouncedInputValue = useDebounce(inputValue, 300);\n const menuRef = useRef<HTMLUListElement>(null);\n\n // Process search input to handle URLs\n const processedSearchInput = useMemo(() => {\n if (debouncedInputValue.startsWith(\"https://\")) {\n const match = debouncedInputValue.match(\n /https:\\/\\/[^/]+\\/([^/]+\\/[^/]+)/,\n );\n return match ? match[1] : debouncedInputValue;\n }\n return debouncedInputValue;\n }, [debouncedInputValue]);\n\n // URL search functionality\n const { urlSearchResults, isUrlSearchLoading } = useUrlSearch(\n inputValue,\n provider,\n );\n\n // Repository data management\n const {\n repositories,\n selectedRepository,\n fetchNextPage,\n hasNextPage,\n isLoading,\n isFetchingNextPage,\n isError,\n isSearchLoading,\n } = useRepositoryData(\n provider,\n disabled,\n processedSearchInput,\n urlSearchResults,\n inputValue,\n value,\n repositoryName,\n );\n\n /**\n * `selectedRepository` is derived from fetched/search results. A repo selected\n * from \"Most Recent\" may not be present in those results yet, so use the\n * locally selected item as a fallback to keep the UI stable.\n */\n const effectiveSelectedRepository = selectedRepository ?? localSelectedItem;\n\n // Get recent repositories filtered by provider and input keyword\n const recentRepositories = useMemo(() => {\n const allRecentRepos = storedRecentRepositories;\n const providerFilteredRepos = allRecentRepos.filter(\n (repo) => repo.git_provider === provider,\n );\n\n // If no input value, return all recent repos for this provider\n if (!inputValue?.trim()) {\n return providerFilteredRepos;\n }\n\n // Filter by input keyword\n const filterText = inputValue.startsWith(\"https://\")\n ? processedSearchInput\n : inputValue;\n\n return providerFilteredRepos.filter((repo) =>\n repo.full_name.toLowerCase().includes(filterText.toLowerCase()),\n );\n }, [storedRecentRepositories, provider, inputValue, processedSearchInput]);\n\n // Helper function to prioritize recent repositories at the top\n const prioritizeRecentRepositories = useCallback(\n (repoList: GitRepository[]) => {\n const recentRepoIds = new Set(recentRepositories.map((repo) => repo.id));\n const otherRepos = repoList.filter((repo) => !recentRepoIds.has(repo.id));\n return [...recentRepositories, ...otherRepos];\n },\n [recentRepositories],\n );\n\n // Filter repositories based on input value\n const filteredRepositories = useMemo(() => {\n let baseRepositories: GitRepository[];\n\n // If we have URL search results, show them directly (no filtering needed)\n if (urlSearchResults.length > 0) {\n baseRepositories = repositories;\n }\n // If we have a selected repository and the input matches it exactly, show all repositories\n else if (inputValue === selectedRepository?.full_name) {\n baseRepositories = repositories;\n }\n // If no input value, show all repositories\n else if (!inputValue?.trim()) {\n baseRepositories = repositories;\n }\n // For URL inputs, use the processed search input for filtering\n else {\n const filterText = inputValue.startsWith(\"https://\")\n ? processedSearchInput\n : inputValue;\n\n baseRepositories = repositories.filter((repo) =>\n repo.full_name.toLowerCase().includes(filterText.toLowerCase()),\n );\n }\n\n // Prioritize recent repositories at the top\n return prioritizeRecentRepositories(baseRepositories);\n }, [\n repositories,\n inputValue,\n selectedRepository,\n urlSearchResults,\n processedSearchInput,\n prioritizeRecentRepositories,\n ]);\n\n // Handle selection\n const handleSelectionChange = useCallback(\n (selectedItem: GitRepository | null) => {\n setLocalSelectedItem(selectedItem);\n onChange?.(selectedItem || undefined);\n // Update input value to show selected item\n if (selectedItem) {\n setInputValue(selectedItem.full_name);\n }\n },\n [onChange],\n );\n\n // Handle clear selection\n const handleClear = useCallback(() => {\n setLocalSelectedItem(null);\n handleSelectionChange(null);\n setInputValue(\"\");\n }, [handleSelectionChange]);\n\n // Handle scroll to bottom for pagination\n const handleMenuScroll = useCallback(\n (event: React.UIEvent<HTMLUListElement>) => {\n const { scrollTop, scrollHeight, clientHeight } = event.currentTarget;\n const isNearBottom = scrollTop + clientHeight >= scrollHeight - 10;\n\n if (isNearBottom && hasNextPage && !isFetchingNextPage) {\n fetchNextPage();\n }\n },\n [hasNextPage, isFetchingNextPage, fetchNextPage],\n );\n\n const {\n isOpen,\n getToggleButtonProps,\n getMenuProps,\n getInputProps,\n highlightedIndex,\n getItemProps,\n selectedItem,\n } = useCombobox({\n items: filteredRepositories,\n itemToString: (item) => item?.full_name || \"\",\n selectedItem: localSelectedItem,\n onSelectedItemChange: ({ selectedItem: newSelectedItem }) => {\n handleSelectionChange(newSelectedItem);\n },\n inputValue,\n // Override Downshift's default input-click behavior to avoid closing/reopening\n // the menu, which would reset scroll position and break search continuity.\n stateReducer: (state, actionAndChanges) =>\n actionAndChanges.type === useCombobox.stateChangeTypes.InputClick &&\n state.isOpen\n ? { ...actionAndChanges.changes, isOpen: true }\n : actionAndChanges.changes,\n });\n\n // Sync localSelectedItem with external value prop\n useEffect(() => {\n if (selectedRepository) {\n setLocalSelectedItem(selectedRepository);\n } else if (value === null) {\n setLocalSelectedItem(null);\n }\n }, [selectedRepository, value]);\n\n // Initialize input value when selectedRepository changes (but not when user is typing)\n useEffect(() => {\n if (isOpen) return;\n setInputValue(effectiveSelectedRepository?.full_name ?? \"\");\n }, [effectiveSelectedRepository, isOpen]);\n\n const isLoadingState =\n isLoading || isSearchLoading || isFetchingNextPage || isUrlSearchLoading;\n\n const renderItem = (\n item: GitRepository,\n index: number,\n itemHighlightedIndex: number,\n itemSelectedItem: GitRepository | null,\n itemGetItemProps: any, // eslint-disable-line @typescript-eslint/no-explicit-any\n ) => (\n <DropdownItem\n key={item.id}\n item={item}\n index={index}\n isSelected={itemSelectedItem?.id === item.id}\n getItemProps={itemGetItemProps}\n getDisplayText={(repo) => repo.full_name}\n getItemKey={(repo) => repo.id}\n />\n );\n\n const renderEmptyState = (emptyInputValue: string) => (\n <EmptyState\n inputValue={emptyInputValue}\n searchMessage={t(I18nKey.HOME$NO_REPOSITORY_FOUND)}\n emptyMessage={t(I18nKey.COMMON$NO_REPOSITORY)}\n testId=\"git-repo-dropdown-empty\"\n />\n );\n\n // Create sticky top item for recent repositories\n const stickyTopItem = useMemo(() => {\n if (recentRepositories.length === 0) {\n return null;\n }\n\n return (\n <div>\n <Typography.Text className=\"text-xs text-content-2 font-semibold leading-4 pl-2\">\n {t(I18nKey.COMMON$MOST_RECENT)}\n </Typography.Text>\n </div>\n );\n }, [recentRepositories, localSelectedItem, getItemProps, t]);\n\n return (\n <div className={cn(\"relative\", className)}>\n <div className=\"group relative text-[var(--oh-muted)] hover:text-white\">\n <div className=\"absolute left-2 top-1/2 transform -translate-y-1/2 z-10\">\n {isLoadingState ? (\n <div className=\"animate-spin h-4 w-4 border-2 border-transparent border-t-white rounded-full\" />\n ) : (\n <RepoIcon width={16} height={16} />\n )}\n </div>\n <input\n {...getInputProps({\n disabled,\n placeholder,\n className: cn(\n formControlFieldClassName,\n \"text-inherit shadow-none pl-7 pr-16 text-sm font-normal leading-5\",\n \"placeholder:text-[var(--oh-muted)]\",\n \"disabled:cursor-not-allowed disabled:opacity-60\",\n ),\n // Direct onChange for cursor position preservation\n onChange: (e: React.ChangeEvent<HTMLInputElement>) => {\n setInputValue(e.target.value);\n },\n })}\n data-testid=\"git-repo-dropdown\"\n />\n\n <div className=\"absolute right-1 top-1/2 transform -translate-y-1/2 flex items-center\">\n {effectiveSelectedRepository && (\n <ClearButton disabled={disabled} onClear={handleClear} />\n )}\n\n <ToggleButton\n isOpen={isOpen}\n disabled={disabled}\n getToggleButtonProps={getToggleButtonProps}\n />\n </div>\n </div>\n\n <GenericDropdownMenu\n isOpen={isOpen}\n filteredItems={filteredRepositories}\n inputValue={inputValue}\n highlightedIndex={highlightedIndex}\n selectedItem={selectedItem}\n getMenuProps={getMenuProps}\n getItemProps={getItemProps}\n onScroll={handleMenuScroll}\n menuRef={menuRef}\n renderItem={renderItem}\n renderEmptyState={renderEmptyState}\n stickyTopItem={stickyTopItem}\n testId=\"git-repo-dropdown-menu\"\n numberOfRecentItems={recentRepositories.length}\n itemKey={(repo) => repo.id}\n />\n\n <ErrorMessage isError={isError} />\n </div>\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"git-repo-dropdown.cjs","names":[],"sources":["../../../../../src/components/features/home/git-repo-dropdown/git-repo-dropdown.tsx"],"sourcesContent":["import React, {\n useState,\n useMemo,\n useCallback,\n useRef,\n useEffect,\n} from \"react\";\nimport { useCombobox } from \"downshift\";\nimport { useTranslation } from \"react-i18next\";\nimport { Provider } from \"#/types/settings\";\nimport { GitRepository } from \"#/types/git\";\nimport { useDebounce } from \"#/hooks/use-debounce\";\nimport { cn } from \"#/utils/utils\";\nimport { formControlFieldClassName } from \"#/utils/form-control-classes\";\n\nimport { ClearButton } from \"../shared/clear-button\";\nimport { ToggleButton } from \"../shared/toggle-button\";\nimport { ErrorMessage } from \"../shared/error-message\";\nimport { DropdownItem } from \"../shared/dropdown-item\";\nimport { EmptyState } from \"../shared/empty-state\";\nimport { useUrlSearch } from \"./use-url-search\";\nimport { useRepositoryData } from \"./use-repository-data\";\nimport { GenericDropdownMenu } from \"../shared/generic-dropdown-menu\";\nimport { I18nKey } from \"#/i18n/declaration\";\nimport RepoIcon from \"#/icons/repo.svg?react\";\nimport { useHomeStore } from \"#/stores/home-store\";\nimport { Typography } from \"#/ui/typography\";\n\nexport interface GitRepoDropdownProps {\n provider: Provider;\n value?: string | null;\n repositoryName?: string | null;\n placeholder?: string;\n className?: string;\n disabled?: boolean;\n onChange?: (repository?: GitRepository) => void;\n}\n\nexport function GitRepoDropdown({\n provider,\n value,\n repositoryName,\n placeholder,\n className,\n disabled = false,\n onChange,\n}: GitRepoDropdownProps) {\n const { t } = useTranslation(\"openhands\");\n const { recentRepositories: storedRecentRepositories } = useHomeStore();\n const [inputValue, setInputValue] = useState(\"\");\n const [localSelectedItem, setLocalSelectedItem] =\n useState<GitRepository | null>(null);\n const debouncedInputValue = useDebounce(inputValue, 300);\n const menuRef = useRef<HTMLUListElement>(null);\n\n // Process search input to handle URLs\n const processedSearchInput = useMemo(() => {\n if (debouncedInputValue.startsWith(\"https://\")) {\n const match = debouncedInputValue.match(\n /https:\\/\\/[^/]+\\/([^/]+\\/[^/]+)/,\n );\n return match ? match[1] : debouncedInputValue;\n }\n return debouncedInputValue;\n }, [debouncedInputValue]);\n\n // URL search functionality\n const { urlSearchResults, isUrlSearchLoading } = useUrlSearch(\n inputValue,\n provider,\n );\n\n // Repository data management\n const {\n repositories,\n selectedRepository,\n fetchNextPage,\n hasNextPage,\n isLoading,\n isFetchingNextPage,\n isError,\n isSearchLoading,\n } = useRepositoryData(\n provider,\n disabled,\n processedSearchInput,\n urlSearchResults,\n inputValue,\n value,\n repositoryName,\n );\n\n /**\n * `selectedRepository` is derived from fetched/search results. A repo selected\n * from \"Most Recent\" may not be present in those results yet, so use the\n * locally selected item as a fallback to keep the UI stable.\n */\n const effectiveSelectedRepository = selectedRepository ?? localSelectedItem;\n\n // Get recent repositories filtered by provider and input keyword\n const recentRepositories = useMemo(() => {\n const allRecentRepos = storedRecentRepositories;\n const providerFilteredRepos = allRecentRepos.filter(\n (repo) => repo.git_provider === provider,\n );\n\n // If no input value, return all recent repos for this provider\n if (!inputValue?.trim()) {\n return providerFilteredRepos;\n }\n\n // Filter by input keyword\n const filterText = inputValue.startsWith(\"https://\")\n ? processedSearchInput\n : inputValue;\n\n return providerFilteredRepos.filter((repo) =>\n repo.full_name.toLowerCase().includes(filterText.toLowerCase()),\n );\n }, [storedRecentRepositories, provider, inputValue, processedSearchInput]);\n\n // Helper function to prioritize recent repositories at the top\n const prioritizeRecentRepositories = useCallback(\n (repoList: GitRepository[]) => {\n const recentRepoIds = new Set(recentRepositories.map((repo) => repo.id));\n const otherRepos = repoList.filter((repo) => !recentRepoIds.has(repo.id));\n return [...recentRepositories, ...otherRepos];\n },\n [recentRepositories],\n );\n\n // Filter repositories based on input value\n const filteredRepositories = useMemo(() => {\n let baseRepositories: GitRepository[];\n\n // If we have URL search results, show them directly (no filtering needed)\n if (urlSearchResults.length > 0) {\n baseRepositories = repositories;\n }\n // If we have a selected repository and the input matches it exactly, show all repositories\n else if (inputValue === selectedRepository?.full_name) {\n baseRepositories = repositories;\n }\n // If no input value, show all repositories\n else if (!inputValue?.trim()) {\n baseRepositories = repositories;\n }\n // For URL inputs, use the processed search input for filtering\n else {\n const filterText = inputValue.startsWith(\"https://\")\n ? processedSearchInput\n : inputValue;\n\n baseRepositories = repositories.filter((repo) =>\n repo.full_name.toLowerCase().includes(filterText.toLowerCase()),\n );\n }\n\n // Prioritize recent repositories at the top\n return prioritizeRecentRepositories(baseRepositories);\n }, [\n repositories,\n inputValue,\n selectedRepository,\n urlSearchResults,\n processedSearchInput,\n prioritizeRecentRepositories,\n ]);\n\n // Handle selection\n const handleSelectionChange = useCallback(\n (selectedItem: GitRepository | null) => {\n setLocalSelectedItem(selectedItem);\n onChange?.(selectedItem || undefined);\n // Update input value to show selected item\n if (selectedItem) {\n setInputValue(selectedItem.full_name);\n }\n },\n [onChange],\n );\n\n // Handle clear selection\n const handleClear = useCallback(() => {\n setLocalSelectedItem(null);\n handleSelectionChange(null);\n setInputValue(\"\");\n }, [handleSelectionChange]);\n\n // Handle scroll to bottom for pagination\n const handleMenuScroll = useCallback(\n (event: React.UIEvent<HTMLUListElement>) => {\n const { scrollTop, scrollHeight, clientHeight } = event.currentTarget;\n const isNearBottom = scrollTop + clientHeight >= scrollHeight - 10;\n\n if (isNearBottom && hasNextPage && !isFetchingNextPage) {\n fetchNextPage();\n }\n },\n [hasNextPage, isFetchingNextPage, fetchNextPage],\n );\n\n const {\n isOpen,\n getToggleButtonProps,\n getMenuProps,\n getInputProps,\n highlightedIndex,\n getItemProps,\n selectedItem,\n } = useCombobox({\n items: filteredRepositories,\n itemToString: (item) => item?.full_name || \"\",\n selectedItem: localSelectedItem,\n onSelectedItemChange: ({ selectedItem: newSelectedItem }) => {\n handleSelectionChange(newSelectedItem);\n },\n inputValue,\n // Override Downshift's default input-click behavior to avoid closing/reopening\n // the menu, which would reset scroll position and break search continuity.\n stateReducer: (state, actionAndChanges) =>\n actionAndChanges.type === useCombobox.stateChangeTypes.InputClick &&\n state.isOpen\n ? { ...actionAndChanges.changes, isOpen: true }\n : actionAndChanges.changes,\n });\n\n // Sync localSelectedItem with external value prop\n useEffect(() => {\n if (selectedRepository) {\n setLocalSelectedItem(selectedRepository);\n } else if (value === null) {\n setLocalSelectedItem(null);\n }\n }, [selectedRepository, value]);\n\n // Initialize input value when selectedRepository changes (but not when user is typing)\n useEffect(() => {\n if (isOpen) return;\n setInputValue(effectiveSelectedRepository?.full_name ?? \"\");\n }, [effectiveSelectedRepository, isOpen]);\n\n const isLoadingState =\n isLoading || isSearchLoading || isFetchingNextPage || isUrlSearchLoading;\n\n const renderItem = (\n item: GitRepository,\n index: number,\n itemHighlightedIndex: number,\n itemSelectedItem: GitRepository | null,\n itemGetItemProps: any, // eslint-disable-line @typescript-eslint/no-explicit-any\n ) => (\n <DropdownItem\n key={item.id}\n item={item}\n index={index}\n isSelected={itemSelectedItem?.id === item.id}\n getItemProps={itemGetItemProps}\n getDisplayText={(repo) => repo.full_name}\n getItemKey={(repo) => repo.id}\n />\n );\n\n const renderEmptyState = (emptyInputValue: string) => (\n <EmptyState\n inputValue={emptyInputValue}\n searchMessage={t(I18nKey.HOME$NO_REPOSITORY_FOUND)}\n emptyMessage={t(I18nKey.COMMON$NO_REPOSITORY)}\n testId=\"git-repo-dropdown-empty\"\n />\n );\n\n // Create sticky top item for recent repositories\n const stickyTopItem = useMemo(() => {\n if (recentRepositories.length === 0) {\n return null;\n }\n\n return (\n <div>\n <Typography.Text className=\"text-xs text-content-2 font-semibold leading-4 pl-2\">\n {t(I18nKey.COMMON$MOST_RECENT)}\n </Typography.Text>\n </div>\n );\n }, [recentRepositories, localSelectedItem, getItemProps, t]);\n\n return (\n <div className={cn(\"relative\", className)}>\n <div className=\"group relative text-[var(--oh-muted)] hover:text-white\">\n <div className=\"absolute left-2 top-1/2 transform -translate-y-1/2 z-10\">\n {isLoadingState ? (\n <div className=\"animate-spin h-4 w-4 border-2 border-transparent border-t-white rounded-full\" />\n ) : (\n <RepoIcon width={16} height={16} />\n )}\n </div>\n <input\n {...getInputProps({\n disabled,\n placeholder:\n placeholder ?? t(I18nKey.COMMON$SEARCH_REPOSITORIES_PLACEHOLDER),\n className: cn(\n formControlFieldClassName,\n \"text-inherit shadow-none pl-7 pr-16 text-sm font-normal leading-5\",\n \"placeholder:text-[var(--oh-muted)]\",\n \"disabled:cursor-not-allowed disabled:opacity-60\",\n ),\n // Direct onChange for cursor position preservation\n onChange: (e: React.ChangeEvent<HTMLInputElement>) => {\n setInputValue(e.target.value);\n },\n })}\n data-testid=\"git-repo-dropdown\"\n />\n\n <div className=\"absolute right-1 top-1/2 transform -translate-y-1/2 flex items-center\">\n {effectiveSelectedRepository && (\n <ClearButton disabled={disabled} onClear={handleClear} />\n )}\n\n <ToggleButton\n isOpen={isOpen}\n disabled={disabled}\n getToggleButtonProps={getToggleButtonProps}\n />\n </div>\n </div>\n\n <GenericDropdownMenu\n isOpen={isOpen}\n filteredItems={filteredRepositories}\n inputValue={inputValue}\n highlightedIndex={highlightedIndex}\n selectedItem={selectedItem}\n getMenuProps={getMenuProps}\n getItemProps={getItemProps}\n onScroll={handleMenuScroll}\n menuRef={menuRef}\n renderItem={renderItem}\n renderEmptyState={renderEmptyState}\n stickyTopItem={stickyTopItem}\n testId=\"git-repo-dropdown-menu\"\n numberOfRecentItems={recentRepositories.length}\n itemKey={(repo) => repo.id}\n />\n\n <ErrorMessage isError={isError} />\n </div>\n );\n}\n"],"mappings":"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"}
@@ -18,7 +18,7 @@ import { useHomeStore as h } from "../../../../stores/home-store.js";
18
18
  import { useCallback as g, useEffect as _, useMemo as v, useRef as y, useState as b } from "react";
19
19
  import { jsx as x, jsxs as S } from "react/jsx-runtime";
20
20
  //#region src/components/features/home/git-repo-dropdown/git-repo-dropdown.tsx
21
- function C({ provider: C, value: w, repositoryName: te, placeholder: T = "Search repositories...", className: E, disabled: D = !1, onChange: O }) {
21
+ function C({ provider: C, value: w, repositoryName: te, placeholder: T, className: E, disabled: D = !1, onChange: O }) {
22
22
  let { t: k } = e("openhands"), { recentRepositories: A } = h(), [j, M] = b(""), [N, P] = b(null), F = o(j, 300), ne = y(null), I = v(() => {
23
23
  if (F.startsWith("https://")) {
24
24
  let e = F.match(/https:\/\/[^/]+\/([^/]+\/[^/]+)/);
@@ -121,7 +121,7 @@ function C({ provider: C, value: w, repositoryName: te, placeholder: T = "Search
121
121
  /* @__PURE__ */ x("input", {
122
122
  ...se({
123
123
  disabled: D,
124
- placeholder: T,
124
+ placeholder: T ?? k(t.COMMON$SEARCH_REPOSITORIES_PLACEHOLDER),
125
125
  className: n(r, "text-inherit shadow-none pl-7 pr-16 text-sm font-normal leading-5", "placeholder:text-[var(--oh-muted)]", "disabled:cursor-not-allowed disabled:opacity-60"),
126
126
  onChange: (e) => {
127
127
  M(e.target.value);
@@ -1 +1 @@
1
- {"version":3,"file":"git-repo-dropdown.js","names":[],"sources":["../../../../../src/components/features/home/git-repo-dropdown/git-repo-dropdown.tsx"],"sourcesContent":["import React, {\n useState,\n useMemo,\n useCallback,\n useRef,\n useEffect,\n} from \"react\";\nimport { useCombobox } from \"downshift\";\nimport { useTranslation } from \"react-i18next\";\nimport { Provider } from \"#/types/settings\";\nimport { GitRepository } from \"#/types/git\";\nimport { useDebounce } from \"#/hooks/use-debounce\";\nimport { cn } from \"#/utils/utils\";\nimport { formControlFieldClassName } from \"#/utils/form-control-classes\";\n\nimport { ClearButton } from \"../shared/clear-button\";\nimport { ToggleButton } from \"../shared/toggle-button\";\nimport { ErrorMessage } from \"../shared/error-message\";\nimport { DropdownItem } from \"../shared/dropdown-item\";\nimport { EmptyState } from \"../shared/empty-state\";\nimport { useUrlSearch } from \"./use-url-search\";\nimport { useRepositoryData } from \"./use-repository-data\";\nimport { GenericDropdownMenu } from \"../shared/generic-dropdown-menu\";\nimport { I18nKey } from \"#/i18n/declaration\";\nimport RepoIcon from \"#/icons/repo.svg?react\";\nimport { useHomeStore } from \"#/stores/home-store\";\nimport { Typography } from \"#/ui/typography\";\n\nexport interface GitRepoDropdownProps {\n provider: Provider;\n value?: string | null;\n repositoryName?: string | null;\n placeholder?: string;\n className?: string;\n disabled?: boolean;\n onChange?: (repository?: GitRepository) => void;\n}\n\nexport function GitRepoDropdown({\n provider,\n value,\n repositoryName,\n placeholder = \"Search repositories...\",\n className,\n disabled = false,\n onChange,\n}: GitRepoDropdownProps) {\n const { t } = useTranslation(\"openhands\");\n const { recentRepositories: storedRecentRepositories } = useHomeStore();\n const [inputValue, setInputValue] = useState(\"\");\n const [localSelectedItem, setLocalSelectedItem] =\n useState<GitRepository | null>(null);\n const debouncedInputValue = useDebounce(inputValue, 300);\n const menuRef = useRef<HTMLUListElement>(null);\n\n // Process search input to handle URLs\n const processedSearchInput = useMemo(() => {\n if (debouncedInputValue.startsWith(\"https://\")) {\n const match = debouncedInputValue.match(\n /https:\\/\\/[^/]+\\/([^/]+\\/[^/]+)/,\n );\n return match ? match[1] : debouncedInputValue;\n }\n return debouncedInputValue;\n }, [debouncedInputValue]);\n\n // URL search functionality\n const { urlSearchResults, isUrlSearchLoading } = useUrlSearch(\n inputValue,\n provider,\n );\n\n // Repository data management\n const {\n repositories,\n selectedRepository,\n fetchNextPage,\n hasNextPage,\n isLoading,\n isFetchingNextPage,\n isError,\n isSearchLoading,\n } = useRepositoryData(\n provider,\n disabled,\n processedSearchInput,\n urlSearchResults,\n inputValue,\n value,\n repositoryName,\n );\n\n /**\n * `selectedRepository` is derived from fetched/search results. A repo selected\n * from \"Most Recent\" may not be present in those results yet, so use the\n * locally selected item as a fallback to keep the UI stable.\n */\n const effectiveSelectedRepository = selectedRepository ?? localSelectedItem;\n\n // Get recent repositories filtered by provider and input keyword\n const recentRepositories = useMemo(() => {\n const allRecentRepos = storedRecentRepositories;\n const providerFilteredRepos = allRecentRepos.filter(\n (repo) => repo.git_provider === provider,\n );\n\n // If no input value, return all recent repos for this provider\n if (!inputValue?.trim()) {\n return providerFilteredRepos;\n }\n\n // Filter by input keyword\n const filterText = inputValue.startsWith(\"https://\")\n ? processedSearchInput\n : inputValue;\n\n return providerFilteredRepos.filter((repo) =>\n repo.full_name.toLowerCase().includes(filterText.toLowerCase()),\n );\n }, [storedRecentRepositories, provider, inputValue, processedSearchInput]);\n\n // Helper function to prioritize recent repositories at the top\n const prioritizeRecentRepositories = useCallback(\n (repoList: GitRepository[]) => {\n const recentRepoIds = new Set(recentRepositories.map((repo) => repo.id));\n const otherRepos = repoList.filter((repo) => !recentRepoIds.has(repo.id));\n return [...recentRepositories, ...otherRepos];\n },\n [recentRepositories],\n );\n\n // Filter repositories based on input value\n const filteredRepositories = useMemo(() => {\n let baseRepositories: GitRepository[];\n\n // If we have URL search results, show them directly (no filtering needed)\n if (urlSearchResults.length > 0) {\n baseRepositories = repositories;\n }\n // If we have a selected repository and the input matches it exactly, show all repositories\n else if (inputValue === selectedRepository?.full_name) {\n baseRepositories = repositories;\n }\n // If no input value, show all repositories\n else if (!inputValue?.trim()) {\n baseRepositories = repositories;\n }\n // For URL inputs, use the processed search input for filtering\n else {\n const filterText = inputValue.startsWith(\"https://\")\n ? processedSearchInput\n : inputValue;\n\n baseRepositories = repositories.filter((repo) =>\n repo.full_name.toLowerCase().includes(filterText.toLowerCase()),\n );\n }\n\n // Prioritize recent repositories at the top\n return prioritizeRecentRepositories(baseRepositories);\n }, [\n repositories,\n inputValue,\n selectedRepository,\n urlSearchResults,\n processedSearchInput,\n prioritizeRecentRepositories,\n ]);\n\n // Handle selection\n const handleSelectionChange = useCallback(\n (selectedItem: GitRepository | null) => {\n setLocalSelectedItem(selectedItem);\n onChange?.(selectedItem || undefined);\n // Update input value to show selected item\n if (selectedItem) {\n setInputValue(selectedItem.full_name);\n }\n },\n [onChange],\n );\n\n // Handle clear selection\n const handleClear = useCallback(() => {\n setLocalSelectedItem(null);\n handleSelectionChange(null);\n setInputValue(\"\");\n }, [handleSelectionChange]);\n\n // Handle scroll to bottom for pagination\n const handleMenuScroll = useCallback(\n (event: React.UIEvent<HTMLUListElement>) => {\n const { scrollTop, scrollHeight, clientHeight } = event.currentTarget;\n const isNearBottom = scrollTop + clientHeight >= scrollHeight - 10;\n\n if (isNearBottom && hasNextPage && !isFetchingNextPage) {\n fetchNextPage();\n }\n },\n [hasNextPage, isFetchingNextPage, fetchNextPage],\n );\n\n const {\n isOpen,\n getToggleButtonProps,\n getMenuProps,\n getInputProps,\n highlightedIndex,\n getItemProps,\n selectedItem,\n } = useCombobox({\n items: filteredRepositories,\n itemToString: (item) => item?.full_name || \"\",\n selectedItem: localSelectedItem,\n onSelectedItemChange: ({ selectedItem: newSelectedItem }) => {\n handleSelectionChange(newSelectedItem);\n },\n inputValue,\n // Override Downshift's default input-click behavior to avoid closing/reopening\n // the menu, which would reset scroll position and break search continuity.\n stateReducer: (state, actionAndChanges) =>\n actionAndChanges.type === useCombobox.stateChangeTypes.InputClick &&\n state.isOpen\n ? { ...actionAndChanges.changes, isOpen: true }\n : actionAndChanges.changes,\n });\n\n // Sync localSelectedItem with external value prop\n useEffect(() => {\n if (selectedRepository) {\n setLocalSelectedItem(selectedRepository);\n } else if (value === null) {\n setLocalSelectedItem(null);\n }\n }, [selectedRepository, value]);\n\n // Initialize input value when selectedRepository changes (but not when user is typing)\n useEffect(() => {\n if (isOpen) return;\n setInputValue(effectiveSelectedRepository?.full_name ?? \"\");\n }, [effectiveSelectedRepository, isOpen]);\n\n const isLoadingState =\n isLoading || isSearchLoading || isFetchingNextPage || isUrlSearchLoading;\n\n const renderItem = (\n item: GitRepository,\n index: number,\n itemHighlightedIndex: number,\n itemSelectedItem: GitRepository | null,\n itemGetItemProps: any, // eslint-disable-line @typescript-eslint/no-explicit-any\n ) => (\n <DropdownItem\n key={item.id}\n item={item}\n index={index}\n isSelected={itemSelectedItem?.id === item.id}\n getItemProps={itemGetItemProps}\n getDisplayText={(repo) => repo.full_name}\n getItemKey={(repo) => repo.id}\n />\n );\n\n const renderEmptyState = (emptyInputValue: string) => (\n <EmptyState\n inputValue={emptyInputValue}\n searchMessage={t(I18nKey.HOME$NO_REPOSITORY_FOUND)}\n emptyMessage={t(I18nKey.COMMON$NO_REPOSITORY)}\n testId=\"git-repo-dropdown-empty\"\n />\n );\n\n // Create sticky top item for recent repositories\n const stickyTopItem = useMemo(() => {\n if (recentRepositories.length === 0) {\n return null;\n }\n\n return (\n <div>\n <Typography.Text className=\"text-xs text-content-2 font-semibold leading-4 pl-2\">\n {t(I18nKey.COMMON$MOST_RECENT)}\n </Typography.Text>\n </div>\n );\n }, [recentRepositories, localSelectedItem, getItemProps, t]);\n\n return (\n <div className={cn(\"relative\", className)}>\n <div className=\"group relative text-[var(--oh-muted)] hover:text-white\">\n <div className=\"absolute left-2 top-1/2 transform -translate-y-1/2 z-10\">\n {isLoadingState ? (\n <div className=\"animate-spin h-4 w-4 border-2 border-transparent border-t-white rounded-full\" />\n ) : (\n <RepoIcon width={16} height={16} />\n )}\n </div>\n <input\n {...getInputProps({\n disabled,\n placeholder,\n className: cn(\n formControlFieldClassName,\n \"text-inherit shadow-none pl-7 pr-16 text-sm font-normal leading-5\",\n \"placeholder:text-[var(--oh-muted)]\",\n \"disabled:cursor-not-allowed disabled:opacity-60\",\n ),\n // Direct onChange for cursor position preservation\n onChange: (e: React.ChangeEvent<HTMLInputElement>) => {\n setInputValue(e.target.value);\n },\n })}\n data-testid=\"git-repo-dropdown\"\n />\n\n <div className=\"absolute right-1 top-1/2 transform -translate-y-1/2 flex items-center\">\n {effectiveSelectedRepository && (\n <ClearButton disabled={disabled} onClear={handleClear} />\n )}\n\n <ToggleButton\n isOpen={isOpen}\n disabled={disabled}\n getToggleButtonProps={getToggleButtonProps}\n />\n </div>\n </div>\n\n <GenericDropdownMenu\n isOpen={isOpen}\n filteredItems={filteredRepositories}\n inputValue={inputValue}\n highlightedIndex={highlightedIndex}\n selectedItem={selectedItem}\n getMenuProps={getMenuProps}\n getItemProps={getItemProps}\n onScroll={handleMenuScroll}\n menuRef={menuRef}\n renderItem={renderItem}\n renderEmptyState={renderEmptyState}\n stickyTopItem={stickyTopItem}\n testId=\"git-repo-dropdown-menu\"\n numberOfRecentItems={recentRepositories.length}\n itemKey={(repo) => repo.id}\n />\n\n <ErrorMessage isError={isError} />\n </div>\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"git-repo-dropdown.js","names":[],"sources":["../../../../../src/components/features/home/git-repo-dropdown/git-repo-dropdown.tsx"],"sourcesContent":["import React, {\n useState,\n useMemo,\n useCallback,\n useRef,\n useEffect,\n} from \"react\";\nimport { useCombobox } from \"downshift\";\nimport { useTranslation } from \"react-i18next\";\nimport { Provider } from \"#/types/settings\";\nimport { GitRepository } from \"#/types/git\";\nimport { useDebounce } from \"#/hooks/use-debounce\";\nimport { cn } from \"#/utils/utils\";\nimport { formControlFieldClassName } from \"#/utils/form-control-classes\";\n\nimport { ClearButton } from \"../shared/clear-button\";\nimport { ToggleButton } from \"../shared/toggle-button\";\nimport { ErrorMessage } from \"../shared/error-message\";\nimport { DropdownItem } from \"../shared/dropdown-item\";\nimport { EmptyState } from \"../shared/empty-state\";\nimport { useUrlSearch } from \"./use-url-search\";\nimport { useRepositoryData } from \"./use-repository-data\";\nimport { GenericDropdownMenu } from \"../shared/generic-dropdown-menu\";\nimport { I18nKey } from \"#/i18n/declaration\";\nimport RepoIcon from \"#/icons/repo.svg?react\";\nimport { useHomeStore } from \"#/stores/home-store\";\nimport { Typography } from \"#/ui/typography\";\n\nexport interface GitRepoDropdownProps {\n provider: Provider;\n value?: string | null;\n repositoryName?: string | null;\n placeholder?: string;\n className?: string;\n disabled?: boolean;\n onChange?: (repository?: GitRepository) => void;\n}\n\nexport function GitRepoDropdown({\n provider,\n value,\n repositoryName,\n placeholder,\n className,\n disabled = false,\n onChange,\n}: GitRepoDropdownProps) {\n const { t } = useTranslation(\"openhands\");\n const { recentRepositories: storedRecentRepositories } = useHomeStore();\n const [inputValue, setInputValue] = useState(\"\");\n const [localSelectedItem, setLocalSelectedItem] =\n useState<GitRepository | null>(null);\n const debouncedInputValue = useDebounce(inputValue, 300);\n const menuRef = useRef<HTMLUListElement>(null);\n\n // Process search input to handle URLs\n const processedSearchInput = useMemo(() => {\n if (debouncedInputValue.startsWith(\"https://\")) {\n const match = debouncedInputValue.match(\n /https:\\/\\/[^/]+\\/([^/]+\\/[^/]+)/,\n );\n return match ? match[1] : debouncedInputValue;\n }\n return debouncedInputValue;\n }, [debouncedInputValue]);\n\n // URL search functionality\n const { urlSearchResults, isUrlSearchLoading } = useUrlSearch(\n inputValue,\n provider,\n );\n\n // Repository data management\n const {\n repositories,\n selectedRepository,\n fetchNextPage,\n hasNextPage,\n isLoading,\n isFetchingNextPage,\n isError,\n isSearchLoading,\n } = useRepositoryData(\n provider,\n disabled,\n processedSearchInput,\n urlSearchResults,\n inputValue,\n value,\n repositoryName,\n );\n\n /**\n * `selectedRepository` is derived from fetched/search results. A repo selected\n * from \"Most Recent\" may not be present in those results yet, so use the\n * locally selected item as a fallback to keep the UI stable.\n */\n const effectiveSelectedRepository = selectedRepository ?? localSelectedItem;\n\n // Get recent repositories filtered by provider and input keyword\n const recentRepositories = useMemo(() => {\n const allRecentRepos = storedRecentRepositories;\n const providerFilteredRepos = allRecentRepos.filter(\n (repo) => repo.git_provider === provider,\n );\n\n // If no input value, return all recent repos for this provider\n if (!inputValue?.trim()) {\n return providerFilteredRepos;\n }\n\n // Filter by input keyword\n const filterText = inputValue.startsWith(\"https://\")\n ? processedSearchInput\n : inputValue;\n\n return providerFilteredRepos.filter((repo) =>\n repo.full_name.toLowerCase().includes(filterText.toLowerCase()),\n );\n }, [storedRecentRepositories, provider, inputValue, processedSearchInput]);\n\n // Helper function to prioritize recent repositories at the top\n const prioritizeRecentRepositories = useCallback(\n (repoList: GitRepository[]) => {\n const recentRepoIds = new Set(recentRepositories.map((repo) => repo.id));\n const otherRepos = repoList.filter((repo) => !recentRepoIds.has(repo.id));\n return [...recentRepositories, ...otherRepos];\n },\n [recentRepositories],\n );\n\n // Filter repositories based on input value\n const filteredRepositories = useMemo(() => {\n let baseRepositories: GitRepository[];\n\n // If we have URL search results, show them directly (no filtering needed)\n if (urlSearchResults.length > 0) {\n baseRepositories = repositories;\n }\n // If we have a selected repository and the input matches it exactly, show all repositories\n else if (inputValue === selectedRepository?.full_name) {\n baseRepositories = repositories;\n }\n // If no input value, show all repositories\n else if (!inputValue?.trim()) {\n baseRepositories = repositories;\n }\n // For URL inputs, use the processed search input for filtering\n else {\n const filterText = inputValue.startsWith(\"https://\")\n ? processedSearchInput\n : inputValue;\n\n baseRepositories = repositories.filter((repo) =>\n repo.full_name.toLowerCase().includes(filterText.toLowerCase()),\n );\n }\n\n // Prioritize recent repositories at the top\n return prioritizeRecentRepositories(baseRepositories);\n }, [\n repositories,\n inputValue,\n selectedRepository,\n urlSearchResults,\n processedSearchInput,\n prioritizeRecentRepositories,\n ]);\n\n // Handle selection\n const handleSelectionChange = useCallback(\n (selectedItem: GitRepository | null) => {\n setLocalSelectedItem(selectedItem);\n onChange?.(selectedItem || undefined);\n // Update input value to show selected item\n if (selectedItem) {\n setInputValue(selectedItem.full_name);\n }\n },\n [onChange],\n );\n\n // Handle clear selection\n const handleClear = useCallback(() => {\n setLocalSelectedItem(null);\n handleSelectionChange(null);\n setInputValue(\"\");\n }, [handleSelectionChange]);\n\n // Handle scroll to bottom for pagination\n const handleMenuScroll = useCallback(\n (event: React.UIEvent<HTMLUListElement>) => {\n const { scrollTop, scrollHeight, clientHeight } = event.currentTarget;\n const isNearBottom = scrollTop + clientHeight >= scrollHeight - 10;\n\n if (isNearBottom && hasNextPage && !isFetchingNextPage) {\n fetchNextPage();\n }\n },\n [hasNextPage, isFetchingNextPage, fetchNextPage],\n );\n\n const {\n isOpen,\n getToggleButtonProps,\n getMenuProps,\n getInputProps,\n highlightedIndex,\n getItemProps,\n selectedItem,\n } = useCombobox({\n items: filteredRepositories,\n itemToString: (item) => item?.full_name || \"\",\n selectedItem: localSelectedItem,\n onSelectedItemChange: ({ selectedItem: newSelectedItem }) => {\n handleSelectionChange(newSelectedItem);\n },\n inputValue,\n // Override Downshift's default input-click behavior to avoid closing/reopening\n // the menu, which would reset scroll position and break search continuity.\n stateReducer: (state, actionAndChanges) =>\n actionAndChanges.type === useCombobox.stateChangeTypes.InputClick &&\n state.isOpen\n ? { ...actionAndChanges.changes, isOpen: true }\n : actionAndChanges.changes,\n });\n\n // Sync localSelectedItem with external value prop\n useEffect(() => {\n if (selectedRepository) {\n setLocalSelectedItem(selectedRepository);\n } else if (value === null) {\n setLocalSelectedItem(null);\n }\n }, [selectedRepository, value]);\n\n // Initialize input value when selectedRepository changes (but not when user is typing)\n useEffect(() => {\n if (isOpen) return;\n setInputValue(effectiveSelectedRepository?.full_name ?? \"\");\n }, [effectiveSelectedRepository, isOpen]);\n\n const isLoadingState =\n isLoading || isSearchLoading || isFetchingNextPage || isUrlSearchLoading;\n\n const renderItem = (\n item: GitRepository,\n index: number,\n itemHighlightedIndex: number,\n itemSelectedItem: GitRepository | null,\n itemGetItemProps: any, // eslint-disable-line @typescript-eslint/no-explicit-any\n ) => (\n <DropdownItem\n key={item.id}\n item={item}\n index={index}\n isSelected={itemSelectedItem?.id === item.id}\n getItemProps={itemGetItemProps}\n getDisplayText={(repo) => repo.full_name}\n getItemKey={(repo) => repo.id}\n />\n );\n\n const renderEmptyState = (emptyInputValue: string) => (\n <EmptyState\n inputValue={emptyInputValue}\n searchMessage={t(I18nKey.HOME$NO_REPOSITORY_FOUND)}\n emptyMessage={t(I18nKey.COMMON$NO_REPOSITORY)}\n testId=\"git-repo-dropdown-empty\"\n />\n );\n\n // Create sticky top item for recent repositories\n const stickyTopItem = useMemo(() => {\n if (recentRepositories.length === 0) {\n return null;\n }\n\n return (\n <div>\n <Typography.Text className=\"text-xs text-content-2 font-semibold leading-4 pl-2\">\n {t(I18nKey.COMMON$MOST_RECENT)}\n </Typography.Text>\n </div>\n );\n }, [recentRepositories, localSelectedItem, getItemProps, t]);\n\n return (\n <div className={cn(\"relative\", className)}>\n <div className=\"group relative text-[var(--oh-muted)] hover:text-white\">\n <div className=\"absolute left-2 top-1/2 transform -translate-y-1/2 z-10\">\n {isLoadingState ? (\n <div className=\"animate-spin h-4 w-4 border-2 border-transparent border-t-white rounded-full\" />\n ) : (\n <RepoIcon width={16} height={16} />\n )}\n </div>\n <input\n {...getInputProps({\n disabled,\n placeholder:\n placeholder ?? t(I18nKey.COMMON$SEARCH_REPOSITORIES_PLACEHOLDER),\n className: cn(\n formControlFieldClassName,\n \"text-inherit shadow-none pl-7 pr-16 text-sm font-normal leading-5\",\n \"placeholder:text-[var(--oh-muted)]\",\n \"disabled:cursor-not-allowed disabled:opacity-60\",\n ),\n // Direct onChange for cursor position preservation\n onChange: (e: React.ChangeEvent<HTMLInputElement>) => {\n setInputValue(e.target.value);\n },\n })}\n data-testid=\"git-repo-dropdown\"\n />\n\n <div className=\"absolute right-1 top-1/2 transform -translate-y-1/2 flex items-center\">\n {effectiveSelectedRepository && (\n <ClearButton disabled={disabled} onClear={handleClear} />\n )}\n\n <ToggleButton\n isOpen={isOpen}\n disabled={disabled}\n getToggleButtonProps={getToggleButtonProps}\n />\n </div>\n </div>\n\n <GenericDropdownMenu\n isOpen={isOpen}\n filteredItems={filteredRepositories}\n inputValue={inputValue}\n highlightedIndex={highlightedIndex}\n selectedItem={selectedItem}\n getMenuProps={getMenuProps}\n getItemProps={getItemProps}\n onScroll={handleMenuScroll}\n menuRef={menuRef}\n renderItem={renderItem}\n renderEmptyState={renderEmptyState}\n stickyTopItem={stickyTopItem}\n testId=\"git-repo-dropdown-menu\"\n numberOfRecentItems={recentRepositories.length}\n itemKey={(repo) => repo.id}\n />\n\n <ErrorMessage isError={isError} />\n </div>\n );\n}\n"],"mappings":"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"}
@@ -1,2 +1,2 @@
1
- require(`../../../_virtual/_rolldown/runtime.cjs`);const e=require(`../../../node_modules/react-i18next/dist/es/useTranslation.cjs`),t=require(`../../../i18n/declaration.cjs`),n=require(`../../../utils/utils.cjs`),r=require(`../../../ui/typography.cjs`),i=require(`../../../icons/settings-gear.cjs`),a=require(`../../../icons/close.cjs`),o=require(`./settings-nav-header.cjs`),s=require(`./settings-nav-divider.cjs`),c=require(`../sidebar/sidebar-layout.cjs`),l=require(`./settings-nav-link.cjs`),u=require(`../sidebar/sidebar-nav-link.cjs`),d=require(`./backend-synced-settings-badge.cjs`);let f=require(`react/jsx-runtime`);function p({navigationItems:i}){let{t:a}=e.useTranslation(`openhands`),o=i.filter(e=>e.type===`item`);return(0,f.jsxs)(`aside`,{"data-testid":`settings-navbar-desktop`,className:n.cn(`hidden md:flex md:w-[260px] md:shrink-0 md:flex-col md:gap-2`,`md:sticky md:top-8 md:self-start md:pl-8`),children:[(0,f.jsx)(r.Typography.Text,{className:`px-2 text-sm font-normal text-white`,children:a(t.I18nKey.SETTINGS$TITLE)}),(0,f.jsx)(`div`,{className:`flex flex-col gap-0.5 pt-0.5`,children:o.map(e=>(0,f.jsx)(u.SidebarNavLink,{to:e.item.to,label:a(e.item.text),end:!0,testId:`sidebar-settings-${e.item.to}`,icon:e.item.icon,disabled:e.disabled,disabledReason:e.disabled&&e.disabledAgentName?a(t.I18nKey.SETTINGS$AGENT_DISABLED_TOOLTIP,{agentName:e.disabledAgentName}):void 0},e.item.to))}),(0,f.jsx)(`div`,{className:`px-2 pt-3`,children:(0,f.jsx)(d.BackendSyncedSettingsBadge,{})})]})}function m({isMobileMenuOpen:u,onCloseMobileMenu:p,navigationItems:m}){let{t:h}=e.useTranslation(`openhands`);return(0,f.jsxs)(f.Fragment,{children:[u&&(0,f.jsx)(`div`,{className:`fixed inset-0 z-40 bg-black bg-opacity-50 md:hidden`,onClick:p}),(0,f.jsxs)(`nav`,{"data-testid":`settings-navbar`,className:n.cn(`flex flex-col gap-6 transition-transform duration-300 ease-in-out`,`fixed inset-0 z-50 w-full bg-[var(--oh-surface-deep)] p-4 transform md:hidden`,u?`translate-x-0`:`-translate-x-full`),children:[(0,f.jsxs)(`div`,{className:`flex items-center justify-between`,children:[(0,f.jsxs)(`div`,{className:`ml-1 flex items-center gap-2 sm:ml-4.5`,children:[(0,f.jsx)(i.default,{width:16,height:16}),(0,f.jsx)(r.Typography.H2,{children:h(t.I18nKey.SETTINGS$TITLE)})]}),(0,f.jsx)(`button`,{type:`button`,onClick:p,className:n.cn(`cursor-pointer rounded-md p-0.5 hover:bg-tertiary md:hidden`,c.navInteractiveTransitionClassName),"aria-label":`Close navigation menu`,children:(0,f.jsx)(a.default,{width:32,height:32})})]}),(0,f.jsx)(`div`,{className:`flex flex-col gap-2`,children:m.map((e,t)=>e.type===`header`?(0,f.jsx)(o.SettingsNavHeader,{text:e.text},`header-${e.text}`):e.type===`divider`?(0,f.jsx)(s.SettingsNavDivider,{},`divider-${t}`):(0,f.jsx)(l.SettingsNavLink,{item:e.item,onClick:p,disabled:e.disabled,disabledAgentName:e.disabledAgentName},e.item.to))}),(0,f.jsx)(`div`,{className:`px-2 pt-3`,children:(0,f.jsx)(d.BackendSyncedSettingsBadge,{})})]})]})}function h(e){return(0,f.jsxs)(f.Fragment,{children:[(0,f.jsx)(p,{navigationItems:e.navigationItems}),(0,f.jsx)(m,{...e})]})}exports.SettingsDesktopSidebar=p,exports.SettingsNavigation=h;
1
+ require(`../../../_virtual/_rolldown/runtime.cjs`);const e=require(`../../../node_modules/react-i18next/dist/es/useTranslation.cjs`),t=require(`../../../i18n/declaration.cjs`),n=require(`../../../utils/utils.cjs`),r=require(`../../../ui/typography.cjs`),i=require(`../../../icons/settings-gear.cjs`),a=require(`../../../icons/close.cjs`),o=require(`./settings-nav-header.cjs`),s=require(`./settings-nav-divider.cjs`),c=require(`../sidebar/sidebar-layout.cjs`),l=require(`./settings-nav-link.cjs`),u=require(`../sidebar/sidebar-nav-link.cjs`),d=require(`./backend-synced-settings-badge.cjs`);let f=require(`react/jsx-runtime`);function p({navigationItems:i}){let{t:a}=e.useTranslation(`openhands`),o=i.filter(e=>e.type===`item`);return(0,f.jsxs)(`aside`,{"data-testid":`settings-navbar-desktop`,className:n.cn(`hidden md:flex md:w-[260px] md:shrink-0 md:flex-col md:gap-2`,`md:sticky md:top-8 md:self-start md:pl-8`),children:[(0,f.jsx)(r.Typography.Text,{className:`px-2 text-sm font-normal text-white`,children:a(t.I18nKey.SETTINGS$TITLE)}),(0,f.jsx)(`div`,{className:`flex flex-col gap-0.5 pt-0.5`,children:o.map(e=>(0,f.jsx)(u.SidebarNavLink,{to:e.item.to,label:a(e.item.text),end:!0,testId:`sidebar-settings-${e.item.to}`,icon:e.item.icon,disabled:e.disabled,disabledReason:e.disabled&&e.disabledAgentName?a(t.I18nKey.SETTINGS$AGENT_DISABLED_TOOLTIP,{agentName:e.disabledAgentName}):void 0},e.item.to))}),(0,f.jsx)(`div`,{className:`px-2 pt-3`,children:(0,f.jsx)(d.BackendSyncedSettingsBadge,{})})]})}function m({isMobileMenuOpen:u,onCloseMobileMenu:p,navigationItems:m}){let{t:h}=e.useTranslation(`openhands`);return(0,f.jsxs)(f.Fragment,{children:[u&&(0,f.jsx)(`div`,{className:`fixed inset-0 z-40 bg-black bg-opacity-50 md:hidden`,onClick:p}),(0,f.jsxs)(`nav`,{"data-testid":`settings-navbar`,className:n.cn(`flex flex-col gap-6 transition-transform duration-300 ease-in-out`,`fixed inset-0 z-50 w-full bg-[var(--oh-surface-deep)] p-4 transform md:hidden`,u?`translate-x-0`:`-translate-x-full`),children:[(0,f.jsxs)(`div`,{className:`flex items-center justify-between`,children:[(0,f.jsxs)(`div`,{className:`ml-1 flex items-center gap-2 sm:ml-4.5`,children:[(0,f.jsx)(i.default,{width:16,height:16}),(0,f.jsx)(r.Typography.H2,{children:h(t.I18nKey.SETTINGS$TITLE)})]}),(0,f.jsx)(`button`,{type:`button`,onClick:p,className:n.cn(`cursor-pointer rounded-md p-0.5 hover:bg-tertiary md:hidden`,c.navInteractiveTransitionClassName),"aria-label":h(t.I18nKey.SIDEBAR$CLOSE_MENU),children:(0,f.jsx)(a.default,{width:32,height:32})})]}),(0,f.jsx)(`div`,{className:`flex flex-col gap-2`,children:m.map((e,t)=>e.type===`header`?(0,f.jsx)(o.SettingsNavHeader,{text:e.text},`header-${e.text}`):e.type===`divider`?(0,f.jsx)(s.SettingsNavDivider,{},`divider-${t}`):(0,f.jsx)(l.SettingsNavLink,{item:e.item,onClick:p,disabled:e.disabled,disabledAgentName:e.disabledAgentName},e.item.to))}),(0,f.jsx)(`div`,{className:`px-2 pt-3`,children:(0,f.jsx)(d.BackendSyncedSettingsBadge,{})})]})]})}function h(e){return(0,f.jsxs)(f.Fragment,{children:[(0,f.jsx)(p,{navigationItems:e.navigationItems}),(0,f.jsx)(m,{...e})]})}exports.SettingsDesktopSidebar=p,exports.SettingsNavigation=h;
2
2
  //# sourceMappingURL=settings-navigation.cjs.map