@env-hopper/frontend-core 2.0.1-alpha

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 (385) hide show
  1. package/LICENSE +21 -0
  2. package/dist/apple-touch-180x180.png +0 -0
  3. package/dist/disable.well-known/appspecific/com.chrome.devtools.json +6 -0
  4. package/dist/env-hopper-16x16.png +0 -0
  5. package/dist/env-hopper-192x192.png +0 -0
  6. package/dist/env-hopper-32x32.png +0 -0
  7. package/dist/env-hopper-48x48.png +0 -0
  8. package/dist/env-hopper-512x512.png +0 -0
  9. package/dist/env-hopper-square.svg +114 -0
  10. package/dist/esm/App.d.ts +12 -0
  11. package/dist/esm/App.js +12 -0
  12. package/dist/esm/App.js.map +1 -0
  13. package/dist/esm/__tests__/modules/fuzzyMatchLogic/autoCompleteFilter.test.d.ts +1 -0
  14. package/dist/esm/__tests__/modules/fuzzyMatchLogic/features/prefixFracTokenMiddles.test.d.ts +1 -0
  15. package/dist/esm/__tests__/modules/fuzzyMatchLogic/fixLayout.test.d.ts +1 -0
  16. package/dist/esm/__tests__/modules/fuzzyMatchLogic/postFiltration.test.d.ts +1 -0
  17. package/dist/esm/__tests__/modules/fuzzyMatchLogic/testUtils.d.ts +2 -0
  18. package/dist/esm/__tests__/modules/fuzzyMatchLogic/tokenize.test.d.ts +1 -0
  19. package/dist/esm/__tests__/modules/resouceJump/findBestMatchByUrl.test.d.ts +1 -0
  20. package/dist/esm/__tests__/setupTests.d.ts +0 -0
  21. package/dist/esm/__tests__/util/availabilityMatrixUtils.test.d.ts +1 -0
  22. package/dist/esm/api/ApiQueryMagazine.d.ts +12 -0
  23. package/dist/esm/api/ApiQueryMagazine.js +16 -0
  24. package/dist/esm/api/ApiQueryMagazine.js.map +1 -0
  25. package/dist/esm/api/data/useQueryBootstrapConfig.d.ts +3 -0
  26. package/dist/esm/api/data/useQueryBootstrapConfig.js +14 -0
  27. package/dist/esm/api/data/useQueryBootstrapConfig.js.map +1 -0
  28. package/dist/esm/api/infra/createQueryClient.d.ts +7 -0
  29. package/dist/esm/api/infra/createQueryClient.js +23 -0
  30. package/dist/esm/api/infra/createQueryClient.js.map +1 -0
  31. package/dist/esm/api/infra/trpc.d.ts +93 -0
  32. package/dist/esm/api/infra/trpc.js +8 -0
  33. package/dist/esm/api/infra/trpc.js.map +1 -0
  34. package/dist/esm/api/unsorted/indexDataFetcher.d.ts +11 -0
  35. package/dist/esm/api/unsorted/indexDataFetcher.js +35 -0
  36. package/dist/esm/api/unsorted/indexDataFetcher.js.map +1 -0
  37. package/dist/esm/appPropsFactory.d.ts +2 -0
  38. package/dist/esm/appPropsFactory.js +32 -0
  39. package/dist/esm/appPropsFactory.js.map +1 -0
  40. package/dist/esm/assets/env-hopper-logo.svg.js +16 -0
  41. package/dist/esm/assets/env-hopper-logo.svg.js.map +1 -0
  42. package/dist/esm/components/ThemeSwitcher.d.ts +1 -0
  43. package/dist/esm/components/ThemeSwitcher.js +25 -0
  44. package/dist/esm/components/ThemeSwitcher.js.map +1 -0
  45. package/dist/esm/components/theme-provider.d.ts +2 -0
  46. package/dist/esm/components/theme-provider.js +10 -0
  47. package/dist/esm/components/theme-provider.js.map +1 -0
  48. package/dist/esm/components/ui/badge.d.ts +9 -0
  49. package/dist/esm/components/ui/breadcrumb.d.ts +46 -0
  50. package/dist/esm/components/ui/button.d.ts +16 -0
  51. package/dist/esm/components/ui/button.js +52 -0
  52. package/dist/esm/components/ui/button.js.map +1 -0
  53. package/dist/esm/components/ui/card.d.ts +38 -0
  54. package/dist/esm/components/ui/collapsible.d.ts +5 -0
  55. package/dist/esm/components/ui/dialog.d.ts +21 -0
  56. package/dist/esm/components/ui/dropdown-menu.d.ts +25 -0
  57. package/dist/esm/components/ui/input.d.ts +10 -0
  58. package/dist/esm/components/ui/popover.d.ts +7 -0
  59. package/dist/esm/components/ui/popover.js +14 -0
  60. package/dist/esm/components/ui/popover.js.map +1 -0
  61. package/dist/esm/components/ui/scroll-area.d.ts +5 -0
  62. package/dist/esm/components/ui/separator.d.ts +4 -0
  63. package/dist/esm/components/ui/separator.js +27 -0
  64. package/dist/esm/components/ui/separator.js.map +1 -0
  65. package/dist/esm/components/ui/tabs.d.ts +7 -0
  66. package/dist/esm/index.d.ts +3 -0
  67. package/dist/esm/index.js +7 -0
  68. package/dist/esm/index.js.map +1 -0
  69. package/dist/esm/lib/utils.d.ts +2 -0
  70. package/dist/esm/lib/utils.js +9 -0
  71. package/dist/esm/lib/utils.js.map +1 -0
  72. package/dist/esm/main.d.ts +0 -0
  73. package/dist/esm/modules/config/BootstrapConfigContext.d.ts +9 -0
  74. package/dist/esm/modules/config/BootstrapConfigContext.js +25 -0
  75. package/dist/esm/modules/config/BootstrapConfigContext.js.map +1 -0
  76. package/dist/esm/modules/config/GlobalConfigContext.d.ts +14 -0
  77. package/dist/esm/modules/config/GlobalConfigContext.js +21 -0
  78. package/dist/esm/modules/config/GlobalConfigContext.js.map +1 -0
  79. package/dist/esm/modules/environment/ApiQueryMagazineEnvironment.d.ts +12 -0
  80. package/dist/esm/modules/environment/ApiQueryMagazineEnvironment.js +13 -0
  81. package/dist/esm/modules/environment/ApiQueryMagazineEnvironment.js.map +1 -0
  82. package/dist/esm/modules/environment/EnvironmentContext.d.ts +15 -0
  83. package/dist/esm/modules/environment/EnvironmentContext.js +67 -0
  84. package/dist/esm/modules/environment/EnvironmentContext.js.map +1 -0
  85. package/dist/esm/modules/environment/types.d.ts +4 -0
  86. package/dist/esm/modules/environment/ui/EhEnvSelector.d.ts +9 -0
  87. package/dist/esm/modules/environment/ui/EhEnvSelector.js +167 -0
  88. package/dist/esm/modules/environment/ui/EhEnvSelector.js.map +1 -0
  89. package/dist/esm/modules/fuzzyMatchLogic/autoCompleteFilter.d.ts +3 -0
  90. package/dist/esm/modules/fuzzyMatchLogic/autoCompleteFilter.js +43 -0
  91. package/dist/esm/modules/fuzzyMatchLogic/autoCompleteFilter.js.map +1 -0
  92. package/dist/esm/modules/fuzzyMatchLogic/features/prefixFrac.d.ts +1 -0
  93. package/dist/esm/modules/fuzzyMatchLogic/features/prefixFrac.js +12 -0
  94. package/dist/esm/modules/fuzzyMatchLogic/features/prefixFrac.js.map +1 -0
  95. package/dist/esm/modules/fuzzyMatchLogic/features/prefixFracTokenMiddles.d.ts +2 -0
  96. package/dist/esm/modules/fuzzyMatchLogic/fixLayout.d.ts +2 -0
  97. package/dist/esm/modules/fuzzyMatchLogic/postFiltration.d.ts +2 -0
  98. package/dist/esm/modules/fuzzyMatchLogic/scoring.d.ts +2 -0
  99. package/dist/esm/modules/fuzzyMatchLogic/scoring.js +23 -0
  100. package/dist/esm/modules/fuzzyMatchLogic/scoring.js.map +1 -0
  101. package/dist/esm/modules/fuzzyMatchLogic/tokenize.d.ts +3 -0
  102. package/dist/esm/modules/fuzzyMatchLogic/types.d.ts +25 -0
  103. package/dist/esm/modules/fuzzyMatchLogic/utils.d.ts +2 -0
  104. package/dist/esm/modules/fuzzyMatchLogic/utils.js +12 -0
  105. package/dist/esm/modules/fuzzyMatchLogic/utils.js.map +1 -0
  106. package/dist/esm/modules/pluginCore/PluginManagerContext.d.ts +24 -0
  107. package/dist/esm/modules/pluginCore/PluginManagerContext.js +62 -0
  108. package/dist/esm/modules/pluginCore/PluginManagerContext.js.map +1 -0
  109. package/dist/esm/modules/pluginCore/makePluginManagerContext.d.ts +7 -0
  110. package/dist/esm/modules/pluginCore/makePluginManagerContext.js +27 -0
  111. package/dist/esm/modules/pluginCore/makePluginManagerContext.js.map +1 -0
  112. package/dist/esm/modules/pluginCore/types.d.ts +36 -0
  113. package/dist/esm/modules/pluginCore/types.js +7 -0
  114. package/dist/esm/modules/pluginCore/types.js.map +1 -0
  115. package/dist/esm/modules/resourceJump/ApiQueryMagazineResourceJump.d.ts +27 -0
  116. package/dist/esm/modules/resourceJump/ApiQueryMagazineResourceJump.js +26 -0
  117. package/dist/esm/modules/resourceJump/ApiQueryMagazineResourceJump.js.map +1 -0
  118. package/dist/esm/modules/resourceJump/ResourceJumpContext.d.ts +17 -0
  119. package/dist/esm/modules/resourceJump/ResourceJumpContext.js +88 -0
  120. package/dist/esm/modules/resourceJump/ResourceJumpContext.js.map +1 -0
  121. package/dist/esm/modules/resourceJump/buildJumpUrl.d.ts +2 -0
  122. package/dist/esm/modules/resourceJump/buildJumpUrl.js +32 -0
  123. package/dist/esm/modules/resourceJump/buildJumpUrl.js.map +1 -0
  124. package/dist/esm/modules/resourceJump/findBestMatchByUrl.d.ts +17 -0
  125. package/dist/esm/modules/resourceJump/findBestMatchByUrl.js +85 -0
  126. package/dist/esm/modules/resourceJump/findBestMatchByUrl.js.map +1 -0
  127. package/dist/esm/modules/resourceJump/helpers.d.ts +28 -0
  128. package/dist/esm/modules/resourceJump/helpers.js +19 -0
  129. package/dist/esm/modules/resourceJump/helpers.js.map +1 -0
  130. package/dist/esm/modules/resourceJump/routeLoader.d.ts +11 -0
  131. package/dist/esm/modules/resourceJump/routeLoader.js +43 -0
  132. package/dist/esm/modules/resourceJump/routeLoader.js.map +1 -0
  133. package/dist/esm/modules/resourceJump/types.d.ts +18 -0
  134. package/dist/esm/modules/resourceJump/ui/EhJumpResourceSelector.d.ts +20 -0
  135. package/dist/esm/modules/resourceJump/ui/EhJumpResourceSelector.js +209 -0
  136. package/dist/esm/modules/resourceJump/ui/EhJumpResourceSelector.js.map +1 -0
  137. package/dist/esm/modules/resourceJump/ui/JumpALink.d.ts +14 -0
  138. package/dist/esm/modules/resourceJump/ui/JumpALink.js +38 -0
  139. package/dist/esm/modules/resourceJump/ui/JumpALink.js.map +1 -0
  140. package/dist/esm/modules/resourceJump/ui/JumpMainButton.d.ts +5 -0
  141. package/dist/esm/modules/resourceJump/ui/JumpMainButton.js +66 -0
  142. package/dist/esm/modules/resourceJump/ui/JumpMainButton.js.map +1 -0
  143. package/dist/esm/modules/resourceJump/ui/ResouceJumpLayout.d.ts +5 -0
  144. package/dist/esm/modules/resourceJump/ui/ResouceJumpLayout.js +42 -0
  145. package/dist/esm/modules/resourceJump/ui/ResouceJumpLayout.js.map +1 -0
  146. package/dist/esm/plugins/builtin/pageUrl/PageUrlPluginContext.d.ts +9 -0
  147. package/dist/esm/plugins/builtin/pageUrl/PageUrlPluginContext.js +36 -0
  148. package/dist/esm/plugins/builtin/pageUrl/PageUrlPluginContext.js.map +1 -0
  149. package/dist/esm/plugins/builtin/pageUrl/pageUrlAutoCompletePlugin.d.ts +11 -0
  150. package/dist/esm/plugins/builtin/pageUrl/pageUrlAutoCompletePlugin.js +44 -0
  151. package/dist/esm/plugins/builtin/pageUrl/pageUrlAutoCompletePlugin.js.map +1 -0
  152. package/dist/esm/plugins/builtin/pageUrl/pageUrlJumpPlugin.d.ts +7 -0
  153. package/dist/esm/plugins/builtin/pageUrl/pageUrlJumpPlugin.js +29 -0
  154. package/dist/esm/plugins/builtin/pageUrl/pageUrlJumpPlugin.js.map +1 -0
  155. package/dist/esm/plugins/builtin/pageUrl/pageUrlTypes.d.ts +11 -0
  156. package/dist/esm/routeTree.gen.d.ts +142 -0
  157. package/dist/esm/routeTree.gen.js +77 -0
  158. package/dist/esm/routeTree.gen.js.map +1 -0
  159. package/dist/esm/routes/__root.d.ts +2 -0
  160. package/dist/esm/routes/__root.js +22 -0
  161. package/dist/esm/routes/__root.js.map +1 -0
  162. package/dist/esm/routes/_layout/app/$appSlug/sub/$subValue.d.ts +1 -0
  163. package/dist/esm/routes/_layout/app/$appSlug.d.ts +1 -0
  164. package/dist/esm/routes/_layout/app/_appSlug/sub/_subValue.js +18 -0
  165. package/dist/esm/routes/_layout/app/_appSlug/sub/_subValue.js.map +1 -0
  166. package/dist/esm/routes/_layout/app/_appSlug.js +18 -0
  167. package/dist/esm/routes/_layout/app/_appSlug.js.map +1 -0
  168. package/dist/esm/routes/_layout/env/$envSlug/app/$appSlug/sub/$subValue.d.ts +1 -0
  169. package/dist/esm/routes/_layout/env/$envSlug/app/$appSlug.d.ts +1 -0
  170. package/dist/esm/routes/_layout/env/$envSlug/sub/$subValue.d.ts +1 -0
  171. package/dist/esm/routes/_layout/env/$envSlug.d.ts +1 -0
  172. package/dist/esm/routes/_layout/env/_envSlug/app/_appSlug/sub/_subValue.js +20 -0
  173. package/dist/esm/routes/_layout/env/_envSlug/app/_appSlug/sub/_subValue.js.map +1 -0
  174. package/dist/esm/routes/_layout/env/_envSlug/app/_appSlug.js +18 -0
  175. package/dist/esm/routes/_layout/env/_envSlug/app/_appSlug.js.map +1 -0
  176. package/dist/esm/routes/_layout/env/_envSlug/sub/_subValue.js +18 -0
  177. package/dist/esm/routes/_layout/env/_envSlug/sub/_subValue.js.map +1 -0
  178. package/dist/esm/routes/_layout/env/_envSlug.js +18 -0
  179. package/dist/esm/routes/_layout/env/_envSlug.js.map +1 -0
  180. package/dist/esm/routes/_layout/index.d.ts +1 -0
  181. package/dist/esm/routes/_layout/index.js +18 -0
  182. package/dist/esm/routes/_layout/index.js.map +1 -0
  183. package/dist/esm/routes/_layout.d.ts +1 -0
  184. package/dist/esm/routes/_layout.js +12 -0
  185. package/dist/esm/routes/_layout.js.map +1 -0
  186. package/dist/esm/types/ehTypes.d.ts +31 -0
  187. package/dist/esm/types/slateTypes.d.ts +28 -0
  188. package/dist/esm/types/tanstackQuery.d.ts +11 -0
  189. package/dist/esm/types/types.d.ts +16 -0
  190. package/dist/esm/types/userBehaviourTypes.d.ts +76 -0
  191. package/dist/esm/types/utilityTypes.d.ts +1 -0
  192. package/dist/esm/ui/components/ActionCard.d.ts +9 -0
  193. package/dist/esm/ui/components/AppIcon.d.ts +7 -0
  194. package/dist/esm/ui/components/commandInput/EhBaseSelector.d.ts +6 -0
  195. package/dist/esm/ui/components/commandInput/EhBaseSelector.js +22 -0
  196. package/dist/esm/ui/components/commandInput/EhBaseSelector.js.map +1 -0
  197. package/dist/esm/ui/components/commandInput/types.d.ts +13 -0
  198. package/dist/esm/ui/components/contextDebug.d.ts +2 -0
  199. package/dist/esm/ui/components/contextDebug.js +12 -0
  200. package/dist/esm/ui/components/contextDebug.js.map +1 -0
  201. package/dist/esm/ui/components/controlPanel/BaseDropdownSelector.d.ts +11 -0
  202. package/dist/esm/ui/components/controlPanel/env/EnvDropdownContent.d.ts +5 -0
  203. package/dist/esm/ui/components/controlPanel/env/EnvDropdownSelector.d.ts +1 -0
  204. package/dist/esm/ui/components/error/DefaultErrorComponent.d.ts +2 -0
  205. package/dist/esm/ui/components/error/DefaultErrorComponent.js +13 -0
  206. package/dist/esm/ui/components/error/DefaultErrorComponent.js.map +1 -0
  207. package/dist/esm/ui/components/error/RooutErrorPage.d.ts +2 -0
  208. package/dist/esm/ui/components/error/RooutErrorPage.js +12 -0
  209. package/dist/esm/ui/components/error/RooutErrorPage.js.map +1 -0
  210. package/dist/esm/ui/components/footer/Footer.d.ts +1 -0
  211. package/dist/esm/ui/components/footer/Footer.js +38 -0
  212. package/dist/esm/ui/components/footer/Footer.js.map +1 -0
  213. package/dist/esm/ui/components/header/PlaygroundHeader.d.ts +1 -0
  214. package/dist/esm/ui/components/header/PlaygroundHeader.js +23 -0
  215. package/dist/esm/ui/components/header/PlaygroundHeader.js.map +1 -0
  216. package/dist/esm/ui/components/leftPanel/LeftPanel.d.ts +5 -0
  217. package/dist/esm/ui/components/quickBar/EnvQuickJumpBar.d.ts +5 -0
  218. package/dist/esm/ui/components/quickBar/EnvQuickJumpBar.js +52 -0
  219. package/dist/esm/ui/components/quickBar/EnvQuickJumpBar.js.map +1 -0
  220. package/dist/esm/ui/components/quickBar/QuickJumpBar.d.ts +5 -0
  221. package/dist/esm/ui/components/widgetPanel/AddWidgetCard.d.ts +5 -0
  222. package/dist/esm/ui/components/widgetPanel/WidgetGrid.d.ts +6 -0
  223. package/dist/esm/ui/components/widgets/CredentialsWidget.d.ts +11 -0
  224. package/dist/esm/ui/components/widgets/VersionWidget.d.ts +7 -0
  225. package/dist/esm/ui/error/NotFoundError.d.ts +1 -0
  226. package/dist/esm/ui/error/NotFoundError.js +16 -0
  227. package/dist/esm/ui/error/NotFoundError.js.map +1 -0
  228. package/dist/esm/ui/layout/Footer.d.ts +1 -0
  229. package/dist/esm/ui/layout/Header.d.ts +1 -0
  230. package/dist/esm/ui/layout/LoadingScreen.d.ts +4 -0
  231. package/dist/esm/ui/layout/LoadingScreen.js +14 -0
  232. package/dist/esm/ui/layout/LoadingScreen.js.map +1 -0
  233. package/dist/esm/ui/layout/MainLayout.d.ts +5 -0
  234. package/dist/esm/ui/layout/MainLayout.js +18 -0
  235. package/dist/esm/ui/layout/MainLayout.js.map +1 -0
  236. package/dist/esm/ui/layout/SideColumn.d.ts +1 -0
  237. package/dist/esm/ui/layout/TopLevelProviders.d.ts +5 -0
  238. package/dist/esm/ui/layout/TopLevelProviders.js +42 -0
  239. package/dist/esm/ui/layout/TopLevelProviders.js.map +1 -0
  240. package/dist/esm/ui/main/JumpTabContent.d.ts +1 -0
  241. package/dist/esm/ui/main/Tabs.d.ts +4 -0
  242. package/dist/esm/userDb/DbContext.d.ts +9 -0
  243. package/dist/esm/userDb/DbContext.js +18 -0
  244. package/dist/esm/userDb/DbContext.js.map +1 -0
  245. package/dist/esm/userDb/EhDb.d.ts +12 -0
  246. package/dist/esm/userDb/EhDb.js +20 -0
  247. package/dist/esm/userDb/EhDb.js.map +1 -0
  248. package/dist/esm/util/availabilityMatrixUtils.d.ts +12 -0
  249. package/dist/esm/util/availabilityMatrixUtils.js +57 -0
  250. package/dist/esm/util/availabilityMatrixUtils.js.map +1 -0
  251. package/dist/esm/util/createEhRouter.d.ts +7 -0
  252. package/dist/esm/util/createEhRouter.js +16 -0
  253. package/dist/esm/util/createEhRouter.js.map +1 -0
  254. package/dist/esm/util/highlightMatches.d.ts +2 -0
  255. package/dist/esm/util/highlightMatches.js +19 -0
  256. package/dist/esm/util/highlightMatches.js.map +1 -0
  257. package/dist/esm/util/reactQueryUtils.d.ts +6 -0
  258. package/dist/esm/util/reactQueryUtils.js +10 -0
  259. package/dist/esm/util/reactQueryUtils.js.map +1 -0
  260. package/dist/esm/util/route-utils.d.ts +6 -0
  261. package/dist/esm/util/route-utils.js +72 -0
  262. package/dist/esm/util/route-utils.js.map +1 -0
  263. package/dist/favicon.ico +0 -0
  264. package/dist/robots.txt +2 -0
  265. package/package.json +110 -0
  266. package/src/App.tsx +28 -0
  267. package/src/__tests__/modules/fuzzyMatchLogic/autoCompleteFilter.test.ts +185 -0
  268. package/src/__tests__/modules/fuzzyMatchLogic/features/prefixFracTokenMiddles.test.ts +23 -0
  269. package/src/__tests__/modules/fuzzyMatchLogic/fixLayout.test.ts +11 -0
  270. package/src/__tests__/modules/fuzzyMatchLogic/postFiltration.test.ts +15 -0
  271. package/src/__tests__/modules/fuzzyMatchLogic/testUtils.ts +8 -0
  272. package/src/__tests__/modules/fuzzyMatchLogic/tokenize.test.ts +65 -0
  273. package/src/__tests__/modules/resouceJump/findBestMatchByUrl.test.ts +234 -0
  274. package/src/__tests__/setupTests.tsx +0 -0
  275. package/src/__tests__/util/availabilityMatrixUtils.test.ts +61 -0
  276. package/src/api/ApiQueryMagazine.ts +15 -0
  277. package/src/api/data/useQueryBootstrapConfig.ts +17 -0
  278. package/src/api/infra/createQueryClient.ts +25 -0
  279. package/src/api/infra/trpc.ts +6 -0
  280. package/src/api/unsorted/indexDataFetcher.ts +46 -0
  281. package/src/appPropsFactory.ts +35 -0
  282. package/src/assets/env-hopper-logo.svg +114 -0
  283. package/src/components/ThemeSwitcher.tsx +22 -0
  284. package/src/components/theme-provider.tsx +8 -0
  285. package/src/components/ui/badge.tsx +37 -0
  286. package/src/components/ui/breadcrumb.tsx +128 -0
  287. package/src/components/ui/button.tsx +60 -0
  288. package/src/components/ui/card.tsx +95 -0
  289. package/src/components/ui/collapsible.tsx +31 -0
  290. package/src/components/ui/dialog.tsx +182 -0
  291. package/src/components/ui/dropdown-menu.tsx +257 -0
  292. package/src/components/ui/input.tsx +28 -0
  293. package/src/components/ui/popover.tsx +48 -0
  294. package/src/components/ui/scroll-area.tsx +58 -0
  295. package/src/components/ui/separator.tsx +26 -0
  296. package/src/components/ui/tabs.tsx +65 -0
  297. package/src/index.css +211 -0
  298. package/src/index.tsx +3 -0
  299. package/src/lib/utils.ts +7 -0
  300. package/src/main.tsx +53 -0
  301. package/src/modules/config/BootstrapConfigContext.tsx +33 -0
  302. package/src/modules/config/GlobalConfigContext.tsx +42 -0
  303. package/src/modules/environment/ApiQueryMagazineEnvironment.ts +12 -0
  304. package/src/modules/environment/EnvironmentContext.tsx +115 -0
  305. package/src/modules/environment/types.ts +4 -0
  306. package/src/modules/environment/ui/EhEnvSelector.tsx +191 -0
  307. package/src/modules/fuzzyMatchLogic/autoCompleteFilter.ts +186 -0
  308. package/src/modules/fuzzyMatchLogic/features/prefixFrac.ts +13 -0
  309. package/src/modules/fuzzyMatchLogic/features/prefixFracTokenMiddles.ts +29 -0
  310. package/src/modules/fuzzyMatchLogic/fixLayout.ts +24 -0
  311. package/src/modules/fuzzyMatchLogic/postFiltration.ts +35 -0
  312. package/src/modules/fuzzyMatchLogic/scoring.ts +42 -0
  313. package/src/modules/fuzzyMatchLogic/tokenize.ts +32 -0
  314. package/src/modules/fuzzyMatchLogic/types.ts +33 -0
  315. package/src/modules/fuzzyMatchLogic/utils.ts +18 -0
  316. package/src/modules/pluginCore/PluginManagerContext.tsx +107 -0
  317. package/src/modules/pluginCore/makePluginManagerContext.ts +35 -0
  318. package/src/modules/pluginCore/types.ts +54 -0
  319. package/src/modules/resourceJump/ApiQueryMagazineResourceJump.ts +31 -0
  320. package/src/modules/resourceJump/ResourceJumpContext.tsx +142 -0
  321. package/src/modules/resourceJump/buildJumpUrl.ts +40 -0
  322. package/src/modules/resourceJump/findBestMatchByUrl.ts +121 -0
  323. package/src/modules/resourceJump/helpers.ts +117 -0
  324. package/src/modules/resourceJump/routeLoader.ts +58 -0
  325. package/src/modules/resourceJump/types.ts +21 -0
  326. package/src/modules/resourceJump/ui/EhJumpResourceSelector.tsx +259 -0
  327. package/src/modules/resourceJump/ui/JumpALink.tsx +54 -0
  328. package/src/modules/resourceJump/ui/JumpMainButton.tsx +97 -0
  329. package/src/modules/resourceJump/ui/ResouceJumpLayout.tsx +73 -0
  330. package/src/plugins/builtin/pageUrl/PageUrlPluginContext.tsx +55 -0
  331. package/src/plugins/builtin/pageUrl/pageUrlAutoCompletePlugin.ts +66 -0
  332. package/src/plugins/builtin/pageUrl/pageUrlJumpPlugin.ts +36 -0
  333. package/src/plugins/builtin/pageUrl/pageUrlTypes.ts +17 -0
  334. package/src/routeTree.gen.ts +249 -0
  335. package/src/routes/__root.tsx +23 -0
  336. package/src/routes/_layout/app/$appSlug/sub/$subValue.tsx +15 -0
  337. package/src/routes/_layout/app/$appSlug.tsx +15 -0
  338. package/src/routes/_layout/env/$envSlug/app/$appSlug/sub/$subValue.tsx +17 -0
  339. package/src/routes/_layout/env/$envSlug/app/$appSlug.tsx +15 -0
  340. package/src/routes/_layout/env/$envSlug/sub/$subValue.tsx +15 -0
  341. package/src/routes/_layout/env/$envSlug.tsx +15 -0
  342. package/src/routes/_layout/index.tsx +16 -0
  343. package/src/routes/_layout.tsx +9 -0
  344. package/src/types/ehTypes.ts +40 -0
  345. package/src/types/slateTypes.ts +22 -0
  346. package/src/types/tanstackQuery.ts +14 -0
  347. package/src/types/types.ts +20 -0
  348. package/src/types/userBehaviourTypes.ts +100 -0
  349. package/src/types/utilityTypes.ts +1 -0
  350. package/src/types/vite-env.d.ts +1 -0
  351. package/src/ui/components/ActionCard.tsx +30 -0
  352. package/src/ui/components/AppIcon.tsx +48 -0
  353. package/src/ui/components/commandInput/EhBaseSelector.tsx +51 -0
  354. package/src/ui/components/commandInput/types.ts +22 -0
  355. package/src/ui/components/contextDebug.tsx +13 -0
  356. package/src/ui/components/controlPanel/BaseDropdownSelector.tsx +163 -0
  357. package/src/ui/components/controlPanel/env/EnvDropdownContent.tsx +114 -0
  358. package/src/ui/components/controlPanel/env/EnvDropdownSelector.tsx +21 -0
  359. package/src/ui/components/error/DefaultErrorComponent.tsx +38 -0
  360. package/src/ui/components/error/RooutErrorPage.tsx +10 -0
  361. package/src/ui/components/footer/Footer.tsx +32 -0
  362. package/src/ui/components/header/PlaygroundHeader.tsx +26 -0
  363. package/src/ui/components/leftPanel/LeftPanel.tsx +194 -0
  364. package/src/ui/components/quickBar/EnvQuickJumpBar.tsx +65 -0
  365. package/src/ui/components/quickBar/QuickJumpBar.tsx +101 -0
  366. package/src/ui/components/widgetPanel/AddWidgetCard.tsx +17 -0
  367. package/src/ui/components/widgetPanel/WidgetGrid.tsx +18 -0
  368. package/src/ui/components/widgets/CredentialsWidget.tsx +55 -0
  369. package/src/ui/components/widgets/VersionWidget.tsx +29 -0
  370. package/src/ui/error/NotFoundError.tsx +13 -0
  371. package/src/ui/layout/Footer.tsx +16 -0
  372. package/src/ui/layout/Header.tsx +14 -0
  373. package/src/ui/layout/LoadingScreen.tsx +17 -0
  374. package/src/ui/layout/MainLayout.tsx +27 -0
  375. package/src/ui/layout/SideColumn.tsx +3 -0
  376. package/src/ui/layout/TopLevelProviders.tsx +52 -0
  377. package/src/ui/main/JumpTabContent.tsx +12 -0
  378. package/src/ui/main/Tabs.tsx +29 -0
  379. package/src/userDb/DbContext.tsx +22 -0
  380. package/src/userDb/EhDb.ts +22 -0
  381. package/src/util/availabilityMatrixUtils.ts +80 -0
  382. package/src/util/createEhRouter.ts +20 -0
  383. package/src/util/highlightMatches.tsx +29 -0
  384. package/src/util/reactQueryUtils.ts +12 -0
  385. package/src/util/route-utils.ts +71 -0
