@nous-research/ui 0.15.0 → 0.17.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 (258) hide show
  1. package/CHANGELOG.md +266 -0
  2. package/README.md +24 -4
  3. package/dist/fonts.js +1 -0
  4. package/dist/hooks/use-below-breakpoint.d.ts +2 -0
  5. package/dist/hooks/use-below-breakpoint.js +17 -0
  6. package/dist/hooks/use-capped-frame.js +1 -0
  7. package/dist/hooks/use-confirm-delete.d.ts +10 -0
  8. package/dist/hooks/use-confirm-delete.js +35 -0
  9. package/dist/hooks/use-css-var-dims.js +1 -0
  10. package/dist/hooks/use-gpu-tier.js +1 -0
  11. package/dist/hooks/use-render-loop.js +1 -0
  12. package/dist/hooks/use-smooth-controls.js +1 -0
  13. package/dist/hooks/use-toast.d.ts +7 -0
  14. package/dist/hooks/use-toast.js +21 -0
  15. package/dist/index.d.ts +11 -1
  16. package/dist/index.js +23 -1
  17. package/dist/ui/basic-page.js +1 -0
  18. package/dist/ui/components/animated-count.js +1 -0
  19. package/dist/ui/components/ascii.js +1 -0
  20. package/dist/ui/components/badge.js +2 -1
  21. package/dist/ui/components/badges/nous-girl.js +1 -0
  22. package/dist/ui/components/blend-mode.js +1 -0
  23. package/dist/ui/components/blink.js +1 -0
  24. package/dist/ui/components/bottom-sheet.d.ts +15 -0
  25. package/dist/ui/components/bottom-sheet.js +192 -0
  26. package/dist/ui/components/button.js +2 -1
  27. package/dist/ui/components/card.d.ts +5 -0
  28. package/dist/ui/components/card.js +74 -0
  29. package/dist/ui/components/checkbox.d.ts +1 -1
  30. package/dist/ui/components/checkbox.js +2 -1
  31. package/dist/ui/components/command-block.js +4 -3
  32. package/dist/ui/components/confirm-dialog.d.ts +13 -0
  33. package/dist/ui/components/confirm-dialog.js +113 -0
  34. package/dist/ui/components/cursor.js +1 -0
  35. package/dist/ui/components/dialog.d.ts +15 -0
  36. package/dist/ui/components/dialog.js +171 -0
  37. package/dist/ui/components/dropdown-menu.js +1 -0
  38. package/dist/ui/components/fit-text/index.js +1 -0
  39. package/dist/ui/components/graphs/bar-chart.js +1 -0
  40. package/dist/ui/components/graphs/index.js +1 -0
  41. package/dist/ui/components/graphs/line-chart.js +1 -0
  42. package/dist/ui/components/graphs/utils.js +1 -0
  43. package/dist/ui/components/grid/index.js +1 -0
  44. package/dist/ui/components/hover-bg.js +1 -0
  45. package/dist/ui/components/icons/arrow.js +1 -0
  46. package/dist/ui/components/icons/check.js +1 -0
  47. package/dist/ui/components/icons/chevron.js +1 -0
  48. package/dist/ui/components/icons/discord.js +1 -0
  49. package/dist/ui/components/icons/eye.js +1 -0
  50. package/dist/ui/components/icons/gear.js +1 -0
  51. package/dist/ui/components/icons/github.js +1 -0
  52. package/dist/ui/components/icons/hamburger.js +1 -0
  53. package/dist/ui/components/icons/heart.js +1 -0
  54. package/dist/ui/components/icons/index.js +1 -0
  55. package/dist/ui/components/icons/link.js +1 -0
  56. package/dist/ui/components/icons/minus.js +1 -0
  57. package/dist/ui/components/icons/search.js +1 -0
  58. package/dist/ui/components/image-distortion.js +1 -0
  59. package/dist/ui/components/input.d.ts +1 -0
  60. package/dist/ui/components/input.js +21 -0
  61. package/dist/ui/components/label.d.ts +1 -0
  62. package/dist/ui/components/label.js +18 -0
  63. package/dist/ui/components/leva-client.js +1 -0
  64. package/dist/ui/components/list-item.js +3 -2
  65. package/dist/ui/components/overlays/blend-modes.js +1 -0
  66. package/dist/ui/components/overlays/glitch.js +1 -0
  67. package/dist/ui/components/overlays/greys.js +1 -0
  68. package/dist/ui/components/overlays/index.js +1 -0
  69. package/dist/ui/components/overlays/lens-layers.js +1 -0
  70. package/dist/ui/components/overlays/lens.js +1 -0
  71. package/dist/ui/components/overlays/noise.js +1 -0
  72. package/dist/ui/components/overlays/vignette.js +1 -0
  73. package/dist/ui/components/poster.js +1 -0
  74. package/dist/ui/components/progress.js +1 -0
  75. package/dist/ui/components/scene-canvas.js +1 -0
  76. package/dist/ui/components/scramble.js +1 -0
  77. package/dist/ui/components/segmented.js +5 -4
  78. package/dist/ui/components/select.js +1 -0
  79. package/dist/ui/components/selection-switcher.js +1 -0
  80. package/dist/ui/components/separator.d.ts +5 -0
  81. package/dist/ui/components/separator.js +22 -0
  82. package/dist/ui/components/shader.js +1 -0
  83. package/dist/ui/components/socials.js +1 -0
  84. package/dist/ui/components/spinner.js +1 -0
  85. package/dist/ui/components/stats.js +2 -1
  86. package/dist/ui/components/switch.js +1 -0
  87. package/dist/ui/components/tabs.js +4 -3
  88. package/dist/ui/components/terminal-demo.js +2 -1
  89. package/dist/ui/components/theme-toggle.js +1 -0
  90. package/dist/ui/components/tier-card.js +2 -1
  91. package/dist/ui/components/toast.d.ts +8 -0
  92. package/dist/ui/components/toast.js +39 -0
  93. package/dist/ui/components/tv.js +1 -0
  94. package/dist/ui/components/typography/h1.js +1 -0
  95. package/dist/ui/components/typography/h2.js +1 -0
  96. package/dist/ui/components/typography/index.js +1 -0
  97. package/dist/ui/components/typography/legend.js +1 -0
  98. package/dist/ui/components/typography/small.js +1 -0
  99. package/dist/ui/components/watchlist.js +2 -1
  100. package/dist/ui/footer.js +1 -0
  101. package/dist/ui/globals.css +47 -3
  102. package/dist/ui/header.js +1 -0
  103. package/dist/ui/layout-wrapper.js +2 -1
  104. package/dist/utils/color.js +1 -0
  105. package/dist/utils/index.js +1 -0
  106. package/dist/utils/poly.js +1 -0
  107. package/package.json +5 -3
  108. package/src/assets/filler-bg0.webp +0 -0
  109. package/src/assets.d.ts +38 -0
  110. package/src/fonts/Collapse-Bold.woff2 +0 -0
  111. package/src/fonts/Collapse-BoldItalic.woff2 +0 -0
  112. package/src/fonts/Collapse-Italic.woff2 +0 -0
  113. package/src/fonts/Collapse-Light.woff2 +0 -0
  114. package/src/fonts/Collapse-LightItalic.woff2 +0 -0
  115. package/src/fonts/Collapse-Regular.woff2 +0 -0
  116. package/src/fonts/Collapse-Thin.woff2 +0 -0
  117. package/src/fonts/Collapse-ThinItalic.woff2 +0 -0
  118. package/src/fonts/Mondwest-Regular.woff2 +0 -0
  119. package/src/fonts/Neuebit-Bold.woff2 +0 -0
  120. package/src/fonts/RulesCompressed-Medium.woff2 +0 -0
  121. package/src/fonts/RulesCompressed-Regular.woff2 +0 -0
  122. package/src/fonts/RulesExpanded-Bold.woff2 +0 -0
  123. package/src/fonts/RulesExpanded-Regular.woff2 +0 -0
  124. package/src/fonts.ts +6 -0
  125. package/src/hooks/use-below-breakpoint.ts +21 -0
  126. package/src/hooks/use-capped-frame.ts +18 -0
  127. package/src/hooks/use-confirm-delete.ts +43 -0
  128. package/src/hooks/use-css-var-dims.ts +39 -0
  129. package/src/hooks/use-gpu-tier.ts +165 -0
  130. package/src/hooks/use-render-loop.ts +121 -0
  131. package/src/hooks/use-smooth-controls.ts +318 -0
  132. package/src/hooks/use-toast.ts +29 -0
  133. package/src/index.ts +130 -0
  134. package/src/ui/basic-page.tsx +34 -0
  135. package/src/ui/build.css +4 -0
  136. package/src/ui/components/animated-count.stories.tsx +67 -0
  137. package/src/ui/components/animated-count.tsx +168 -0
  138. package/src/ui/components/ascii.stories.tsx +30 -0
  139. package/src/ui/components/ascii.tsx +110 -0
  140. package/src/ui/components/badge.stories.tsx +31 -0
  141. package/src/ui/components/badge.tsx +60 -0
  142. package/src/ui/components/badges/nous-girl.tsx +52 -0
  143. package/src/ui/components/blend-mode.stories.tsx +33 -0
  144. package/src/ui/components/blend-mode.tsx +129 -0
  145. package/src/ui/components/blink.stories.tsx +32 -0
  146. package/src/ui/components/blink.tsx +21 -0
  147. package/src/ui/components/bottom-sheet.stories.tsx +43 -0
  148. package/src/ui/components/bottom-sheet.tsx +227 -0
  149. package/src/ui/components/button.stories.tsx +68 -0
  150. package/src/ui/components/button.tsx +170 -0
  151. package/src/ui/components/card.stories.tsx +63 -0
  152. package/src/ui/components/card.tsx +85 -0
  153. package/src/ui/components/checkbox.stories.tsx +113 -0
  154. package/src/ui/components/checkbox.tsx +36 -0
  155. package/src/ui/components/command-block.stories.tsx +52 -0
  156. package/src/ui/components/command-block.tsx +86 -0
  157. package/src/ui/components/confirm-dialog.stories.tsx +91 -0
  158. package/src/ui/components/confirm-dialog.tsx +130 -0
  159. package/src/ui/components/cursor.tsx +115 -0
  160. package/src/ui/components/dialog.stories.tsx +169 -0
  161. package/src/ui/components/dialog.tsx +177 -0
  162. package/src/ui/components/dropdown-menu.stories.tsx +52 -0
  163. package/src/ui/components/dropdown-menu.tsx +117 -0
  164. package/src/ui/components/fit-text/fit-text.css +42 -0
  165. package/src/ui/components/fit-text/index.stories.tsx +33 -0
  166. package/src/ui/components/fit-text/index.tsx +45 -0
  167. package/src/ui/components/forms.stories.tsx +173 -0
  168. package/src/ui/components/graphs/bar-chart.tsx +153 -0
  169. package/src/ui/components/graphs/index.stories.tsx +64 -0
  170. package/src/ui/components/graphs/index.tsx +4 -0
  171. package/src/ui/components/graphs/line-chart.tsx +213 -0
  172. package/src/ui/components/graphs/utils.tsx +265 -0
  173. package/src/ui/components/grid/grid.css +79 -0
  174. package/src/ui/components/grid/index.tsx +19 -0
  175. package/src/ui/components/hover-bg.stories.tsx +29 -0
  176. package/src/ui/components/hover-bg.tsx +15 -0
  177. package/src/ui/components/icons/arrow.tsx +42 -0
  178. package/src/ui/components/icons/check.tsx +14 -0
  179. package/src/ui/components/icons/chevron.tsx +45 -0
  180. package/src/ui/components/icons/discord.tsx +16 -0
  181. package/src/ui/components/icons/eye.tsx +12 -0
  182. package/src/ui/components/icons/gear.tsx +51 -0
  183. package/src/ui/components/icons/github.tsx +16 -0
  184. package/src/ui/components/icons/hamburger.tsx +52 -0
  185. package/src/ui/components/icons/heart.tsx +12 -0
  186. package/src/ui/components/icons/index.ts +12 -0
  187. package/src/ui/components/icons/link.tsx +14 -0
  188. package/src/ui/components/icons/minus.tsx +14 -0
  189. package/src/ui/components/icons/search.tsx +28 -0
  190. package/src/ui/components/image-distortion.stories.tsx +120 -0
  191. package/src/ui/components/image-distortion.tsx +498 -0
  192. package/src/ui/components/input.stories.tsx +39 -0
  193. package/src/ui/components/input.tsx +20 -0
  194. package/src/ui/components/label.stories.tsx +26 -0
  195. package/src/ui/components/label.tsx +16 -0
  196. package/src/ui/components/leva-client.tsx +14 -0
  197. package/src/ui/components/list-item.stories.tsx +83 -0
  198. package/src/ui/components/list-item.tsx +37 -0
  199. package/src/ui/components/overlays/blend-modes.ts +13 -0
  200. package/src/ui/components/overlays/glitch.tsx +243 -0
  201. package/src/ui/components/overlays/greys.tsx +386 -0
  202. package/src/ui/components/overlays/index.tsx +47 -0
  203. package/src/ui/components/overlays/lens-layers.tsx +119 -0
  204. package/src/ui/components/overlays/lens.ts +91 -0
  205. package/src/ui/components/overlays/noise.tsx +174 -0
  206. package/src/ui/components/overlays/vignette.tsx +60 -0
  207. package/src/ui/components/poster.stories.tsx +513 -0
  208. package/src/ui/components/poster.tsx +411 -0
  209. package/src/ui/components/progress.stories.tsx +48 -0
  210. package/src/ui/components/progress.tsx +56 -0
  211. package/src/ui/components/scene-canvas.tsx +254 -0
  212. package/src/ui/components/scramble.stories.tsx +49 -0
  213. package/src/ui/components/scramble.tsx +95 -0
  214. package/src/ui/components/segmented.stories.tsx +101 -0
  215. package/src/ui/components/segmented.tsx +81 -0
  216. package/src/ui/components/select.stories.tsx +88 -0
  217. package/src/ui/components/select.tsx +267 -0
  218. package/src/ui/components/selection-switcher.tsx +44 -0
  219. package/src/ui/components/separator.stories.tsx +33 -0
  220. package/src/ui/components/separator.tsx +24 -0
  221. package/src/ui/components/shader.tsx +83 -0
  222. package/src/ui/components/socials.tsx +42 -0
  223. package/src/ui/components/spinner.stories.tsx +101 -0
  224. package/src/ui/components/spinner.tsx +60 -0
  225. package/src/ui/components/stats.stories.tsx +24 -0
  226. package/src/ui/components/stats.tsx +53 -0
  227. package/src/ui/components/switch.stories.tsx +77 -0
  228. package/src/ui/components/switch.tsx +48 -0
  229. package/src/ui/components/tabs.stories.tsx +101 -0
  230. package/src/ui/components/tabs.tsx +66 -0
  231. package/src/ui/components/terminal-demo.stories.tsx +67 -0
  232. package/src/ui/components/terminal-demo.tsx +189 -0
  233. package/src/ui/components/theme-toggle.stories.tsx +47 -0
  234. package/src/ui/components/theme-toggle.tsx +66 -0
  235. package/src/ui/components/tier-card.stories.tsx +217 -0
  236. package/src/ui/components/tier-card.tsx +190 -0
  237. package/src/ui/components/toast.stories.tsx +55 -0
  238. package/src/ui/components/toast.tsx +49 -0
  239. package/src/ui/components/tv.stories.tsx +37 -0
  240. package/src/ui/components/tv.tsx +257 -0
  241. package/src/ui/components/typography/h1.tsx +18 -0
  242. package/src/ui/components/typography/h2.tsx +18 -0
  243. package/src/ui/components/typography/index.tsx +54 -0
  244. package/src/ui/components/typography/legend.tsx +24 -0
  245. package/src/ui/components/typography/small.tsx +11 -0
  246. package/src/ui/components/watchlist.stories.tsx +33 -0
  247. package/src/ui/components/watchlist.tsx +105 -0
  248. package/src/ui/fonts.css +63 -0
  249. package/src/ui/footer.tsx +111 -0
  250. package/src/ui/globals.css +395 -0
  251. package/src/ui/header.tsx +398 -0
  252. package/src/ui/layout-wrapper.tsx +11 -0
  253. package/src/utils/color.ts +21 -0
  254. package/src/utils/index.ts +62 -0
  255. package/src/utils/poly.ts +26 -0
  256. package/dist/ui/components/modal/index.d.ts +0 -8
  257. package/dist/ui/components/modal/index.js +0 -34
  258. package/dist/ui/components/modal/modal.css +0 -36
