@nous-research/ui 0.15.0 → 0.16.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 (216) hide show
  1. package/CHANGELOG.md +227 -0
  2. package/README.md +24 -4
  3. package/dist/fonts.js +1 -0
  4. package/dist/hooks/use-capped-frame.js +1 -0
  5. package/dist/hooks/use-css-var-dims.js +1 -0
  6. package/dist/hooks/use-gpu-tier.js +1 -0
  7. package/dist/hooks/use-render-loop.js +1 -0
  8. package/dist/hooks/use-smooth-controls.js +1 -0
  9. package/dist/index.js +1 -0
  10. package/dist/ui/basic-page.js +1 -0
  11. package/dist/ui/components/animated-count.js +1 -0
  12. package/dist/ui/components/ascii.js +1 -0
  13. package/dist/ui/components/badge.js +2 -1
  14. package/dist/ui/components/badges/nous-girl.js +1 -0
  15. package/dist/ui/components/blend-mode.js +1 -0
  16. package/dist/ui/components/blink.js +1 -0
  17. package/dist/ui/components/button.js +2 -1
  18. package/dist/ui/components/checkbox.js +1 -0
  19. package/dist/ui/components/command-block.js +4 -3
  20. package/dist/ui/components/cursor.js +1 -0
  21. package/dist/ui/components/dropdown-menu.js +1 -0
  22. package/dist/ui/components/fit-text/index.js +1 -0
  23. package/dist/ui/components/graphs/bar-chart.js +1 -0
  24. package/dist/ui/components/graphs/index.js +1 -0
  25. package/dist/ui/components/graphs/line-chart.js +1 -0
  26. package/dist/ui/components/graphs/utils.js +1 -0
  27. package/dist/ui/components/grid/index.js +1 -0
  28. package/dist/ui/components/hover-bg.js +1 -0
  29. package/dist/ui/components/icons/arrow.js +1 -0
  30. package/dist/ui/components/icons/check.js +1 -0
  31. package/dist/ui/components/icons/chevron.js +1 -0
  32. package/dist/ui/components/icons/discord.js +1 -0
  33. package/dist/ui/components/icons/eye.js +1 -0
  34. package/dist/ui/components/icons/gear.js +1 -0
  35. package/dist/ui/components/icons/github.js +1 -0
  36. package/dist/ui/components/icons/hamburger.js +1 -0
  37. package/dist/ui/components/icons/heart.js +1 -0
  38. package/dist/ui/components/icons/index.js +1 -0
  39. package/dist/ui/components/icons/link.js +1 -0
  40. package/dist/ui/components/icons/minus.js +1 -0
  41. package/dist/ui/components/icons/search.js +1 -0
  42. package/dist/ui/components/image-distortion.js +1 -0
  43. package/dist/ui/components/leva-client.js +1 -0
  44. package/dist/ui/components/list-item.js +3 -2
  45. package/dist/ui/components/modal/index.js +1 -0
  46. package/dist/ui/components/modal/modal.css +1 -1
  47. package/dist/ui/components/overlays/blend-modes.js +1 -0
  48. package/dist/ui/components/overlays/glitch.js +1 -0
  49. package/dist/ui/components/overlays/greys.js +1 -0
  50. package/dist/ui/components/overlays/index.js +1 -0
  51. package/dist/ui/components/overlays/lens-layers.js +1 -0
  52. package/dist/ui/components/overlays/lens.js +1 -0
  53. package/dist/ui/components/overlays/noise.js +1 -0
  54. package/dist/ui/components/overlays/vignette.js +1 -0
  55. package/dist/ui/components/poster.js +1 -0
  56. package/dist/ui/components/progress.js +1 -0
  57. package/dist/ui/components/scene-canvas.js +1 -0
  58. package/dist/ui/components/scramble.js +1 -0
  59. package/dist/ui/components/segmented.js +5 -4
  60. package/dist/ui/components/select.js +1 -0
  61. package/dist/ui/components/selection-switcher.js +1 -0
  62. package/dist/ui/components/shader.js +1 -0
  63. package/dist/ui/components/socials.js +1 -0
  64. package/dist/ui/components/spinner.js +1 -0
  65. package/dist/ui/components/stats.js +2 -1
  66. package/dist/ui/components/switch.js +1 -0
  67. package/dist/ui/components/tabs.js +4 -3
  68. package/dist/ui/components/terminal-demo.js +2 -1
  69. package/dist/ui/components/theme-toggle.js +1 -0
  70. package/dist/ui/components/tier-card.js +2 -1
  71. package/dist/ui/components/tv.js +1 -0
  72. package/dist/ui/components/typography/h1.js +1 -0
  73. package/dist/ui/components/typography/h2.js +1 -0
  74. package/dist/ui/components/typography/index.js +1 -0
  75. package/dist/ui/components/typography/legend.js +1 -0
  76. package/dist/ui/components/typography/small.js +1 -0
  77. package/dist/ui/components/watchlist.js +2 -1
  78. package/dist/ui/footer.js +1 -0
  79. package/dist/ui/globals.css +33 -1
  80. package/dist/ui/header.js +1 -0
  81. package/dist/ui/layout-wrapper.js +2 -1
  82. package/dist/utils/color.js +1 -0
  83. package/dist/utils/index.js +1 -0
  84. package/dist/utils/poly.js +1 -0
  85. package/package.json +4 -2
  86. package/src/assets/filler-bg0.webp +0 -0
  87. package/src/assets.d.ts +38 -0
  88. package/src/fonts/Collapse-Bold.woff2 +0 -0
  89. package/src/fonts/Collapse-BoldItalic.woff2 +0 -0
  90. package/src/fonts/Collapse-Italic.woff2 +0 -0
  91. package/src/fonts/Collapse-Light.woff2 +0 -0
  92. package/src/fonts/Collapse-LightItalic.woff2 +0 -0
  93. package/src/fonts/Collapse-Regular.woff2 +0 -0
  94. package/src/fonts/Collapse-Thin.woff2 +0 -0
  95. package/src/fonts/Collapse-ThinItalic.woff2 +0 -0
  96. package/src/fonts/Mondwest-Regular.woff2 +0 -0
  97. package/src/fonts/Neuebit-Bold.woff2 +0 -0
  98. package/src/fonts/RulesCompressed-Medium.woff2 +0 -0
  99. package/src/fonts/RulesCompressed-Regular.woff2 +0 -0
  100. package/src/fonts/RulesExpanded-Bold.woff2 +0 -0
  101. package/src/fonts/RulesExpanded-Regular.woff2 +0 -0
  102. package/src/fonts.ts +6 -0
  103. package/src/hooks/use-capped-frame.ts +18 -0
  104. package/src/hooks/use-css-var-dims.ts +39 -0
  105. package/src/hooks/use-gpu-tier.ts +165 -0
  106. package/src/hooks/use-render-loop.ts +121 -0
  107. package/src/hooks/use-smooth-controls.ts +318 -0
  108. package/src/index.ts +109 -0
  109. package/src/ui/basic-page.tsx +34 -0
  110. package/src/ui/build.css +4 -0
  111. package/src/ui/components/animated-count.stories.tsx +67 -0
  112. package/src/ui/components/animated-count.tsx +168 -0
  113. package/src/ui/components/ascii.stories.tsx +30 -0
  114. package/src/ui/components/ascii.tsx +110 -0
  115. package/src/ui/components/badge.stories.tsx +31 -0
  116. package/src/ui/components/badge.tsx +60 -0
  117. package/src/ui/components/badges/nous-girl.tsx +52 -0
  118. package/src/ui/components/blend-mode.stories.tsx +33 -0
  119. package/src/ui/components/blend-mode.tsx +129 -0
  120. package/src/ui/components/blink.stories.tsx +32 -0
  121. package/src/ui/components/blink.tsx +21 -0
  122. package/src/ui/components/button.stories.tsx +68 -0
  123. package/src/ui/components/button.tsx +170 -0
  124. package/src/ui/components/checkbox.stories.tsx +113 -0
  125. package/src/ui/components/checkbox.tsx +36 -0
  126. package/src/ui/components/command-block.stories.tsx +52 -0
  127. package/src/ui/components/command-block.tsx +86 -0
  128. package/src/ui/components/cursor.tsx +115 -0
  129. package/src/ui/components/dropdown-menu.stories.tsx +52 -0
  130. package/src/ui/components/dropdown-menu.tsx +117 -0
  131. package/src/ui/components/fit-text/fit-text.css +42 -0
  132. package/src/ui/components/fit-text/index.stories.tsx +33 -0
  133. package/src/ui/components/fit-text/index.tsx +45 -0
  134. package/src/ui/components/graphs/bar-chart.tsx +153 -0
  135. package/src/ui/components/graphs/index.stories.tsx +64 -0
  136. package/src/ui/components/graphs/index.tsx +4 -0
  137. package/src/ui/components/graphs/line-chart.tsx +213 -0
  138. package/src/ui/components/graphs/utils.tsx +265 -0
  139. package/src/ui/components/grid/grid.css +79 -0
  140. package/src/ui/components/grid/index.tsx +19 -0
  141. package/src/ui/components/hover-bg.stories.tsx +29 -0
  142. package/src/ui/components/hover-bg.tsx +15 -0
  143. package/src/ui/components/icons/arrow.tsx +42 -0
  144. package/src/ui/components/icons/check.tsx +14 -0
  145. package/src/ui/components/icons/chevron.tsx +45 -0
  146. package/src/ui/components/icons/discord.tsx +16 -0
  147. package/src/ui/components/icons/eye.tsx +12 -0
  148. package/src/ui/components/icons/gear.tsx +51 -0
  149. package/src/ui/components/icons/github.tsx +16 -0
  150. package/src/ui/components/icons/hamburger.tsx +52 -0
  151. package/src/ui/components/icons/heart.tsx +12 -0
  152. package/src/ui/components/icons/index.ts +12 -0
  153. package/src/ui/components/icons/link.tsx +14 -0
  154. package/src/ui/components/icons/minus.tsx +14 -0
  155. package/src/ui/components/icons/search.tsx +28 -0
  156. package/src/ui/components/image-distortion.stories.tsx +120 -0
  157. package/src/ui/components/image-distortion.tsx +498 -0
  158. package/src/ui/components/leva-client.tsx +14 -0
  159. package/src/ui/components/list-item.stories.tsx +83 -0
  160. package/src/ui/components/list-item.tsx +37 -0
  161. package/src/ui/components/modal/index.stories.tsx +46 -0
  162. package/src/ui/components/modal/index.tsx +48 -0
  163. package/src/ui/components/modal/modal.css +36 -0
  164. package/src/ui/components/overlays/blend-modes.ts +13 -0
  165. package/src/ui/components/overlays/glitch.tsx +243 -0
  166. package/src/ui/components/overlays/greys.tsx +386 -0
  167. package/src/ui/components/overlays/index.tsx +47 -0
  168. package/src/ui/components/overlays/lens-layers.tsx +119 -0
  169. package/src/ui/components/overlays/lens.ts +91 -0
  170. package/src/ui/components/overlays/noise.tsx +174 -0
  171. package/src/ui/components/overlays/vignette.tsx +60 -0
  172. package/src/ui/components/poster.stories.tsx +513 -0
  173. package/src/ui/components/poster.tsx +411 -0
  174. package/src/ui/components/progress.stories.tsx +48 -0
  175. package/src/ui/components/progress.tsx +56 -0
  176. package/src/ui/components/scene-canvas.tsx +254 -0
  177. package/src/ui/components/scramble.stories.tsx +49 -0
  178. package/src/ui/components/scramble.tsx +95 -0
  179. package/src/ui/components/segmented.stories.tsx +101 -0
  180. package/src/ui/components/segmented.tsx +81 -0
  181. package/src/ui/components/select.stories.tsx +88 -0
  182. package/src/ui/components/select.tsx +267 -0
  183. package/src/ui/components/selection-switcher.tsx +44 -0
  184. package/src/ui/components/shader.tsx +83 -0
  185. package/src/ui/components/socials.tsx +42 -0
  186. package/src/ui/components/spinner.stories.tsx +101 -0
  187. package/src/ui/components/spinner.tsx +60 -0
  188. package/src/ui/components/stats.stories.tsx +24 -0
  189. package/src/ui/components/stats.tsx +53 -0
  190. package/src/ui/components/switch.stories.tsx +77 -0
  191. package/src/ui/components/switch.tsx +48 -0
  192. package/src/ui/components/tabs.stories.tsx +101 -0
  193. package/src/ui/components/tabs.tsx +66 -0
  194. package/src/ui/components/terminal-demo.stories.tsx +67 -0
  195. package/src/ui/components/terminal-demo.tsx +189 -0
  196. package/src/ui/components/theme-toggle.stories.tsx +47 -0
  197. package/src/ui/components/theme-toggle.tsx +66 -0
  198. package/src/ui/components/tier-card.stories.tsx +217 -0
  199. package/src/ui/components/tier-card.tsx +190 -0
  200. package/src/ui/components/tv.stories.tsx +37 -0
  201. package/src/ui/components/tv.tsx +257 -0
  202. package/src/ui/components/typography/h1.tsx +18 -0
  203. package/src/ui/components/typography/h2.tsx +18 -0
  204. package/src/ui/components/typography/index.tsx +54 -0
  205. package/src/ui/components/typography/legend.tsx +24 -0
  206. package/src/ui/components/typography/small.tsx +11 -0
  207. package/src/ui/components/watchlist.stories.tsx +33 -0
  208. package/src/ui/components/watchlist.tsx +105 -0
  209. package/src/ui/fonts.css +63 -0
  210. package/src/ui/footer.tsx +111 -0
  211. package/src/ui/globals.css +383 -0
  212. package/src/ui/header.tsx +398 -0
  213. package/src/ui/layout-wrapper.tsx +11 -0
  214. package/src/utils/color.ts +21 -0
  215. package/src/utils/index.ts +62 -0
  216. package/src/utils/poly.ts +26 -0
