@medialane/ui 0.120.0 → 0.121.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (448) hide show
  1. package/dist/components/action-button.cjs.map +1 -1
  2. package/dist/components/action-button.d.cts +0 -9
  3. package/dist/components/action-button.d.ts +0 -9
  4. package/dist/components/action-button.js.map +1 -1
  5. package/dist/components/action-dialog.cjs.map +1 -1
  6. package/dist/components/action-dialog.d.cts +0 -1
  7. package/dist/components/action-dialog.d.ts +0 -1
  8. package/dist/components/action-dialog.js.map +1 -1
  9. package/dist/components/activity-card.cjs.map +1 -1
  10. package/dist/components/activity-card.d.cts +0 -3
  11. package/dist/components/activity-card.d.ts +0 -3
  12. package/dist/components/activity-card.js.map +1 -1
  13. package/dist/components/activity-row.cjs.map +1 -1
  14. package/dist/components/activity-row.d.cts +0 -1
  15. package/dist/components/activity-row.d.ts +0 -1
  16. package/dist/components/activity-row.js.map +1 -1
  17. package/dist/components/activity-ticker.cjs.map +1 -1
  18. package/dist/components/activity-ticker.d.cts +0 -1
  19. package/dist/components/activity-ticker.d.ts +0 -1
  20. package/dist/components/activity-ticker.js.map +1 -1
  21. package/dist/components/address-display.cjs.map +1 -1
  22. package/dist/components/address-display.d.cts +0 -1
  23. package/dist/components/address-display.d.ts +0 -1
  24. package/dist/components/address-display.js.map +1 -1
  25. package/dist/components/animated-token-media.cjs.map +1 -1
  26. package/dist/components/animated-token-media.d.cts +0 -10
  27. package/dist/components/animated-token-media.d.ts +0 -10
  28. package/dist/components/animated-token-media.js.map +1 -1
  29. package/dist/components/asset-card.cjs.map +1 -1
  30. package/dist/components/asset-card.d.cts +0 -29
  31. package/dist/components/asset-card.d.ts +0 -29
  32. package/dist/components/asset-card.js.map +1 -1
  33. package/dist/components/asset-collection-bar.cjs.map +1 -1
  34. package/dist/components/asset-collection-bar.d.cts +0 -1
  35. package/dist/components/asset-collection-bar.d.ts +0 -1
  36. package/dist/components/asset-collection-bar.js.map +1 -1
  37. package/dist/components/asset-license-summary.cjs.map +1 -1
  38. package/dist/components/asset-license-summary.d.cts +0 -8
  39. package/dist/components/asset-license-summary.d.ts +0 -8
  40. package/dist/components/asset-license-summary.js.map +1 -1
  41. package/dist/components/asset-lightbox.cjs.map +1 -1
  42. package/dist/components/asset-lightbox.js.map +1 -1
  43. package/dist/components/asset-marketplace-panel.cjs.map +1 -1
  44. package/dist/components/asset-marketplace-panel.d.cts +0 -13
  45. package/dist/components/asset-marketplace-panel.d.ts +0 -13
  46. package/dist/components/asset-marketplace-panel.js.map +1 -1
  47. package/dist/components/asset-markets-tab.cjs.map +1 -1
  48. package/dist/components/asset-markets-tab.js.map +1 -1
  49. package/dist/components/asset-overview-content.cjs.map +1 -1
  50. package/dist/components/asset-overview-content.d.cts +0 -7
  51. package/dist/components/asset-overview-content.d.ts +0 -7
  52. package/dist/components/asset-overview-content.js.map +1 -1
  53. package/dist/components/asset-picker-cell.cjs.map +1 -1
  54. package/dist/components/asset-picker-cell.d.cts +0 -4
  55. package/dist/components/asset-picker-cell.d.ts +0 -4
  56. package/dist/components/asset-picker-cell.js.map +1 -1
  57. package/dist/components/asset-picker.cjs.map +1 -1
  58. package/dist/components/asset-picker.d.cts +0 -4
  59. package/dist/components/asset-picker.d.ts +0 -4
  60. package/dist/components/asset-picker.js.map +1 -1
  61. package/dist/components/asset-search-picker.cjs.map +1 -1
  62. package/dist/components/asset-search-picker.d.cts +0 -2
  63. package/dist/components/asset-search-picker.d.ts +0 -2
  64. package/dist/components/asset-search-picker.js.map +1 -1
  65. package/dist/components/asset-top-sections.cjs.map +1 -1
  66. package/dist/components/asset-top-sections.d.cts +0 -18
  67. package/dist/components/asset-top-sections.d.ts +0 -18
  68. package/dist/components/asset-top-sections.js.map +1 -1
  69. package/dist/components/asset-utility-icons.cjs.map +1 -1
  70. package/dist/components/asset-utility-icons.d.cts +0 -5
  71. package/dist/components/asset-utility-icons.d.ts +0 -5
  72. package/dist/components/asset-utility-icons.js.map +1 -1
  73. package/dist/components/claim-rail.cjs.map +1 -1
  74. package/dist/components/claim-rail.d.cts +0 -9
  75. package/dist/components/claim-rail.d.ts +0 -9
  76. package/dist/components/claim-rail.js.map +1 -1
  77. package/dist/components/club-owner-actions.cjs.map +1 -1
  78. package/dist/components/club-owner-actions.d.cts +0 -2
  79. package/dist/components/club-owner-actions.d.ts +0 -2
  80. package/dist/components/club-owner-actions.js.map +1 -1
  81. package/dist/components/coin-card.cjs.map +1 -1
  82. package/dist/components/coin-card.d.cts +0 -2
  83. package/dist/components/coin-card.d.ts +0 -2
  84. package/dist/components/coin-card.js.map +1 -1
  85. package/dist/components/coin-launch-preview.cjs.map +1 -1
  86. package/dist/components/coin-launch-preview.d.cts +0 -7
  87. package/dist/components/coin-launch-preview.d.ts +0 -7
  88. package/dist/components/coin-launch-preview.js.map +1 -1
  89. package/dist/components/coins-explorer.cjs.map +1 -1
  90. package/dist/components/coins-explorer.d.cts +0 -1
  91. package/dist/components/coins-explorer.d.ts +0 -1
  92. package/dist/components/coins-explorer.js.map +1 -1
  93. package/dist/components/collection-card.cjs.map +1 -1
  94. package/dist/components/collection-card.d.cts +0 -2
  95. package/dist/components/collection-card.d.ts +0 -2
  96. package/dist/components/collection-card.js.map +1 -1
  97. package/dist/components/collection-filters.cjs.map +1 -1
  98. package/dist/components/collection-filters.js.map +1 -1
  99. package/dist/components/collection-hero-banner.cjs.map +1 -1
  100. package/dist/components/collection-hero-banner.d.cts +0 -7
  101. package/dist/components/collection-hero-banner.d.ts +0 -7
  102. package/dist/components/collection-hero-banner.js.map +1 -1
  103. package/dist/components/collection-traits-tab.cjs.map +1 -1
  104. package/dist/components/collection-traits-tab.js.map +1 -1
  105. package/dist/components/community-rewards-section.cjs.map +1 -1
  106. package/dist/components/community-rewards-section.js.map +1 -1
  107. package/dist/components/create-form-primitives.cjs.map +1 -1
  108. package/dist/components/create-form-primitives.d.cts +0 -2
  109. package/dist/components/create-form-primitives.d.ts +0 -2
  110. package/dist/components/create-form-primitives.js.map +1 -1
  111. package/dist/components/creation-record.cjs.map +1 -1
  112. package/dist/components/creation-record.d.cts +0 -5
  113. package/dist/components/creation-record.d.ts +0 -5
  114. package/dist/components/creation-record.js.map +1 -1
  115. package/dist/components/creator-airdrop-banner.cjs.map +1 -1
  116. package/dist/components/creator-airdrop-banner.d.cts +0 -1
  117. package/dist/components/creator-airdrop-banner.d.ts +0 -1
  118. package/dist/components/creator-airdrop-banner.js.map +1 -1
  119. package/dist/components/creator-analytics.cjs.map +1 -1
  120. package/dist/components/creator-analytics.js.map +1 -1
  121. package/dist/components/creator-score-inline.cjs.map +1 -1
  122. package/dist/components/creator-score-inline.d.cts +0 -5
  123. package/dist/components/creator-score-inline.d.ts +0 -5
  124. package/dist/components/creator-score-inline.js.map +1 -1
  125. package/dist/components/cta-card-grid.cjs.map +1 -1
  126. package/dist/components/cta-card-grid.d.cts +0 -2
  127. package/dist/components/cta-card-grid.d.ts +0 -2
  128. package/dist/components/cta-card-grid.js.map +1 -1
  129. package/dist/components/currency-icon.cjs.map +1 -1
  130. package/dist/components/currency-icon.d.cts +0 -5
  131. package/dist/components/currency-icon.d.ts +0 -5
  132. package/dist/components/currency-icon.js.map +1 -1
  133. package/dist/components/discover-collections-strip.cjs.map +1 -1
  134. package/dist/components/discover-collections-strip.d.cts +0 -4
  135. package/dist/components/discover-collections-strip.d.ts +0 -4
  136. package/dist/components/discover-collections-strip.js.map +1 -1
  137. package/dist/components/discover-creators-strip.cjs.map +1 -1
  138. package/dist/components/discover-creators-strip.js.map +1 -1
  139. package/dist/components/discover-feed-section.cjs.map +1 -1
  140. package/dist/components/discover-feed-section.d.cts +0 -11
  141. package/dist/components/discover-feed-section.d.ts +0 -11
  142. package/dist/components/discover-feed-section.js.map +1 -1
  143. package/dist/components/discover-hero.cjs.map +1 -1
  144. package/dist/components/discover-hero.js.map +1 -1
  145. package/dist/components/drop-countdown.cjs.map +1 -1
  146. package/dist/components/drop-countdown.js.map +1 -1
  147. package/dist/components/dual-price.cjs +0 -6
  148. package/dist/components/dual-price.cjs.map +1 -1
  149. package/dist/components/dual-price.d.cts +0 -30
  150. package/dist/components/dual-price.d.ts +0 -30
  151. package/dist/components/dual-price.js +0 -6
  152. package/dist/components/dual-price.js.map +1 -1
  153. package/dist/components/empty-or-error.cjs.map +1 -1
  154. package/dist/components/empty-or-error.d.cts +0 -1
  155. package/dist/components/empty-or-error.d.ts +0 -1
  156. package/dist/components/empty-or-error.js.map +1 -1
  157. package/dist/components/gradient-button.cjs.map +1 -1
  158. package/dist/components/gradient-button.d.cts +0 -13
  159. package/dist/components/gradient-button.d.ts +0 -13
  160. package/dist/components/gradient-button.js.map +1 -1
  161. package/dist/components/help-icon.cjs.map +1 -1
  162. package/dist/components/help-icon.d.cts +0 -4
  163. package/dist/components/help-icon.d.ts +0 -4
  164. package/dist/components/help-icon.js.map +1 -1
  165. package/dist/components/hero-slider.cjs.map +1 -1
  166. package/dist/components/hero-slider.d.cts +0 -1
  167. package/dist/components/hero-slider.d.ts +0 -1
  168. package/dist/components/hero-slider.js.map +1 -1
  169. package/dist/components/ip-type-badge.cjs.map +1 -1
  170. package/dist/components/ip-type-badge.d.cts +0 -2
  171. package/dist/components/ip-type-badge.d.ts +0 -2
  172. package/dist/components/ip-type-badge.js.map +1 -1
  173. package/dist/components/ip-type-display.cjs.map +1 -1
  174. package/dist/components/ip-type-display.js.map +1 -1
  175. package/dist/components/ip-type-fields.cjs.map +1 -1
  176. package/dist/components/ip-type-fields.d.cts +0 -2
  177. package/dist/components/ip-type-fields.d.ts +0 -2
  178. package/dist/components/ip-type-fields.js.map +1 -1
  179. package/dist/components/launchpad-filter-bar.cjs.map +1 -1
  180. package/dist/components/launchpad-filter-bar.d.cts +0 -8
  181. package/dist/components/launchpad-filter-bar.d.ts +0 -8
  182. package/dist/components/launchpad-filter-bar.js.map +1 -1
  183. package/dist/components/launchpad-services.cjs +0 -1
  184. package/dist/components/launchpad-services.cjs.map +1 -1
  185. package/dist/components/launchpad-services.d.cts +0 -36
  186. package/dist/components/launchpad-services.d.ts +0 -36
  187. package/dist/components/launchpad-services.js +0 -1
  188. package/dist/components/launchpad-services.js.map +1 -1
  189. package/dist/components/launchpad-strip.cjs.map +1 -1
  190. package/dist/components/launchpad-strip.d.cts +0 -6
  191. package/dist/components/launchpad-strip.d.ts +0 -6
  192. package/dist/components/launchpad-strip.js.map +1 -1
  193. package/dist/components/license-terms-builder.cjs.map +1 -1
  194. package/dist/components/license-terms-builder.d.cts +0 -34
  195. package/dist/components/license-terms-builder.d.ts +0 -34
  196. package/dist/components/license-terms-builder.js.map +1 -1
  197. package/dist/components/listing-card.cjs.map +1 -1
  198. package/dist/components/listing-card.d.cts +0 -19
  199. package/dist/components/listing-card.d.ts +0 -19
  200. package/dist/components/listing-card.js.map +1 -1
  201. package/dist/components/load-more-sentinel.cjs.map +1 -1
  202. package/dist/components/load-more-sentinel.d.cts +0 -6
  203. package/dist/components/load-more-sentinel.d.ts +0 -6
  204. package/dist/components/load-more-sentinel.js.map +1 -1
  205. package/dist/components/medialane-collection-card.cjs +1 -4
  206. package/dist/components/medialane-collection-card.cjs.map +1 -1
  207. package/dist/components/medialane-collection-card.d.cts +0 -13
  208. package/dist/components/medialane-collection-card.d.ts +0 -13
  209. package/dist/components/medialane-collection-card.js +1 -4
  210. package/dist/components/medialane-collection-card.js.map +1 -1
  211. package/dist/components/motion-primitives.cjs.map +1 -1
  212. package/dist/components/motion-primitives.js.map +1 -1
  213. package/dist/components/nav-command-menu.cjs.map +1 -1
  214. package/dist/components/nav-command-menu.d.cts +0 -32
  215. package/dist/components/nav-command-menu.d.ts +0 -32
  216. package/dist/components/nav-command-menu.js.map +1 -1
  217. package/dist/components/nav-shell.cjs.map +1 -1
  218. package/dist/components/nav-shell.d.cts +0 -57
  219. package/dist/components/nav-shell.d.ts +0 -57
  220. package/dist/components/nav-shell.js.map +1 -1
  221. package/dist/components/nav-theme-toggle.cjs.map +1 -1
  222. package/dist/components/nav-theme-toggle.d.cts +0 -4
  223. package/dist/components/nav-theme-toggle.d.ts +0 -4
  224. package/dist/components/nav-theme-toggle.js.map +1 -1
  225. package/dist/components/order-sort-control.cjs.map +1 -1
  226. package/dist/components/order-sort-control.d.cts +0 -2
  227. package/dist/components/order-sort-control.d.ts +0 -2
  228. package/dist/components/order-sort-control.js.map +1 -1
  229. package/dist/components/page-container.cjs +0 -3
  230. package/dist/components/page-container.cjs.map +1 -1
  231. package/dist/components/page-container.d.cts +0 -6
  232. package/dist/components/page-container.d.ts +0 -6
  233. package/dist/components/page-container.js +0 -3
  234. package/dist/components/page-container.js.map +1 -1
  235. package/dist/components/parent-attribution-banner.cjs.map +1 -1
  236. package/dist/components/parent-attribution-banner.d.cts +0 -1
  237. package/dist/components/parent-attribution-banner.d.ts +0 -1
  238. package/dist/components/parent-attribution-banner.js.map +1 -1
  239. package/dist/components/portfolio-activity.cjs.map +1 -1
  240. package/dist/components/portfolio-activity.js.map +1 -1
  241. package/dist/components/portfolio-bento-tile.cjs +8 -3
  242. package/dist/components/portfolio-bento-tile.cjs.map +1 -1
  243. package/dist/components/portfolio-bento-tile.d.cts +5 -13
  244. package/dist/components/portfolio-bento-tile.d.ts +5 -13
  245. package/dist/components/portfolio-bento-tile.js +8 -3
  246. package/dist/components/portfolio-bento-tile.js.map +1 -1
  247. package/dist/components/portfolio-chip-filter.cjs.map +1 -1
  248. package/dist/components/portfolio-chip-filter.d.cts +0 -14
  249. package/dist/components/portfolio-chip-filter.d.ts +0 -14
  250. package/dist/components/portfolio-chip-filter.js.map +1 -1
  251. package/dist/components/portfolio-header.cjs.map +1 -1
  252. package/dist/components/portfolio-header.d.cts +0 -11
  253. package/dist/components/portfolio-header.d.ts +0 -11
  254. package/dist/components/portfolio-header.js.map +1 -1
  255. package/dist/components/portfolio-overview.cjs +6 -1
  256. package/dist/components/portfolio-overview.cjs.map +1 -1
  257. package/dist/components/portfolio-overview.d.cts +0 -18
  258. package/dist/components/portfolio-overview.d.ts +0 -18
  259. package/dist/components/portfolio-overview.js +6 -1
  260. package/dist/components/portfolio-overview.js.map +1 -1
  261. package/dist/components/price-history-chart.cjs.map +1 -1
  262. package/dist/components/price-history-chart.js.map +1 -1
  263. package/dist/components/rewards/badge-catalog.cjs.map +1 -1
  264. package/dist/components/rewards/badge-catalog.d.cts +0 -5
  265. package/dist/components/rewards/badge-catalog.d.ts +0 -5
  266. package/dist/components/rewards/badge-catalog.js.map +1 -1
  267. package/dist/components/rewards/badge-shelf.cjs.map +1 -1
  268. package/dist/components/rewards/badge-shelf.d.cts +0 -5
  269. package/dist/components/rewards/badge-shelf.d.ts +0 -5
  270. package/dist/components/rewards/badge-shelf.js.map +1 -1
  271. package/dist/components/rewards/badge-unlock-toast-content.cjs.map +1 -1
  272. package/dist/components/rewards/badge-unlock-toast-content.d.cts +0 -2
  273. package/dist/components/rewards/badge-unlock-toast-content.d.ts +0 -2
  274. package/dist/components/rewards/badge-unlock-toast-content.js.map +1 -1
  275. package/dist/components/rewards/journey-path.cjs.map +1 -1
  276. package/dist/components/rewards/journey-path.d.cts +0 -9
  277. package/dist/components/rewards/journey-path.d.ts +0 -9
  278. package/dist/components/rewards/journey-path.js.map +1 -1
  279. package/dist/components/rewards/leaderboard-table.cjs.map +1 -1
  280. package/dist/components/rewards/leaderboard-table.d.cts +0 -11
  281. package/dist/components/rewards/leaderboard-table.d.ts +0 -11
  282. package/dist/components/rewards/leaderboard-table.js.map +1 -1
  283. package/dist/components/rewards/level-badge.cjs.map +1 -1
  284. package/dist/components/rewards/level-badge.d.cts +0 -10
  285. package/dist/components/rewards/level-badge.d.ts +0 -10
  286. package/dist/components/rewards/level-badge.js.map +1 -1
  287. package/dist/components/rewards/level-journey-list.cjs.map +1 -1
  288. package/dist/components/rewards/level-journey-list.d.cts +0 -7
  289. package/dist/components/rewards/level-journey-list.d.ts +0 -7
  290. package/dist/components/rewards/level-journey-list.js.map +1 -1
  291. package/dist/components/rewards/level-up-celebration.cjs.map +1 -1
  292. package/dist/components/rewards/level-up-celebration.d.cts +0 -4
  293. package/dist/components/rewards/level-up-celebration.d.ts +0 -4
  294. package/dist/components/rewards/level-up-celebration.js.map +1 -1
  295. package/dist/components/rewards/reward-toast.cjs.map +1 -1
  296. package/dist/components/rewards/reward-toast.d.cts +0 -8
  297. package/dist/components/rewards/reward-toast.d.ts +0 -8
  298. package/dist/components/rewards/reward-toast.js.map +1 -1
  299. package/dist/components/rewards/score-summary-card.cjs.map +1 -1
  300. package/dist/components/rewards/score-summary-card.d.cts +0 -5
  301. package/dist/components/rewards/score-summary-card.d.ts +0 -5
  302. package/dist/components/rewards/score-summary-card.js.map +1 -1
  303. package/dist/components/rewards/use-rewards-celebrations.cjs.map +1 -1
  304. package/dist/components/rewards/use-rewards-celebrations.d.cts +0 -10
  305. package/dist/components/rewards/use-rewards-celebrations.d.ts +0 -10
  306. package/dist/components/rewards/use-rewards-celebrations.js.map +1 -1
  307. package/dist/components/rewards/xp-progress.cjs.map +1 -1
  308. package/dist/components/rewards/xp-progress.d.cts +0 -4
  309. package/dist/components/rewards/xp-progress.d.ts +0 -4
  310. package/dist/components/rewards/xp-progress.js.map +1 -1
  311. package/dist/components/rewards/xp-toast-content.cjs.map +1 -1
  312. package/dist/components/rewards/xp-toast-content.d.cts +0 -7
  313. package/dist/components/rewards/xp-toast-content.d.ts +0 -7
  314. package/dist/components/rewards/xp-toast-content.js.map +1 -1
  315. package/dist/components/scroll-section.cjs.map +1 -1
  316. package/dist/components/scroll-section.d.cts +0 -6
  317. package/dist/components/scroll-section.d.ts +0 -6
  318. package/dist/components/scroll-section.js.map +1 -1
  319. package/dist/components/service-form-shell.cjs.map +1 -1
  320. package/dist/components/service-form-shell.d.cts +0 -14
  321. package/dist/components/service-form-shell.d.ts +0 -14
  322. package/dist/components/service-form-shell.js.map +1 -1
  323. package/dist/components/service-header.cjs.map +1 -1
  324. package/dist/components/service-header.d.cts +0 -11
  325. package/dist/components/service-header.d.ts +0 -11
  326. package/dist/components/service-header.js.map +1 -1
  327. package/dist/components/share-button.cjs.map +1 -1
  328. package/dist/components/share-button.js.map +1 -1
  329. package/dist/components/stat-tile.cjs.map +1 -1
  330. package/dist/components/stat-tile.d.cts +0 -4
  331. package/dist/components/stat-tile.d.ts +0 -4
  332. package/dist/components/stat-tile.js.map +1 -1
  333. package/dist/components/step-nav.cjs.map +1 -1
  334. package/dist/components/step-nav.d.cts +0 -11
  335. package/dist/components/step-nav.d.ts +0 -11
  336. package/dist/components/step-nav.js.map +1 -1
  337. package/dist/components/theme-ambient-background.cjs.map +1 -1
  338. package/dist/components/theme-ambient-background.d.cts +0 -6
  339. package/dist/components/theme-ambient-background.d.ts +0 -6
  340. package/dist/components/theme-ambient-background.js.map +1 -1
  341. package/dist/components/token-card.cjs.map +1 -1
  342. package/dist/components/token-card.d.cts +0 -6
  343. package/dist/components/token-card.d.ts +0 -6
  344. package/dist/components/token-card.js.map +1 -1
  345. package/dist/components/token-glyph.cjs.map +1 -1
  346. package/dist/components/token-glyph.d.cts +0 -2
  347. package/dist/components/token-glyph.d.ts +0 -2
  348. package/dist/components/token-glyph.js.map +1 -1
  349. package/dist/data/brand.cjs.map +1 -1
  350. package/dist/data/brand.d.cts +0 -6
  351. package/dist/data/brand.d.ts +0 -6
  352. package/dist/data/brand.js.map +1 -1
  353. package/dist/data/coins.cjs.map +1 -1
  354. package/dist/data/coins.d.cts +0 -9
  355. package/dist/data/coins.d.ts +0 -9
  356. package/dist/data/coins.js.map +1 -1
  357. package/dist/data/ip-templates.cjs.map +1 -1
  358. package/dist/data/ip-templates.d.cts +0 -12
  359. package/dist/data/ip-templates.d.ts +0 -12
  360. package/dist/data/ip-templates.js.map +1 -1
  361. package/dist/data/ip-types.cjs.map +1 -1
  362. package/dist/data/ip-types.d.cts +0 -2
  363. package/dist/data/ip-types.d.ts +0 -2
  364. package/dist/data/ip-types.js.map +1 -1
  365. package/dist/data/ip.cjs.map +1 -1
  366. package/dist/data/ip.d.cts +0 -1
  367. package/dist/data/ip.d.ts +0 -1
  368. package/dist/data/ip.js.map +1 -1
  369. package/dist/data/launchpad-services.cjs +0 -10
  370. package/dist/data/launchpad-services.cjs.map +1 -1
  371. package/dist/data/launchpad-services.d.cts +0 -10
  372. package/dist/data/launchpad-services.d.ts +0 -10
  373. package/dist/data/launchpad-services.js +0 -10
  374. package/dist/data/launchpad-services.js.map +1 -1
  375. package/dist/data/living-render-collections.cjs +0 -2
  376. package/dist/data/living-render-collections.cjs.map +1 -1
  377. package/dist/data/living-render-collections.d.cts +0 -11
  378. package/dist/data/living-render-collections.d.ts +0 -11
  379. package/dist/data/living-render-collections.js +0 -2
  380. package/dist/data/living-render-collections.js.map +1 -1
  381. package/dist/data/notification.cjs.map +1 -1
  382. package/dist/data/notification.d.cts +0 -3
  383. package/dist/data/notification.d.ts +0 -3
  384. package/dist/index.cjs.map +1 -1
  385. package/dist/index.js.map +1 -1
  386. package/dist/preset/tailwind.cjs +0 -9
  387. package/dist/preset/tailwind.cjs.map +1 -1
  388. package/dist/preset/tailwind.js +0 -9
  389. package/dist/preset/tailwind.js.map +1 -1
  390. package/dist/utils/address.cjs.map +1 -1
  391. package/dist/utils/address.d.cts +0 -4
  392. package/dist/utils/address.d.ts +0 -4
  393. package/dist/utils/address.js.map +1 -1
  394. package/dist/utils/api-fetch.cjs.map +1 -1
  395. package/dist/utils/api-fetch.d.cts +0 -21
  396. package/dist/utils/api-fetch.d.ts +0 -21
  397. package/dist/utils/api-fetch.js.map +1 -1
  398. package/dist/utils/format.cjs.map +1 -1
  399. package/dist/utils/format.d.cts +0 -11
  400. package/dist/utils/format.d.ts +0 -11
  401. package/dist/utils/format.js.map +1 -1
  402. package/dist/utils/ipfs.cjs.map +1 -1
  403. package/dist/utils/ipfs.d.cts +0 -8
  404. package/dist/utils/ipfs.d.ts +0 -8
  405. package/dist/utils/ipfs.js.map +1 -1
  406. package/dist/utils/license-summary.cjs.map +1 -1
  407. package/dist/utils/license-summary.d.cts +0 -8
  408. package/dist/utils/license-summary.d.ts +0 -8
  409. package/dist/utils/license-summary.js.map +1 -1
  410. package/dist/utils/notification-storage.cjs.map +1 -1
  411. package/dist/utils/notification-storage.js.map +1 -1
  412. package/dist/utils/portfolio-counts.cjs.map +1 -1
  413. package/dist/utils/portfolio-counts.d.cts +0 -18
  414. package/dist/utils/portfolio-counts.d.ts +0 -18
  415. package/dist/utils/portfolio-counts.js.map +1 -1
  416. package/dist/utils/proxy-body.cjs.map +1 -1
  417. package/dist/utils/proxy-body.d.cts +0 -11
  418. package/dist/utils/proxy-body.d.ts +0 -11
  419. package/dist/utils/proxy-body.js.map +1 -1
  420. package/dist/utils/time.cjs.map +1 -1
  421. package/dist/utils/time.d.cts +0 -5
  422. package/dist/utils/time.d.ts +0 -5
  423. package/dist/utils/time.js.map +1 -1
  424. package/dist/utils/use-collections.cjs.map +1 -1
  425. package/dist/utils/use-collections.d.cts +1 -11
  426. package/dist/utils/use-collections.d.ts +1 -11
  427. package/dist/utils/use-collections.js.map +1 -1
  428. package/dist/utils/use-intersection-active.cjs.map +1 -1
  429. package/dist/utils/use-intersection-active.d.cts +0 -7
  430. package/dist/utils/use-intersection-active.d.ts +0 -7
  431. package/dist/utils/use-intersection-active.js.map +1 -1
  432. package/dist/utils/use-notifications.cjs.map +1 -1
  433. package/dist/utils/use-notifications.js.map +1 -1
  434. package/dist/utils/use-orders.cjs.map +1 -1
  435. package/dist/utils/use-orders.d.cts +0 -2
  436. package/dist/utils/use-orders.d.ts +0 -2
  437. package/dist/utils/use-orders.js.map +1 -1
  438. package/dist/utils/use-profiles.cjs.map +1 -1
  439. package/dist/utils/use-profiles.js.map +1 -1
  440. package/dist/utils/use-remix-offers.cjs.map +1 -1
  441. package/dist/utils/use-remix-offers.d.cts +0 -1
  442. package/dist/utils/use-remix-offers.d.ts +0 -1
  443. package/dist/utils/use-remix-offers.js.map +1 -1
  444. package/dist/utils/use-rewards.cjs.map +1 -1
  445. package/dist/utils/use-rewards.d.cts +0 -4
  446. package/dist/utils/use-rewards.d.ts +0 -4
  447. package/dist/utils/use-rewards.js.map +1 -1
  448. package/package.json +1 -1
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/components/coin-launch-preview.tsx"],"sourcesContent":["\"use client\";\n\nimport Image from \"next/image\";\nimport { TrendingUp } from \"lucide-react\";\nimport { cn } from \"../utils/cn.js\";\n\nexport interface CoinPreviewData {\n name: string;\n symbol: string;\n description: string;\n /** Local object URL or resolved IPFS URL for the feature image */\n imageUrl: string | null;\n /** Human supply (already validated) or null while typing */\n supplyHuman: number | null;\n quoteSymbol: string;\n teamPct: number;\n}\n\nconst LAUNCH_PRICE = 0.01; // fixed, quote per coin\n\n/** Warm coin gradient — rose → orange, the brand's coin palette. */\nconst COIN_GRADIENT = \"linear-gradient(135deg, #f6608f, #fb8b46)\";\n\n/** Live preview for the Creator Coin launch studio — builds the coin in front\n * of the creator as they type. Mirrors the coin's eventual discovery card so\n * what they design is what their fans will see. Quiet borderless panel in the\n * launchpad-rail language; the warm coin gradient appears only as the avatar\n * ring and the allocation bar. */\nexport function CoinLaunchPreview({ data, className }: { data: CoinPreviewData; className?: string }) {\n const { name, symbol, description, imageUrl, supplyHuman, quoteSymbol, teamPct } = data;\n const marketCap = supplyHuman != null ? supplyHuman * LAUNCH_PRICE : null;\n const yourCoins = supplyHuman != null ? supplyHuman * (teamPct / 100) : null;\n\n return (\n <div className={cn(\"rounded-2xl bg-muted/50 dark:bg-card overflow-hidden\", className)}>\n <div className=\"px-5 pt-4\">\n <p className=\"section-label\">Live preview</p>\n </div>\n\n <div className=\"p-5 space-y-4\">\n {/* Identity — avatar in the warm coin ring */}\n <div className=\"flex items-center gap-3\">\n <div className=\"shrink-0 rounded-full p-[2px]\" style={{ background: COIN_GRADIENT }}>\n <div className=\"relative h-14 w-14 rounded-full overflow-hidden bg-card flex items-center justify-center\">\n {imageUrl ? (\n <Image src={imageUrl} alt=\"\" fill sizes=\"56px\" className=\"object-cover\" unoptimized />\n ) : (\n <TrendingUp className=\"h-6 w-6 text-brand-rose/70\" />\n )}\n </div>\n </div>\n <div className=\"min-w-0 flex-1\">\n <p className={cn(\"font-bold text-lg leading-snug truncate\", !name && \"text-muted-foreground/50\")}>\n {name || \"Your coin\"}\n </p>\n <p className=\"text-sm text-muted-foreground tabular-nums\">{symbol ? `$${symbol}` : \"$SYMBOL\"}</p>\n </div>\n </div>\n\n {description ? (\n <p className=\"text-sm text-muted-foreground leading-relaxed line-clamp-3\">{description}</p>\n ) : null}\n\n {/* Economics */}\n <div className=\"grid grid-cols-3 gap-2 rounded-xl bg-muted/50 dark:bg-muted/30 p-3 text-sm\">\n <div>\n <p className=\"text-2xs uppercase tracking-wide text-muted-foreground\">Launch price</p>\n <p className=\"font-semibold tabular-nums\">{LAUNCH_PRICE} {quoteSymbol}</p>\n </div>\n <div>\n <p className=\"text-2xs uppercase tracking-wide text-muted-foreground\">Market cap</p>\n <p className=\"font-semibold tabular-nums text-brand-maeve\">\n {marketCap != null ? `${marketCap.toLocaleString()} ${quoteSymbol}` : \"—\"}\n </p>\n </div>\n <div>\n <p className=\"text-2xs uppercase tracking-wide text-muted-foreground\">Your share</p>\n <p className=\"font-semibold tabular-nums\">\n {yourCoins != null ? `${teamPct}% · ${yourCoins.toLocaleString()}` : `${teamPct}%`}\n </p>\n </div>\n </div>\n\n {/* Allocation split bar */}\n <div className=\"space-y-1.5\">\n <div className=\"flex h-2.5 rounded-full overflow-hidden bg-muted-foreground/15\">\n {teamPct > 0 && <div style={{ width: `${teamPct}%`, background: COIN_GRADIENT }} />}\n <div className=\"flex-1\" />\n </div>\n <div className=\"flex justify-between text-2xs text-muted-foreground\">\n <span className=\"font-medium text-brand-rose\">You {teamPct}%</span>\n <span>Community {100 - teamPct}%</span>\n </div>\n </div>\n\n <p className=\"text-2xs text-muted-foreground/70\">\n Liquidity is locked forever — nobody can pull it, including us.\n </p>\n </div>\n </div>\n );\n}\n"],"mappings":";AAoCQ,cAeE,YAfF;AAlCR,OAAO,WAAW;AAClB,SAAS,kBAAkB;AAC3B,SAAS,UAAU;AAcnB,MAAM,eAAe;AAGrB,MAAM,gBAAgB;AAOf,SAAS,kBAAkB,EAAE,MAAM,UAAU,GAAkD;AACpG,QAAM,EAAE,MAAM,QAAQ,aAAa,UAAU,aAAa,aAAa,QAAQ,IAAI;AACnF,QAAM,YAAY,eAAe,OAAO,cAAc,eAAe;AACrE,QAAM,YAAY,eAAe,OAAO,eAAe,UAAU,OAAO;AAExE,SACE,qBAAC,SAAI,WAAW,GAAG,wDAAwD,SAAS,GAClF;AAAA,wBAAC,SAAI,WAAU,aACb,8BAAC,OAAE,WAAU,iBAAgB,0BAAY,GAC3C;AAAA,IAEA,qBAAC,SAAI,WAAU,iBAEb;AAAA,2BAAC,SAAI,WAAU,2BACb;AAAA,4BAAC,SAAI,WAAU,iCAAgC,OAAO,EAAE,YAAY,cAAc,GAChF,8BAAC,SAAI,WAAU,4FACZ,qBACC,oBAAC,SAAM,KAAK,UAAU,KAAI,IAAG,MAAI,MAAC,OAAM,QAAO,WAAU,gBAAe,aAAW,MAAC,IAEpF,oBAAC,cAAW,WAAU,8BAA6B,GAEvD,GACF;AAAA,QACA,qBAAC,SAAI,WAAU,kBACb;AAAA,8BAAC,OAAE,WAAW,GAAG,2CAA2C,CAAC,QAAQ,0BAA0B,GAC5F,kBAAQ,aACX;AAAA,UACA,oBAAC,OAAE,WAAU,8CAA8C,mBAAS,IAAI,MAAM,KAAK,WAAU;AAAA,WAC/F;AAAA,SACF;AAAA,MAEC,cACC,oBAAC,OAAE,WAAU,8DAA8D,uBAAY,IACrF;AAAA,MAGJ,qBAAC,SAAI,WAAU,8EACb;AAAA,6BAAC,SACC;AAAA,8BAAC,OAAE,WAAU,0DAAyD,0BAAY;AAAA,UAClF,qBAAC,OAAE,WAAU,8BAA8B;AAAA;AAAA,YAAa;AAAA,YAAE;AAAA,aAAY;AAAA,WACxE;AAAA,QACA,qBAAC,SACC;AAAA,8BAAC,OAAE,WAAU,0DAAyD,wBAAU;AAAA,UAChF,oBAAC,OAAE,WAAU,+CACV,uBAAa,OAAO,GAAG,UAAU,eAAe,CAAC,IAAI,WAAW,KAAK,UACxE;AAAA,WACF;AAAA,QACA,qBAAC,SACC;AAAA,8BAAC,OAAE,WAAU,0DAAyD,wBAAU;AAAA,UAChF,oBAAC,OAAE,WAAU,8BACV,uBAAa,OAAO,GAAG,OAAO,UAAO,UAAU,eAAe,CAAC,KAAK,GAAG,OAAO,KACjF;AAAA,WACF;AAAA,SACF;AAAA,MAGA,qBAAC,SAAI,WAAU,eACb;AAAA,6BAAC,SAAI,WAAU,kEACZ;AAAA,oBAAU,KAAK,oBAAC,SAAI,OAAO,EAAE,OAAO,GAAG,OAAO,KAAK,YAAY,cAAc,GAAG;AAAA,UACjF,oBAAC,SAAI,WAAU,UAAS;AAAA,WAC1B;AAAA,QACA,qBAAC,SAAI,WAAU,uDACb;AAAA,+BAAC,UAAK,WAAU,+BAA8B;AAAA;AAAA,YAAK;AAAA,YAAQ;AAAA,aAAC;AAAA,UAC5D,qBAAC,UAAK;AAAA;AAAA,YAAW,MAAM;AAAA,YAAQ;AAAA,aAAC;AAAA,WAClC;AAAA,SACF;AAAA,MAEA,oBAAC,OAAE,WAAU,qCAAoC,kFAEjD;AAAA,OACF;AAAA,KACF;AAEJ;","names":[]}
1
+ {"version":3,"sources":["../../src/components/coin-launch-preview.tsx"],"sourcesContent":["\"use client\";\n\nimport Image from \"next/image\";\nimport { TrendingUp } from \"lucide-react\";\nimport { cn } from \"../utils/cn.js\";\n\nexport interface CoinPreviewData {\n name: string;\n symbol: string;\n description: string;\n\n imageUrl: string | null;\n\n supplyHuman: number | null;\n quoteSymbol: string;\n teamPct: number;\n}\n\nconst LAUNCH_PRICE = 0.01;\n\nconst COIN_GRADIENT = \"linear-gradient(135deg, #f6608f, #fb8b46)\";\n\nexport function CoinLaunchPreview({ data, className }: { data: CoinPreviewData; className?: string }) {\n const { name, symbol, description, imageUrl, supplyHuman, quoteSymbol, teamPct } = data;\n const marketCap = supplyHuman != null ? supplyHuman * LAUNCH_PRICE : null;\n const yourCoins = supplyHuman != null ? supplyHuman * (teamPct / 100) : null;\n\n return (\n <div className={cn(\"rounded-2xl bg-muted/50 dark:bg-card overflow-hidden\", className)}>\n <div className=\"px-5 pt-4\">\n <p className=\"section-label\">Live preview</p>\n </div>\n\n <div className=\"p-5 space-y-4\">\n\n <div className=\"flex items-center gap-3\">\n <div className=\"shrink-0 rounded-full p-[2px]\" style={{ background: COIN_GRADIENT }}>\n <div className=\"relative h-14 w-14 rounded-full overflow-hidden bg-card flex items-center justify-center\">\n {imageUrl ? (\n <Image src={imageUrl} alt=\"\" fill sizes=\"56px\" className=\"object-cover\" unoptimized />\n ) : (\n <TrendingUp className=\"h-6 w-6 text-brand-rose/70\" />\n )}\n </div>\n </div>\n <div className=\"min-w-0 flex-1\">\n <p className={cn(\"font-bold text-lg leading-snug truncate\", !name && \"text-muted-foreground/50\")}>\n {name || \"Your coin\"}\n </p>\n <p className=\"text-sm text-muted-foreground tabular-nums\">{symbol ? `$${symbol}` : \"$SYMBOL\"}</p>\n </div>\n </div>\n\n {description ? (\n <p className=\"text-sm text-muted-foreground leading-relaxed line-clamp-3\">{description}</p>\n ) : null}\n\n <div className=\"grid grid-cols-3 gap-2 rounded-xl bg-muted/50 dark:bg-muted/30 p-3 text-sm\">\n <div>\n <p className=\"text-2xs uppercase tracking-wide text-muted-foreground\">Launch price</p>\n <p className=\"font-semibold tabular-nums\">{LAUNCH_PRICE} {quoteSymbol}</p>\n </div>\n <div>\n <p className=\"text-2xs uppercase tracking-wide text-muted-foreground\">Market cap</p>\n <p className=\"font-semibold tabular-nums text-brand-maeve\">\n {marketCap != null ? `${marketCap.toLocaleString()} ${quoteSymbol}` : \"—\"}\n </p>\n </div>\n <div>\n <p className=\"text-2xs uppercase tracking-wide text-muted-foreground\">Your share</p>\n <p className=\"font-semibold tabular-nums\">\n {yourCoins != null ? `${teamPct}% · ${yourCoins.toLocaleString()}` : `${teamPct}%`}\n </p>\n </div>\n </div>\n\n <div className=\"space-y-1.5\">\n <div className=\"flex h-2.5 rounded-full overflow-hidden bg-muted-foreground/15\">\n {teamPct > 0 && <div style={{ width: `${teamPct}%`, background: COIN_GRADIENT }} />}\n <div className=\"flex-1\" />\n </div>\n <div className=\"flex justify-between text-2xs text-muted-foreground\">\n <span className=\"font-medium text-brand-rose\">You {teamPct}%</span>\n <span>Community {100 - teamPct}%</span>\n </div>\n </div>\n\n <p className=\"text-2xs text-muted-foreground/70\">\n Liquidity is locked forever — nobody can pull it, including us.\n </p>\n </div>\n </div>\n );\n}\n"],"mappings":";AA8BQ,cAeE,YAfF;AA5BR,OAAO,WAAW;AAClB,SAAS,kBAAkB;AAC3B,SAAS,UAAU;AAcnB,MAAM,eAAe;AAErB,MAAM,gBAAgB;AAEf,SAAS,kBAAkB,EAAE,MAAM,UAAU,GAAkD;AACpG,QAAM,EAAE,MAAM,QAAQ,aAAa,UAAU,aAAa,aAAa,QAAQ,IAAI;AACnF,QAAM,YAAY,eAAe,OAAO,cAAc,eAAe;AACrE,QAAM,YAAY,eAAe,OAAO,eAAe,UAAU,OAAO;AAExE,SACE,qBAAC,SAAI,WAAW,GAAG,wDAAwD,SAAS,GAClF;AAAA,wBAAC,SAAI,WAAU,aACb,8BAAC,OAAE,WAAU,iBAAgB,0BAAY,GAC3C;AAAA,IAEA,qBAAC,SAAI,WAAU,iBAEb;AAAA,2BAAC,SAAI,WAAU,2BACb;AAAA,4BAAC,SAAI,WAAU,iCAAgC,OAAO,EAAE,YAAY,cAAc,GAChF,8BAAC,SAAI,WAAU,4FACZ,qBACC,oBAAC,SAAM,KAAK,UAAU,KAAI,IAAG,MAAI,MAAC,OAAM,QAAO,WAAU,gBAAe,aAAW,MAAC,IAEpF,oBAAC,cAAW,WAAU,8BAA6B,GAEvD,GACF;AAAA,QACA,qBAAC,SAAI,WAAU,kBACb;AAAA,8BAAC,OAAE,WAAW,GAAG,2CAA2C,CAAC,QAAQ,0BAA0B,GAC5F,kBAAQ,aACX;AAAA,UACA,oBAAC,OAAE,WAAU,8CAA8C,mBAAS,IAAI,MAAM,KAAK,WAAU;AAAA,WAC/F;AAAA,SACF;AAAA,MAEC,cACC,oBAAC,OAAE,WAAU,8DAA8D,uBAAY,IACrF;AAAA,MAEJ,qBAAC,SAAI,WAAU,8EACb;AAAA,6BAAC,SACC;AAAA,8BAAC,OAAE,WAAU,0DAAyD,0BAAY;AAAA,UAClF,qBAAC,OAAE,WAAU,8BAA8B;AAAA;AAAA,YAAa;AAAA,YAAE;AAAA,aAAY;AAAA,WACxE;AAAA,QACA,qBAAC,SACC;AAAA,8BAAC,OAAE,WAAU,0DAAyD,wBAAU;AAAA,UAChF,oBAAC,OAAE,WAAU,+CACV,uBAAa,OAAO,GAAG,UAAU,eAAe,CAAC,IAAI,WAAW,KAAK,UACxE;AAAA,WACF;AAAA,QACA,qBAAC,SACC;AAAA,8BAAC,OAAE,WAAU,0DAAyD,wBAAU;AAAA,UAChF,oBAAC,OAAE,WAAU,8BACV,uBAAa,OAAO,GAAG,OAAO,UAAO,UAAU,eAAe,CAAC,KAAK,GAAG,OAAO,KACjF;AAAA,WACF;AAAA,SACF;AAAA,MAEA,qBAAC,SAAI,WAAU,eACb;AAAA,6BAAC,SAAI,WAAU,kEACZ;AAAA,oBAAU,KAAK,oBAAC,SAAI,OAAO,EAAE,OAAO,GAAG,OAAO,KAAK,YAAY,cAAc,GAAG;AAAA,UACjF,oBAAC,SAAI,WAAU,UAAS;AAAA,WAC1B;AAAA,QACA,qBAAC,SAAI,WAAU,uDACb;AAAA,+BAAC,UAAK,WAAU,+BAA8B;AAAA;AAAA,YAAK;AAAA,YAAQ;AAAA,aAAC;AAAA,UAC5D,qBAAC,UAAK;AAAA;AAAA,YAAW,MAAM;AAAA,YAAQ;AAAA,aAAC;AAAA,WAClC;AAAA,SACF;AAAA,MAEA,oBAAC,OAAE,WAAU,qCAAoC,kFAEjD;AAAA,OACF;AAAA,KACF;AAEJ;","names":[]}
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/components/coins-explorer.tsx"],"sourcesContent":["\"use client\";\n\n/**\n * CoinsExplorer — the shared coin-discovery surface (chain-agnostic).\n *\n * Owns view/search/filter/sort UI only. The data source (`useCoins`), the price\n * read (`usePrice`), and the link target (`coinHref`) are injected, so each app\n * (and each chain) wires its own without forking this component.\n *\n * Type + sort live in a centered Filters dialog (the \"action focus\" pattern, and\n * matching the marketplace page); the header stays to two lines.\n */\n\nimport { useState, useMemo, useEffect } from \"react\";\nimport { Coins, LayoutGrid, List, Search, SlidersHorizontal, X } from \"lucide-react\";\nimport { cn } from \"../utils/cn.js\";\nimport { CoinCard, CoinRow, CoinCardSkeleton, type UseCoinPrice } from \"./coin-card.js\";\nimport type { CoinCollectionLike } from \"../data/coins.js\";\n\nexport type CoinFilter = \"all\" | \"creator\" | \"memecoin\";\nexport type CoinSort = \"recent\" | \"name\";\nexport type UseCoins = (opts: { filter: CoinFilter; sort: CoinSort }) => {\n collections: CoinCollectionLike[];\n isLoading: boolean;\n};\n\nexport interface CoinsExplorerProps {\n useCoins: UseCoins;\n usePrice: UseCoinPrice;\n /** Build the link target for a coin (internal or per-chain trading app). */\n coinHref: (collection: CoinCollectionLike) => string;\n heading?: boolean;\n}\n\nconst FILTER_TABS: { label: string; value: CoinFilter }[] = [\n { label: \"All\", value: \"all\" },\n { label: \"Creator Coins\", value: \"creator\" },\n { label: \"Memecoins\", value: \"memecoin\" },\n];\n\n// Recency default — never raw swap volume (05 §11 anti-wash hygiene).\nconst SORT_OPTIONS: { label: string; value: CoinSort }[] = [\n { label: \"Recently launched\", value: \"recent\" },\n { label: \"Name\", value: \"name\" },\n];\n\nconst filterLabel = (v: CoinFilter) => FILTER_TABS.find((t) => t.value === v)?.label ?? \"\";\nconst sortLabel = (v: CoinSort) => SORT_OPTIONS.find((o) => o.value === v)?.label ?? \"\";\n\nexport function CoinsExplorer({ useCoins, usePrice, coinHref, heading = true }: CoinsExplorerProps) {\n const [filter, setFilter] = useState<CoinFilter>(\"all\");\n const [sort, setSort] = useState<CoinSort>(\"recent\");\n const [view, setView] = useState<\"grid\" | \"table\">(\"grid\");\n const [query, setQuery] = useState(\"\");\n const [filtersOpen, setFiltersOpen] = useState(false);\n\n const filterCount = (filter !== \"all\" ? 1 : 0) + (sort !== \"recent\" ? 1 : 0);\n\n useEffect(() => {\n if (!filtersOpen) return;\n const onKey = (e: KeyboardEvent) => e.key === \"Escape\" && setFiltersOpen(false);\n window.addEventListener(\"keydown\", onKey);\n return () => window.removeEventListener(\"keydown\", onKey);\n }, [filtersOpen]);\n\n const { collections, isLoading } = useCoins({ filter, sort });\n const items = useMemo(() => {\n const q = query.trim().toLowerCase();\n if (!q) return collections;\n return collections.filter(\n (c) => (c.name ?? \"\").toLowerCase().includes(q) || (c.symbol ?? \"\").toLowerCase().includes(q)\n );\n }, [collections, query]);\n\n return (\n <div className=\"space-y-6\">\n {heading && (\n <div className=\"space-y-2\">\n <div className=\"flex items-center gap-2 text-primary\">\n <Coins className=\"h-5 w-5\" />\n <span className=\"text-sm font-semibold\">Coins</span>\n </div>\n <h1 className=\"text-3xl\">Creator coins &amp; memecoins</h1>\n </div>\n )}\n\n {/* Controls — search + filters + view, kept lean. */}\n <div className=\"space-y-3 border-b border-border/60 pb-3\">\n <div className=\"flex items-center gap-2\">\n <div className=\"relative flex-1\">\n <Search className=\"pointer-events-none absolute left-3 top-1/2 h-3.5 w-3.5 -translate-y-1/2 text-muted-foreground\" />\n <input\n value={query}\n onChange={(e) => setQuery(e.target.value)}\n placeholder=\"Search coins by name or symbol…\"\n className=\"w-full rounded-lg border border-border bg-background py-2 pl-9 pr-3 text-sm outline-none focus:border-primary/50\"\n />\n </div>\n <button\n onClick={() => setFiltersOpen(true)}\n className=\"inline-flex shrink-0 items-center gap-1.5 rounded-lg border border-border px-3 py-2 text-xs font-medium text-foreground hover:border-primary/50\"\n >\n <SlidersHorizontal className=\"h-3.5 w-3.5\" />\n Filters\n {filterCount > 0 && (\n <span className=\"ml-0.5 inline-flex h-4 min-w-4 items-center justify-center rounded-full bg-primary px-1 text-2xs font-semibold text-primary-foreground\">\n {filterCount}\n </span>\n )}\n </button>\n <div className=\"inline-flex shrink-0 rounded-lg border border-border p-0.5\">\n {([{ v: \"grid\", Icon: LayoutGrid }, { v: \"table\", Icon: List }] as const).map(({ v, Icon }) => (\n <button\n key={v}\n onClick={() => setView(v)}\n aria-label={v === \"grid\" ? \"Grid view\" : \"Table view\"}\n className={cn(\"rounded-md p-1.5 transition-colors\", view === v ? \"bg-primary/10 text-primary\" : \"text-muted-foreground hover:text-foreground\")}\n >\n <Icon className=\"h-4 w-4\" />\n </button>\n ))}\n </div>\n </div>\n\n {/* Active-filter chips */}\n {filterCount > 0 && (\n <div className=\"flex flex-wrap items-center gap-1.5\">\n {filter !== \"all\" && (\n <Chip onClear={() => setFilter(\"all\")}>{filterLabel(filter)}</Chip>\n )}\n {sort !== \"recent\" && (\n <Chip onClear={() => setSort(\"recent\")}>{sortLabel(sort)}</Chip>\n )}\n </div>\n )}\n </div>\n\n {isLoading && items.length === 0 ? (\n <div className=\"grid grid-cols-1 gap-4 sm:grid-cols-2 md:grid-cols-3 xl:grid-cols-4\">\n {Array.from({ length: 8 }).map((_, i) => <CoinCardSkeleton key={i} />)}\n </div>\n ) : items.length === 0 ? (\n <div className=\"rounded-xl border border-border/60 py-16 text-center text-muted-foreground\">\n {query.trim() ? `No coins match \"${query.trim()}\".` : \"No coins yet.\"}\n </div>\n ) : view === \"table\" ? (\n <div className=\"space-y-2\">\n {items.map((c) => <CoinRow key={`${c.chain}-${c.contractAddress}`} collection={c} usePrice={usePrice} href={coinHref(c)} />)}\n </div>\n ) : (\n <div className=\"grid grid-cols-1 gap-4 sm:grid-cols-2 md:grid-cols-3 xl:grid-cols-4\">\n {items.map((c) => <CoinCard key={`${c.chain}-${c.contractAddress}`} collection={c} usePrice={usePrice} href={coinHref(c)} />)}\n </div>\n )}\n\n {filtersOpen && (\n <div className=\"fixed inset-0 z-50 flex items-center justify-center p-4\" role=\"dialog\" aria-modal=\"true\" aria-label=\"Filters\">\n <div className=\"absolute inset-0 bg-background/70 backdrop-blur-sm\" onClick={() => setFiltersOpen(false)} />\n <div className=\"card-base relative z-10 w-full max-w-sm space-y-5 p-5\">\n <div className=\"flex items-center justify-between\">\n <h2 className=\"flex items-center gap-2 text-base font-bold\">\n <SlidersHorizontal className=\"h-4 w-4 text-primary\" />\n Filters\n </h2>\n {filterCount > 0 && (\n <button\n onClick={() => { setFilter(\"all\"); setSort(\"recent\"); }}\n className=\"text-xs font-medium text-muted-foreground hover:text-foreground\"\n >\n Clear all\n </button>\n )}\n </div>\n\n <FilterGroup label=\"Type\">\n {FILTER_TABS.map(({ label, value }) => (\n <PillButton key={value} active={filter === value} onClick={() => setFilter(value)}>{label}</PillButton>\n ))}\n </FilterGroup>\n\n <FilterGroup label=\"Sort\">\n {SORT_OPTIONS.map(({ label, value }) => (\n <PillButton key={value} active={sort === value} onClick={() => setSort(value)}>{label}</PillButton>\n ))}\n </FilterGroup>\n\n <button\n onClick={() => setFiltersOpen(false)}\n className=\"w-full rounded-lg bg-gradient-to-r from-brand-blue to-brand-purple py-2.5 text-sm font-semibold text-white\"\n >\n Show {items.length} {items.length === 1 ? \"coin\" : \"coins\"}\n </button>\n </div>\n </div>\n )}\n </div>\n );\n}\n\nfunction FilterGroup({ label, children }: { label: string; children: React.ReactNode }) {\n return (\n <div className=\"space-y-2\">\n <p className=\"text-2xs font-medium text-muted-foreground\">{label}</p>\n <div className=\"flex flex-wrap gap-1.5\">{children}</div>\n </div>\n );\n}\n\nfunction PillButton({ active, onClick, children }: { active: boolean; onClick: () => void; children: React.ReactNode }) {\n return (\n <button\n onClick={onClick}\n className={cn(\n \"rounded-lg border px-3 py-1.5 text-xs font-medium transition-colors\",\n active ? \"border-primary bg-primary/10 text-primary\" : \"border-border text-muted-foreground hover:border-primary/50 hover:text-foreground\"\n )}\n >\n {children}\n </button>\n );\n}\n\nfunction Chip({ children, onClear }: { children: React.ReactNode; onClear: () => void }) {\n return (\n <span className=\"inline-flex items-center gap-1 rounded-full border border-primary/30 bg-primary/10 px-2.5 py-1 text-xs font-medium text-primary\">\n {children}\n <button onClick={onClear} aria-label=\"Clear filter\" className=\"hover:text-primary/60\">\n <X className=\"h-3 w-3\" />\n </button>\n </span>\n );\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../../src/components/coins-explorer.tsx"],"sourcesContent":["\"use client\";\n\nimport { useState, useMemo, useEffect } from \"react\";\nimport { Coins, LayoutGrid, List, Search, SlidersHorizontal, X } from \"lucide-react\";\nimport { cn } from \"../utils/cn.js\";\nimport { CoinCard, CoinRow, CoinCardSkeleton, type UseCoinPrice } from \"./coin-card.js\";\nimport type { CoinCollectionLike } from \"../data/coins.js\";\n\nexport type CoinFilter = \"all\" | \"creator\" | \"memecoin\";\nexport type CoinSort = \"recent\" | \"name\";\nexport type UseCoins = (opts: { filter: CoinFilter; sort: CoinSort }) => {\n collections: CoinCollectionLike[];\n isLoading: boolean;\n};\n\nexport interface CoinsExplorerProps {\n useCoins: UseCoins;\n usePrice: UseCoinPrice;\n\n coinHref: (collection: CoinCollectionLike) => string;\n heading?: boolean;\n}\n\nconst FILTER_TABS: { label: string; value: CoinFilter }[] = [\n { label: \"All\", value: \"all\" },\n { label: \"Creator Coins\", value: \"creator\" },\n { label: \"Memecoins\", value: \"memecoin\" },\n];\n\nconst SORT_OPTIONS: { label: string; value: CoinSort }[] = [\n { label: \"Recently launched\", value: \"recent\" },\n { label: \"Name\", value: \"name\" },\n];\n\nconst filterLabel = (v: CoinFilter) => FILTER_TABS.find((t) => t.value === v)?.label ?? \"\";\nconst sortLabel = (v: CoinSort) => SORT_OPTIONS.find((o) => o.value === v)?.label ?? \"\";\n\nexport function CoinsExplorer({ useCoins, usePrice, coinHref, heading = true }: CoinsExplorerProps) {\n const [filter, setFilter] = useState<CoinFilter>(\"all\");\n const [sort, setSort] = useState<CoinSort>(\"recent\");\n const [view, setView] = useState<\"grid\" | \"table\">(\"grid\");\n const [query, setQuery] = useState(\"\");\n const [filtersOpen, setFiltersOpen] = useState(false);\n\n const filterCount = (filter !== \"all\" ? 1 : 0) + (sort !== \"recent\" ? 1 : 0);\n\n useEffect(() => {\n if (!filtersOpen) return;\n const onKey = (e: KeyboardEvent) => e.key === \"Escape\" && setFiltersOpen(false);\n window.addEventListener(\"keydown\", onKey);\n return () => window.removeEventListener(\"keydown\", onKey);\n }, [filtersOpen]);\n\n const { collections, isLoading } = useCoins({ filter, sort });\n const items = useMemo(() => {\n const q = query.trim().toLowerCase();\n if (!q) return collections;\n return collections.filter(\n (c) => (c.name ?? \"\").toLowerCase().includes(q) || (c.symbol ?? \"\").toLowerCase().includes(q)\n );\n }, [collections, query]);\n\n return (\n <div className=\"space-y-6\">\n {heading && (\n <div className=\"space-y-2\">\n <div className=\"flex items-center gap-2 text-primary\">\n <Coins className=\"h-5 w-5\" />\n <span className=\"text-sm font-semibold\">Coins</span>\n </div>\n <h1 className=\"text-3xl\">Creator coins &amp; memecoins</h1>\n </div>\n )}\n\n <div className=\"space-y-3 border-b border-border/60 pb-3\">\n <div className=\"flex items-center gap-2\">\n <div className=\"relative flex-1\">\n <Search className=\"pointer-events-none absolute left-3 top-1/2 h-3.5 w-3.5 -translate-y-1/2 text-muted-foreground\" />\n <input\n value={query}\n onChange={(e) => setQuery(e.target.value)}\n placeholder=\"Search coins by name or symbol…\"\n className=\"w-full rounded-lg border border-border bg-background py-2 pl-9 pr-3 text-sm outline-none focus:border-primary/50\"\n />\n </div>\n <button\n onClick={() => setFiltersOpen(true)}\n className=\"inline-flex shrink-0 items-center gap-1.5 rounded-lg border border-border px-3 py-2 text-xs font-medium text-foreground hover:border-primary/50\"\n >\n <SlidersHorizontal className=\"h-3.5 w-3.5\" />\n Filters\n {filterCount > 0 && (\n <span className=\"ml-0.5 inline-flex h-4 min-w-4 items-center justify-center rounded-full bg-primary px-1 text-2xs font-semibold text-primary-foreground\">\n {filterCount}\n </span>\n )}\n </button>\n <div className=\"inline-flex shrink-0 rounded-lg border border-border p-0.5\">\n {([{ v: \"grid\", Icon: LayoutGrid }, { v: \"table\", Icon: List }] as const).map(({ v, Icon }) => (\n <button\n key={v}\n onClick={() => setView(v)}\n aria-label={v === \"grid\" ? \"Grid view\" : \"Table view\"}\n className={cn(\"rounded-md p-1.5 transition-colors\", view === v ? \"bg-primary/10 text-primary\" : \"text-muted-foreground hover:text-foreground\")}\n >\n <Icon className=\"h-4 w-4\" />\n </button>\n ))}\n </div>\n </div>\n\n {filterCount > 0 && (\n <div className=\"flex flex-wrap items-center gap-1.5\">\n {filter !== \"all\" && (\n <Chip onClear={() => setFilter(\"all\")}>{filterLabel(filter)}</Chip>\n )}\n {sort !== \"recent\" && (\n <Chip onClear={() => setSort(\"recent\")}>{sortLabel(sort)}</Chip>\n )}\n </div>\n )}\n </div>\n\n {isLoading && items.length === 0 ? (\n <div className=\"grid grid-cols-1 gap-4 sm:grid-cols-2 md:grid-cols-3 xl:grid-cols-4\">\n {Array.from({ length: 8 }).map((_, i) => <CoinCardSkeleton key={i} />)}\n </div>\n ) : items.length === 0 ? (\n <div className=\"rounded-xl border border-border/60 py-16 text-center text-muted-foreground\">\n {query.trim() ? `No coins match \"${query.trim()}\".` : \"No coins yet.\"}\n </div>\n ) : view === \"table\" ? (\n <div className=\"space-y-2\">\n {items.map((c) => <CoinRow key={`${c.chain}-${c.contractAddress}`} collection={c} usePrice={usePrice} href={coinHref(c)} />)}\n </div>\n ) : (\n <div className=\"grid grid-cols-1 gap-4 sm:grid-cols-2 md:grid-cols-3 xl:grid-cols-4\">\n {items.map((c) => <CoinCard key={`${c.chain}-${c.contractAddress}`} collection={c} usePrice={usePrice} href={coinHref(c)} />)}\n </div>\n )}\n\n {filtersOpen && (\n <div className=\"fixed inset-0 z-50 flex items-center justify-center p-4\" role=\"dialog\" aria-modal=\"true\" aria-label=\"Filters\">\n <div className=\"absolute inset-0 bg-background/70 backdrop-blur-sm\" onClick={() => setFiltersOpen(false)} />\n <div className=\"card-base relative z-10 w-full max-w-sm space-y-5 p-5\">\n <div className=\"flex items-center justify-between\">\n <h2 className=\"flex items-center gap-2 text-base font-bold\">\n <SlidersHorizontal className=\"h-4 w-4 text-primary\" />\n Filters\n </h2>\n {filterCount > 0 && (\n <button\n onClick={() => { setFilter(\"all\"); setSort(\"recent\"); }}\n className=\"text-xs font-medium text-muted-foreground hover:text-foreground\"\n >\n Clear all\n </button>\n )}\n </div>\n\n <FilterGroup label=\"Type\">\n {FILTER_TABS.map(({ label, value }) => (\n <PillButton key={value} active={filter === value} onClick={() => setFilter(value)}>{label}</PillButton>\n ))}\n </FilterGroup>\n\n <FilterGroup label=\"Sort\">\n {SORT_OPTIONS.map(({ label, value }) => (\n <PillButton key={value} active={sort === value} onClick={() => setSort(value)}>{label}</PillButton>\n ))}\n </FilterGroup>\n\n <button\n onClick={() => setFiltersOpen(false)}\n className=\"w-full rounded-lg bg-gradient-to-r from-brand-blue to-brand-purple py-2.5 text-sm font-semibold text-white\"\n >\n Show {items.length} {items.length === 1 ? \"coin\" : \"coins\"}\n </button>\n </div>\n </div>\n )}\n </div>\n );\n}\n\nfunction FilterGroup({ label, children }: { label: string; children: React.ReactNode }) {\n return (\n <div className=\"space-y-2\">\n <p className=\"text-2xs font-medium text-muted-foreground\">{label}</p>\n <div className=\"flex flex-wrap gap-1.5\">{children}</div>\n </div>\n );\n}\n\nfunction PillButton({ active, onClick, children }: { active: boolean; onClick: () => void; children: React.ReactNode }) {\n return (\n <button\n onClick={onClick}\n className={cn(\n \"rounded-lg border px-3 py-1.5 text-xs font-medium transition-colors\",\n active ? \"border-primary bg-primary/10 text-primary\" : \"border-border text-muted-foreground hover:border-primary/50 hover:text-foreground\"\n )}\n >\n {children}\n </button>\n );\n}\n\nfunction Chip({ children, onClear }: { children: React.ReactNode; onClear: () => void }) {\n return (\n <span className=\"inline-flex items-center gap-1 rounded-full border border-primary/30 bg-primary/10 px-2.5 py-1 text-xs font-medium text-primary\">\n {children}\n <button onClick={onClear} aria-label=\"Clear filter\" className=\"hover:text-primary/60\">\n <X className=\"h-3 w-3\" />\n </button>\n </span>\n );\n}\n"],"mappings":"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","names":[]}
@@ -14,7 +14,6 @@ type UseCoins = (opts: {
14
14
  interface CoinsExplorerProps {
15
15
  useCoins: UseCoins;
16
16
  usePrice: UseCoinPrice;
17
- /** Build the link target for a coin (internal or per-chain trading app). */
18
17
  coinHref: (collection: CoinCollectionLike) => string;
19
18
  heading?: boolean;
20
19
  }
@@ -14,7 +14,6 @@ type UseCoins = (opts: {
14
14
  interface CoinsExplorerProps {
15
15
  useCoins: UseCoins;
16
16
  usePrice: UseCoinPrice;
17
- /** Build the link target for a coin (internal or per-chain trading app). */
18
17
  coinHref: (collection: CoinCollectionLike) => string;
19
18
  heading?: boolean;
20
19
  }
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/components/coins-explorer.tsx"],"sourcesContent":["\"use client\";\n\n/**\n * CoinsExplorer — the shared coin-discovery surface (chain-agnostic).\n *\n * Owns view/search/filter/sort UI only. The data source (`useCoins`), the price\n * read (`usePrice`), and the link target (`coinHref`) are injected, so each app\n * (and each chain) wires its own without forking this component.\n *\n * Type + sort live in a centered Filters dialog (the \"action focus\" pattern, and\n * matching the marketplace page); the header stays to two lines.\n */\n\nimport { useState, useMemo, useEffect } from \"react\";\nimport { Coins, LayoutGrid, List, Search, SlidersHorizontal, X } from \"lucide-react\";\nimport { cn } from \"../utils/cn.js\";\nimport { CoinCard, CoinRow, CoinCardSkeleton, type UseCoinPrice } from \"./coin-card.js\";\nimport type { CoinCollectionLike } from \"../data/coins.js\";\n\nexport type CoinFilter = \"all\" | \"creator\" | \"memecoin\";\nexport type CoinSort = \"recent\" | \"name\";\nexport type UseCoins = (opts: { filter: CoinFilter; sort: CoinSort }) => {\n collections: CoinCollectionLike[];\n isLoading: boolean;\n};\n\nexport interface CoinsExplorerProps {\n useCoins: UseCoins;\n usePrice: UseCoinPrice;\n /** Build the link target for a coin (internal or per-chain trading app). */\n coinHref: (collection: CoinCollectionLike) => string;\n heading?: boolean;\n}\n\nconst FILTER_TABS: { label: string; value: CoinFilter }[] = [\n { label: \"All\", value: \"all\" },\n { label: \"Creator Coins\", value: \"creator\" },\n { label: \"Memecoins\", value: \"memecoin\" },\n];\n\n// Recency default — never raw swap volume (05 §11 anti-wash hygiene).\nconst SORT_OPTIONS: { label: string; value: CoinSort }[] = [\n { label: \"Recently launched\", value: \"recent\" },\n { label: \"Name\", value: \"name\" },\n];\n\nconst filterLabel = (v: CoinFilter) => FILTER_TABS.find((t) => t.value === v)?.label ?? \"\";\nconst sortLabel = (v: CoinSort) => SORT_OPTIONS.find((o) => o.value === v)?.label ?? \"\";\n\nexport function CoinsExplorer({ useCoins, usePrice, coinHref, heading = true }: CoinsExplorerProps) {\n const [filter, setFilter] = useState<CoinFilter>(\"all\");\n const [sort, setSort] = useState<CoinSort>(\"recent\");\n const [view, setView] = useState<\"grid\" | \"table\">(\"grid\");\n const [query, setQuery] = useState(\"\");\n const [filtersOpen, setFiltersOpen] = useState(false);\n\n const filterCount = (filter !== \"all\" ? 1 : 0) + (sort !== \"recent\" ? 1 : 0);\n\n useEffect(() => {\n if (!filtersOpen) return;\n const onKey = (e: KeyboardEvent) => e.key === \"Escape\" && setFiltersOpen(false);\n window.addEventListener(\"keydown\", onKey);\n return () => window.removeEventListener(\"keydown\", onKey);\n }, [filtersOpen]);\n\n const { collections, isLoading } = useCoins({ filter, sort });\n const items = useMemo(() => {\n const q = query.trim().toLowerCase();\n if (!q) return collections;\n return collections.filter(\n (c) => (c.name ?? \"\").toLowerCase().includes(q) || (c.symbol ?? \"\").toLowerCase().includes(q)\n );\n }, [collections, query]);\n\n return (\n <div className=\"space-y-6\">\n {heading && (\n <div className=\"space-y-2\">\n <div className=\"flex items-center gap-2 text-primary\">\n <Coins className=\"h-5 w-5\" />\n <span className=\"text-sm font-semibold\">Coins</span>\n </div>\n <h1 className=\"text-3xl\">Creator coins &amp; memecoins</h1>\n </div>\n )}\n\n {/* Controls — search + filters + view, kept lean. */}\n <div className=\"space-y-3 border-b border-border/60 pb-3\">\n <div className=\"flex items-center gap-2\">\n <div className=\"relative flex-1\">\n <Search className=\"pointer-events-none absolute left-3 top-1/2 h-3.5 w-3.5 -translate-y-1/2 text-muted-foreground\" />\n <input\n value={query}\n onChange={(e) => setQuery(e.target.value)}\n placeholder=\"Search coins by name or symbol…\"\n className=\"w-full rounded-lg border border-border bg-background py-2 pl-9 pr-3 text-sm outline-none focus:border-primary/50\"\n />\n </div>\n <button\n onClick={() => setFiltersOpen(true)}\n className=\"inline-flex shrink-0 items-center gap-1.5 rounded-lg border border-border px-3 py-2 text-xs font-medium text-foreground hover:border-primary/50\"\n >\n <SlidersHorizontal className=\"h-3.5 w-3.5\" />\n Filters\n {filterCount > 0 && (\n <span className=\"ml-0.5 inline-flex h-4 min-w-4 items-center justify-center rounded-full bg-primary px-1 text-2xs font-semibold text-primary-foreground\">\n {filterCount}\n </span>\n )}\n </button>\n <div className=\"inline-flex shrink-0 rounded-lg border border-border p-0.5\">\n {([{ v: \"grid\", Icon: LayoutGrid }, { v: \"table\", Icon: List }] as const).map(({ v, Icon }) => (\n <button\n key={v}\n onClick={() => setView(v)}\n aria-label={v === \"grid\" ? \"Grid view\" : \"Table view\"}\n className={cn(\"rounded-md p-1.5 transition-colors\", view === v ? \"bg-primary/10 text-primary\" : \"text-muted-foreground hover:text-foreground\")}\n >\n <Icon className=\"h-4 w-4\" />\n </button>\n ))}\n </div>\n </div>\n\n {/* Active-filter chips */}\n {filterCount > 0 && (\n <div className=\"flex flex-wrap items-center gap-1.5\">\n {filter !== \"all\" && (\n <Chip onClear={() => setFilter(\"all\")}>{filterLabel(filter)}</Chip>\n )}\n {sort !== \"recent\" && (\n <Chip onClear={() => setSort(\"recent\")}>{sortLabel(sort)}</Chip>\n )}\n </div>\n )}\n </div>\n\n {isLoading && items.length === 0 ? (\n <div className=\"grid grid-cols-1 gap-4 sm:grid-cols-2 md:grid-cols-3 xl:grid-cols-4\">\n {Array.from({ length: 8 }).map((_, i) => <CoinCardSkeleton key={i} />)}\n </div>\n ) : items.length === 0 ? (\n <div className=\"rounded-xl border border-border/60 py-16 text-center text-muted-foreground\">\n {query.trim() ? `No coins match \"${query.trim()}\".` : \"No coins yet.\"}\n </div>\n ) : view === \"table\" ? (\n <div className=\"space-y-2\">\n {items.map((c) => <CoinRow key={`${c.chain}-${c.contractAddress}`} collection={c} usePrice={usePrice} href={coinHref(c)} />)}\n </div>\n ) : (\n <div className=\"grid grid-cols-1 gap-4 sm:grid-cols-2 md:grid-cols-3 xl:grid-cols-4\">\n {items.map((c) => <CoinCard key={`${c.chain}-${c.contractAddress}`} collection={c} usePrice={usePrice} href={coinHref(c)} />)}\n </div>\n )}\n\n {filtersOpen && (\n <div className=\"fixed inset-0 z-50 flex items-center justify-center p-4\" role=\"dialog\" aria-modal=\"true\" aria-label=\"Filters\">\n <div className=\"absolute inset-0 bg-background/70 backdrop-blur-sm\" onClick={() => setFiltersOpen(false)} />\n <div className=\"card-base relative z-10 w-full max-w-sm space-y-5 p-5\">\n <div className=\"flex items-center justify-between\">\n <h2 className=\"flex items-center gap-2 text-base font-bold\">\n <SlidersHorizontal className=\"h-4 w-4 text-primary\" />\n Filters\n </h2>\n {filterCount > 0 && (\n <button\n onClick={() => { setFilter(\"all\"); setSort(\"recent\"); }}\n className=\"text-xs font-medium text-muted-foreground hover:text-foreground\"\n >\n Clear all\n </button>\n )}\n </div>\n\n <FilterGroup label=\"Type\">\n {FILTER_TABS.map(({ label, value }) => (\n <PillButton key={value} active={filter === value} onClick={() => setFilter(value)}>{label}</PillButton>\n ))}\n </FilterGroup>\n\n <FilterGroup label=\"Sort\">\n {SORT_OPTIONS.map(({ label, value }) => (\n <PillButton key={value} active={sort === value} onClick={() => setSort(value)}>{label}</PillButton>\n ))}\n </FilterGroup>\n\n <button\n onClick={() => setFiltersOpen(false)}\n className=\"w-full rounded-lg bg-gradient-to-r from-brand-blue to-brand-purple py-2.5 text-sm font-semibold text-white\"\n >\n Show {items.length} {items.length === 1 ? \"coin\" : \"coins\"}\n </button>\n </div>\n </div>\n )}\n </div>\n );\n}\n\nfunction FilterGroup({ label, children }: { label: string; children: React.ReactNode }) {\n return (\n <div className=\"space-y-2\">\n <p className=\"text-2xs font-medium text-muted-foreground\">{label}</p>\n <div className=\"flex flex-wrap gap-1.5\">{children}</div>\n </div>\n );\n}\n\nfunction PillButton({ active, onClick, children }: { active: boolean; onClick: () => void; children: React.ReactNode }) {\n return (\n <button\n onClick={onClick}\n className={cn(\n \"rounded-lg border px-3 py-1.5 text-xs font-medium transition-colors\",\n active ? \"border-primary bg-primary/10 text-primary\" : \"border-border text-muted-foreground hover:border-primary/50 hover:text-foreground\"\n )}\n >\n {children}\n </button>\n );\n}\n\nfunction Chip({ children, onClear }: { children: React.ReactNode; onClear: () => void }) {\n return (\n <span className=\"inline-flex items-center gap-1 rounded-full border border-primary/30 bg-primary/10 px-2.5 py-1 text-xs font-medium text-primary\">\n {children}\n <button onClick={onClear} aria-label=\"Clear filter\" className=\"hover:text-primary/60\">\n <X className=\"h-3 w-3\" />\n </button>\n </span>\n );\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../../src/components/coins-explorer.tsx"],"sourcesContent":["\"use client\";\n\nimport { useState, useMemo, useEffect } from \"react\";\nimport { Coins, LayoutGrid, List, Search, SlidersHorizontal, X } from \"lucide-react\";\nimport { cn } from \"../utils/cn.js\";\nimport { CoinCard, CoinRow, CoinCardSkeleton, type UseCoinPrice } from \"./coin-card.js\";\nimport type { CoinCollectionLike } from \"../data/coins.js\";\n\nexport type CoinFilter = \"all\" | \"creator\" | \"memecoin\";\nexport type CoinSort = \"recent\" | \"name\";\nexport type UseCoins = (opts: { filter: CoinFilter; sort: CoinSort }) => {\n collections: CoinCollectionLike[];\n isLoading: boolean;\n};\n\nexport interface CoinsExplorerProps {\n useCoins: UseCoins;\n usePrice: UseCoinPrice;\n\n coinHref: (collection: CoinCollectionLike) => string;\n heading?: boolean;\n}\n\nconst FILTER_TABS: { label: string; value: CoinFilter }[] = [\n { label: \"All\", value: \"all\" },\n { label: \"Creator Coins\", value: \"creator\" },\n { label: \"Memecoins\", value: \"memecoin\" },\n];\n\nconst SORT_OPTIONS: { label: string; value: CoinSort }[] = [\n { label: \"Recently launched\", value: \"recent\" },\n { label: \"Name\", value: \"name\" },\n];\n\nconst filterLabel = (v: CoinFilter) => FILTER_TABS.find((t) => t.value === v)?.label ?? \"\";\nconst sortLabel = (v: CoinSort) => SORT_OPTIONS.find((o) => o.value === v)?.label ?? \"\";\n\nexport function CoinsExplorer({ useCoins, usePrice, coinHref, heading = true }: CoinsExplorerProps) {\n const [filter, setFilter] = useState<CoinFilter>(\"all\");\n const [sort, setSort] = useState<CoinSort>(\"recent\");\n const [view, setView] = useState<\"grid\" | \"table\">(\"grid\");\n const [query, setQuery] = useState(\"\");\n const [filtersOpen, setFiltersOpen] = useState(false);\n\n const filterCount = (filter !== \"all\" ? 1 : 0) + (sort !== \"recent\" ? 1 : 0);\n\n useEffect(() => {\n if (!filtersOpen) return;\n const onKey = (e: KeyboardEvent) => e.key === \"Escape\" && setFiltersOpen(false);\n window.addEventListener(\"keydown\", onKey);\n return () => window.removeEventListener(\"keydown\", onKey);\n }, [filtersOpen]);\n\n const { collections, isLoading } = useCoins({ filter, sort });\n const items = useMemo(() => {\n const q = query.trim().toLowerCase();\n if (!q) return collections;\n return collections.filter(\n (c) => (c.name ?? \"\").toLowerCase().includes(q) || (c.symbol ?? \"\").toLowerCase().includes(q)\n );\n }, [collections, query]);\n\n return (\n <div className=\"space-y-6\">\n {heading && (\n <div className=\"space-y-2\">\n <div className=\"flex items-center gap-2 text-primary\">\n <Coins className=\"h-5 w-5\" />\n <span className=\"text-sm font-semibold\">Coins</span>\n </div>\n <h1 className=\"text-3xl\">Creator coins &amp; memecoins</h1>\n </div>\n )}\n\n <div className=\"space-y-3 border-b border-border/60 pb-3\">\n <div className=\"flex items-center gap-2\">\n <div className=\"relative flex-1\">\n <Search className=\"pointer-events-none absolute left-3 top-1/2 h-3.5 w-3.5 -translate-y-1/2 text-muted-foreground\" />\n <input\n value={query}\n onChange={(e) => setQuery(e.target.value)}\n placeholder=\"Search coins by name or symbol…\"\n className=\"w-full rounded-lg border border-border bg-background py-2 pl-9 pr-3 text-sm outline-none focus:border-primary/50\"\n />\n </div>\n <button\n onClick={() => setFiltersOpen(true)}\n className=\"inline-flex shrink-0 items-center gap-1.5 rounded-lg border border-border px-3 py-2 text-xs font-medium text-foreground hover:border-primary/50\"\n >\n <SlidersHorizontal className=\"h-3.5 w-3.5\" />\n Filters\n {filterCount > 0 && (\n <span className=\"ml-0.5 inline-flex h-4 min-w-4 items-center justify-center rounded-full bg-primary px-1 text-2xs font-semibold text-primary-foreground\">\n {filterCount}\n </span>\n )}\n </button>\n <div className=\"inline-flex shrink-0 rounded-lg border border-border p-0.5\">\n {([{ v: \"grid\", Icon: LayoutGrid }, { v: \"table\", Icon: List }] as const).map(({ v, Icon }) => (\n <button\n key={v}\n onClick={() => setView(v)}\n aria-label={v === \"grid\" ? \"Grid view\" : \"Table view\"}\n className={cn(\"rounded-md p-1.5 transition-colors\", view === v ? \"bg-primary/10 text-primary\" : \"text-muted-foreground hover:text-foreground\")}\n >\n <Icon className=\"h-4 w-4\" />\n </button>\n ))}\n </div>\n </div>\n\n {filterCount > 0 && (\n <div className=\"flex flex-wrap items-center gap-1.5\">\n {filter !== \"all\" && (\n <Chip onClear={() => setFilter(\"all\")}>{filterLabel(filter)}</Chip>\n )}\n {sort !== \"recent\" && (\n <Chip onClear={() => setSort(\"recent\")}>{sortLabel(sort)}</Chip>\n )}\n </div>\n )}\n </div>\n\n {isLoading && items.length === 0 ? (\n <div className=\"grid grid-cols-1 gap-4 sm:grid-cols-2 md:grid-cols-3 xl:grid-cols-4\">\n {Array.from({ length: 8 }).map((_, i) => <CoinCardSkeleton key={i} />)}\n </div>\n ) : items.length === 0 ? (\n <div className=\"rounded-xl border border-border/60 py-16 text-center text-muted-foreground\">\n {query.trim() ? `No coins match \"${query.trim()}\".` : \"No coins yet.\"}\n </div>\n ) : view === \"table\" ? (\n <div className=\"space-y-2\">\n {items.map((c) => <CoinRow key={`${c.chain}-${c.contractAddress}`} collection={c} usePrice={usePrice} href={coinHref(c)} />)}\n </div>\n ) : (\n <div className=\"grid grid-cols-1 gap-4 sm:grid-cols-2 md:grid-cols-3 xl:grid-cols-4\">\n {items.map((c) => <CoinCard key={`${c.chain}-${c.contractAddress}`} collection={c} usePrice={usePrice} href={coinHref(c)} />)}\n </div>\n )}\n\n {filtersOpen && (\n <div className=\"fixed inset-0 z-50 flex items-center justify-center p-4\" role=\"dialog\" aria-modal=\"true\" aria-label=\"Filters\">\n <div className=\"absolute inset-0 bg-background/70 backdrop-blur-sm\" onClick={() => setFiltersOpen(false)} />\n <div className=\"card-base relative z-10 w-full max-w-sm space-y-5 p-5\">\n <div className=\"flex items-center justify-between\">\n <h2 className=\"flex items-center gap-2 text-base font-bold\">\n <SlidersHorizontal className=\"h-4 w-4 text-primary\" />\n Filters\n </h2>\n {filterCount > 0 && (\n <button\n onClick={() => { setFilter(\"all\"); setSort(\"recent\"); }}\n className=\"text-xs font-medium text-muted-foreground hover:text-foreground\"\n >\n Clear all\n </button>\n )}\n </div>\n\n <FilterGroup label=\"Type\">\n {FILTER_TABS.map(({ label, value }) => (\n <PillButton key={value} active={filter === value} onClick={() => setFilter(value)}>{label}</PillButton>\n ))}\n </FilterGroup>\n\n <FilterGroup label=\"Sort\">\n {SORT_OPTIONS.map(({ label, value }) => (\n <PillButton key={value} active={sort === value} onClick={() => setSort(value)}>{label}</PillButton>\n ))}\n </FilterGroup>\n\n <button\n onClick={() => setFiltersOpen(false)}\n className=\"w-full rounded-lg bg-gradient-to-r from-brand-blue to-brand-purple py-2.5 text-sm font-semibold text-white\"\n >\n Show {items.length} {items.length === 1 ? \"coin\" : \"coins\"}\n </button>\n </div>\n </div>\n )}\n </div>\n );\n}\n\nfunction FilterGroup({ label, children }: { label: string; children: React.ReactNode }) {\n return (\n <div className=\"space-y-2\">\n <p className=\"text-2xs font-medium text-muted-foreground\">{label}</p>\n <div className=\"flex flex-wrap gap-1.5\">{children}</div>\n </div>\n );\n}\n\nfunction PillButton({ active, onClick, children }: { active: boolean; onClick: () => void; children: React.ReactNode }) {\n return (\n <button\n onClick={onClick}\n className={cn(\n \"rounded-lg border px-3 py-1.5 text-xs font-medium transition-colors\",\n active ? \"border-primary bg-primary/10 text-primary\" : \"border-border text-muted-foreground hover:border-primary/50 hover:text-foreground\"\n )}\n >\n {children}\n </button>\n );\n}\n\nfunction Chip({ children, onClear }: { children: React.ReactNode; onClear: () => void }) {\n return (\n <span className=\"inline-flex items-center gap-1 rounded-full border border-primary/30 bg-primary/10 px-2.5 py-1 text-xs font-medium text-primary\">\n {children}\n <button onClick={onClear} aria-label=\"Clear filter\" className=\"hover:text-primary/60\">\n <X className=\"h-3 w-3\" />\n </button>\n </span>\n );\n}\n"],"mappings":";AAkEU,SACE,KADF;AAhEV,SAAS,UAAU,SAAS,iBAAiB;AAC7C,SAAS,OAAO,YAAY,MAAM,QAAQ,mBAAmB,SAAS;AACtE,SAAS,UAAU;AACnB,SAAS,UAAU,SAAS,wBAA2C;AAkBvE,MAAM,cAAsD;AAAA,EAC1D,EAAE,OAAO,OAAO,OAAO,MAAM;AAAA,EAC7B,EAAE,OAAO,iBAAiB,OAAO,UAAU;AAAA,EAC3C,EAAE,OAAO,aAAa,OAAO,WAAW;AAC1C;AAEA,MAAM,eAAqD;AAAA,EACzD,EAAE,OAAO,qBAAqB,OAAO,SAAS;AAAA,EAC9C,EAAE,OAAO,QAAQ,OAAO,OAAO;AACjC;AAEA,MAAM,cAAc,CAAC,MAAkB,YAAY,KAAK,CAAC,MAAM,EAAE,UAAU,CAAC,GAAG,SAAS;AACxF,MAAM,YAAY,CAAC,MAAgB,aAAa,KAAK,CAAC,MAAM,EAAE,UAAU,CAAC,GAAG,SAAS;AAE9E,SAAS,cAAc,EAAE,UAAU,UAAU,UAAU,UAAU,KAAK,GAAuB;AAClG,QAAM,CAAC,QAAQ,SAAS,IAAI,SAAqB,KAAK;AACtD,QAAM,CAAC,MAAM,OAAO,IAAI,SAAmB,QAAQ;AACnD,QAAM,CAAC,MAAM,OAAO,IAAI,SAA2B,MAAM;AACzD,QAAM,CAAC,OAAO,QAAQ,IAAI,SAAS,EAAE;AACrC,QAAM,CAAC,aAAa,cAAc,IAAI,SAAS,KAAK;AAEpD,QAAM,eAAe,WAAW,QAAQ,IAAI,MAAM,SAAS,WAAW,IAAI;AAE1E,YAAU,MAAM;AACd,QAAI,CAAC,YAAa;AAClB,UAAM,QAAQ,CAAC,MAAqB,EAAE,QAAQ,YAAY,eAAe,KAAK;AAC9E,WAAO,iBAAiB,WAAW,KAAK;AACxC,WAAO,MAAM,OAAO,oBAAoB,WAAW,KAAK;AAAA,EAC1D,GAAG,CAAC,WAAW,CAAC;AAEhB,QAAM,EAAE,aAAa,UAAU,IAAI,SAAS,EAAE,QAAQ,KAAK,CAAC;AAC5D,QAAM,QAAQ,QAAQ,MAAM;AAC1B,UAAM,IAAI,MAAM,KAAK,EAAE,YAAY;AACnC,QAAI,CAAC,EAAG,QAAO;AACf,WAAO,YAAY;AAAA,MACjB,CAAC,OAAO,EAAE,QAAQ,IAAI,YAAY,EAAE,SAAS,CAAC,MAAM,EAAE,UAAU,IAAI,YAAY,EAAE,SAAS,CAAC;AAAA,IAC9F;AAAA,EACF,GAAG,CAAC,aAAa,KAAK,CAAC;AAEvB,SACE,qBAAC,SAAI,WAAU,aACZ;AAAA,eACC,qBAAC,SAAI,WAAU,aACb;AAAA,2BAAC,SAAI,WAAU,wCACb;AAAA,4BAAC,SAAM,WAAU,WAAU;AAAA,QAC3B,oBAAC,UAAK,WAAU,yBAAwB,mBAAK;AAAA,SAC/C;AAAA,MACA,oBAAC,QAAG,WAAU,YAAW,uCAA6B;AAAA,OACxD;AAAA,IAGF,qBAAC,SAAI,WAAU,4CACb;AAAA,2BAAC,SAAI,WAAU,2BACb;AAAA,6BAAC,SAAI,WAAU,mBACb;AAAA,8BAAC,UAAO,WAAU,kGAAiG;AAAA,UACnH;AAAA,YAAC;AAAA;AAAA,cACC,OAAO;AAAA,cACP,UAAU,CAAC,MAAM,SAAS,EAAE,OAAO,KAAK;AAAA,cACxC,aAAY;AAAA,cACZ,WAAU;AAAA;AAAA,UACZ;AAAA,WACF;AAAA,QACA;AAAA,UAAC;AAAA;AAAA,YACC,SAAS,MAAM,eAAe,IAAI;AAAA,YAClC,WAAU;AAAA,YAEV;AAAA,kCAAC,qBAAkB,WAAU,eAAc;AAAA,cAAE;AAAA,cAE5C,cAAc,KACb,oBAAC,UAAK,WAAU,0IACb,uBACH;AAAA;AAAA;AAAA,QAEJ;AAAA,QACA,oBAAC,SAAI,WAAU,8DACX,WAAC,EAAE,GAAG,QAAQ,MAAM,WAAW,GAAG,EAAE,GAAG,SAAS,MAAM,KAAK,CAAC,EAAY,IAAI,CAAC,EAAE,GAAG,KAAK,MACvF;AAAA,UAAC;AAAA;AAAA,YAEC,SAAS,MAAM,QAAQ,CAAC;AAAA,YACxB,cAAY,MAAM,SAAS,cAAc;AAAA,YACzC,WAAW,GAAG,sCAAsC,SAAS,IAAI,+BAA+B,6CAA6C;AAAA,YAE7I,8BAAC,QAAK,WAAU,WAAU;AAAA;AAAA,UALrB;AAAA,QAMP,CACD,GACH;AAAA,SACF;AAAA,MAEC,cAAc,KACb,qBAAC,SAAI,WAAU,uCACZ;AAAA,mBAAW,SACV,oBAAC,QAAK,SAAS,MAAM,UAAU,KAAK,GAAI,sBAAY,MAAM,GAAE;AAAA,QAE7D,SAAS,YACR,oBAAC,QAAK,SAAS,MAAM,QAAQ,QAAQ,GAAI,oBAAU,IAAI,GAAE;AAAA,SAE7D;AAAA,OAEJ;AAAA,IAEC,aAAa,MAAM,WAAW,IAC7B,oBAAC,SAAI,WAAU,uEACZ,gBAAM,KAAK,EAAE,QAAQ,EAAE,CAAC,EAAE,IAAI,CAAC,GAAG,MAAM,oBAAC,sBAAsB,CAAG,CAAE,GACvE,IACE,MAAM,WAAW,IACnB,oBAAC,SAAI,WAAU,8EACZ,gBAAM,KAAK,IAAI,mBAAmB,MAAM,KAAK,CAAC,OAAO,iBACxD,IACE,SAAS,UACX,oBAAC,SAAI,WAAU,aACZ,gBAAM,IAAI,CAAC,MAAM,oBAAC,WAAgD,YAAY,GAAG,UAAoB,MAAM,SAAS,CAAC,KAAtF,GAAG,EAAE,KAAK,IAAI,EAAE,eAAe,EAA0D,CAAE,GAC7H,IAEA,oBAAC,SAAI,WAAU,uEACZ,gBAAM,IAAI,CAAC,MAAM,oBAAC,YAAiD,YAAY,GAAG,UAAoB,MAAM,SAAS,CAAC,KAAtF,GAAG,EAAE,KAAK,IAAI,EAAE,eAAe,EAA0D,CAAE,GAC9H;AAAA,IAGD,eACC,qBAAC,SAAI,WAAU,2DAA0D,MAAK,UAAS,cAAW,QAAO,cAAW,WAClH;AAAA,0BAAC,SAAI,WAAU,sDAAqD,SAAS,MAAM,eAAe,KAAK,GAAG;AAAA,MAC1G,qBAAC,SAAI,WAAU,yDACb;AAAA,6BAAC,SAAI,WAAU,qCACb;AAAA,+BAAC,QAAG,WAAU,+CACZ;AAAA,gCAAC,qBAAkB,WAAU,wBAAuB;AAAA,YAAE;AAAA,aAExD;AAAA,UACC,cAAc,KACb;AAAA,YAAC;AAAA;AAAA,cACC,SAAS,MAAM;AAAE,0BAAU,KAAK;AAAG,wBAAQ,QAAQ;AAAA,cAAG;AAAA,cACtD,WAAU;AAAA,cACX;AAAA;AAAA,UAED;AAAA,WAEJ;AAAA,QAEA,oBAAC,eAAY,OAAM,QAChB,sBAAY,IAAI,CAAC,EAAE,OAAO,MAAM,MAC/B,oBAAC,cAAuB,QAAQ,WAAW,OAAO,SAAS,MAAM,UAAU,KAAK,GAAI,mBAAnE,KAAyE,CAC3F,GACH;AAAA,QAEA,oBAAC,eAAY,OAAM,QAChB,uBAAa,IAAI,CAAC,EAAE,OAAO,MAAM,MAChC,oBAAC,cAAuB,QAAQ,SAAS,OAAO,SAAS,MAAM,QAAQ,KAAK,GAAI,mBAA/D,KAAqE,CACvF,GACH;AAAA,QAEA;AAAA,UAAC;AAAA;AAAA,YACC,SAAS,MAAM,eAAe,KAAK;AAAA,YACnC,WAAU;AAAA,YACX;AAAA;AAAA,cACO,MAAM;AAAA,cAAO;AAAA,cAAE,MAAM,WAAW,IAAI,SAAS;AAAA;AAAA;AAAA,QACrD;AAAA,SACF;AAAA,OACF;AAAA,KAEJ;AAEJ;AAEA,SAAS,YAAY,EAAE,OAAO,SAAS,GAAiD;AACtF,SACE,qBAAC,SAAI,WAAU,aACb;AAAA,wBAAC,OAAE,WAAU,8CAA8C,iBAAM;AAAA,IACjE,oBAAC,SAAI,WAAU,0BAA0B,UAAS;AAAA,KACpD;AAEJ;AAEA,SAAS,WAAW,EAAE,QAAQ,SAAS,SAAS,GAAwE;AACtH,SACE;AAAA,IAAC;AAAA;AAAA,MACC;AAAA,MACA,WAAW;AAAA,QACT;AAAA,QACA,SAAS,8CAA8C;AAAA,MACzD;AAAA,MAEC;AAAA;AAAA,EACH;AAEJ;AAEA,SAAS,KAAK,EAAE,UAAU,QAAQ,GAAuD;AACvF,SACE,qBAAC,UAAK,WAAU,mIACb;AAAA;AAAA,IACD,oBAAC,YAAO,SAAS,SAAS,cAAW,gBAAe,WAAU,yBAC5D,8BAAC,KAAE,WAAU,WAAU,GACzB;AAAA,KACF;AAEJ;","names":[]}
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/components/collection-card.tsx"],"sourcesContent":["\"use client\";\n\nimport { useState } from \"react\";\nimport Link from \"next/link\";\nimport Image from \"next/image\";\nimport { Loader2, Settings2 } from \"lucide-react\";\nimport { cn } from \"../utils/cn.js\";\nimport { ipfsToHttp } from \"../utils/ipfs.js\";\nimport { formatDisplayPrice } from \"../utils/format.js\";\nimport { MotionCard } from \"./motion-primitives.js\";\nimport { CurrencyIcon } from \"./currency-icon.js\";\nimport type { ApiCollection } from \"@medialane/sdk\";\n\n/** \"0.990000 USDC\" → { amount: \"0.99\", symbol: \"USDC\" } */\nfunction parseFloorPrice(floor: string | null | undefined): { amount: string; symbol: string } | null {\n if (!floor) return null;\n const formatted = formatDisplayPrice(floor);\n const parts = formatted.trim().split(/\\s+/);\n if (parts.length < 2) return null;\n const symbol = parts[parts.length - 1];\n const amount = parts.slice(0, -1).join(\" \");\n return { amount, symbol };\n}\n\n/** ui's pinned SDK lags the apps — extend structurally for newer fields. */\ntype CollectionWithProfile = ApiCollection & {\n profile?: { hasGatedContent?: boolean } | null;\n};\n\nexport interface CollectionCardProps {\n collection: ApiCollection;\n /** Override the card link destination. Defaults to /collections/:contractAddress. */\n href?: string;\n /** Shows settings gear icon linking to this path — used in portfolio pages */\n settingsHref?: string;\n className?: string;\n}\n\nexport function CollectionCard({ collection, href, settingsHref, className }: CollectionCardProps) {\n const [imgError, setImgError] = useState(false);\n const imageUrl = collection.image ? ipfsToHttp(collection.image) : null;\n const showImage = imageUrl && !imgError;\n const initial = (collection.name ?? collection.contractAddress).charAt(0).toUpperCase();\n const floor = parseFloorPrice(collection.floorPrice);\n const hasExclusiveContent = !!(collection as CollectionWithProfile).profile?.hasGatedContent;\n\n return (\n <div className={cn(hasExclusiveContent && \"card-exclusive-wrapper btn-border-animated\")}>\n <MotionCard className={cn(\"card-base group relative\", className)}>\n {settingsHref && (\n <Link\n href={settingsHref}\n onClick={(e) => e.stopPropagation()}\n className=\"absolute top-2 right-2 z-10 min-h-11 min-w-11 rounded-full flex items-center justify-center\"\n aria-label=\"Collection settings\"\n >\n <span className=\"flex h-7 w-7 items-center justify-center rounded-full bg-black/40 backdrop-blur-sm text-white/70 hover:text-white hover:bg-black/60 transition-colors\">\n <Settings2 className=\"h-3.5 w-3.5\" />\n </span>\n </Link>\n )}\n\n <Link href={href ?? `/collections/${collection.contractAddress}`} className=\"block relative h-full\">\n <div className=\"relative aspect-[3/4] w-full overflow-hidden bg-muted\">\n {showImage ? (\n <Image\n src={imageUrl}\n alt={collection.name ?? \"Collection\"}\n fill\n className=\"object-cover transition-transform duration-500 group-hover:scale-105\"\n onError={() => setImgError(true)}\n unoptimized\n />\n ) : (\n <div className=\"absolute inset-0 bg-gradient-to-br from-brand-purple/30 via-brand-blue/20 to-brand-navy/40 flex items-center justify-center\">\n <span className=\"text-8xl font-black text-white/10 select-none tracking-tighter\">\n {initial}\n </span>\n </div>\n )}\n\n <div className=\"absolute bottom-0 left-0 right-0 px-3 pb-3 flex flex-col gap-1.5 items-start\">\n {!collection.name && collection.metadataStatus === \"PENDING\" ? (\n <span className=\"flex items-center gap-1 text-2xs text-white/60 backdrop-blur-md bg-black/30 rounded-full px-2 py-0.5\">\n <Loader2 className=\"h-2.5 w-2.5 animate-spin\" />\n Indexing…\n </span>\n ) : (\n <p\n className=\"font-bold text-sm text-white leading-tight backdrop-blur-md bg-black/30 rounded-lg px-2.5 py-1 max-w-full truncate\"\n style={{ textShadow: \"0 1px 6px rgba(0,0,0,0.5)\" }}\n >\n {collection.name ?? \"Unnamed Collection\"}\n </p>\n )}\n\n <div className=\"flex items-center gap-1.5 flex-wrap\">\n {collection.totalSupply != null && (\n <span className=\"text-2xs font-medium text-white/80 backdrop-blur-md bg-black/30 rounded-full px-2 py-0.5\">\n {collection.totalSupply.toLocaleString()} items\n </span>\n )}\n {floor && (\n <span className=\"inline-flex items-center gap-1 text-2xs font-bold text-white/90 backdrop-blur-md bg-black/30 rounded-full px-2 py-0.5\">\n Floor\n <CurrencyIcon symbol={floor.symbol} size={10} />\n {floor.amount}\n </span>\n )}\n {hasExclusiveContent && (\n <span className=\"inline-flex items-center gap-1 text-2xs font-bold text-white backdrop-blur-md bg-black/50 rounded-full px-2 py-0.5\">\n ✦ Exclusive\n </span>\n )}\n </div>\n </div>\n </div>\n </Link>\n </MotionCard>\n </div>\n );\n}\n\nexport function CollectionCardSkeleton() {\n return (\n <div className=\"card-base overflow-hidden\">\n <div className=\"relative aspect-[3/4] w-full overflow-hidden bg-muted\">\n <div className=\"absolute inset-0 animate-pulse bg-muted\" />\n <div className=\"absolute bottom-3 left-3 right-3 space-y-1.5\">\n <div className=\"h-4 w-2/3 rounded-md bg-muted-foreground/20 animate-pulse\" />\n <div className=\"h-3 w-1/3 rounded-md bg-muted-foreground/20 animate-pulse\" />\n </div>\n </div>\n </div>\n );\n}\n"],"mappings":"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","names":["Link","Image"]}
1
+ {"version":3,"sources":["../../src/components/collection-card.tsx"],"sourcesContent":["\"use client\";\n\nimport { useState } from \"react\";\nimport Link from \"next/link\";\nimport Image from \"next/image\";\nimport { Loader2, Settings2 } from \"lucide-react\";\nimport { cn } from \"../utils/cn.js\";\nimport { ipfsToHttp } from \"../utils/ipfs.js\";\nimport { formatDisplayPrice } from \"../utils/format.js\";\nimport { MotionCard } from \"./motion-primitives.js\";\nimport { CurrencyIcon } from \"./currency-icon.js\";\nimport type { ApiCollection } from \"@medialane/sdk\";\n\nfunction parseFloorPrice(floor: string | null | undefined): { amount: string; symbol: string } | null {\n if (!floor) return null;\n const formatted = formatDisplayPrice(floor);\n const parts = formatted.trim().split(/\\s+/);\n if (parts.length < 2) return null;\n const symbol = parts[parts.length - 1];\n const amount = parts.slice(0, -1).join(\" \");\n return { amount, symbol };\n}\n\ntype CollectionWithProfile = ApiCollection & {\n profile?: { hasGatedContent?: boolean } | null;\n};\n\nexport interface CollectionCardProps {\n collection: ApiCollection;\n\n href?: string;\n\n settingsHref?: string;\n className?: string;\n}\n\nexport function CollectionCard({ collection, href, settingsHref, className }: CollectionCardProps) {\n const [imgError, setImgError] = useState(false);\n const imageUrl = collection.image ? ipfsToHttp(collection.image) : null;\n const showImage = imageUrl && !imgError;\n const initial = (collection.name ?? collection.contractAddress).charAt(0).toUpperCase();\n const floor = parseFloorPrice(collection.floorPrice);\n const hasExclusiveContent = !!(collection as CollectionWithProfile).profile?.hasGatedContent;\n\n return (\n <div className={cn(hasExclusiveContent && \"card-exclusive-wrapper btn-border-animated\")}>\n <MotionCard className={cn(\"card-base group relative\", className)}>\n {settingsHref && (\n <Link\n href={settingsHref}\n onClick={(e) => e.stopPropagation()}\n className=\"absolute top-2 right-2 z-10 min-h-11 min-w-11 rounded-full flex items-center justify-center\"\n aria-label=\"Collection settings\"\n >\n <span className=\"flex h-7 w-7 items-center justify-center rounded-full bg-black/40 backdrop-blur-sm text-white/70 hover:text-white hover:bg-black/60 transition-colors\">\n <Settings2 className=\"h-3.5 w-3.5\" />\n </span>\n </Link>\n )}\n\n <Link href={href ?? `/collections/${collection.contractAddress}`} className=\"block relative h-full\">\n <div className=\"relative aspect-[3/4] w-full overflow-hidden bg-muted\">\n {showImage ? (\n <Image\n src={imageUrl}\n alt={collection.name ?? \"Collection\"}\n fill\n className=\"object-cover transition-transform duration-500 group-hover:scale-105\"\n onError={() => setImgError(true)}\n unoptimized\n />\n ) : (\n <div className=\"absolute inset-0 bg-gradient-to-br from-brand-purple/30 via-brand-blue/20 to-brand-navy/40 flex items-center justify-center\">\n <span className=\"text-8xl font-black text-white/10 select-none tracking-tighter\">\n {initial}\n </span>\n </div>\n )}\n\n <div className=\"absolute bottom-0 left-0 right-0 px-3 pb-3 flex flex-col gap-1.5 items-start\">\n {!collection.name && collection.metadataStatus === \"PENDING\" ? (\n <span className=\"flex items-center gap-1 text-2xs text-white/60 backdrop-blur-md bg-black/30 rounded-full px-2 py-0.5\">\n <Loader2 className=\"h-2.5 w-2.5 animate-spin\" />\n Indexing…\n </span>\n ) : (\n <p\n className=\"font-bold text-sm text-white leading-tight backdrop-blur-md bg-black/30 rounded-lg px-2.5 py-1 max-w-full truncate\"\n style={{ textShadow: \"0 1px 6px rgba(0,0,0,0.5)\" }}\n >\n {collection.name ?? \"Unnamed Collection\"}\n </p>\n )}\n\n <div className=\"flex items-center gap-1.5 flex-wrap\">\n {collection.totalSupply != null && (\n <span className=\"text-2xs font-medium text-white/80 backdrop-blur-md bg-black/30 rounded-full px-2 py-0.5\">\n {collection.totalSupply.toLocaleString()} items\n </span>\n )}\n {floor && (\n <span className=\"inline-flex items-center gap-1 text-2xs font-bold text-white/90 backdrop-blur-md bg-black/30 rounded-full px-2 py-0.5\">\n Floor\n <CurrencyIcon symbol={floor.symbol} size={10} />\n {floor.amount}\n </span>\n )}\n {hasExclusiveContent && (\n <span className=\"inline-flex items-center gap-1 text-2xs font-bold text-white backdrop-blur-md bg-black/50 rounded-full px-2 py-0.5\">\n ✦ Exclusive\n </span>\n )}\n </div>\n </div>\n </div>\n </Link>\n </MotionCard>\n </div>\n );\n}\n\nexport function CollectionCardSkeleton() {\n return (\n <div className=\"card-base overflow-hidden\">\n <div className=\"relative aspect-[3/4] w-full overflow-hidden bg-muted\">\n <div className=\"absolute inset-0 animate-pulse bg-muted\" />\n <div className=\"absolute bottom-3 left-3 right-3 space-y-1.5\">\n <div className=\"h-4 w-2/3 rounded-md bg-muted-foreground/20 animate-pulse\" />\n <div className=\"h-3 w-1/3 rounded-md bg-muted-foreground/20 animate-pulse\" />\n </div>\n </div>\n </div>\n );\n}\n"],"mappings":"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","names":["Link","Image"]}
@@ -3,9 +3,7 @@ import { ApiCollection } from '@medialane/sdk';
3
3
 