@@ -0,0 +1,257 @@
1
+ 'use client'
2
+
3
+ import { useEffect, useRef } from 'react'
4
+
5
+ const VERT = /* glsl */ `attribute vec2 a;void main(){gl_Position=vec4(a,0,1);}`
6
+
7
+ const FRAG = /* glsl */ `precision highp float;
8
+ uniform float t;
9
+ uniform vec2 r;
10
+
11
+ const float FBM_STR = .08;
12
+
13
+ float h(vec2 p) { return fract(sin(dot(p, vec2(127.1, 311.7))) * 43758.5453); }
14
+
15
+ float n2(vec2 p) {
16
+ vec2 i = floor(p), f = fract(p);
17
+ f = f * f * (3. - 2. * f);
18
+
19
+ return mix(
20
+ mix(h(i), h(i + vec2(1, 0)), f.x),
21
+ mix(h(i + vec2(0, 1)), h(i + vec2(1, 1)), f.x),
22
+ f.y
23
+ );
24
+ }
25
+
26
+ float fbm(vec2 p) {
27
+ float v = 0., a = .5;
28
+
29
+ for (int i = 0; i < 4; i++) {
30
+ v += a * n2(p);
31
+ p *= 2.1;
32
+ a *= .45;
33
+ }
34
+
35
+ return v;
36
+ }
37
+
38
+ float drift(float speed, float s) {
39
+ return fract(t * speed + s + .02 * sin(t * .4 + s * 3.));
40
+ }
41
+
42
+ float brushAt(vec2 uv, float y, float th, float s) {
43
+ float hw = .34 + .08 * h(vec2(s, 77.));
44
+ float cx = .5;
45
+ float xn = (uv.x - (cx - hw)) / (2. * hw);
46
+ float env = smoothstep(0., .03, xn) * smoothstep(1., .97, xn);
47
+ float localTh = th * env;
48
+
49
+ if (localTh < .002) return 0.;
50
+
51
+ float morph = floor(t * 8.) * .7 + s;
52
+ float top = y - localTh * .5 + fbm(vec2(uv.x * 6., morph)) * FBM_STR;
53
+ float bot = y + localTh * .5 - fbm(vec2(uv.x * 6., morph + 30.)) * FBM_STR;
54
+ float x0 = cx - hw + fbm(vec2(uv.y * 8., morph + 60.)) * FBM_STR;
55
+ float x1 = cx + hw - fbm(vec2(uv.y * 8., morph + 90.)) * FBM_STR;
56
+
57
+ float dMin = min(min(uv.y - top, bot - uv.y), min(uv.x - x0, x1 - uv.x));
58
+
59
+ float bristle = n2(vec2(uv.x * 60., uv.y * 8. + s)) * .4
60
+ + n2(vec2(uv.x * 25., (uv.y - y) * 120. + s)) * .35
61
+ + n2(vec2(uv.x * 90., uv.y * 3. + s * 2.)) * .25;
62
+
63
+ float eaten = smoothstep(.03, 0., dMin) * (1. - smoothstep(.2, .5, bristle));
64
+
65
+ return clamp(smoothstep(0., .003, dMin) * (1. - eaten), 0., 1.);
66
+ }
67
+
68
+ void main() {
69
+ vec2 uv = gl_FragCoord.xy / r;
70
+ uv = vec2(uv.x * cos(.095) - uv.y * sin(.095), uv.x * sin(.095) + uv.y * cos(.095));
71
+ uv += vec2(fbm(uv * 4. + t * .06), fbm(uv * 4. + 8. + t * .05)) * .012;
72
+
73
+ vec3 c = vec3(.992, .992, .051);
74
+
75
+ float smScroll = -drift(.04, 5.) * 2.;
76
+ float sm = 0.;
77
+
78
+ for (int i = 0; i < 20; i++) {
79
+ sm = max(sm, brushAt(uv, mod(float(i) * .1 + smScroll, 2.) - .5, .04, float(i) + 10.));
80
+ }
81
+
82
+ float d1 = drift(.15, 1.), d2 = drift(.15, 1.37), d3 = drift(.15, 1.58), d4 = drift(.15, 1.82);
83
+ float big = max(
84
+ max(brushAt(uv, 1.1 - d1 * 1.4, .28, 1.), brushAt(uv, 1.1 - d2 * 1.4, .18, 2.)),
85
+ max(brushAt(uv, 1.1 - d3 * 1.4, .3, 3.), brushAt(uv, 1.1 - d4 * 1.4, .15, 4.))
86
+ );
87
+
88
+ c = mix(c, vec3(0.), clamp(max(sm, big), 0., 1.));
89
+ c *= .94 + .06 * sin(uv.y * r.y * 6.283);
90
+
91
+ vec2 raw = gl_FragCoord.xy / r;
92
+ float dx = min(raw.x - .22, .90 - raw.x);
93
+ float dy = min(raw.y - .29, .86 - raw.y);
94
+ float cycle = floor(t * .4);
95
+ float edge = mix(smoothstep(.22, 0., max(min(dx, dy), 0.)), 1., step(.75, h(vec2(cycle, 13.))))
96
+ * smoothstep(.85, 1., sin(t * 2.5) * .5 + .5)
97
+ * (.7 + .3 * h(vec2(cycle, 7.)));
98
+
99
+ float scanY = floor(gl_FragCoord.y);
100
+ float rowNoise = h(vec2(scanY, floor(t * 30.)));
101
+ c *= 1. - edge * max(step(.45, rowNoise), step(.3, h(vec2(gl_FragCoord.x + scanY * 7., floor(t * 45.)))) * step(.2, rowNoise));
102
+
103
+ gl_FragColor = vec4(clamp(c, 0., 1.), 1.);
104
+ }`
105
+
106
+ function useGL(ref: React.RefObject<HTMLCanvasElement | null>) {
107
+ const raf = useRef(0)
108
+
109
+ useEffect(() => {
110
+ const c = ref.current
111
+
112
+ if (!c) {
113
+ return
114
+ }
115
+
116
+ const gl = c.getContext('webgl')
117
+
118
+ if (!gl) {
119
+ return
120
+ }
121
+
122
+ const sh = (type: number, src: string) => {
123
+ const s = gl.createShader(type)!
124
+ gl.shaderSource(s, src)
125
+ gl.compileShader(s)
126
+
127
+ return s
128
+ }
129
+
130
+ const p = gl.createProgram()!
131
+ gl.attachShader(p, sh(gl.VERTEX_SHADER, VERT))
132
+ gl.attachShader(p, sh(gl.FRAGMENT_SHADER, FRAG))
133
+ gl.linkProgram(p)
134
+ gl.useProgram(p)
135
+
136
+ gl.bindBuffer(gl.ARRAY_BUFFER, gl.createBuffer())
137
+ gl.bufferData(
138
+ gl.ARRAY_BUFFER,
139
+ new Float32Array([-1, -1, 1, -1, -1, 1, 1, 1]),
140
+ gl.STATIC_DRAW
141
+ )
142
+
143
+ const a = gl.getAttribLocation(p, 'a')
144
+ gl.enableVertexAttribArray(a)
145
+ gl.vertexAttribPointer(a, 2, gl.FLOAT, false, 0, 0)
146
+
147
+ const uT = gl.getUniformLocation(p, 't')
148
+ const uR = gl.getUniformLocation(p, 'r')
149
+
150
+ const resize = () => {
151
+ const rect = c.getBoundingClientRect()
152
+ const dpr = Math.min(devicePixelRatio, 2)
153
+
154
+ c.width = rect.width * dpr
155
+ c.height = rect.height * dpr
156
+
157
+ gl.viewport(0, 0, c.width, c.height)
158
+ }
159
+
160
+ resize()
161
+
162
+ const ro = new ResizeObserver(resize)
163
+ ro.observe(c)
164
+
165
+ const t0 = performance.now()
166
+
167
+ let visible = !document.hidden
168
+ let inView = true
169
+ let raf2 = 0
170
+
171
+ const tick = () => {
172
+ gl.uniform1f(uT, (performance.now() - t0) / 1e3)
173
+ gl.uniform2f(uR, c.width, c.height)
174
+ gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4)
175
+
176
+ raf2 = requestAnimationFrame(tick)
177
+ }
178
+
179
+ const start = () => {
180
+ if (visible && inView && !raf2) {
181
+ raf2 = requestAnimationFrame(tick)
182
+ }
183
+ }
184
+
185
+ const stop = () => {
186
+ if (raf2) {
187
+ cancelAnimationFrame(raf2)
188
+ raf2 = 0
189
+ }
190
+ }
191
+
192
+ const onVisibility = () => {
193
+ visible = !document.hidden
194
+ visible ? start() : stop()
195
+ }
196
+
197
+ const io = new IntersectionObserver(
198
+ entries => {
199
+ inView = entries.some(e => e.isIntersecting)
200
+ inView ? start() : stop()
201
+ },
202
+ { threshold: 0 }
203
+ )
204
+
205
+ io.observe(c)
206
+ document.addEventListener('visibilitychange', onVisibility)
207
+
208
+ start()
209
+ raf.current = raf2
210
+
211
+ return () => {
212
+ stop()
213
+ io.disconnect()
214
+ document.removeEventListener('visibilitychange', onVisibility)
215
+ ro.disconnect()
216
+ }
217
+ }, [ref])
218
+ }
219
+
220
+ export function TV({ className }: { className?: string }) {
221
+ const canvasRef = useRef<HTMLCanvasElement>(null)
222
+ useGL(canvasRef)
223
+
224
+ return (
225
+ <div className={['relative', className].filter(Boolean).join(' ')}>
226
+ <svg className="relative h-full w-full" fill="none" viewBox="0 0 210 173">
227
+ <path
228
+ d="M30.8342 2.44471 6.08268 36.683c-.24437.338-.38254.7412-.39689 1.158L1.57754 157.126c-.03891 1.129.82339 2.087 1.95096 2.167l162.4835 11.463c.433.031.866-.074 1.238-.3l35.718-21.69c.607-.369.986-1.02 1.008-1.73l4.102-130.9871c.035-1.1269-.826-2.0806-1.951-2.1604L32.6847 1.58029c-.7248-.05144-1.4247.27551-1.8505.86442Z"
229
+ fill="#FDFD0D"
230
+ stroke="#FDFD0D"
231
+ strokeWidth="3.15"
232
+ />
233
+
234
+ <path
235
+ d="M203.09 17.1483 35.6844 5.83395l-4.2 121.94805 168.4906 13.076z"
236
+ fill="#000"
237
+ stroke="#FDFD0D"
238
+ strokeWidth="4.2"
239
+ />
240
+
241
+ <path
242
+ d="M190.491 29.7483 48.2859 18.434l-4.2 98.848 143.2901 10.976z"
243
+ fill="#FDFD0D"
244
+ />
245
+ </svg>
246
+
247
+ <canvas
248
+ className="absolute inset-0 h-full w-full"
249
+ ref={canvasRef}
250
+ style={{
251
+ clipPath:
252
+ 'polygon(23% 10.65%, 90.71% 17.2%, 89.23% 74.13%, 20.99% 67.79%)'
253
+ }}
254
+ />
255
+ </div>
256
+ )
257
+ }
@@ -0,0 +1,18 @@
1
+ import { forwardRef } from 'react'
2
+
3
+ import { cn } from '../../../utils'
4
+
5
+ import { Typography, type TypographyProps } from '.'
6
+
7
+ export const H1 = forwardRef<HTMLHeadingElement, TypographyProps<'h1'>>(
8
+ ({ className, ...props }, ref) => {
9
+ return (
10
+ <Typography
11
+ as="h1"
12
+ className={cn('font-bold', className)}
13
+ variant="xl"
14
+ {...{ ref, ...props }}
15
+ />
16
+ )
17
+ }
18
+ )
@@ -0,0 +1,18 @@
1
+ import { forwardRef } from 'react'
2
+
3
+ import { cn } from '../../../utils'
4
+
5
+ import { Typography, type TypographyProps } from '.'
6
+
7
+ export const H2 = forwardRef<HTMLHeadingElement, TypographyProps<'h2'>>(
8
+ ({ className, ...props }, ref) => {
9
+ return (
10
+ <Typography
11
+ as="h2"
12
+ className={cn('font-bold', className)}
13
+ variant="lg"
14
+ {...{ ref, ...props }}
15
+ />
16
+ )
17
+ }
18
+ )
@@ -0,0 +1,54 @@
1
+ import { cva, type VariantProps } from 'class-variance-authority'
2
+ import { createElement } from 'react'
3
+
4
+ import { cn, type PolyProps, polyRef } from '../../../utils'
5
+
6
+ const typographyVariants = cva('font-sans', {
7
+ variants: {
8
+ compressed: { true: 'font-compressed' },
9
+ courier: { true: 'font-courier' },
10
+ expanded: { true: 'font-expanded' },
11
+ mondwest: { true: 'font-mondwest tracking-[0.1875rem]' },
12
+ mono: { true: 'font-mono' },
13
+ sans: { true: 'font-sans' },
14
+ variant: {
15
+ lg: 'text-[2.625rem] leading-[1] tracking-[0.0525rem]',
16
+ md: 'text-[2.625rem] leading-[1] tracking-[0.0525rem]',
17
+ sm: 'leading-1.4 text-[.9375rem] tracking-[0.1875rem]',
18
+ xl: 'text-[4.5rem] leading-[1] tracking-[0.135rem]'
19
+ }
20
+ }
21
+ })
22
+
23
+ export const Typography = polyRef<'span', OwnProps>(
24
+ (
25
+ {
26
+ as,
27
+ className,
28
+ compressed,
29
+ courier,
30
+ expanded,
31
+ mondwest,
32
+ mono,
33
+ variant,
34
+ ...rest
35
+ },
36
+ ref
37
+ ) => {
38
+ const fonts = { compressed, courier, expanded, mondwest, mono }
39
+ const fontVariant = { ...fonts, sans: !Object.values(fonts).some(Boolean) }
40
+
41
+ return createElement((as ?? 'span') as React.ElementType, {
42
+ ...rest,
43
+ className: cn(typographyVariants({ ...fontVariant, variant }), className),
44
+ ref
45
+ })
46
+ }
47
+ )
48
+
49
+ type OwnProps = VariantProps<typeof typographyVariants>
50
+
51
+ export type TypographyProps<T extends React.ElementType = 'span'> = PolyProps<
52
+ T,
53
+ OwnProps
54
+ >
@@ -0,0 +1,24 @@
1
+ import { cn } from '../../../utils'
2
+
3
+ import { Small } from './small'
4
+
5
+ export function Legend({
6
+ children,
7
+ className,
8
+ label,
9
+ sub,
10
+ ...props
11
+ }: LegendProps) {
12
+ return (
13
+ <hgroup className={cn('flex flex-col gap-2', className)} {...props}>
14
+ <Small>{label}</Small>
15
+ {sub && <Small className="opacity-50">- {sub}</Small>}
16
+ {children}
17
+ </hgroup>
18
+ )
19
+ }
20
+
21
+ interface LegendProps extends React.ComponentProps<'hgroup'> {
22
+ label: React.ReactNode
23
+ sub?: React.ReactNode
24
+ }
@@ -0,0 +1,11 @@
1
+ import { forwardRef } from 'react'
2
+
3
+ import { Typography, type TypographyProps } from '.'
4
+
5
+ export const Small = forwardRef<HTMLSpanElement, TypographyProps<any>>(
6
+ (props, ref) => {
7
+ return (
8
+ <Typography as="small" mondwest variant="sm" {...{ ref, ...props }} />
9
+ )
10
+ }
11
+ )
@@ -0,0 +1,33 @@
1
+ import type { Meta, StoryObj } from '@storybook/react-vite'
2
+
3
+ import { Watchlist } from './watchlist'
4
+
5
+ const ADDRESSES = [
6
+ ['0x7a16fF8270133F063aAb6C9977183D9e72835428', '0.50%'],
7
+ ['0xd4e96eF8eEE8678dBFF4d535E015d1a77e7Cc62c', '1.20%'],
8
+ ['0x2B5AD5c4795c026514f8317c7a215E218DcCD6cF', '3.00%'],
9
+ ['0x8ba1f109551bD432803012645Ac136ddd64DBA72', '4.10%']
10
+ ].map(([label, right]) => ({ label, right, url: '#' }))
11
+
12
+ const meta = {
13
+ component: Watchlist,
14
+ title: 'Components/Data Display/Watchlist'
15
+ } satisfies Meta<typeof Watchlist>
16
+
17
+ export default meta
18
+
19
+ type Story = StoryObj<typeof meta>
20
+
21
+ export const WithCounterAndScramble: Story = {
22
+ args: { counter: true, items: ADDRESSES, scramble: true }
23
+ }
24
+
25
+ export const Simple: Story = {
26
+ args: {
27
+ items: [
28
+ { label: 'One', right: '11%' },
29
+ { label: 'Two', right: '22%' },
30
+ { label: 'Three', right: '33%' }
31
+ ]
32
+ }
33
+ }
@@ -0,0 +1,105 @@
1
+ 'use client'
2
+
3
+ import { cn } from '../../utils'
4
+
5
+ import { Scramble } from './ascii'
6
+ import { LinkIcon } from './icons'
7
+ import { Typography } from './typography'
8
+
9
+ const ETH_RE = /^0x[a-fA-F0-9]{40}$/
10
+ const truncate = (a: string) => `${a.slice(0, 6)}${'·'.repeat(8)}${a.slice(-4)}`
11
+
12
+ export function Watchlist({
13
+ className,
14
+ counter = false,
15
+ items,
16
+ scramble = false,
17
+ ...props
18
+ }: WatchlistProps) {
19
+ return (
20
+ <div className={cn('flex flex-col gap-3', className)} {...props}>
21
+ {items.map(({ label, right, url }, i) => {
22
+ const isStr = typeof label === 'string'
23
+ const eth = isStr && ETH_RE.test(label)
24
+ const text = eth ? truncate(label) : (label as string)
25
+
26
+ return (
27
+ <a
28
+ className={cn(
29
+ 'grid items-center gap-2.5 px-2.5 py-1.5',
30
+ 'text-display leading-[1.4]',
31
+ 'hover:bg-midground/10! hover:ring-2 hover:ring-current/20',
32
+ 'transition-all duration-500 hover:duration-0',
33
+ 'opacity-(--midground-alpha)'
34
+ )}
35
+ href={url}
36
+ key={i}
37
+ rel="noopener noreferrer"
38
+ style={{
39
+ background: `color-mix(in oklch, var(--color-midground) ${10 * Math.max(0, 1 - i / 9)}%, transparent)`,
40
+ gridTemplateColumns: [
41
+ counter && 'auto auto',
42
+ '1fr',
43
+ right && 'auto',
44
+ url && 'auto auto'
45
+ ]
46
+ .filter(Boolean)
47
+ .join(' ')
48
+ }}
49
+ target="_blank"
50
+ >
51
+ {counter && (
52
+ <>
53
+ <Typography
54
+ className="text-lg tracking-[0.35em] opacity-40"
55
+ compressed
56
+ >
57
+ {String(i + 1).padStart(2, '0')}
58
+ </Typography>
59
+
60
+ <span className="text-[0.8125rem] font-bold tracking-[0.4em] opacity-20">
61
+ :
62
+ </span>
63
+ </>
64
+ )}
65
+
66
+ {isStr ? (
67
+ <Typography
68
+ className="min-w-0 overflow-hidden text-lg font-bold tracking-[0.35em]"
69
+ {...(eth ? { mono: true } : { compressed: true })}
70
+ >
71
+ {scramble ? <Scramble delay={i * 80} text={text} /> : text}
72
+ </Typography>
73
+ ) : (
74
+ label
75
+ )}
76
+
77
+ {right && (
78
+ <Typography
79
+ className="text-right text-sm tracking-widest opacity-40"
80
+ mono
81
+ >
82
+ {right}
83
+ </Typography>
84
+ )}
85
+
86
+ {url && (
87
+ <>
88
+ <span className="text-[0.8125rem] tracking-[0.4em] opacity-20">
89
+ :
90
+ </span>
91
+ <LinkIcon className="text-midground size-3.5" />
92
+ </>
93
+ )}
94
+ </a>
95
+ )
96
+ })}
97
+ </div>
98
+ )
99
+ }
100
+
101
+ interface WatchlistProps extends React.ComponentProps<'div'> {
102
+ counter?: boolean
103
+ items: { label?: React.ReactNode; right?: React.ReactNode; url?: string }[]
104
+ scramble?: boolean
105
+ }
@@ -0,0 +1,63 @@
1
+ @font-face {
2
+ font-family: 'Collapse';
3
+ font-style: normal;
4
+ font-weight: 400;
5
+ font-display: swap;
6
+ src: url('../fonts/Collapse-Regular.woff2') format('woff2');
7
+ }
8
+
9
+ @font-face {
10
+ font-family: 'Collapse';
11
+ font-style: normal;
12
+ font-weight: 700;
13
+ font-display: swap;
14
+ src: url('../fonts/Collapse-Bold.woff2') format('woff2');
15
+ }
16
+
17
+ @font-face {
18
+ font-family: 'Rules Compressed';
19
+ font-style: normal;
20
+ font-weight: 400;
21
+ font-display: swap;
22
+ src: url('../fonts/RulesCompressed-Regular.woff2') format('woff2');
23
+ }
24
+
25
+ @font-face {
26
+ font-family: 'Rules Compressed';
27
+ font-style: normal;
28
+ font-weight: 600;
29
+ font-display: swap;
30
+ src: url('../fonts/RulesCompressed-Medium.woff2') format('woff2');
31
+ }
32
+
33
+ @font-face {
34
+ font-family: 'Rules Expanded';
35
+ font-style: normal;
36
+ font-weight: 400;
37
+ font-display: swap;
38
+ src: url('../fonts/RulesExpanded-Regular.woff2') format('woff2');
39
+ }
40
+
41
+ @font-face {
42
+ font-family: 'Rules Expanded';
43
+ font-style: normal;
44
+ font-weight: 700;
45
+ font-display: swap;
46
+ src: url('../fonts/RulesExpanded-Bold.woff2') format('woff2');
47
+ }
48
+
49
+ @font-face {
50
+ font-family: 'Mondwest';
51
+ font-style: normal;
52
+ font-weight: 400;
53
+ font-display: swap;
54
+ src: url('../fonts/Mondwest-Regular.woff2') format('woff2');
55
+ }
56
+
57
+ :root {
58
+ --font-sans: 'Collapse', sans-serif;
59
+ --font-mono: 'Courier Prime', monospace;
60
+ --font-rules-compressed: 'Rules Compressed', sans-serif;
61
+ --font-rules-expanded: 'Rules Expanded', sans-serif;
62
+ --font-mondwest: 'Mondwest', sans-serif;
63
+ }
@@ -0,0 +1,111 @@
1
+ 'use client'
2
+
3
+ import { useRef } from 'react'
4
+
5
+ import { useCssVarDims } from '../hooks/use-css-var-dims'
6
+ import { Cell, Grid } from './components/grid'
7
+ import { Socials, type SocialLink } from './components/socials'
8
+ import { ThemeToggle } from './components/theme-toggle'
9
+ import { Small } from './components/typography/small'
10
+
11
+ const DEFAULT_GROUPS: FooterGroup[] = [
12
+ { label: 'Product', links: ['Overview', 'Features', 'Pricing'] },
13
+ { label: 'Resources', links: ['Docs', 'Blog', 'Support'] },
14
+ { label: 'Company', links: ['About', 'Careers', 'Contact'] },
15
+ { label: 'Legal', links: ['Privacy', 'Terms', 'License'] }
16
+ ]
17
+
18
+ export function Footer({
19
+ className,
20
+ groups = DEFAULT_GROUPS,
21
+ LinkComponent = 'a',
22
+ socials,
23
+ socialsLabel = 'Socials',
24
+ style,
25
+ themeLabel = 'Theme',
26
+ themeToggle = false
27
+ }: FooterProps) {
28
+ const ref = useRef<HTMLElement>(null)
29
+ useCssVarDims('footer', ref)
30
+
31
+ const hasSocials = (socials?.length ?? 0) > 0
32
+ const hasChrome = hasSocials || themeToggle
33
+
34
+ return (
35
+ <footer className={className} ref={ref} style={style}>
36
+ <Grid>
37
+ <Cell>
38
+ <Small className="opacity-50">&copy;{new Date().getFullYear()}</Small>
39
+ </Cell>
40
+
41
+ {groups.map(({ label, links }) => (
42
+ <Cell key={label}>
43
+ <Small className="opacity-50">{label}</Small>
44
+
45
+ <nav className="mt-3 flex flex-col gap-2">
46
+ {links.map(link => {
47
+ const href = typeof link === 'string'
48
+ ? `/${link.toLowerCase()}`
49
+ : link.href
50
+
51
+ const label = typeof link === 'string' ? link : link.label
52
+
53
+ return (
54
+ <Small
55
+ as={LinkComponent}
56
+ className="underline"
57
+ href={href}
58
+ key={label}
59
+ >
60
+ {label}
61
+ </Small>
62
+ )
63
+ })}
64
+ </nav>
65
+ </Cell>
66
+ ))}
67
+ </Grid>
68
+
69
+ {hasChrome && (
70
+ <Grid>
71
+ {hasSocials && (
72
+ <Cell className="flex items-start justify-between">
73
+ <Small className="opacity-50">{socialsLabel}</Small>
74
+
75
+ <Socials items={socials!} />
76
+ </Cell>
77
+ )}
78
+
79
+ {themeToggle && (
80
+ <Cell className="flex items-start justify-between">
81
+ <Small className="opacity-50">{themeLabel}</Small>
82
+
83
+ <ThemeToggle />
84
+ </Cell>
85
+ )}
86
+ </Grid>
87
+ )}
88
+ </footer>
89
+ )
90
+ }
91
+
92
+ export interface FooterGroup {
93
+ label: string
94
+ links: (FooterLink | string)[]
95
+ }
96
+
97
+ export interface FooterLink {
98
+ href: string
99
+ label: string
100
+ }
101
+
102
+ export interface FooterProps {
103
+ className?: string
104
+ groups?: FooterGroup[]
105
+ LinkComponent?: React.ElementType
106
+ socials?: SocialLink[]
107
+ socialsLabel?: string
108
+ style?: React.CSSProperties
109
+ themeLabel?: string
110
+ themeToggle?: boolean
111
+ }