@@ -0,0 +1,45 @@
1
+ import type { SVGProps } from 'react'
2
+
3
+ import { cn } from '../../../utils'
4
+
5
+ export function ChevronIcon({
6
+ className,
7
+ direction = 'left',
8
+ ...props
9
+ }: ChevronIconProps) {
10
+ return (
11
+ <svg
12
+ className={cn(
13
+ direction === 'left' && 'rotate-90',
14
+ direction === 'right' && '-rotate-90',
15
+ className
16
+ )}
17
+ fill="none"
18
+ viewBox="0 0 8 13"
19
+ {...props}
20
+ >
21
+ <path
22
+ clipRule="evenodd"
23
+ d="M0 7.49765h5V4.9969H1e-7z"
24
+ fill="currentColor"
25
+ fillRule="evenodd"
26
+ />
27
+ <path
28
+ clipRule="evenodd"
29
+ d="M2.5 2.49765v7.5h2.50075v-7.5z"
30
+ fill="currentColor"
31
+ fillRule="evenodd"
32
+ />
33
+ <path
34
+ clipRule="evenodd"
35
+ d="M5 .0000031V2.4996h2.4996V.0000032zM5 9.99805v2.49965h2.4996V9.99805z"
36
+ fill="currentColor"
37
+ fillRule="evenodd"
38
+ />
39
+ </svg>
40
+ )
41
+ }
42
+
43
+ interface ChevronIconProps extends SVGProps<SVGSVGElement> {
44
+ direction?: 'left' | 'right'
45
+ }
@@ -0,0 +1,16 @@
1
+ import type { SVGProps } from 'react'
2
+
3
+ import { cn } from '../../../utils'
4
+
5
+ export function DiscordIcon({ className, ...props }: SVGProps<SVGSVGElement>) {
6
+ return (
7
+ <svg
8
+ className={cn('size-4', className)}
9
+ fill="currentColor"
10
+ viewBox="0 0 24 24"
11
+ {...props}
12
+ >
13
+ <path d="M20.317 4.37a19.791 19.791 0 0 0-4.885-1.515.074.074 0 0 0-.079.037c-.21.375-.444.864-.608 1.25a18.27 18.27 0 0 0-5.487 0 12.64 12.64 0 0 0-.617-1.25.077.077 0 0 0-.079-.037A19.736 19.736 0 0 0 3.677 4.37a.07.07 0 0 0-.032.027C.533 9.046-.32 13.58.099 18.057a.082.082 0 0 0 .031.057 19.9 19.9 0 0 0 5.993 3.03.078.078 0 0 0 .084-.028c.462-.63.874-1.295 1.226-1.994a.076.076 0 0 0-.041-.106 13.107 13.107 0 0 1-1.872-.892.077.077 0 0 1-.008-.128 10.2 10.2 0 0 0 .372-.292.074.074 0 0 1 .077-.01c3.928 1.793 8.18 1.793 12.062 0a.074.074 0 0 1 .078.01c.12.098.246.198.373.292a.077.077 0 0 1-.006.127 12.299 12.299 0 0 1-1.873.892.077.077 0 0 0-.041.107c.36.698.772 1.362 1.225 1.993a.076.076 0 0 0 .084.028 19.839 19.839 0 0 0 6.002-3.03.077.077 0 0 0 .032-.054c.5-5.177-.838-9.674-3.549-13.66a.061.061 0 0 0-.031-.03zM8.02 15.33c-1.183 0-2.157-1.085-2.157-2.419 0-1.333.956-2.419 2.157-2.419 1.21 0 2.176 1.096 2.157 2.42 0 1.333-.956 2.418-2.157 2.418zm7.975 0c-1.183 0-2.157-1.085-2.157-2.419 0-1.333.956-2.419 2.157-2.419 1.21 0 2.176 1.096 2.157 2.42 0 1.333-.946 2.418-2.157 2.418z" />
14
+ </svg>
15
+ )
16
+ }
@@ -0,0 +1,12 @@
1
+ import type { SVGProps } from 'react'
2
+
3
+ export function EyeIcon(props: SVGProps<SVGSVGElement>) {
4
+ return (
5
+ <svg fill="none" viewBox="0 0 26 17" {...props}>
6
+ <g clipRule="evenodd" fill="currentColor" fillRule="evenodd">
7
+ <path d="M2.36308 9.45484H0V7.09067h2.36308zM23.6387 9.45484h2.3631V7.09067h-2.3631zM4.73047 2.36462h3.54519v2.36309H4.73047zM8.27539 0h9.45451v2.36417H8.27539zM8.27539 14.1808h9.45451v2.3641H8.27539zM4.73047 14.1808h3.54519v-2.3631H4.73047zM21.2718 2.36462h-3.5452v2.36309h3.5452zM21.2718 14.1808h-3.5452v-2.3631h3.5452zM2.36719 4.72751h2.36308v2.36308H2.36719zM2.36719 11.8179h2.36308V9.45486H2.36719z" />
8
+ <path d="M23.6346 4.72751h-2.3631v2.36308h2.3631zM23.6346 11.8179h-2.3631V9.45486h2.3631zM14.7716 6.50014h-3.5452V4.13705h3.5452zm0 0v3.54516h-3.5452v2.3631h3.5452v-2.3631h2.363V6.50014zm-3.5452 0v3.54516H8.86328V6.50014z" />
9
+ </g>
10
+ </svg>
11
+ )
12
+ }
@@ -0,0 +1,51 @@
1
+ import {
2
+ isValidElement,
3
+ type PropsWithChildren,
4
+ type ReactNode,
5
+ type SVGProps
6
+ } from 'react'
7
+
8
+ const VIEWBOX = '0 0 34 38'
9
+
10
+ const GEAR_PATH =
11
+ 'M10.1249 3.37446h5.0624v3.37446h-5.0624zM23.6262 3.37446h-5.0625v3.37446h5.0625zM18.5637 0v3.37446h-3.3764L15.1877 0zM3.3762 6.74879l6.7487.00013.0003 3.37588h-6.749zM30.3748 6.74879l-6.7486.00013-.0003 3.37588h6.7489zM0 26.9988v-16.874h3.3762l-.00019 16.874zM33.7505 26.9988v-16.874h-3.3757l-.0003 16.874zM10.1248 30.3751H3.37586l.00015-3.3763 6.74879.0003zM23.6262 30.3751h6.749l-.0007-3.3763-6.7483.0003zM15.1873 33.7495h-5.0624l-.0001-3.3744 5.0625-.0001zM18.5637 33.7495h5.0625v-3.3744l-5.0625-.0001zM15.1874 37.1245l-.0001-3.375h3.3764l-.0003 3.375z'
12
+
13
+ export function GearIcon({
14
+ children,
15
+ innerScale = 0.55,
16
+ ...props
17
+ }: GearIconProps) {
18
+ const isSvg = isValidElement(children) && children.type === 'svg'
19
+
20
+ const viewBox = isSvg
21
+ ? ((children.props as { viewBox?: string }).viewBox ?? VIEWBOX)
22
+ : VIEWBOX
23
+
24
+ const inner = isSvg
25
+ ? (children.props as { children?: ReactNode }).children
26
+ : children
27
+
28
+ return (
29
+ <svg fill="none" viewBox={VIEWBOX} {...props}>
30
+ <g clipRule="evenodd" fill="currentColor" fillRule="evenodd">
31
+ <path d={GEAR_PATH} />
32
+ </g>
33
+
34
+ {children && (
35
+ <svg
36
+ height={26 * innerScale}
37
+ width={26 * innerScale}
38
+ x={17 - (26 * innerScale) / 2}
39
+ y={19 - (26 * innerScale) / 2}
40
+ {...{ viewBox }}
41
+ >
42
+ {inner}
43
+ </svg>
44
+ )}
45
+ </svg>
46
+ )
47
+ }
48
+
49
+ interface GearIconProps extends PropsWithChildren<SVGProps<SVGSVGElement>> {
50
+ innerScale?: number
51
+ }
@@ -0,0 +1,16 @@
1
+ import type { SVGProps } from 'react'
2
+
3
+ import { cn } from '../../../utils'
4
+
5
+ export function GitHubIcon({ className, ...props }: SVGProps<SVGSVGElement>) {
6
+ return (
7
+ <svg
8
+ className={cn('size-4', className)}
9
+ fill="currentColor"
10
+ viewBox="0 0 24 24"
11
+ {...props}
12
+ >
13
+ <path d="M12 0C5.37 0 0 5.37 0 12c0 5.31 3.435 9.795 8.205 11.385.6.105.825-.255.825-.57 0-.285-.015-1.23-.015-2.235-3.015.555-3.795-.735-4.035-1.41-.135-.345-.72-1.41-1.23-1.695-.42-.225-1.02-.78-.015-.795.945-.015 1.62.87 1.845 1.23 1.08 1.815 2.805 1.305 3.495.99.105-.78.42-1.305.765-1.605-2.67-.3-5.46-1.335-5.46-5.925 0-1.305.465-2.385 1.23-3.225-.12-.3-.54-1.53.12-3.18 0 0 1.005-.315 3.3 1.23.96-.27 1.98-.405 3-.405s2.04.135 3 .405c2.295-1.56 3.3-1.23 3.3-1.23.66 1.65.24 2.88.12 3.18.765.84 1.23 1.905 1.23 3.225 0 4.605-2.805 5.625-5.475 5.925.435.375.81 1.095.81 2.22 0 1.605-.015 2.895-.015 3.3 0 .315.225.69.825.57A12.02 12.02 0 0 0 24 12c0-6.63-5.37-12-12-12z" />
14
+ </svg>
15
+ )
16
+ }
@@ -0,0 +1,52 @@
1
+ import type { SVGProps } from 'react'
2
+
3
+ import { cn } from '../../../utils'
4
+
5
+ export function HamburgerIcon({
6
+ className,
7
+ open = false,
8
+ ...props
9
+ }: HamburgerIconProps) {
10
+ return (
11
+ <svg
12
+ className={cn('size-5', className)}
13
+ fill="none"
14
+ stroke="currentColor"
15
+ strokeLinecap="round"
16
+ strokeWidth={1.5}
17
+ viewBox="0 0 24 24"
18
+ {...props}
19
+ >
20
+ <line
21
+ className="origin-center transition-transform duration-200 ease-out"
22
+ style={{ transform: open ? 'rotate(45deg)' : 'translateY(-4px)' }}
23
+ x1={4}
24
+ x2={20}
25
+ y1={12}
26
+ y2={12}
27
+ />
28
+
29
+ <line
30
+ className="transition-opacity duration-200 ease-out"
31
+ style={{ opacity: open ? 0 : 1 }}
32
+ x1={4}
33
+ x2={20}
34
+ y1={12}
35
+ y2={12}
36
+ />
37
+
38
+ <line
39
+ className="origin-center transition-transform duration-200 ease-out"
40
+ style={{ transform: open ? 'rotate(-45deg)' : 'translateY(4px)' }}
41
+ x1={4}
42
+ x2={20}
43
+ y1={12}
44
+ y2={12}
45
+ />
46
+ </svg>
47
+ )
48
+ }
49
+
50
+ interface HamburgerIconProps extends SVGProps<SVGSVGElement> {
51
+ open?: boolean
52
+ }
@@ -0,0 +1,12 @@
1
+ import type { SVGProps } from 'react'
2
+
3
+ export function HeartIcon(props: SVGProps<SVGSVGElement>) {
4
+ return (
5
+ <svg fill="none" viewBox="0 0 14 12" {...props}>
6
+ <path
7
+ d="M13.2 0v5.65714h-1.8857v1.88572H9.42857v1.88571H7.54286v1.88573H5.65714V9.42857H3.77143V7.54286H1.88571V5.65714H0V0h5.65714v1.88571h1.88572V0z"
8
+ fill="currentColor"
9
+ />
10
+ </svg>
11
+ )
12
+ }
@@ -0,0 +1,12 @@
1
+ export * from './arrow'
2
+ export * from './check'
3
+ export * from './chevron'
4
+ export * from './discord'
5
+ export * from './eye'
6
+ export * from './gear'
7
+ export * from './github'
8
+ export * from './hamburger'
9
+ export * from './heart'
10
+ export * from './link'
11
+ export * from './minus'
12
+ export * from './search'
@@ -0,0 +1,14 @@
1
+ import type { SVGProps } from 'react'
2
+
3
+ export function LinkIcon(props: SVGProps<SVGSVGElement>) {
4
+ return (
5
+ <svg fill="none" viewBox="0 0 17 7" {...props}>
6
+ <path
7
+ d="M.264191.25061 6.27068.265071V2.26334h3.96512v1.99578l4.0238.00091.043-2.06986-4.0381.07302-.0142-2.01254L16.271.250649l-.0144 6.006381h-5.992l-.0287-1.9981-3.96528-.0002v1.99826l-6.02085-.0001zM6.24063 2.26197l-3.97944-.01436v1.99827l3.97954.01446z"
8
+ fill="currentColor"
9
+ stroke="currentColor"
10
+ strokeWidth=".5"
11
+ />
12
+ </svg>
13
+ )
14
+ }
@@ -0,0 +1,14 @@
1
+ import type { SVGProps } from 'react'
2
+
3
+ export function MinusIcon(props: SVGProps<SVGSVGElement>) {
4
+ return (
5
+ <svg fill="none" viewBox="0 0 12 3" {...props}>
6
+ <path
7
+ clipRule="evenodd"
8
+ d="M12 0 0-5.2e-7-1e-7 2.50075H12z"
9
+ fill="currentColor"
10
+ fillRule="evenodd"
11
+ />
12
+ </svg>
13
+ )
14
+ }
@@ -0,0 +1,28 @@
1
+ import type { SVGProps } from 'react'
2
+
3
+ export function SearchIcon(props: SVGProps<SVGSVGElement>) {
4
+ return (
5
+ <svg fill="none" viewBox="0 0 20 21" {...props}>
6
+ <path
7
+ clipRule="evenodd"
8
+ d="M7.49773 1.6664h6.66637V0H7.49773zM14.1641 15.0001H7.49773v1.6664h6.66637z"
9
+ fill="currentColor"
10
+ fillRule="evenodd"
11
+ />
12
+
13
+ <path
14
+ clipRule="evenodd"
15
+ d="M5.8336 3.33278H7.5v-1.6664H5.8336zM15.8359 13.3329h-1.6671v1.6672h1.6671zM4.16877 5.00017h1.66717V3.33301H4.16877zM17.5 11.6665h-1.6664v1.6664H17.5zM4.16406 11.6665V5.00012h-1.6664v6.66638zM17.4977 5.00012v6.66638h1.6664V5.00012z"
16
+ fill="currentColor"
17
+ fillRule="evenodd"
18
+ />
19
+
20
+ <path
21
+ clipRule="evenodd"
22
+ d="M17.5 5.00017V3.33301h-1.6664v1.66716zM15.8359 3.33278v-1.6664h-1.6671v1.6664zM15.8359 10.0002V6.6665h-1.6671v3.3337zM14.1641 6.6664V5h-1.6664v1.6664zM5.8335 13.3328v-1.6664H4.16633V15H7.5v-1.6671zM4.16406 16.6664V15h-1.6664v1.6664zM2.5 18.3331v-1.6664H.8336v1.6664z"
23
+ fill="currentColor"
24
+ fillRule="evenodd"
25
+ />
26
+ </svg>
27
+ )
28
+ }
@@ -0,0 +1,120 @@
1
+ import type { Meta, StoryObj } from '@storybook/react-vite'
2
+ import { useState } from 'react'
3
+
4
+ import fillerBg from '../../assets/filler-bg0.webp'
5
+ import { Button } from './button'
6
+ import { ImageDistortion } from './image-distortion'
7
+
8
+ const meta: Meta<typeof ImageDistortion> = {
9
+ args: { active: true, src: fillerBg.src ?? (fillerBg as unknown as string) },
10
+ component: ImageDistortion,
11
+ title: 'Components/ImageDistortion'
12
+ }
13
+
14
+ export default meta
15
+
16
+ type Story = StoryObj<typeof ImageDistortion>
17
+
18
+ function Frame({ children }: { children: React.ReactNode }) {
19
+ return (
20
+ <div
21
+ className="bg-background-base relative h-[420px] w-[560px] overflow-hidden border border-current/20"
22
+ style={{ backgroundColor: 'var(--background)' }}
23
+ >
24
+ {children}
25
+ </div>
26
+ )
27
+ }
28
+
29
+ export const Default: Story = {
30
+ render: args => (
31
+ <Frame>
32
+ <ImageDistortion {...args} />
33
+ </Frame>
34
+ )
35
+ }
36
+
37
+ export const Tinted: Story = {
38
+ args: { tint: '#88ccaa' },
39
+ render: args => (
40
+ <Frame>
41
+ <ImageDistortion {...args} />
42
+ </Frame>
43
+ )
44
+ }
45
+
46
+ export const TintStrength: Story = {
47
+ render: () => {
48
+ const src = fillerBg.src ?? (fillerBg as unknown as string)
49
+
50
+ return (
51
+ <div className="grid grid-cols-3 gap-4">
52
+ {[
53
+ ['#88ccaa', 'mint'],
54
+ ['#ccaa88', 'amber'],
55
+ ['#ff4444', 'fatal']
56
+ ].map(([tint, label]) => (
57
+ <div className="flex flex-col gap-2" key={label}>
58
+ <span className="text-xs uppercase tracking-widest opacity-50">
59
+ {label}
60
+ </span>
61
+
62
+ <Frame>
63
+ <ImageDistortion
64
+ src={src}
65
+ tint={tint}
66
+ tintStrength={{ active: 0.55, inactive: 0.25 }}
67
+ />
68
+ </Frame>
69
+ </div>
70
+ ))}
71
+ </div>
72
+ )
73
+ }
74
+ }
75
+
76
+ /**
77
+ * Runs the haptic-distortion effect on a choreographed motion pattern so
78
+ * the image looks alive without needing a real pointer. Perfect for
79
+ * screen recordings, posters, and social cuts.
80
+ */
81
+ export const AutoPlay: Story = {
82
+ render: () => {
83
+ const src = fillerBg.src ?? (fillerBg as unknown as string)
84
+
85
+ return (
86
+ <div className="grid grid-cols-3 gap-4">
87
+ {(['slash', 'gentle', 'aggressive'] as const).map(pattern => (
88
+ <div className="flex flex-col gap-2" key={pattern}>
89
+ <span className="text-xs uppercase tracking-widest opacity-50">
90
+ {pattern}
91
+ </span>
92
+
93
+ <Frame>
94
+ <ImageDistortion autoPlay={pattern} src={src} tint="#ccaa88" />
95
+ </Frame>
96
+ </div>
97
+ ))}
98
+ </div>
99
+ )
100
+ }
101
+ }
102
+
103
+ export const ToggleActive: Story = {
104
+ render: () => {
105
+ const [active, setActive] = useState(true)
106
+ const src = fillerBg.src ?? (fillerBg as unknown as string)
107
+
108
+ return (
109
+ <div className="flex flex-col gap-3">
110
+ <Frame>
111
+ <ImageDistortion active={active} src={src} tint="#ff4444" />
112
+ </Frame>
113
+
114
+ <Button onClick={() => setActive(v => !v)}>
115
+ {active ? 'Active' : 'Inactive'}
116
+ </Button>
117
+ </div>
118
+ )
119
+ }
120
+ }