@@ -0,0 +1,48 @@
1
+ import { useState } from 'react'
2
+ import { Package } from 'lucide-react'
3
+ import type { EhAppIndexed } from '@env-hopper/backend-core'
4
+ import { useBootstrapConfig } from '~/modules/config/BootstrapConfigContext'
5
+
6
+ interface AppIconProps {
7
+ app: EhAppIndexed
8
+ className?: string
9
+ }
10
+
11
+ export function AppIcon({ app, className = 'w-4 h-4' }: AppIconProps) {
12
+ const { appsMeta } = useBootstrapConfig()
13
+ const [imageError, setImageError] = useState(false)
14
+
15
+ const handleImageError = () => {
16
+ setImageError(true)
17
+ }
18
+
19
+ // Determine icon source with fallback chain:
20
+ // 1. app.iconName -> /static/icon/${iconName}
21
+ // 2. indexData.appsMeta.defaultIcon -> /static/icon/${defaultIcon}
22
+ // 3. Package icon (default)
23
+ const getIconSrc = (): string | null => {
24
+ if (app.iconName) {
25
+ return `/static/icon/${app.iconName}`
26
+ }
27
+ if (appsMeta.defaultIcon) {
28
+ return `/static/icon/${appsMeta.defaultIcon}`
29
+ }
30
+ return null
31
+ }
32
+
33
+ const iconSrc = getIconSrc()
34
+
35
+ // Use icon if available and no error, otherwise fallback to Package icon
36
+ if (iconSrc && !imageError) {
37
+ return (
38
+ <img
39
+ src={iconSrc}
40
+ alt={`${app.displayName} icon`}
41
+ className={className}
42
+ onError={handleImageError}
43
+ />
44
+ )
45
+ }
46
+
47
+ return <Package className={className} />
48
+ }
@@ -0,0 +1,51 @@
1
+ import React from 'react'
2
+ import { Popover } from '~/components/ui/popover'
3
+ import { cn } from '~/lib/utils'
4
+
5
+ export function EhBaseSelectorRoot({
6
+ className,
7
+ ...props
8
+ }: React.ComponentPropsWithoutRef<'div'>) {
9
+ return (
10
+ <div
11
+ className={cn(
12
+ `flex flex-col gap-1 duration-100 transition-all`,
13
+ className,
14
+ )}
15
+ {...props}
16
+ />
17
+ )
18
+ }
19
+
20
+ export function EhBaseSelectorLabel({
21
+ className,
22
+ ...props
23
+ }: React.ComponentPropsWithoutRef<'div'>) {
24
+ return (
25
+ <div
26
+ className={cn(`text-secondary-foreground/50 text-sm`, className)}
27
+ {...props}
28
+ />
29
+ )
30
+ }
31
+
32
+ export function EhBaseSelectorPopover({
33
+ ...props
34
+ }: React.ComponentPropsWithoutRef<typeof Popover>) {
35
+ return <Popover {...props} />
36
+ }
37
+
38
+ export function EhBaseSelectorCommand({
39
+ className,
40
+ ...props
41
+ }: React.ComponentPropsWithoutRef<'div'>) {
42
+ return (
43
+ <div
44
+ className={cn(
45
+ 'hover:cursor-text hover:bg-accent hover:border-neutral-950 rounded-sm border-1 p-3 group',
46
+ className,
47
+ )}
48
+ {...props}
49
+ />
50
+ )
51
+ }
@@ -0,0 +1,22 @@
1
+ import type { BaseAutoCompletableItem } from '~/modules/pluginCore/types'
2
+
3
+ export interface SpecialAutoCompleteItem {
4
+ type: 'special'
5
+ slug: 'specia-all-apps'
6
+ }
7
+
8
+ export function isSpecialAutoCompleteItem(
9
+ item: BaseAutoCompletableItem | SpecialAutoCompleteItem | null | undefined,
10
+ ): item is SpecialAutoCompleteItem {
11
+ return item?.type === 'special'
12
+ }
13
+
14
+ export type AutoCompleteItem = BaseAutoCompletableItem | SpecialAutoCompleteItem
15
+
16
+ export interface AppAutoCompleteAmend {
17
+ isChild?: boolean
18
+ }
19
+
20
+ export interface AutoCompleteContext {
21
+ searchString: string
22
+ }
@@ -0,0 +1,13 @@
1
+ import { useEnvironmentContext } from '~/modules/environment/EnvironmentContext'
2
+ import { useResourceJumpContext } from '~/modules/resourceJump/ResourceJumpContext'
3
+
4
+ const ContextDebug: React.FC = () => {
5
+ const { currentResourceJump } = useResourceJumpContext()
6
+ const { currentEnv } = useEnvironmentContext()
7
+
8
+ return (
9
+ <pre>{JSON.stringify({ currentResourceJump, currentEnv }, null, 2)}</pre>
10
+ )
11
+ }
12
+
13
+ export default ContextDebug
@@ -0,0 +1,163 @@
1
+ import React, { useState } from 'react'
2
+ import { useCombobox } from 'downshift'
3
+ import type { ReactNode } from 'react'
4
+ import type { BaseDropdownContentProps } from '~/types/ehTypes'
5
+ import { Button } from '~/components/ui/button'
6
+ import { Input } from '~/components/ui/input'
7
+
8
+ interface BaseDropdownSelectorProps {
9
+ value?: string
10
+ placeholder: string
11
+ onSelect: (value: string) => void
12
+ children: (props: BaseDropdownContentProps) => ReactNode
13
+ className?: string
14
+ }
15
+
16
+ interface DropdownInputProps {
17
+ value?: string
18
+ placeholder: string
19
+ onSelect: (value: string) => void
20
+ onClose: () => void
21
+ children: (props: BaseDropdownContentProps) => ReactNode
22
+ className?: string
23
+ }
24
+
25
+ function DropdownInput({
26
+ value,
27
+ placeholder,
28
+ onSelect,
29
+ onClose,
30
+ children,
31
+ className = '',
32
+ }: DropdownInputProps) {
33
+ const [isUntouched, setIsUntouched] = useState(true)
34
+ const [inputValue, setInputValue] = useState(value || '')
35
+
36
+ const {
37
+ isOpen,
38
+ getMenuProps,
39
+ getInputProps,
40
+ getItemProps,
41
+ highlightedIndex,
42
+ // closeMenu,
43
+ } = useCombobox({
44
+ inputValue,
45
+ isOpen: true, // Always open when this component is rendered
46
+ onInputValueChange: ({ inputValue: newInputValue }) => {
47
+ console.log('✏️ Input value changed:', newInputValue)
48
+ setInputValue(newInputValue || '')
49
+ setIsUntouched(false)
50
+ },
51
+ // onSelectedItemChange: ({ selectedItem: changedSelectedItem }) => {
52
+ // if (changedSelectedItem !== null) {
53
+ // console.log('✅ Item selected:', changedSelectedItem)
54
+ // onSelect(changedSelectedItem)
55
+ // closeMenu()
56
+ // onClose()
57
+ // }
58
+ // },
59
+ onIsOpenChange: ({ isOpen: newIsOpen }) => {
60
+ console.log('📋 Combobox open change:', newIsOpen)
61
+ if (newIsOpen === false) {
62
+ console.log('📋 Combobox closed, closing dropdown')
63
+ onClose()
64
+ }
65
+ },
66
+ items: [], // We'll handle items in the children component
67
+ })
68
+
69
+ // Handle input blur
70
+ const handleInputBlur = (e: React.FocusEvent<HTMLInputElement>) => {
71
+ console.log('🔄 Input blur', {
72
+ relatedTarget: e.relatedTarget,
73
+ currentTarget: e.currentTarget,
74
+ target: e.target,
75
+ })
76
+ // Let Downshift handle the blur behavior
77
+ }
78
+
79
+ // Handle input focus
80
+ const handleInputFocus = (e: React.FocusEvent<HTMLInputElement>) => {
81
+ console.log('🎯 Input focused', e.target)
82
+ }
83
+
84
+ return (
85
+ <div className="relative">
86
+ <Input
87
+ {...getInputProps({
88
+ onBlur: handleInputBlur,
89
+ onFocus: handleInputFocus,
90
+ placeholder,
91
+ className: `min-w-[200px] ${className}`,
92
+ autoFocus: true,
93
+ })}
94
+ />
95
+ {isOpen && (
96
+ <div
97
+ {...getMenuProps()}
98
+ className="absolute top-full left-0 mt-1 min-w-[300px] bg-popover border border-border rounded-md shadow-md z-50"
99
+ >
100
+ {children({
101
+ searchValue: inputValue,
102
+ onSelect: (selectedValue: string) => {
103
+ console.log('✅ Child component selected:', selectedValue)
104
+ onSelect(selectedValue)
105
+ onClose()
106
+ },
107
+ getMenuProps,
108
+ getItemProps,
109
+ highlightedIndex,
110
+ isOpen,
111
+ isUntouched,
112
+ })}
113
+ </div>
114
+ )}
115
+ </div>
116
+ )
117
+ }
118
+
119
+ export function BaseDropdownSelector({
120
+ value,
121
+ placeholder,
122
+ onSelect,
123
+ children,
124
+ className = '',
125
+ }: BaseDropdownSelectorProps) {
126
+ const [isInputMode, setIsInputMode] = useState(false)
127
+
128
+ // Handle button click - switch to input mode
129
+ const handleButtonClick = () => {
130
+ console.log('🔘 Button clicked')
131
+ setIsInputMode(true)
132
+ }
133
+
134
+ const handleClose = () => {
135
+ setIsInputMode(false)
136
+ }
137
+
138
+ if (isInputMode) {
139
+ return (
140
+ <DropdownInput
141
+ value={value}
142
+ placeholder={placeholder}
143
+ onSelect={onSelect}
144
+ onClose={handleClose}
145
+ className={className}
146
+ >
147
+ {children}
148
+ </DropdownInput>
149
+ )
150
+ }
151
+
152
+ return (
153
+ <Button
154
+ variant="ghost"
155
+ size="default"
156
+ className={`px-4 font-normal ${className} min-w-[15ch]`}
157
+ onClick={handleButtonClick}
158
+ type="button"
159
+ >
160
+ {value || placeholder}
161
+ </Button>
162
+ )
163
+ }
@@ -0,0 +1,114 @@
1
+ import { Server } from 'lucide-react'
2
+ import type { BaseDropdownContentProps, EhEnvIndexed } from '~/types/ehTypes'
3
+ import { Badge } from '~/components/ui/badge'
4
+ import { useBootstrapConfig } from '~/modules/config/BootstrapConfigContext'
5
+
6
+ interface EnvDropdownContentProps extends BaseDropdownContentProps {}
7
+
8
+ export function EnvDropdownContent({
9
+ searchValue = '',
10
+ onSelect,
11
+ getItemProps,
12
+ highlightedIndex = -1,
13
+ isUntouched,
14
+ }: EnvDropdownContentProps) {
15
+ const indexData = useBootstrapConfig()
16
+ const listEnvs = Object.values(indexData.envs)
17
+
18
+ // Filter environments based on search value
19
+ const filteredEnvs = listEnvs.filter((env) => {
20
+ if (isUntouched) {
21
+ return true
22
+ }
23
+ return (
24
+ env.displayName.toLowerCase().includes(searchValue.toLowerCase()) ||
25
+ env.slug.toLowerCase().includes(searchValue.toLowerCase())
26
+ )
27
+ })
28
+
29
+ // Get environment type for styling
30
+ const getEnvType = (slug: string) => {
31
+ if (slug.includes('prod')) return 'production'
32
+ if (slug.includes('preprod') || slug.includes('staging')) return 'staging'
33
+ if (slug.includes('dev') || slug.includes('int')) return 'development'
34
+ return 'other'
35
+ }
36
+
37
+ // Get badge variant based on environment type
38
+ const getBadgeVariant = (type: string) => {
39
+ switch (type) {
40
+ case 'production':
41
+ return 'destructive'
42
+ case 'staging':
43
+ return 'secondary'
44
+ case 'development':
45
+ return 'default'
46
+ default:
47
+ return 'outline'
48
+ }
49
+ }
50
+
51
+ // Get environment color
52
+ const getEnvColor = (slug: string) => {
53
+ if (slug.startsWith('cross')) return 'text-blue-600'
54
+ if (slug.startsWith('preprod')) return 'text-orange-600'
55
+ if (slug.startsWith('g64')) return 'text-violet-600'
56
+ return 'text-gray-600'
57
+ }
58
+
59
+ const handleEnvSelect = (env: EhEnvIndexed) => {
60
+ if (onSelect) {
61
+ onSelect(env.slug)
62
+ }
63
+ }
64
+
65
+ if (filteredEnvs.length === 0) {
66
+ return (
67
+ <div className="p-4 text-center text-muted-foreground">
68
+ No environments found for "{searchValue}"
69
+ </div>
70
+ )
71
+ }
72
+
73
+ return (
74
+ <div className="max-h-[300px] overflow-y-auto">
75
+ {filteredEnvs.map((env, index) => {
76
+ const envType = getEnvType(env.slug)
77
+ const isHighlighted = highlightedIndex === index
78
+
79
+ return (
80
+ <div
81
+ key={env.slug}
82
+ {...(getItemProps
83
+ ? getItemProps({
84
+ item: env.slug,
85
+ index,
86
+ onClick: () => handleEnvSelect(env),
87
+ })
88
+ : {})}
89
+ className={`flex items-center justify-between p-3 cursor-pointer transition-colors ${
90
+ isHighlighted
91
+ ? 'bg-accent text-accent-foreground'
92
+ : 'hover:bg-accent hover:text-accent-foreground'
93
+ }`}
94
+ >
95
+ <div className="flex items-center gap-3">
96
+ <Server className="h-4 w-4 text-muted-foreground" />
97
+ <div className="flex flex-col">
98
+ <span className={`font-medium ${getEnvColor(env.slug)}`}>
99
+ {env.displayName}
100
+ </span>
101
+ <span className="text-xs text-muted-foreground">
102
+ {env.slug}
103
+ </span>
104
+ </div>
105
+ </div>
106
+ <Badge variant={getBadgeVariant(envType)} className="text-xs">
107
+ {envType}
108
+ </Badge>
109
+ </div>
110
+ )
111
+ })}
112
+ </div>
113
+ )
114
+ }
@@ -0,0 +1,21 @@
1
+ import { BaseDropdownSelector } from '../BaseDropdownSelector'
2
+ import { EnvDropdownContent } from './EnvDropdownContent'
3
+ import { useEnvironmentContext } from '~/modules/environment/EnvironmentContext'
4
+
5
+ export function EnvDropdownSelector() {
6
+ const { currentEnv, setCurrentEnv } = useEnvironmentContext()
7
+
8
+ const handleSelect = (envSlug: string) => {
9
+ setCurrentEnv(envSlug)
10
+ }
11
+
12
+ return (
13
+ <BaseDropdownSelector
14
+ value={currentEnv?.displayName}
15
+ placeholder="Select Env"
16
+ onSelect={handleSelect}
17
+ >
18
+ {(props) => <EnvDropdownContent {...props} />}
19
+ </BaseDropdownSelector>
20
+ )
21
+ }
@@ -0,0 +1,38 @@
1
+ import type { ErrorComponentProps } from '@tanstack/react-router'
2
+
3
+ export function DefaultErrorComponent({ error }: ErrorComponentProps) {
4
+ // const { data: config } = useQuery(ApiQueryMagazine.getConfig());
5
+ // const [localAppVersion] = useLocalStorage(
6
+ // LOCAL_STORAGE_KEY_VERSION,
7
+ // undefined,
8
+ // );
9
+ // const { isDegraded } = useEhServerSync();
10
+ // const configAppVersion = config?.appVersion;
11
+ // const [somethingIsWrong, setSomethingIsWrong] = useState(false);
12
+ // useEffect(() => {
13
+ // const t = setTimeout(() => {
14
+ // setSomethingIsWrong(true);
15
+ // }, 25000);
16
+ // return () => {
17
+ // clearTimeout(t);
18
+ // };
19
+ // }, []);
20
+
21
+ // const isProbablyWillResolvedAfterUpdate =
22
+ // localAppVersion !== undefined &&
23
+ // configAppVersion !== undefined &&
24
+ // localAppVersion !== configAppVersion &&
25
+ // !somethingIsWrong &&
26
+ // !isDegraded;
27
+
28
+ return (
29
+ <>
30
+ <div className={'mt-8 text-center !max-w-none'} role="alert">
31
+ <h1>Oops!</h1>
32
+ <p>Sorry, an unexpected error has occurred. :( </p>
33
+ <pre className={'text-left mt-8 text-sm'}>{<i>{error.message}</i>}</pre>
34
+ <pre className={'text-left mt-8 text-sm'}>{<i>{error.stack}</i>}</pre>
35
+ </div>
36
+ </>
37
+ )
38
+ }
@@ -0,0 +1,10 @@
1
+ import { DefaultErrorComponent } from './DefaultErrorComponent'
2
+ import type { ErrorComponentProps } from '@tanstack/react-router'
3
+
4
+ export function RooutErrorPage(props: ErrorComponentProps) {
5
+ return (
6
+ <div className="p-5">
7
+ <DefaultErrorComponent {...props} />;
8
+ </div>
9
+ )
10
+ }
@@ -0,0 +1,32 @@
1
+ import { ExternalLink } from 'lucide-react'
2
+
3
+ export function Footer() {
4
+ return (
5
+ <footer className="mt-auto border-t border-border bg-background/50 backdrop-blur-sm">
6
+ <div className="max-w-7xl mx-auto px-6 py-4">
7
+ <div className="flex items-center justify-center text-sm text-muted-foreground">
8
+ <span>Built with</span>
9
+ <a
10
+ href="https://ui.shadcn.com/docs/components"
11
+ target="_blank"
12
+ rel="noopener noreferrer"
13
+ className="ml-1 inline-flex items-center gap-1 text-primary hover:text-primary/80 transition-colors font-medium"
14
+ >
15
+ shadcn/ui
16
+ <ExternalLink className="h-3 w-3" />
17
+ </a>
18
+ <span className="mx-2">•</span>
19
+ <a
20
+ href="https://lucide.dev/icons/expand"
21
+ target="_blank"
22
+ rel="noopener noreferrer"
23
+ className="inline-flex items-center gap-1 text-primary hover:text-primary/80 transition-colors font-medium"
24
+ >
25
+ Lucide Icons
26
+ <ExternalLink className="h-3 w-3" />
27
+ </a>
28
+ </div>
29
+ </div>
30
+ </footer>
31
+ )
32
+ }
@@ -0,0 +1,26 @@
1
+ import { Button } from '~/components/ui/button'
2
+ import { ThemeSwitcher } from '~/components/ThemeSwitcher'
3
+ import EnvHopperLogo from '~/assets/env-hopper-logo.svg?react'
4
+
5
+ export function PlaygroundHeader() {
6
+ return (
7
+ <div className="flex items-center justify-between mb-4">
8
+ {/* Left side: Logo + Mode */}
9
+ <div className="flex items-center gap-4">
10
+ <div className="flex items-center gap-2">
11
+ <EnvHopperLogo className="h-16 w-16" />
12
+ <h1 className="text-lg font-bold">Env‑Hopper</h1>
13
+ </div>
14
+ <span className="text-xs text-muted-foreground">Jump</span>
15
+ </div>
16
+
17
+ {/* Right side: Theme switcher + Login */}
18
+ <div className="flex items-center gap-3">
19
+ <ThemeSwitcher />
20
+ <Button size="sm" variant={'outline'}>
21
+ Login
22
+ </Button>
23
+ </div>
24
+ </div>
25
+ )
26
+ }