4
4
  interface CollectionCardProps {
5
5
  collection: ApiCollection;
6
- /** Override the card link destination. Defaults to /collections/:contractAddress. */
7
6
  href?: string;
8
- /** Shows settings gear icon linking to this path — used in portfolio pages */
9
7
  settingsHref?: string;
10
8
  className?: string;
11
9
  }
@@ -3,9 +3,7 @@ import { ApiCollection } from '@medialane/sdk';
3
3
 
4
4
  interface CollectionCardProps {
5
5
  collection: ApiCollection;
6
- /** Override the card link destination. Defaults to /collections/:contractAddress. */
7
6
  href?: string;
8
- /** Shows settings gear icon linking to this path — used in portfolio pages */
9
7
  settingsHref?: string;
10
8
  className?: string;
11
9
  }
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/components/collection-card.tsx"],"sourcesContent":["\"use client\";\n\nimport { useState } from \"react\";\nimport Link from \"next/link\";\nimport Image from \"next/image\";\nimport { Loader2, Settings2 } from \"lucide-react\";\nimport { cn } from \"../utils/cn.js\";\nimport { ipfsToHttp } from \"../utils/ipfs.js\";\nimport { formatDisplayPrice } from \"../utils/format.js\";\nimport { MotionCard } from \"./motion-primitives.js\";\nimport { CurrencyIcon } from \"./currency-icon.js\";\nimport type { ApiCollection } from \"@medialane/sdk\";\n\n/** \"0.990000 USDC\" → { amount: \"0.99\", symbol: \"USDC\" } */\nfunction parseFloorPrice(floor: string | null | undefined): { amount: string; symbol: string } | null {\n if (!floor) return null;\n const formatted = formatDisplayPrice(floor);\n const parts = formatted.trim().split(/\\s+/);\n if (parts.length < 2) return null;\n const symbol = parts[parts.length - 1];\n const amount = parts.slice(0, -1).join(\" \");\n return { amount, symbol };\n}\n\n/** ui's pinned SDK lags the apps — extend structurally for newer fields. */\ntype CollectionWithProfile = ApiCollection & {\n profile?: { hasGatedContent?: boolean } | null;\n};\n\nexport interface CollectionCardProps {\n collection: ApiCollection;\n /** Override the card link destination. Defaults to /collections/:contractAddress. */\n href?: string;\n /** Shows settings gear icon linking to this path — used in portfolio pages */\n settingsHref?: string;\n className?: string;\n}\n\nexport function CollectionCard({ collection, href, settingsHref, className }: CollectionCardProps) {\n const [imgError, setImgError] = useState(false);\n const imageUrl = collection.image ? ipfsToHttp(collection.image) : null;\n const showImage = imageUrl && !imgError;\n const initial = (collection.name ?? collection.contractAddress).charAt(0).toUpperCase();\n const floor = parseFloorPrice(collection.floorPrice);\n const hasExclusiveContent = !!(collection as CollectionWithProfile).profile?.hasGatedContent;\n\n return (\n <div className={cn(hasExclusiveContent && \"card-exclusive-wrapper btn-border-animated\")}>\n <MotionCard className={cn(\"card-base group relative\", className)}>\n {settingsHref && (\n <Link\n href={settingsHref}\n onClick={(e) => e.stopPropagation()}\n className=\"absolute top-2 right-2 z-10 min-h-11 min-w-11 rounded-full flex items-center justify-center\"\n aria-label=\"Collection settings\"\n >\n <span className=\"flex h-7 w-7 items-center justify-center rounded-full bg-black/40 backdrop-blur-sm text-white/70 hover:text-white hover:bg-black/60 transition-colors\">\n <Settings2 className=\"h-3.5 w-3.5\" />\n </span>\n </Link>\n )}\n\n <Link href={href ?? `/collections/${collection.contractAddress}`} className=\"block relative h-full\">\n <div className=\"relative aspect-[3/4] w-full overflow-hidden bg-muted\">\n {showImage ? (\n <Image\n src={imageUrl}\n alt={collection.name ?? \"Collection\"}\n fill\n className=\"object-cover transition-transform duration-500 group-hover:scale-105\"\n onError={() => setImgError(true)}\n unoptimized\n />\n ) : (\n <div className=\"absolute inset-0 bg-gradient-to-br from-brand-purple/30 via-brand-blue/20 to-brand-navy/40 flex items-center justify-center\">\n <span className=\"text-8xl font-black text-white/10 select-none tracking-tighter\">\n {initial}\n </span>\n </div>\n )}\n\n <div className=\"absolute bottom-0 left-0 right-0 px-3 pb-3 flex flex-col gap-1.5 items-start\">\n {!collection.name && collection.metadataStatus === \"PENDING\" ? (\n <span className=\"flex items-center gap-1 text-2xs text-white/60 backdrop-blur-md bg-black/30 rounded-full px-2 py-0.5\">\n <Loader2 className=\"h-2.5 w-2.5 animate-spin\" />\n Indexing…\n </span>\n ) : (\n <p\n className=\"font-bold text-sm text-white leading-tight backdrop-blur-md bg-black/30 rounded-lg px-2.5 py-1 max-w-full truncate\"\n style={{ textShadow: \"0 1px 6px rgba(0,0,0,0.5)\" }}\n >\n {collection.name ?? \"Unnamed Collection\"}\n </p>\n )}\n\n <div className=\"flex items-center gap-1.5 flex-wrap\">\n {collection.totalSupply != null && (\n <span className=\"text-2xs font-medium text-white/80 backdrop-blur-md bg-black/30 rounded-full px-2 py-0.5\">\n {collection.totalSupply.toLocaleString()} items\n </span>\n )}\n {floor && (\n <span className=\"inline-flex items-center gap-1 text-2xs font-bold text-white/90 backdrop-blur-md bg-black/30 rounded-full px-2 py-0.5\">\n Floor\n <CurrencyIcon symbol={floor.symbol} size={10} />\n {floor.amount}\n </span>\n )}\n {hasExclusiveContent && (\n <span className=\"inline-flex items-center gap-1 text-2xs font-bold text-white backdrop-blur-md bg-black/50 rounded-full px-2 py-0.5\">\n ✦ Exclusive\n </span>\n )}\n </div>\n </div>\n </div>\n </Link>\n </MotionCard>\n </div>\n );\n}\n\nexport function CollectionCardSkeleton() {\n return (\n <div className=\"card-base overflow-hidden\">\n <div className=\"relative aspect-[3/4] w-full overflow-hidden bg-muted\">\n <div className=\"absolute inset-0 animate-pulse bg-muted\" />\n <div className=\"absolute bottom-3 left-3 right-3 space-y-1.5\">\n <div className=\"h-4 w-2/3 rounded-md bg-muted-foreground/20 animate-pulse\" />\n <div className=\"h-3 w-1/3 rounded-md bg-muted-foreground/20 animate-pulse\" />\n </div>\n </div>\n </div>\n );\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../../src/components/collection-card.tsx"],"sourcesContent":["\"use client\";\n\nimport { useState } from \"react\";\nimport Link from \"next/link\";\nimport Image from \"next/image\";\nimport { Loader2, Settings2 } from \"lucide-react\";\nimport { cn } from \"../utils/cn.js\";\nimport { ipfsToHttp } from \"../utils/ipfs.js\";\nimport { formatDisplayPrice } from \"../utils/format.js\";\nimport { MotionCard } from \"./motion-primitives.js\";\nimport { CurrencyIcon } from \"./currency-icon.js\";\nimport type { ApiCollection } from \"@medialane/sdk\";\n\nfunction parseFloorPrice(floor: string | null | undefined): { amount: string; symbol: string } | null {\n if (!floor) return null;\n const formatted = formatDisplayPrice(floor);\n const parts = formatted.trim().split(/\\s+/);\n if (parts.length < 2) return null;\n const symbol = parts[parts.length - 1];\n const amount = parts.slice(0, -1).join(\" \");\n return { amount, symbol };\n}\n\ntype CollectionWithProfile = ApiCollection & {\n profile?: { hasGatedContent?: boolean } | null;\n};\n\nexport interface CollectionCardProps {\n collection: ApiCollection;\n\n href?: string;\n\n settingsHref?: string;\n className?: string;\n}\n\nexport function CollectionCard({ collection, href, settingsHref, className }: CollectionCardProps) {\n const [imgError, setImgError] = useState(false);\n const imageUrl = collection.image ? ipfsToHttp(collection.image) : null;\n const showImage = imageUrl && !imgError;\n const initial = (collection.name ?? collection.contractAddress).charAt(0).toUpperCase();\n const floor = parseFloorPrice(collection.floorPrice);\n const hasExclusiveContent = !!(collection as CollectionWithProfile).profile?.hasGatedContent;\n\n return (\n <div className={cn(hasExclusiveContent && \"card-exclusive-wrapper btn-border-animated\")}>\n <MotionCard className={cn(\"card-base group relative\", className)}>\n {settingsHref && (\n <Link\n href={settingsHref}\n onClick={(e) => e.stopPropagation()}\n className=\"absolute top-2 right-2 z-10 min-h-11 min-w-11 rounded-full flex items-center justify-center\"\n aria-label=\"Collection settings\"\n >\n <span className=\"flex h-7 w-7 items-center justify-center rounded-full bg-black/40 backdrop-blur-sm text-white/70 hover:text-white hover:bg-black/60 transition-colors\">\n <Settings2 className=\"h-3.5 w-3.5\" />\n </span>\n </Link>\n )}\n\n <Link href={href ?? `/collections/${collection.contractAddress}`} className=\"block relative h-full\">\n <div className=\"relative aspect-[3/4] w-full overflow-hidden bg-muted\">\n {showImage ? (\n <Image\n src={imageUrl}\n alt={collection.name ?? \"Collection\"}\n fill\n className=\"object-cover transition-transform duration-500 group-hover:scale-105\"\n onError={() => setImgError(true)}\n unoptimized\n />\n ) : (\n <div className=\"absolute inset-0 bg-gradient-to-br from-brand-purple/30 via-brand-blue/20 to-brand-navy/40 flex items-center justify-center\">\n <span className=\"text-8xl font-black text-white/10 select-none tracking-tighter\">\n {initial}\n </span>\n </div>\n )}\n\n <div className=\"absolute bottom-0 left-0 right-0 px-3 pb-3 flex flex-col gap-1.5 items-start\">\n {!collection.name && collection.metadataStatus === \"PENDING\" ? (\n <span className=\"flex items-center gap-1 text-2xs text-white/60 backdrop-blur-md bg-black/30 rounded-full px-2 py-0.5\">\n <Loader2 className=\"h-2.5 w-2.5 animate-spin\" />\n Indexing…\n </span>\n ) : (\n <p\n className=\"font-bold text-sm text-white leading-tight backdrop-blur-md bg-black/30 rounded-lg px-2.5 py-1 max-w-full truncate\"\n style={{ textShadow: \"0 1px 6px rgba(0,0,0,0.5)\" }}\n >\n {collection.name ?? \"Unnamed Collection\"}\n </p>\n )}\n\n <div className=\"flex items-center gap-1.5 flex-wrap\">\n {collection.totalSupply != null && (\n <span className=\"text-2xs font-medium text-white/80 backdrop-blur-md bg-black/30 rounded-full px-2 py-0.5\">\n {collection.totalSupply.toLocaleString()} items\n </span>\n )}\n {floor && (\n <span className=\"inline-flex items-center gap-1 text-2xs font-bold text-white/90 backdrop-blur-md bg-black/30 rounded-full px-2 py-0.5\">\n Floor\n <CurrencyIcon symbol={floor.symbol} size={10} />\n {floor.amount}\n </span>\n )}\n {hasExclusiveContent && (\n <span className=\"inline-flex items-center gap-1 text-2xs font-bold text-white backdrop-blur-md bg-black/50 rounded-full px-2 py-0.5\">\n ✦ Exclusive\n </span>\n )}\n </div>\n </div>\n </div>\n </Link>\n </MotionCard>\n </div>\n );\n}\n\nexport function CollectionCardSkeleton() {\n return (\n <div className=\"card-base overflow-hidden\">\n <div className=\"relative aspect-[3/4] w-full overflow-hidden bg-muted\">\n <div className=\"absolute inset-0 animate-pulse bg-muted\" />\n <div className=\"absolute bottom-3 left-3 right-3 space-y-1.5\">\n <div className=\"h-4 w-2/3 rounded-md bg-muted-foreground/20 animate-pulse\" />\n <div className=\"h-3 w-1/3 rounded-md bg-muted-foreground/20 animate-pulse\" />\n </div>\n </div>\n </div>\n );\n}\n"],"mappings":"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","names":[]}
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/components/collection-filters.tsx"],"sourcesContent":["\"use client\";\n\nimport { useMemo } from \"react\";\nimport { SlidersHorizontal, X, Check } from \"lucide-react\";\nimport { cn } from \"../utils/cn.js\";\nimport type { ApiToken, CollectionTokensSort } from \"@medialane/sdk\";\n\n// Trait types that are per-token stamps rather than real categories — never\n// useful as a filter regardless of how many distinct values they have.\n// \"Registration\" is the Berne Convention registration date, near-unique per\n// mint; filtering by an exact date isn't a meaningful narrowing action.\nconst EXCLUDED_TRAIT_TYPES = new Set([\"registration\"]);\n\nexport const SORT_OPTIONS: { value: CollectionTokensSort; label: string }[] = [\n { value: \"recent\", label: \"Recent\" },\n { value: \"oldest\", label: \"Oldest\" },\n { value: \"name\", label: \"Name\" },\n { value: \"price\", label: \"Price\" },\n];\n\nexport interface TraitSection {\n traitType: string;\n values: { value: string; count: number }[];\n}\n\nexport function useCollectionFilters(\n tokens: ApiToken[],\n selected: Record<string, string[]>,\n onChange: (filters: Record<string, string[]>) => void,\n sort: CollectionTokensSort,\n onSortChange: (sort: CollectionTokensSort) => void,\n) {\n // Build trait map with value counts from loaded tokens, then drop any\n // trait type where every token shares the same single value (can never\n // narrow the result set) or that's a known per-token stamp, not a category.\n const traitSections: TraitSection[] = useMemo(() => {\n const map = new Map<string, Map<string, number>>();\n for (const token of tokens) {\n const attrs = Array.isArray(token.metadata?.attributes)\n ? (token.metadata.attributes as { trait_type?: string; value?: string }[])\n : [];\n for (const attr of attrs) {\n if (!attr.trait_type || attr.value == null) continue;\n if (EXCLUDED_TRAIT_TYPES.has(attr.trait_type.toLowerCase())) continue;\n if (!map.has(attr.trait_type)) map.set(attr.trait_type, new Map());\n const counts = map.get(attr.trait_type)!;\n const v = String(attr.value);\n counts.set(v, (counts.get(v) ?? 0) + 1);\n }\n }\n return Array.from(map.entries())\n .map(([traitType, counts]) => ({\n traitType,\n values: Array.from(counts.entries())\n .map(([value, count]) => ({ value, count }))\n .sort((a, b) => b.count - a.count || a.value.localeCompare(b.value)),\n }))\n .filter((section) => section.values.length >= 2);\n }, [tokens]);\n\n const activeEntries = Object.entries(selected).flatMap(([traitType, values]) =>\n values.map((value) => ({ traitType, value }))\n );\n const traitActiveCount = activeEntries.length;\n const sortIsDefault = sort === \"recent\";\n const totalActiveCount = traitActiveCount + (sortIsDefault ? 0 : 1);\n\n function toggleValue(traitType: string, value: string) {\n const current = selected[traitType] ?? [];\n const next = current.includes(value)\n ? current.filter((v) => v !== value)\n : [...current, value];\n const nextSelected = { ...selected };\n if (next.length === 0) {\n delete nextSelected[traitType];\n } else {\n nextSelected[traitType] = next;\n }\n onChange(nextSelected);\n }\n\n function clearAll() {\n onChange({});\n onSortChange(\"recent\");\n }\n\n function removeFilter(traitType: string, value: string) {\n toggleValue(traitType, value);\n }\n\n return { traitSections, activeEntries, totalActiveCount, sortIsDefault, toggleValue, clearAll, removeFilter };\n}\n\nconst TRIGGER_BTN = \"inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md border border-input bg-background text-sm font-medium transition-colors hover:bg-accent hover:text-accent-foreground h-8 px-3 shrink-0\";\n\nexport interface CollectionFiltersTriggerProps {\n totalActiveCount: number;\n sortIsDefault: boolean;\n sort: CollectionTokensSort;\n activeEntries: { traitType: string; value: string }[];\n onOpen: () => void;\n onSortReset: () => void;\n onRemoveFilter: (traitType: string, value: string) => void;\n onClearAll: () => void;\n}\n\nexport function CollectionFiltersTrigger({\n totalActiveCount,\n sortIsDefault,\n sort,\n activeEntries,\n onOpen,\n onSortReset,\n onRemoveFilter,\n onClearAll,\n}: CollectionFiltersTriggerProps) {\n return (\n <div className=\"flex items-center justify-end gap-2 flex-wrap\">\n <button onClick={onOpen} className={TRIGGER_BTN}>\n <SlidersHorizontal className=\"h-3.5 w-3.5\" />\n Filters\n {totalActiveCount > 0 && (\n <span className=\"h-4 min-w-4 rounded-full bg-primary text-2xs font-bold text-primary-foreground flex items-center justify-center px-1 -mr-1\">\n {totalActiveCount}\n </span>\n )}\n </button>\n\n {/* Sort pill — only shown when not the default */}\n {!sortIsDefault && (\n <button\n onClick={onSortReset}\n className=\"inline-flex items-center gap-1 h-8 px-2.5 rounded-md border border-primary/40 bg-primary/10 text-xs font-medium text-foreground hover:bg-primary/20 transition-colors\"\n >\n <span className=\"text-muted-foreground text-2xs\">Sort:</span>\n <span>{SORT_OPTIONS.find((o) => o.value === sort)?.label}</span>\n <X className=\"h-3 w-3 text-muted-foreground ml-0.5\" />\n </button>\n )}\n\n {/* Active trait pills — one per selected value */}\n {activeEntries.map(({ traitType, value }) => (\n <button\n key={`${traitType}:${value}`}\n onClick={() => onRemoveFilter(traitType, value)}\n className=\"inline-flex items-center gap-1 h-8 px-2.5 rounded-md border border-primary/40 bg-primary/10 text-xs font-medium text-foreground hover:bg-primary/20 transition-colors\"\n >\n <span className=\"text-muted-foreground text-2xs\">{traitType}:</span>\n <span>{value}</span>\n <X className=\"h-3 w-3 text-muted-foreground ml-0.5\" />\n </button>\n ))}\n\n {totalActiveCount > 1 && (\n <button\n onClick={onClearAll}\n className=\"text-xs text-muted-foreground hover:text-foreground underline-offset-2 hover:underline transition-colors\"\n >\n Clear all\n </button>\n )}\n </div>\n );\n}\n\nexport interface CollectionFiltersBodyProps {\n sort: CollectionTokensSort;\n onSortChange: (sort: CollectionTokensSort) => void;\n traitSections: TraitSection[];\n selected: Record<string, string[]>;\n onToggleValue: (traitType: string, value: string) => void;\n}\n\nconst PILL_BASE = \"inline-flex items-center gap-1 h-7 px-2.5 rounded-full border text-xs font-medium transition-colors\";\nconst PILL_ON = \"border-primary bg-primary text-primary-foreground\";\nconst PILL_OFF = \"border-border bg-transparent text-muted-foreground hover:text-foreground hover:border-foreground/30\";\n\nexport function CollectionFiltersBody({\n sort,\n onSortChange,\n traitSections,\n selected,\n onToggleValue,\n}: CollectionFiltersBodyProps) {\n return (\n <div className=\"flex-1 overflow-y-auto px-5 py-3 space-y-4\">\n {/* Sort section */}\n <div>\n <div className=\"mb-2\">\n <span className=\"text-sm font-medium\">Sort by</span>\n </div>\n <div className=\"flex flex-wrap gap-1.5\">\n {SORT_OPTIONS.map(({ value, label }) => {\n const isSelected = sort === value;\n return (\n <button\n key={value}\n onClick={() => onSortChange(value)}\n className={cn(PILL_BASE, isSelected ? PILL_ON : PILL_OFF)}\n >\n {isSelected && <Check className=\"h-3 w-3\" />}\n <span>{label}</span>\n </button>\n );\n })}\n </div>\n </div>\n\n {/* Trait sections — always expanded (pruning already removed the noise) */}\n {traitSections.length > 0 && (\n <>\n <div className=\"h-px w-full bg-border\" />\n {traitSections.map((section, i) => {\n const activeValues = selected[section.traitType] ?? [];\n return (\n <div key={section.traitType}>\n {i > 0 && <div className=\"h-px w-full bg-border mb-4\" />}\n <div className=\"flex items-center justify-between mb-2\">\n <span className=\"text-sm font-medium\">{section.traitType}</span>\n <span className=\"text-2xs text-muted-foreground\">\n {section.values.length} values\n </span>\n </div>\n <div className=\"flex flex-wrap gap-1.5\">\n {section.values.map(({ value, count }) => {\n const isSelected = activeValues.includes(value);\n return (\n <button\n key={value}\n onClick={() => onToggleValue(section.traitType, value)}\n className={cn(PILL_BASE, isSelected ? PILL_ON : PILL_OFF)}\n >\n {isSelected && <Check className=\"h-3 w-3\" />}\n <span>{value}</span>\n <span className={isSelected ? \"opacity-80\" : \"opacity-60\"}>\n {count}\n </span>\n </button>\n );\n })}\n </div>\n </div>\n );\n })}\n </>\n )}\n </div>\n );\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../../src/components/collection-filters.tsx"],"sourcesContent":["\"use client\";\n\nimport { useMemo } from \"react\";\nimport { SlidersHorizontal, X, Check } from \"lucide-react\";\nimport { cn } from \"../utils/cn.js\";\nimport type { ApiToken, CollectionTokensSort } from \"@medialane/sdk\";\n\nconst EXCLUDED_TRAIT_TYPES = new Set([\"registration\"]);\n\nexport const SORT_OPTIONS: { value: CollectionTokensSort; label: string }[] = [\n { value: \"recent\", label: \"Recent\" },\n { value: \"oldest\", label: \"Oldest\" },\n { value: \"name\", label: \"Name\" },\n { value: \"price\", label: \"Price\" },\n];\n\nexport interface TraitSection {\n traitType: string;\n values: { value: string; count: number }[];\n}\n\nexport function useCollectionFilters(\n tokens: ApiToken[],\n selected: Record<string, string[]>,\n onChange: (filters: Record<string, string[]>) => void,\n sort: CollectionTokensSort,\n onSortChange: (sort: CollectionTokensSort) => void,\n) {\n\n const traitSections: TraitSection[] = useMemo(() => {\n const map = new Map<string, Map<string, number>>();\n for (const token of tokens) {\n const attrs = Array.isArray(token.metadata?.attributes)\n ? (token.metadata.attributes as { trait_type?: string; value?: string }[])\n : [];\n for (const attr of attrs) {\n if (!attr.trait_type || attr.value == null) continue;\n if (EXCLUDED_TRAIT_TYPES.has(attr.trait_type.toLowerCase())) continue;\n if (!map.has(attr.trait_type)) map.set(attr.trait_type, new Map());\n const counts = map.get(attr.trait_type)!;\n const v = String(attr.value);\n counts.set(v, (counts.get(v) ?? 0) + 1);\n }\n }\n return Array.from(map.entries())\n .map(([traitType, counts]) => ({\n traitType,\n values: Array.from(counts.entries())\n .map(([value, count]) => ({ value, count }))\n .sort((a, b) => b.count - a.count || a.value.localeCompare(b.value)),\n }))\n .filter((section) => section.values.length >= 2);\n }, [tokens]);\n\n const activeEntries = Object.entries(selected).flatMap(([traitType, values]) =>\n values.map((value) => ({ traitType, value }))\n );\n const traitActiveCount = activeEntries.length;\n const sortIsDefault = sort === \"recent\";\n const totalActiveCount = traitActiveCount + (sortIsDefault ? 0 : 1);\n\n function toggleValue(traitType: string, value: string) {\n const current = selected[traitType] ?? [];\n const next = current.includes(value)\n ? current.filter((v) => v !== value)\n : [...current, value];\n const nextSelected = { ...selected };\n if (next.length === 0) {\n delete nextSelected[traitType];\n } else {\n nextSelected[traitType] = next;\n }\n onChange(nextSelected);\n }\n\n function clearAll() {\n onChange({});\n onSortChange(\"recent\");\n }\n\n function removeFilter(traitType: string, value: string) {\n toggleValue(traitType, value);\n }\n\n return { traitSections, activeEntries, totalActiveCount, sortIsDefault, toggleValue, clearAll, removeFilter };\n}\n\nconst TRIGGER_BTN = \"inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md border border-input bg-background text-sm font-medium transition-colors hover:bg-accent hover:text-accent-foreground h-8 px-3 shrink-0\";\n\nexport interface CollectionFiltersTriggerProps {\n totalActiveCount: number;\n sortIsDefault: boolean;\n sort: CollectionTokensSort;\n activeEntries: { traitType: string; value: string }[];\n onOpen: () => void;\n onSortReset: () => void;\n onRemoveFilter: (traitType: string, value: string) => void;\n onClearAll: () => void;\n}\n\nexport function CollectionFiltersTrigger({\n totalActiveCount,\n sortIsDefault,\n sort,\n activeEntries,\n onOpen,\n onSortReset,\n onRemoveFilter,\n onClearAll,\n}: CollectionFiltersTriggerProps) {\n return (\n <div className=\"flex items-center justify-end gap-2 flex-wrap\">\n <button onClick={onOpen} className={TRIGGER_BTN}>\n <SlidersHorizontal className=\"h-3.5 w-3.5\" />\n Filters\n {totalActiveCount > 0 && (\n <span className=\"h-4 min-w-4 rounded-full bg-primary text-2xs font-bold text-primary-foreground flex items-center justify-center px-1 -mr-1\">\n {totalActiveCount}\n </span>\n )}\n </button>\n\n {!sortIsDefault && (\n <button\n onClick={onSortReset}\n className=\"inline-flex items-center gap-1 h-8 px-2.5 rounded-md border border-primary/40 bg-primary/10 text-xs font-medium text-foreground hover:bg-primary/20 transition-colors\"\n >\n <span className=\"text-muted-foreground text-2xs\">Sort:</span>\n <span>{SORT_OPTIONS.find((o) => o.value === sort)?.label}</span>\n <X className=\"h-3 w-3 text-muted-foreground ml-0.5\" />\n </button>\n )}\n\n {activeEntries.map(({ traitType, value }) => (\n <button\n key={`${traitType}:${value}`}\n onClick={() => onRemoveFilter(traitType, value)}\n className=\"inline-flex items-center gap-1 h-8 px-2.5 rounded-md border border-primary/40 bg-primary/10 text-xs font-medium text-foreground hover:bg-primary/20 transition-colors\"\n >\n <span className=\"text-muted-foreground text-2xs\">{traitType}:</span>\n <span>{value}</span>\n <X className=\"h-3 w-3 text-muted-foreground ml-0.5\" />\n </button>\n ))}\n\n {totalActiveCount > 1 && (\n <button\n onClick={onClearAll}\n className=\"text-xs text-muted-foreground hover:text-foreground underline-offset-2 hover:underline transition-colors\"\n >\n Clear all\n </button>\n )}\n </div>\n );\n}\n\nexport interface CollectionFiltersBodyProps {\n sort: CollectionTokensSort;\n onSortChange: (sort: CollectionTokensSort) => void;\n traitSections: TraitSection[];\n selected: Record<string, string[]>;\n onToggleValue: (traitType: string, value: string) => void;\n}\n\nconst PILL_BASE = \"inline-flex items-center gap-1 h-7 px-2.5 rounded-full border text-xs font-medium transition-colors\";\nconst PILL_ON = \"border-primary bg-primary text-primary-foreground\";\nconst PILL_OFF = \"border-border bg-transparent text-muted-foreground hover:text-foreground hover:border-foreground/30\";\n\nexport function CollectionFiltersBody({\n sort,\n onSortChange,\n traitSections,\n selected,\n onToggleValue,\n}: CollectionFiltersBodyProps) {\n return (\n <div className=\"flex-1 overflow-y-auto px-5 py-3 space-y-4\">\n\n <div>\n <div className=\"mb-2\">\n <span className=\"text-sm font-medium\">Sort by</span>\n </div>\n <div className=\"flex flex-wrap gap-1.5\">\n {SORT_OPTIONS.map(({ value, label }) => {\n const isSelected = sort === value;\n return (\n <button\n key={value}\n onClick={() => onSortChange(value)}\n className={cn(PILL_BASE, isSelected ? PILL_ON : PILL_OFF)}\n >\n {isSelected && <Check className=\"h-3 w-3\" />}\n <span>{label}</span>\n </button>\n );\n })}\n </div>\n </div>\n\n {traitSections.length > 0 && (\n <>\n <div className=\"h-px w-full bg-border\" />\n {traitSections.map((section, i) => {\n const activeValues = selected[section.traitType] ?? [];\n return (\n <div key={section.traitType}>\n {i > 0 && <div className=\"h-px w-full bg-border mb-4\" />}\n <div className=\"flex items-center justify-between mb-2\">\n <span className=\"text-sm font-medium\">{section.traitType}</span>\n <span className=\"text-2xs text-muted-foreground\">\n {section.values.length} values\n </span>\n </div>\n <div className=\"flex flex-wrap gap-1.5\">\n {section.values.map(({ value, count }) => {\n const isSelected = activeValues.includes(value);\n return (\n <button\n key={value}\n onClick={() => onToggleValue(section.traitType, value)}\n className={cn(PILL_BASE, isSelected ? PILL_ON : PILL_OFF)}\n >\n {isSelected && <Check className=\"h-3 w-3\" />}\n <span>{value}</span>\n <span className={isSelected ? \"opacity-80\" : \"opacity-60\"}>\n {count}\n </span>\n </button>\n );\n })}\n </div>\n </div>\n );\n })}\n </>\n )}\n </div>\n );\n}\n"],"mappings":"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","names":[]}
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/components/collection-filters.tsx"],"sourcesContent":["\"use client\";\n\nimport { useMemo } from \"react\";\nimport { SlidersHorizontal, X, Check } from \"lucide-react\";\nimport { cn } from \"../utils/cn.js\";\nimport type { ApiToken, CollectionTokensSort } from \"@medialane/sdk\";\n\n// Trait types that are per-token stamps rather than real categories — never\n// useful as a filter regardless of how many distinct values they have.\n// \"Registration\" is the Berne Convention registration date, near-unique per\n// mint; filtering by an exact date isn't a meaningful narrowing action.\nconst EXCLUDED_TRAIT_TYPES = new Set([\"registration\"]);\n\nexport const SORT_OPTIONS: { value: CollectionTokensSort; label: string }[] = [\n { value: \"recent\", label: \"Recent\" },\n { value: \"oldest\", label: \"Oldest\" },\n { value: \"name\", label: \"Name\" },\n { value: \"price\", label: \"Price\" },\n];\n\nexport interface TraitSection {\n traitType: string;\n values: { value: string; count: number }[];\n}\n\nexport function useCollectionFilters(\n tokens: ApiToken[],\n selected: Record<string, string[]>,\n onChange: (filters: Record<string, string[]>) => void,\n sort: CollectionTokensSort,\n onSortChange: (sort: CollectionTokensSort) => void,\n) {\n // Build trait map with value counts from loaded tokens, then drop any\n // trait type where every token shares the same single value (can never\n // narrow the result set) or that's a known per-token stamp, not a category.\n const traitSections: TraitSection[] = useMemo(() => {\n const map = new Map<string, Map<string, number>>();\n for (const token of tokens) {\n const attrs = Array.isArray(token.metadata?.attributes)\n ? (token.metadata.attributes as { trait_type?: string; value?: string }[])\n : [];\n for (const attr of attrs) {\n if (!attr.trait_type || attr.value == null) continue;\n if (EXCLUDED_TRAIT_TYPES.has(attr.trait_type.toLowerCase())) continue;\n if (!map.has(attr.trait_type)) map.set(attr.trait_type, new Map());\n const counts = map.get(attr.trait_type)!;\n const v = String(attr.value);\n counts.set(v, (counts.get(v) ?? 0) + 1);\n }\n }\n return Array.from(map.entries())\n .map(([traitType, counts]) => ({\n traitType,\n values: Array.from(counts.entries())\n .map(([value, count]) => ({ value, count }))\n .sort((a, b) => b.count - a.count || a.value.localeCompare(b.value)),\n }))\n .filter((section) => section.values.length >= 2);\n }, [tokens]);\n\n const activeEntries = Object.entries(selected).flatMap(([traitType, values]) =>\n values.map((value) => ({ traitType, value }))\n );\n const traitActiveCount = activeEntries.length;\n const sortIsDefault = sort === \"recent\";\n const totalActiveCount = traitActiveCount + (sortIsDefault ? 0 : 1);\n\n function toggleValue(traitType: string, value: string) {\n const current = selected[traitType] ?? [];\n const next = current.includes(value)\n ? current.filter((v) => v !== value)\n : [...current, value];\n const nextSelected = { ...selected };\n if (next.length === 0) {\n delete nextSelected[traitType];\n } else {\n nextSelected[traitType] = next;\n }\n onChange(nextSelected);\n }\n\n function clearAll() {\n onChange({});\n onSortChange(\"recent\");\n }\n\n function removeFilter(traitType: string, value: string) {\n toggleValue(traitType, value);\n }\n\n return { traitSections, activeEntries, totalActiveCount, sortIsDefault, toggleValue, clearAll, removeFilter };\n}\n\nconst TRIGGER_BTN = \"inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md border border-input bg-background text-sm font-medium transition-colors hover:bg-accent hover:text-accent-foreground h-8 px-3 shrink-0\";\n\nexport interface CollectionFiltersTriggerProps {\n totalActiveCount: number;\n sortIsDefault: boolean;\n sort: CollectionTokensSort;\n activeEntries: { traitType: string; value: string }[];\n onOpen: () => void;\n onSortReset: () => void;\n onRemoveFilter: (traitType: string, value: string) => void;\n onClearAll: () => void;\n}\n\nexport function CollectionFiltersTrigger({\n totalActiveCount,\n sortIsDefault,\n sort,\n activeEntries,\n onOpen,\n onSortReset,\n onRemoveFilter,\n onClearAll,\n}: CollectionFiltersTriggerProps) {\n return (\n <div className=\"flex items-center justify-end gap-2 flex-wrap\">\n <button onClick={onOpen} className={TRIGGER_BTN}>\n <SlidersHorizontal className=\"h-3.5 w-3.5\" />\n Filters\n {totalActiveCount > 0 && (\n <span className=\"h-4 min-w-4 rounded-full bg-primary text-2xs font-bold text-primary-foreground flex items-center justify-center px-1 -mr-1\">\n {totalActiveCount}\n </span>\n )}\n </button>\n\n {/* Sort pill — only shown when not the default */}\n {!sortIsDefault && (\n <button\n onClick={onSortReset}\n className=\"inline-flex items-center gap-1 h-8 px-2.5 rounded-md border border-primary/40 bg-primary/10 text-xs font-medium text-foreground hover:bg-primary/20 transition-colors\"\n >\n <span className=\"text-muted-foreground text-2xs\">Sort:</span>\n <span>{SORT_OPTIONS.find((o) => o.value === sort)?.label}</span>\n <X className=\"h-3 w-3 text-muted-foreground ml-0.5\" />\n </button>\n )}\n\n {/* Active trait pills — one per selected value */}\n {activeEntries.map(({ traitType, value }) => (\n <button\n key={`${traitType}:${value}`}\n onClick={() => onRemoveFilter(traitType, value)}\n className=\"inline-flex items-center gap-1 h-8 px-2.5 rounded-md border border-primary/40 bg-primary/10 text-xs font-medium text-foreground hover:bg-primary/20 transition-colors\"\n >\n <span className=\"text-muted-foreground text-2xs\">{traitType}:</span>\n <span>{value}</span>\n <X className=\"h-3 w-3 text-muted-foreground ml-0.5\" />\n </button>\n ))}\n\n {totalActiveCount > 1 && (\n <button\n onClick={onClearAll}\n className=\"text-xs text-muted-foreground hover:text-foreground underline-offset-2 hover:underline transition-colors\"\n >\n Clear all\n </button>\n )}\n </div>\n );\n}\n\nexport interface CollectionFiltersBodyProps {\n sort: CollectionTokensSort;\n onSortChange: (sort: CollectionTokensSort) => void;\n traitSections: TraitSection[];\n selected: Record<string, string[]>;\n onToggleValue: (traitType: string, value: string) => void;\n}\n\nconst PILL_BASE = \"inline-flex items-center gap-1 h-7 px-2.5 rounded-full border text-xs font-medium transition-colors\";\nconst PILL_ON = \"border-primary bg-primary text-primary-foreground\";\nconst PILL_OFF = \"border-border bg-transparent text-muted-foreground hover:text-foreground hover:border-foreground/30\";\n\nexport function CollectionFiltersBody({\n sort,\n onSortChange,\n traitSections,\n selected,\n onToggleValue,\n}: CollectionFiltersBodyProps) {\n return (\n <div className=\"flex-1 overflow-y-auto px-5 py-3 space-y-4\">\n {/* Sort section */}\n <div>\n <div className=\"mb-2\">\n <span className=\"text-sm font-medium\">Sort by</span>\n </div>\n <div className=\"flex flex-wrap gap-1.5\">\n {SORT_OPTIONS.map(({ value, label }) => {\n const isSelected = sort === value;\n return (\n <button\n key={value}\n onClick={() => onSortChange(value)}\n className={cn(PILL_BASE, isSelected ? PILL_ON : PILL_OFF)}\n >\n {isSelected && <Check className=\"h-3 w-3\" />}\n <span>{label}</span>\n </button>\n );\n })}\n </div>\n </div>\n\n {/* Trait sections — always expanded (pruning already removed the noise) */}\n {traitSections.length > 0 && (\n <>\n <div className=\"h-px w-full bg-border\" />\n {traitSections.map((section, i) => {\n const activeValues = selected[section.traitType] ?? [];\n return (\n <div key={section.traitType}>\n {i > 0 && <div className=\"h-px w-full bg-border mb-4\" />}\n <div className=\"flex items-center justify-between mb-2\">\n <span className=\"text-sm font-medium\">{section.traitType}</span>\n <span className=\"text-2xs text-muted-foreground\">\n {section.values.length} values\n </span>\n </div>\n <div className=\"flex flex-wrap gap-1.5\">\n {section.values.map(({ value, count }) => {\n const isSelected = activeValues.includes(value);\n return (\n <button\n key={value}\n onClick={() => onToggleValue(section.traitType, value)}\n className={cn(PILL_BASE, isSelected ? PILL_ON : PILL_OFF)}\n >\n {isSelected && <Check className=\"h-3 w-3\" />}\n <span>{value}</span>\n <span className={isSelected ? \"opacity-80\" : \"opacity-60\"}>\n {count}\n </span>\n </button>\n );\n })}\n </div>\n </div>\n );\n })}\n </>\n )}\n </div>\n );\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../../src/components/collection-filters.tsx"],"sourcesContent":["\"use client\";\n\nimport { useMemo } from \"react\";\nimport { SlidersHorizontal, X, Check } from \"lucide-react\";\nimport { cn } from \"../utils/cn.js\";\nimport type { ApiToken, CollectionTokensSort } from \"@medialane/sdk\";\n\nconst EXCLUDED_TRAIT_TYPES = new Set([\"registration\"]);\n\nexport const SORT_OPTIONS: { value: CollectionTokensSort; label: string }[] = [\n { value: \"recent\", label: \"Recent\" },\n { value: \"oldest\", label: \"Oldest\" },\n { value: \"name\", label: \"Name\" },\n { value: \"price\", label: \"Price\" },\n];\n\nexport interface TraitSection {\n traitType: string;\n values: { value: string; count: number }[];\n}\n\nexport function useCollectionFilters(\n tokens: ApiToken[],\n selected: Record<string, string[]>,\n onChange: (filters: Record<string, string[]>) => void,\n sort: CollectionTokensSort,\n onSortChange: (sort: CollectionTokensSort) => void,\n) {\n\n const traitSections: TraitSection[] = useMemo(() => {\n const map = new Map<string, Map<string, number>>();\n for (const token of tokens) {\n const attrs = Array.isArray(token.metadata?.attributes)\n ? (token.metadata.attributes as { trait_type?: string; value?: string }[])\n : [];\n for (const attr of attrs) {\n if (!attr.trait_type || attr.value == null) continue;\n if (EXCLUDED_TRAIT_TYPES.has(attr.trait_type.toLowerCase())) continue;\n if (!map.has(attr.trait_type)) map.set(attr.trait_type, new Map());\n const counts = map.get(attr.trait_type)!;\n const v = String(attr.value);\n counts.set(v, (counts.get(v) ?? 0) + 1);\n }\n }\n return Array.from(map.entries())\n .map(([traitType, counts]) => ({\n traitType,\n values: Array.from(counts.entries())\n .map(([value, count]) => ({ value, count }))\n .sort((a, b) => b.count - a.count || a.value.localeCompare(b.value)),\n }))\n .filter((section) => section.values.length >= 2);\n }, [tokens]);\n\n const activeEntries = Object.entries(selected).flatMap(([traitType, values]) =>\n values.map((value) => ({ traitType, value }))\n );\n const traitActiveCount = activeEntries.length;\n const sortIsDefault = sort === \"recent\";\n const totalActiveCount = traitActiveCount + (sortIsDefault ? 0 : 1);\n\n function toggleValue(traitType: string, value: string) {\n const current = selected[traitType] ?? [];\n const next = current.includes(value)\n ? current.filter((v) => v !== value)\n : [...current, value];\n const nextSelected = { ...selected };\n if (next.length === 0) {\n delete nextSelected[traitType];\n } else {\n nextSelected[traitType] = next;\n }\n onChange(nextSelected);\n }\n\n function clearAll() {\n onChange({});\n onSortChange(\"recent\");\n }\n\n function removeFilter(traitType: string, value: string) {\n toggleValue(traitType, value);\n }\n\n return { traitSections, activeEntries, totalActiveCount, sortIsDefault, toggleValue, clearAll, removeFilter };\n}\n\nconst TRIGGER_BTN = \"inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md border border-input bg-background text-sm font-medium transition-colors hover:bg-accent hover:text-accent-foreground h-8 px-3 shrink-0\";\n\nexport interface CollectionFiltersTriggerProps {\n totalActiveCount: number;\n sortIsDefault: boolean;\n sort: CollectionTokensSort;\n activeEntries: { traitType: string; value: string }[];\n onOpen: () => void;\n onSortReset: () => void;\n onRemoveFilter: (traitType: string, value: string) => void;\n onClearAll: () => void;\n}\n\nexport function CollectionFiltersTrigger({\n totalActiveCount,\n sortIsDefault,\n sort,\n activeEntries,\n onOpen,\n onSortReset,\n onRemoveFilter,\n onClearAll,\n}: CollectionFiltersTriggerProps) {\n return (\n <div className=\"flex items-center justify-end gap-2 flex-wrap\">\n <button onClick={onOpen} className={TRIGGER_BTN}>\n <SlidersHorizontal className=\"h-3.5 w-3.5\" />\n Filters\n {totalActiveCount > 0 && (\n <span className=\"h-4 min-w-4 rounded-full bg-primary text-2xs font-bold text-primary-foreground flex items-center justify-center px-1 -mr-1\">\n {totalActiveCount}\n </span>\n )}\n </button>\n\n {!sortIsDefault && (\n <button\n onClick={onSortReset}\n className=\"inline-flex items-center gap-1 h-8 px-2.5 rounded-md border border-primary/40 bg-primary/10 text-xs font-medium text-foreground hover:bg-primary/20 transition-colors\"\n >\n <span className=\"text-muted-foreground text-2xs\">Sort:</span>\n <span>{SORT_OPTIONS.find((o) => o.value === sort)?.label}</span>\n <X className=\"h-3 w-3 text-muted-foreground ml-0.5\" />\n </button>\n )}\n\n {activeEntries.map(({ traitType, value }) => (\n <button\n key={`${traitType}:${value}`}\n onClick={() => onRemoveFilter(traitType, value)}\n className=\"inline-flex items-center gap-1 h-8 px-2.5 rounded-md border border-primary/40 bg-primary/10 text-xs font-medium text-foreground hover:bg-primary/20 transition-colors\"\n >\n <span className=\"text-muted-foreground text-2xs\">{traitType}:</span>\n <span>{value}</span>\n <X className=\"h-3 w-3 text-muted-foreground ml-0.5\" />\n </button>\n ))}\n\n {totalActiveCount > 1 && (\n <button\n onClick={onClearAll}\n className=\"text-xs text-muted-foreground hover:text-foreground underline-offset-2 hover:underline transition-colors\"\n >\n Clear all\n </button>\n )}\n </div>\n );\n}\n\nexport interface CollectionFiltersBodyProps {\n sort: CollectionTokensSort;\n onSortChange: (sort: CollectionTokensSort) => void;\n traitSections: TraitSection[];\n selected: Record<string, string[]>;\n onToggleValue: (traitType: string, value: string) => void;\n}\n\nconst PILL_BASE = \"inline-flex items-center gap-1 h-7 px-2.5 rounded-full border text-xs font-medium transition-colors\";\nconst PILL_ON = \"border-primary bg-primary text-primary-foreground\";\nconst PILL_OFF = \"border-border bg-transparent text-muted-foreground hover:text-foreground hover:border-foreground/30\";\n\nexport function CollectionFiltersBody({\n sort,\n onSortChange,\n traitSections,\n selected,\n onToggleValue,\n}: CollectionFiltersBodyProps) {\n return (\n <div className=\"flex-1 overflow-y-auto px-5 py-3 space-y-4\">\n\n <div>\n <div className=\"mb-2\">\n <span className=\"text-sm font-medium\">Sort by</span>\n </div>\n <div className=\"flex flex-wrap gap-1.5\">\n {SORT_OPTIONS.map(({ value, label }) => {\n const isSelected = sort === value;\n return (\n <button\n key={value}\n onClick={() => onSortChange(value)}\n className={cn(PILL_BASE, isSelected ? PILL_ON : PILL_OFF)}\n >\n {isSelected && <Check className=\"h-3 w-3\" />}\n <span>{label}</span>\n </button>\n );\n })}\n </div>\n </div>\n\n {traitSections.length > 0 && (\n <>\n <div className=\"h-px w-full bg-border\" />\n {traitSections.map((section, i) => {\n const activeValues = selected[section.traitType] ?? [];\n return (\n <div key={section.traitType}>\n {i > 0 && <div className=\"h-px w-full bg-border mb-4\" />}\n <div className=\"flex items-center justify-between mb-2\">\n <span className=\"text-sm font-medium\">{section.traitType}</span>\n <span className=\"text-2xs text-muted-foreground\">\n {section.values.length} values\n </span>\n </div>\n <div className=\"flex flex-wrap gap-1.5\">\n {section.values.map(({ value, count }) => {\n const isSelected = activeValues.includes(value);\n return (\n <button\n key={value}\n onClick={() => onToggleValue(section.traitType, value)}\n className={cn(PILL_BASE, isSelected ? PILL_ON : PILL_OFF)}\n >\n {isSelected && <Check className=\"h-3 w-3\" />}\n <span>{value}</span>\n <span className={isSelected ? \"opacity-80\" : \"opacity-60\"}>\n {count}\n </span>\n </button>\n );\n })}\n </div>\n </div>\n );\n })}\n </>\n )}\n </div>\n );\n}\n"],"mappings":"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","names":[]}
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/components/collection-hero-banner.tsx"],"sourcesContent":["\"use client\";\n\nimport { motion, useScroll, useTransform, useReducedMotion } from \"framer-motion\";\nimport { cn } from \"../utils/cn.js\";\nimport { CurrencyIcon } from \"./currency-icon.js\";\nimport { Skeleton } from \"./skeleton.js\";\n\nexport interface CollectionHeroStat {\n label: string;\n display: string;\n /** Currency symbol (STRK, ETH, ...) — renders CurrencyIcon next to the value. Omit for plain counts. */\n symbol?: string | null;\n}\n\nexport interface CollectionHeroBannerProps {\n bannerUrl: string | null;\n loading?: boolean;\n standard?: string | null;\n symbol?: string | null;\n name: string;\n stats: CollectionHeroStat[];\n className?: string;\n}\n\n/**\n * Full-bleed collection hero: page-wide atmospheric blur, a parallax banner\n * image, and a bottom overlay (type/symbol eyebrows, title, stat chips).\n * Deduplicated from io + starknet, which had drifted into two independent\n * copies of the same block.\n */\nexport function CollectionHeroBanner({\n bannerUrl,\n loading = false,\n standard,\n symbol,\n name,\n stats,\n className,\n}: CollectionHeroBannerProps) {\n const { scrollY } = useScroll();\n const shouldReduce = useReducedMotion();\n const y = useTransform(scrollY, [0, 500], [0, shouldReduce ? 0 : 150]);\n\n return (\n <>\n {/* Atmospheric blur background, fixed behind the whole page */}\n <div className=\"fixed inset-0 -z-10 overflow-hidden pointer-events-none\">\n {bannerUrl && (\n <img\n src={bannerUrl}\n alt=\"\"\n aria-hidden\n className=\"absolute inset-0 w-full h-full object-cover opacity-20 scale-110\"\n style={{ filter: \"blur(60px) saturate(1.5)\" }}\n />\n )}\n </div>\n\n {loading ? (\n <Skeleton className={cn(\"w-full h-[50svh]\", className)} />\n ) : (\n <div className={cn(\"relative w-full overflow-hidden h-[50svh]\", className)}>\n {bannerUrl ? (\n <motion.img\n src={bannerUrl}\n alt=\"\"\n aria-hidden\n style={{ y }}\n className=\"absolute inset-0 w-full h-full object-cover scale-110\"\n />\n ) : (\n <div className=\"absolute inset-0 w-full h-full bg-muted\" />\n )}\n\n {/* Bottom overlay: eyebrows + title + stat chips — frosted glass so\n they hold up over any artwork, regardless of site theme. */}\n <div className=\"absolute bottom-0 left-0 right-0 px-4 sm:px-6 pb-4 sm:pb-6 space-y-3 z-10\">\n {(standard || symbol) && (\n <div className=\"flex items-center gap-2 flex-wrap\">\n {standard === \"ERC1155\" ? (\n <Eyebrow>Multi-edition NFT</Eyebrow>\n ) : standard === \"ERC721\" ? (\n <Eyebrow>Single NFT</Eyebrow>\n ) : null}\n {symbol && <Eyebrow className=\"tabular-nums\">{symbol}</Eyebrow>}\n </div>\n )}\n\n <h1\n className=\"text-3xl sm:text-5xl lg:text-7xl font-bold text-white leading-tight\"\n style={{ textShadow: \"0 1px 12px rgba(0,0,0,0.4)\" }}\n >\n {name}\n </h1>\n\n <div className=\"flex gap-2 flex-wrap\">\n {stats.map(({ label, display, symbol: statSymbol }) => (\n <div\n key={label}\n className={cn(\n \"bg-white/15 backdrop-blur-md rounded-xl px-3 py-2 flex flex-col justify-center shrink-0\",\n statSymbol ? \"min-w-[80px]\" : \"min-w-[60px] items-center text-center\"\n )}\n >\n <p className=\"text-[9px] text-white/70 mb-1\">{label}</p>\n {statSymbol ? (\n <div className=\"flex items-center gap-1.5\">\n <CurrencyIcon symbol={statSymbol} size={15} />\n <p className=\"text-sm sm:text-base font-bold text-white tabular-nums leading-tight truncate\">\n {display}\n </p>\n </div>\n ) : (\n <p className=\"text-base sm:text-lg font-bold text-white tabular-nums leading-tight\">{display}</p>\n )}\n </div>\n ))}\n </div>\n </div>\n </div>\n )}\n </>\n );\n}\n\nfunction Eyebrow({ children, className }: { children: React.ReactNode; className?: string }) {\n return (\n <span\n className={cn(\n \"text-[11px] font-semibold text-white/90 bg-white/15 backdrop-blur-md rounded-full px-2.5 py-0.5\",\n className\n )}\n >\n {children}\n </span>\n );\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AA4CI;AA1CJ,2BAAkE;AAClE,gBAAmB;AACnB,2BAA6B;AAC7B,sBAAyB;AAyBlB,SAAS,qBAAqB;AAAA,EACnC;AAAA,EACA,UAAU;AAAA,EACV;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,GAA8B;AAC5B,QAAM,EAAE,QAAQ,QAAI,gCAAU;AAC9B,QAAM,mBAAe,uCAAiB;AACtC,QAAM,QAAI,mCAAa,SAAS,CAAC,GAAG,GAAG,GAAG,CAAC,GAAG,eAAe,IAAI,GAAG,CAAC;AAErE,SACE,4EAEE;AAAA,gDAAC,SAAI,WAAU,2DACZ,uBACC;AAAA,MAAC;AAAA;AAAA,QACC,KAAK;AAAA,QACL,KAAI;AAAA,QACJ,eAAW;AAAA,QACX,WAAU;AAAA,QACV,OAAO,EAAE,QAAQ,2BAA2B;AAAA;AAAA,IAC9C,GAEJ;AAAA,IAEC,UACC,4CAAC,4BAAS,eAAW,cAAG,oBAAoB,SAAS,GAAG,IAExD,6CAAC,SAAI,eAAW,cAAG,6CAA6C,SAAS,GACtE;AAAA,kBACC;AAAA,QAAC,4BAAO;AAAA,QAAP;AAAA,UACC,KAAK;AAAA,UACL,KAAI;AAAA,UACJ,eAAW;AAAA,UACX,OAAO,EAAE,EAAE;AAAA,UACX,WAAU;AAAA;AAAA,MACZ,IAEA,4CAAC,SAAI,WAAU,2CAA0C;AAAA,MAK3D,6CAAC,SAAI,WAAU,6EACX;AAAA,qBAAY,WACZ,6CAAC,SAAI,WAAU,qCACZ;AAAA,uBAAa,YACZ,4CAAC,WAAQ,+BAAiB,IACxB,aAAa,WACf,4CAAC,WAAQ,wBAAU,IACjB;AAAA,UACH,UAAU,4CAAC,WAAQ,WAAU,gBAAgB,kBAAO;AAAA,WACvD;AAAA,QAGF;AAAA,UAAC;AAAA;AAAA,YACC,WAAU;AAAA,YACV,OAAO,EAAE,YAAY,6BAA6B;AAAA,YAEjD;AAAA;AAAA,QACH;AAAA,QAEA,4CAAC,SAAI,WAAU,wBACZ,gBAAM,IAAI,CAAC,EAAE,OAAO,SAAS,QAAQ,WAAW,MAC/C;AAAA,UAAC;AAAA;AAAA,YAEC,eAAW;AAAA,cACT;AAAA,cACA,aAAa,iBAAiB;AAAA,YAChC;AAAA,YAEA;AAAA,0DAAC,OAAE,WAAU,iCAAiC,iBAAM;AAAA,cACnD,aACC,6CAAC,SAAI,WAAU,6BACb;AAAA,4DAAC,qCAAa,QAAQ,YAAY,MAAM,IAAI;AAAA,gBAC5C,4CAAC,OAAE,WAAU,iFACV,mBACH;AAAA,iBACF,IAEA,4CAAC,OAAE,WAAU,wEAAwE,mBAAQ;AAAA;AAAA;AAAA,UAf1F;AAAA,QAiBP,CACD,GACH;AAAA,SACF;AAAA,OACF;AAAA,KAEJ;AAEJ;AAEA,SAAS,QAAQ,EAAE,UAAU,UAAU,GAAsD;AAC3F,SACE;AAAA,IAAC;AAAA;AAAA,MACC,eAAW;AAAA,QACT;AAAA,QACA;AAAA,MACF;AAAA,MAEC;AAAA;AAAA,EACH;AAEJ;","names":[]}
1
+ {"version":3,"sources":["../../src/components/collection-hero-banner.tsx"],"sourcesContent":["\"use client\";\n\nimport { motion, useScroll, useTransform, useReducedMotion } from \"framer-motion\";\nimport { cn } from \"../utils/cn.js\";\nimport { CurrencyIcon } from \"./currency-icon.js\";\nimport { Skeleton } from \"./skeleton.js\";\n\nexport interface CollectionHeroStat {\n label: string;\n display: string;\n\n symbol?: string | null;\n}\n\nexport interface CollectionHeroBannerProps {\n bannerUrl: string | null;\n loading?: boolean;\n standard?: string | null;\n symbol?: string | null;\n name: string;\n stats: CollectionHeroStat[];\n className?: string;\n}\n\nexport function CollectionHeroBanner({\n bannerUrl,\n loading = false,\n standard,\n symbol,\n name,\n stats,\n className,\n}: CollectionHeroBannerProps) {\n const { scrollY } = useScroll();\n const shouldReduce = useReducedMotion();\n const y = useTransform(scrollY, [0, 500], [0, shouldReduce ? 0 : 150]);\n\n return (\n <>\n\n <div className=\"fixed inset-0 -z-10 overflow-hidden pointer-events-none\">\n {bannerUrl && (\n <img\n src={bannerUrl}\n alt=\"\"\n aria-hidden\n className=\"absolute inset-0 w-full h-full object-cover opacity-20 scale-110\"\n style={{ filter: \"blur(60px) saturate(1.5)\" }}\n />\n )}\n </div>\n\n {loading ? (\n <Skeleton className={cn(\"w-full h-[50svh]\", className)} />\n ) : (\n <div className={cn(\"relative w-full overflow-hidden h-[50svh]\", className)}>\n {bannerUrl ? (\n <motion.img\n src={bannerUrl}\n alt=\"\"\n aria-hidden\n style={{ y }}\n className=\"absolute inset-0 w-full h-full object-cover scale-110\"\n />\n ) : (\n <div className=\"absolute inset-0 w-full h-full bg-muted\" />\n )}\n\n <div className=\"absolute bottom-0 left-0 right-0 px-4 sm:px-6 pb-4 sm:pb-6 space-y-3 z-10\">\n {(standard || symbol) && (\n <div className=\"flex items-center gap-2 flex-wrap\">\n {standard === \"ERC1155\" ? (\n <Eyebrow>Multi-edition NFT</Eyebrow>\n ) : standard === \"ERC721\" ? (\n <Eyebrow>Single NFT</Eyebrow>\n ) : null}\n {symbol && <Eyebrow className=\"tabular-nums\">{symbol}</Eyebrow>}\n </div>\n )}\n\n <h1\n className=\"text-3xl sm:text-5xl lg:text-7xl font-bold text-white leading-tight\"\n style={{ textShadow: \"0 1px 12px rgba(0,0,0,0.4)\" }}\n >\n {name}\n </h1>\n\n <div className=\"flex gap-2 flex-wrap\">\n {stats.map(({ label, display, symbol: statSymbol }) => (\n <div\n key={label}\n className={cn(\n \"bg-white/15 backdrop-blur-md rounded-xl px-3 py-2 flex flex-col justify-center shrink-0\",\n statSymbol ? \"min-w-[80px]\" : \"min-w-[60px] items-center text-center\"\n )}\n >\n <p className=\"text-[9px] text-white/70 mb-1\">{label}</p>\n {statSymbol ? (\n <div className=\"flex items-center gap-1.5\">\n <CurrencyIcon symbol={statSymbol} size={15} />\n <p className=\"text-sm sm:text-base font-bold text-white tabular-nums leading-tight truncate\">\n {display}\n </p>\n </div>\n ) : (\n <p className=\"text-base sm:text-lg font-bold text-white tabular-nums leading-tight\">{display}</p>\n )}\n </div>\n ))}\n </div>\n </div>\n </div>\n )}\n </>\n );\n}\n\nfunction Eyebrow({ children, className }: { children: React.ReactNode; className?: string }) {\n return (\n <span\n className={cn(\n \"text-[11px] font-semibold text-white/90 bg-white/15 backdrop-blur-md rounded-full px-2.5 py-0.5\",\n className\n )}\n >\n {children}\n </span>\n );\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAsCI;AApCJ,2BAAkE;AAClE,gBAAmB;AACnB,2BAA6B;AAC7B,sBAAyB;AAmBlB,SAAS,qBAAqB;AAAA,EACnC;AAAA,EACA,UAAU;AAAA,EACV;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,GAA8B;AAC5B,QAAM,EAAE,QAAQ,QAAI,gCAAU;AAC9B,QAAM,mBAAe,uCAAiB;AACtC,QAAM,QAAI,mCAAa,SAAS,CAAC,GAAG,GAAG,GAAG,CAAC,GAAG,eAAe,IAAI,GAAG,CAAC;AAErE,SACE,4EAEE;AAAA,gDAAC,SAAI,WAAU,2DACZ,uBACC;AAAA,MAAC;AAAA;AAAA,QACC,KAAK;AAAA,QACL,KAAI;AAAA,QACJ,eAAW;AAAA,QACX,WAAU;AAAA,QACV,OAAO,EAAE,QAAQ,2BAA2B;AAAA;AAAA,IAC9C,GAEJ;AAAA,IAEC,UACC,4CAAC,4BAAS,eAAW,cAAG,oBAAoB,SAAS,GAAG,IAExD,6CAAC,SAAI,eAAW,cAAG,6CAA6C,SAAS,GACtE;AAAA,kBACC;AAAA,QAAC,4BAAO;AAAA,QAAP;AAAA,UACC,KAAK;AAAA,UACL,KAAI;AAAA,UACJ,eAAW;AAAA,UACX,OAAO,EAAE,EAAE;AAAA,UACX,WAAU;AAAA;AAAA,MACZ,IAEA,4CAAC,SAAI,WAAU,2CAA0C;AAAA,MAG3D,6CAAC,SAAI,WAAU,6EACX;AAAA,qBAAY,WACZ,6CAAC,SAAI,WAAU,qCACZ;AAAA,uBAAa,YACZ,4CAAC,WAAQ,+BAAiB,IACxB,aAAa,WACf,4CAAC,WAAQ,wBAAU,IACjB;AAAA,UACH,UAAU,4CAAC,WAAQ,WAAU,gBAAgB,kBAAO;AAAA,WACvD;AAAA,QAGF;AAAA,UAAC;AAAA;AAAA,YACC,WAAU;AAAA,YACV,OAAO,EAAE,YAAY,6BAA6B;AAAA,YAEjD;AAAA;AAAA,QACH;AAAA,QAEA,4CAAC,SAAI,WAAU,wBACZ,gBAAM,IAAI,CAAC,EAAE,OAAO,SAAS,QAAQ,WAAW,MAC/C;AAAA,UAAC;AAAA;AAAA,YAEC,eAAW;AAAA,cACT;AAAA,cACA,aAAa,iBAAiB;AAAA,YAChC;AAAA,YAEA;AAAA,0DAAC,OAAE,WAAU,iCAAiC,iBAAM;AAAA,cACnD,aACC,6CAAC,SAAI,WAAU,6BACb;AAAA,4DAAC,qCAAa,QAAQ,YAAY,MAAM,IAAI;AAAA,gBAC5C,4CAAC,OAAE,WAAU,iFACV,mBACH;AAAA,iBACF,IAEA,4CAAC,OAAE,WAAU,wEAAwE,mBAAQ;AAAA;AAAA;AAAA,UAf1F;AAAA,QAiBP,CACD,GACH;AAAA,SACF;AAAA,OACF;AAAA,KAEJ;AAEJ;AAEA,SAAS,QAAQ,EAAE,UAAU,UAAU,GAAsD;AAC3F,SACE;AAAA,IAAC;AAAA;AAAA,MACC,eAAW;AAAA,QACT;AAAA,QACA;AAAA,MACF;AAAA,MAEC;AAAA;AAAA,EACH;AAEJ;","names":[]}
@@ -3,7 +3,6 @@ import * as react_jsx_runtime from 'react/jsx-runtime';
3
3
  interface CollectionHeroStat {
4
4
  label: string;
5
5
  display: string;
6
- /** Currency symbol (STRK, ETH, ...) — renders CurrencyIcon next to the value. Omit for plain counts. */
7
6
  symbol?: string | null;
8
7
  }
9
8
  interface CollectionHeroBannerProps {
@@ -15,12 +14,6 @@ interface CollectionHeroBannerProps {
15
14
  stats: CollectionHeroStat[];
16
15
  className?: string;
17
16
  }
18
- /**
19
- * Full-bleed collection hero: page-wide atmospheric blur, a parallax banner
20
- * image, and a bottom overlay (type/symbol eyebrows, title, stat chips).
21
- * Deduplicated from io + starknet, which had drifted into two independent
22
- * copies of the same block.
23
- */
24
17
  declare function CollectionHeroBanner({ bannerUrl, loading, standard, symbol, name, stats, className, }: CollectionHeroBannerProps): react_jsx_runtime.JSX.Element;
25
18
 
26
19
  export { CollectionHeroBanner, type CollectionHeroBannerProps, type CollectionHeroStat };
@@ -3,7 +3,6 @@ import * as react_jsx_runtime from 'react/jsx-runtime';
3
3
  interface CollectionHeroStat {
4
4
  label: string;
5
5
  display: string;
6
- /** Currency symbol (STRK, ETH, ...) — renders CurrencyIcon next to the value. Omit for plain counts. */
7
6
  symbol?: string | null;
8
7
  }
9
8
  interface CollectionHeroBannerProps {
@@ -15,12 +14,6 @@ interface CollectionHeroBannerProps {
15
14
  stats: CollectionHeroStat[];
16
15
  className?: string;
17
16
  }
18
- /**
19
- * Full-bleed collection hero: page-wide atmospheric blur, a parallax banner
20
- * image, and a bottom overlay (type/symbol eyebrows, title, stat chips).
21
- * Deduplicated from io + starknet, which had drifted into two independent
22
- * copies of the same block.
23
- */
24
17
  declare function CollectionHeroBanner({ bannerUrl, loading, standard, symbol, name, stats, className, }: CollectionHeroBannerProps): react_jsx_runtime.JSX.Element;
25
18
 
26
19
  export { CollectionHeroBanner, type CollectionHeroBannerProps, type CollectionHeroStat };
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/components/collection-hero-banner.tsx"],"sourcesContent":["\"use client\";\n\nimport { motion, useScroll, useTransform, useReducedMotion } from \"framer-motion\";\nimport { cn } from \"../utils/cn.js\";\nimport { CurrencyIcon } from \"./currency-icon.js\";\nimport { Skeleton } from \"./skeleton.js\";\n\nexport interface CollectionHeroStat {\n label: string;\n display: string;\n /** Currency symbol (STRK, ETH, ...) — renders CurrencyIcon next to the value. Omit for plain counts. */\n symbol?: string | null;\n}\n\nexport interface CollectionHeroBannerProps {\n bannerUrl: string | null;\n loading?: boolean;\n standard?: string | null;\n symbol?: string | null;\n name: string;\n stats: CollectionHeroStat[];\n className?: string;\n}\n\n/**\n * Full-bleed collection hero: page-wide atmospheric blur, a parallax banner\n * image, and a bottom overlay (type/symbol eyebrows, title, stat chips).\n * Deduplicated from io + starknet, which had drifted into two independent\n * copies of the same block.\n */\nexport function CollectionHeroBanner({\n bannerUrl,\n loading = false,\n standard,\n symbol,\n name,\n stats,\n className,\n}: CollectionHeroBannerProps) {\n const { scrollY } = useScroll();\n const shouldReduce = useReducedMotion();\n const y = useTransform(scrollY, [0, 500], [0, shouldReduce ? 0 : 150]);\n\n return (\n <>\n {/* Atmospheric blur background, fixed behind the whole page */}\n <div className=\"fixed inset-0 -z-10 overflow-hidden pointer-events-none\">\n {bannerUrl && (\n <img\n src={bannerUrl}\n alt=\"\"\n aria-hidden\n className=\"absolute inset-0 w-full h-full object-cover opacity-20 scale-110\"\n style={{ filter: \"blur(60px) saturate(1.5)\" }}\n />\n )}\n </div>\n\n {loading ? (\n <Skeleton className={cn(\"w-full h-[50svh]\", className)} />\n ) : (\n <div className={cn(\"relative w-full overflow-hidden h-[50svh]\", className)}>\n {bannerUrl ? (\n <motion.img\n src={bannerUrl}\n alt=\"\"\n aria-hidden\n style={{ y }}\n className=\"absolute inset-0 w-full h-full object-cover scale-110\"\n />\n ) : (\n <div className=\"absolute inset-0 w-full h-full bg-muted\" />\n )}\n\n {/* Bottom overlay: eyebrows + title + stat chips — frosted glass so\n they hold up over any artwork, regardless of site theme. */}\n <div className=\"absolute bottom-0 left-0 right-0 px-4 sm:px-6 pb-4 sm:pb-6 space-y-3 z-10\">\n {(standard || symbol) && (\n <div className=\"flex items-center gap-2 flex-wrap\">\n {standard === \"ERC1155\" ? (\n <Eyebrow>Multi-edition NFT</Eyebrow>\n ) : standard === \"ERC721\" ? (\n <Eyebrow>Single NFT</Eyebrow>\n ) : null}\n {symbol && <Eyebrow className=\"tabular-nums\">{symbol}</Eyebrow>}\n </div>\n )}\n\n <h1\n className=\"text-3xl sm:text-5xl lg:text-7xl font-bold text-white leading-tight\"\n style={{ textShadow: \"0 1px 12px rgba(0,0,0,0.4)\" }}\n >\n {name}\n </h1>\n\n <div className=\"flex gap-2 flex-wrap\">\n {stats.map(({ label, display, symbol: statSymbol }) => (\n <div\n key={label}\n className={cn(\n \"bg-white/15 backdrop-blur-md rounded-xl px-3 py-2 flex flex-col justify-center shrink-0\",\n statSymbol ? \"min-w-[80px]\" : \"min-w-[60px] items-center text-center\"\n )}\n >\n <p className=\"text-[9px] text-white/70 mb-1\">{label}</p>\n {statSymbol ? (\n <div className=\"flex items-center gap-1.5\">\n <CurrencyIcon symbol={statSymbol} size={15} />\n <p className=\"text-sm sm:text-base font-bold text-white tabular-nums leading-tight truncate\">\n {display}\n </p>\n </div>\n ) : (\n <p className=\"text-base sm:text-lg font-bold text-white tabular-nums leading-tight\">{display}</p>\n )}\n </div>\n ))}\n </div>\n </div>\n </div>\n )}\n </>\n );\n}\n\nfunction Eyebrow({ children, className }: { children: React.ReactNode; className?: string }) {\n return (\n <span\n className={cn(\n \"text-[11px] font-semibold text-white/90 bg-white/15 backdrop-blur-md rounded-full px-2.5 py-0.5\",\n className\n )}\n >\n {children}\n </span>\n );\n}\n"],"mappings":";AA4CI,mBAIM,KA8BI,YAlCV;AA1CJ,SAAS,QAAQ,WAAW,cAAc,wBAAwB;AAClE,SAAS,UAAU;AACnB,SAAS,oBAAoB;AAC7B,SAAS,gBAAgB;AAyBlB,SAAS,qBAAqB;AAAA,EACnC;AAAA,EACA,UAAU;AAAA,EACV;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,GAA8B;AAC5B,QAAM,EAAE,QAAQ,IAAI,UAAU;AAC9B,QAAM,eAAe,iBAAiB;AACtC,QAAM,IAAI,aAAa,SAAS,CAAC,GAAG,GAAG,GAAG,CAAC,GAAG,eAAe,IAAI,GAAG,CAAC;AAErE,SACE,iCAEE;AAAA,wBAAC,SAAI,WAAU,2DACZ,uBACC;AAAA,MAAC;AAAA;AAAA,QACC,KAAK;AAAA,QACL,KAAI;AAAA,QACJ,eAAW;AAAA,QACX,WAAU;AAAA,QACV,OAAO,EAAE,QAAQ,2BAA2B;AAAA;AAAA,IAC9C,GAEJ;AAAA,IAEC,UACC,oBAAC,YAAS,WAAW,GAAG,oBAAoB,SAAS,GAAG,IAExD,qBAAC,SAAI,WAAW,GAAG,6CAA6C,SAAS,GACtE;AAAA,kBACC;AAAA,QAAC,OAAO;AAAA,QAAP;AAAA,UACC,KAAK;AAAA,UACL,KAAI;AAAA,UACJ,eAAW;AAAA,UACX,OAAO,EAAE,EAAE;AAAA,UACX,WAAU;AAAA;AAAA,MACZ,IAEA,oBAAC,SAAI,WAAU,2CAA0C;AAAA,MAK3D,qBAAC,SAAI,WAAU,6EACX;AAAA,qBAAY,WACZ,qBAAC,SAAI,WAAU,qCACZ;AAAA,uBAAa,YACZ,oBAAC,WAAQ,+BAAiB,IACxB,aAAa,WACf,oBAAC,WAAQ,wBAAU,IACjB;AAAA,UACH,UAAU,oBAAC,WAAQ,WAAU,gBAAgB,kBAAO;AAAA,WACvD;AAAA,QAGF;AAAA,UAAC;AAAA;AAAA,YACC,WAAU;AAAA,YACV,OAAO,EAAE,YAAY,6BAA6B;AAAA,YAEjD;AAAA;AAAA,QACH;AAAA,QAEA,oBAAC,SAAI,WAAU,wBACZ,gBAAM,IAAI,CAAC,EAAE,OAAO,SAAS,QAAQ,WAAW,MAC/C;AAAA,UAAC;AAAA;AAAA,YAEC,WAAW;AAAA,cACT;AAAA,cACA,aAAa,iBAAiB;AAAA,YAChC;AAAA,YAEA;AAAA,kCAAC,OAAE,WAAU,iCAAiC,iBAAM;AAAA,cACnD,aACC,qBAAC,SAAI,WAAU,6BACb;AAAA,oCAAC,gBAAa,QAAQ,YAAY,MAAM,IAAI;AAAA,gBAC5C,oBAAC,OAAE,WAAU,iFACV,mBACH;AAAA,iBACF,IAEA,oBAAC,OAAE,WAAU,wEAAwE,mBAAQ;AAAA;AAAA;AAAA,UAf1F;AAAA,QAiBP,CACD,GACH;AAAA,SACF;AAAA,OACF;AAAA,KAEJ;AAEJ;AAEA,SAAS,QAAQ,EAAE,UAAU,UAAU,GAAsD;AAC3F,SACE;AAAA,IAAC;AAAA;AAAA,MACC,WAAW;AAAA,QACT;AAAA,QACA;AAAA,MACF;AAAA,MAEC;AAAA;AAAA,EACH;AAEJ;","names":[]}
1
+ {"version":3,"sources":["../../src/components/collection-hero-banner.tsx"],"sourcesContent":["\"use client\";\n\nimport { motion, useScroll, useTransform, useReducedMotion } from \"framer-motion\";\nimport { cn } from \"../utils/cn.js\";\nimport { CurrencyIcon } from \"./currency-icon.js\";\nimport { Skeleton } from \"./skeleton.js\";\n\nexport interface CollectionHeroStat {\n label: string;\n display: string;\n\n symbol?: string | null;\n}\n\nexport interface CollectionHeroBannerProps {\n bannerUrl: string | null;\n loading?: boolean;\n standard?: string | null;\n symbol?: string | null;\n name: string;\n stats: CollectionHeroStat[];\n className?: string;\n}\n\nexport function CollectionHeroBanner({\n bannerUrl,\n loading = false,\n standard,\n symbol,\n name,\n stats,\n className,\n}: CollectionHeroBannerProps) {\n const { scrollY } = useScroll();\n const shouldReduce = useReducedMotion();\n const y = useTransform(scrollY, [0, 500], [0, shouldReduce ? 0 : 150]);\n\n return (\n <>\n\n <div className=\"fixed inset-0 -z-10 overflow-hidden pointer-events-none\">\n {bannerUrl && (\n <img\n src={bannerUrl}\n alt=\"\"\n aria-hidden\n className=\"absolute inset-0 w-full h-full object-cover opacity-20 scale-110\"\n style={{ filter: \"blur(60px) saturate(1.5)\" }}\n />\n )}\n </div>\n\n {loading ? (\n <Skeleton className={cn(\"w-full h-[50svh]\", className)} />\n ) : (\n <div className={cn(\"relative w-full overflow-hidden h-[50svh]\", className)}>\n {bannerUrl ? (\n <motion.img\n src={bannerUrl}\n alt=\"\"\n aria-hidden\n style={{ y }}\n className=\"absolute inset-0 w-full h-full object-cover scale-110\"\n />\n ) : (\n <div className=\"absolute inset-0 w-full h-full bg-muted\" />\n )}\n\n <div className=\"absolute bottom-0 left-0 right-0 px-4 sm:px-6 pb-4 sm:pb-6 space-y-3 z-10\">\n {(standard || symbol) && (\n <div className=\"flex items-center gap-2 flex-wrap\">\n {standard === \"ERC1155\" ? (\n <Eyebrow>Multi-edition NFT</Eyebrow>\n ) : standard === \"ERC721\" ? (\n <Eyebrow>Single NFT</Eyebrow>\n ) : null}\n {symbol && <Eyebrow className=\"tabular-nums\">{symbol}</Eyebrow>}\n </div>\n )}\n\n <h1\n className=\"text-3xl sm:text-5xl lg:text-7xl font-bold text-white leading-tight\"\n style={{ textShadow: \"0 1px 12px rgba(0,0,0,0.4)\" }}\n >\n {name}\n </h1>\n\n <div className=\"flex gap-2 flex-wrap\">\n {stats.map(({ label, display, symbol: statSymbol }) => (\n <div\n key={label}\n className={cn(\n \"bg-white/15 backdrop-blur-md rounded-xl px-3 py-2 flex flex-col justify-center shrink-0\",\n statSymbol ? \"min-w-[80px]\" : \"min-w-[60px] items-center text-center\"\n )}\n >\n <p className=\"text-[9px] text-white/70 mb-1\">{label}</p>\n {statSymbol ? (\n <div className=\"flex items-center gap-1.5\">\n <CurrencyIcon symbol={statSymbol} size={15} />\n <p className=\"text-sm sm:text-base font-bold text-white tabular-nums leading-tight truncate\">\n {display}\n </p>\n </div>\n ) : (\n <p className=\"text-base sm:text-lg font-bold text-white tabular-nums leading-tight\">{display}</p>\n )}\n </div>\n ))}\n </div>\n </div>\n </div>\n )}\n </>\n );\n}\n\nfunction Eyebrow({ children, className }: { children: React.ReactNode; className?: string }) {\n return (\n <span\n className={cn(\n \"text-[11px] font-semibold text-white/90 bg-white/15 backdrop-blur-md rounded-full px-2.5 py-0.5\",\n className\n )}\n >\n {children}\n </span>\n );\n}\n"],"mappings":";AAsCI,mBAIM,KA4BI,YAhCV;AApCJ,SAAS,QAAQ,WAAW,cAAc,wBAAwB;AAClE,SAAS,UAAU;AACnB,SAAS,oBAAoB;AAC7B,SAAS,gBAAgB;AAmBlB,SAAS,qBAAqB;AAAA,EACnC;AAAA,EACA,UAAU;AAAA,EACV;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,GAA8B;AAC5B,QAAM,EAAE,QAAQ,IAAI,UAAU;AAC9B,QAAM,eAAe,iBAAiB;AACtC,QAAM,IAAI,aAAa,SAAS,CAAC,GAAG,GAAG,GAAG,CAAC,GAAG,eAAe,IAAI,GAAG,CAAC;AAErE,SACE,iCAEE;AAAA,wBAAC,SAAI,WAAU,2DACZ,uBACC;AAAA,MAAC;AAAA;AAAA,QACC,KAAK;AAAA,QACL,KAAI;AAAA,QACJ,eAAW;AAAA,QACX,WAAU;AAAA,QACV,OAAO,EAAE,QAAQ,2BAA2B;AAAA;AAAA,IAC9C,GAEJ;AAAA,IAEC,UACC,oBAAC,YAAS,WAAW,GAAG,oBAAoB,SAAS,GAAG,IAExD,qBAAC,SAAI,WAAW,GAAG,6CAA6C,SAAS,GACtE;AAAA,kBACC;AAAA,QAAC,OAAO;AAAA,QAAP;AAAA,UACC,KAAK;AAAA,UACL,KAAI;AAAA,UACJ,eAAW;AAAA,UACX,OAAO,EAAE,EAAE;AAAA,UACX,WAAU;AAAA;AAAA,MACZ,IAEA,oBAAC,SAAI,WAAU,2CAA0C;AAAA,MAG3D,qBAAC,SAAI,WAAU,6EACX;AAAA,qBAAY,WACZ,qBAAC,SAAI,WAAU,qCACZ;AAAA,uBAAa,YACZ,oBAAC,WAAQ,+BAAiB,IACxB,aAAa,WACf,oBAAC,WAAQ,wBAAU,IACjB;AAAA,UACH,UAAU,oBAAC,WAAQ,WAAU,gBAAgB,kBAAO;AAAA,WACvD;AAAA,QAGF;AAAA,UAAC;AAAA;AAAA,YACC,WAAU;AAAA,YACV,OAAO,EAAE,YAAY,6BAA6B;AAAA,YAEjD;AAAA;AAAA,QACH;AAAA,QAEA,oBAAC,SAAI,WAAU,wBACZ,gBAAM,IAAI,CAAC,EAAE,OAAO,SAAS,QAAQ,WAAW,MAC/C;AAAA,UAAC;AAAA;AAAA,YAEC,WAAW;AAAA,cACT;AAAA,cACA,aAAa,iBAAiB;AAAA,YAChC;AAAA,YAEA;AAAA,kCAAC,OAAE,WAAU,iCAAiC,iBAAM;AAAA,cACnD,aACC,qBAAC,SAAI,WAAU,6BACb;AAAA,oCAAC,gBAAa,QAAQ,YAAY,MAAM,IAAI;AAAA,gBAC5C,oBAAC,OAAE,WAAU,iFACV,mBACH;AAAA,iBACF,IAEA,oBAAC,OAAE,WAAU,wEAAwE,mBAAQ;AAAA;AAAA;AAAA,UAf1F;AAAA,QAiBP,CACD,GACH;AAAA,SACF;AAAA,OACF;AAAA,KAEJ;AAEJ;AAEA,SAAS,QAAQ,EAAE,UAAU,UAAU,GAAsD;AAC3F,SACE;AAAA,IAAC;AAAA;AAAA,MACC,WAAW;AAAA,QACT;AAAA,QACA;AAAA,MACF;AAAA,MAEC;AAAA;AAAA,EACH;AAEJ;","names":[]}