@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,174 @@
1
+ 'use client'
2
+
3
+ import { useEffect, useRef } from 'react'
4
+ import * as THREE from 'three'
5
+
6
+ import { $gpuTier, useGpuTier } from '../../../hooks/use-gpu-tier'
7
+ import { useSmoothControls } from '../../../hooks/use-smooth-controls'
8
+ import { cn, hexToVec3 } from '../../../utils'
9
+
10
+ import { BLEND_MODES } from './blend-modes'
11
+
12
+ const vert = /*glsl*/ `
13
+ varying vec2 vUv;
14
+ void main() {
15
+ vUv = uv;
16
+ gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
17
+ }
18
+ `
19
+
20
+ const frag = /*glsl*/ `
21
+ uniform vec2 uRes;
22
+ uniform float uDpr, uSize, uDensity, uOpacity;
23
+ uniform vec3 uColor;
24
+ varying vec2 vUv;
25
+
26
+ float hash(vec2 p) {
27
+ vec3 p3 = fract(vec3(p.xyx) * 0.1031);
28
+ p3 += dot(p3, p3.yzx + 33.33);
29
+ return fract((p3.x + p3.y) * p3.z);
30
+ }
31
+
32
+ void main() {
33
+ float n = hash(floor(vUv * uRes / (uSize * uDpr)));
34
+ gl_FragColor = vec4(uColor, step(1.0 - uDensity, n)) * uOpacity;
35
+ }
36
+ `
37
+
38
+ export function Noise({ className, style }: NoiseProps) {
39
+ const gpuTier = useGpuTier()
40
+
41
+ const c = useSmoothControls(
42
+ 'Effects/Noise',
43
+ {
44
+ blend: { options: BLEND_MODES, value: 'color-dodge' as const },
45
+ color: { value: '#eaeaea' },
46
+ density: { max: 1, min: 0, step: 0.01, value: 0.11 },
47
+ enabled: { value: true },
48
+ opacity: { max: 1, min: 0, step: 0.01, value: 0.55 },
49
+ size: { max: 10, min: 0.1, step: 0.1, value: 1 }
50
+ },
51
+ { collapsed: true }
52
+ )
53
+
54
+ const canvasRef = useRef<HTMLCanvasElement>(null)
55
+
56
+ const enabled = c.enabled && gpuTier > 0
57
+
58
+ // The noise shader is fully deterministic: given the same uniforms it
59
+ // produces an identical output every frame. The previous version ran
60
+ // it at 60fps forever, which on a retina display with mix-blend-mode
61
+ // is enough to peg the GPU/compositor for the entire lifetime of the
62
+ // page (this is the dominant contributor to the "fans go crazy after
63
+ // a few hours of idle" symptom).
64
+ //
65
+ // Instead we render exactly once on mount, and re-render only when
66
+ // the user-controllable uniforms change or the viewport resizes.
67
+ useEffect(() => {
68
+ if (!canvasRef.current || !enabled) {
69
+ return
70
+ }
71
+
72
+ let renderer: THREE.WebGLRenderer
73
+
74
+ try {
75
+ renderer = new THREE.WebGLRenderer({
76
+ alpha: true,
77
+ canvas: canvasRef.current,
78
+ premultipliedAlpha: false
79
+ })
80
+ } catch {
81
+ $gpuTier.set(0)
82
+
83
+ return
84
+ }
85
+
86
+ renderer.setClearColor(0x000000, 0)
87
+
88
+ const scene = new THREE.Scene()
89
+ const camera = new THREE.OrthographicCamera(-1, 1, 1, -1, 0, 1)
90
+ const geo = new THREE.PlaneGeometry(2, 2)
91
+
92
+ const mat = new THREE.ShaderMaterial({
93
+ fragmentShader: frag,
94
+ transparent: true,
95
+ uniforms: {
96
+ uColor: { value: hexToVec3(c.color) },
97
+ uDensity: { value: c.density },
98
+ uDpr: { value: 1 },
99
+ uOpacity: { value: c.opacity },
100
+ uRes: { value: new THREE.Vector2() },
101
+ uSize: { value: c.size }
102
+ },
103
+ vertexShader: vert
104
+ })
105
+
106
+ scene.add(new THREE.Mesh(geo, mat))
107
+
108
+ // Cap pixel ratio at 1.5 — noise is intentionally pixelated, so the
109
+ // extra retina samples are wasted fillrate.
110
+ const dpr = Math.min(devicePixelRatio, 1.5)
111
+
112
+ const render = () => {
113
+ mat.uniforms.uColor.value = hexToVec3(c.color)
114
+ mat.uniforms.uDensity.value = c.density
115
+ mat.uniforms.uOpacity.value = c.opacity
116
+ mat.uniforms.uSize.value = c.size
117
+ mat.uniforms.uDpr.value = dpr
118
+ renderer.render(scene, camera)
119
+ }
120
+
121
+ const resize = () => {
122
+ renderer.setSize(innerWidth, innerHeight)
123
+ renderer.setPixelRatio(dpr)
124
+ mat.uniforms.uRes.value.set(innerWidth * dpr, innerHeight * dpr)
125
+ render()
126
+ }
127
+
128
+ resize()
129
+ window.addEventListener('resize', resize)
130
+
131
+ // Re-render when the tab becomes visible again, in case the GL
132
+ // context was discarded by the browser while the page was
133
+ // backgrounded — otherwise we can come back to a transparent canvas.
134
+ const onVisibility = () => {
135
+ if (!document.hidden) render()
136
+ }
137
+
138
+ document.addEventListener('visibilitychange', onVisibility)
139
+
140
+ return () => {
141
+ window.removeEventListener('resize', resize)
142
+ document.removeEventListener('visibilitychange', onVisibility)
143
+
144
+ mat.dispose()
145
+ geo.dispose()
146
+ renderer.dispose()
147
+ }
148
+ // eslint-disable-next-line react-hooks/exhaustive-deps
149
+ }, [
150
+ enabled,
151
+ gpuTier,
152
+ c.color,
153
+ c.density,
154
+ c.opacity,
155
+ c.size
156
+ ])
157
+
158
+ if (!enabled) {
159
+ return null
160
+ }
161
+
162
+ return (
163
+ <canvas
164
+ className={cn('h-full w-full', className)}
165
+ ref={canvasRef}
166
+ style={{ mixBlendMode: c.blend, ...style }}
167
+ />
168
+ )
169
+ }
170
+
171
+ interface NoiseProps {
172
+ className?: string
173
+ style?: React.CSSProperties
174
+ }
@@ -0,0 +1,60 @@
1
+ 'use client'
2
+
3
+ import { useSmoothControls } from '../../../hooks/use-smooth-controls'
4
+ import { cn } from '../../../utils'
5
+ import { hexToRgb } from '../../../utils/color'
6
+
7
+ import { BLEND_MODES } from './blend-modes'
8
+
9
+ export function Vignette({ className, style }: VignetteProps) {
10
+ const c = useSmoothControls(
11
+ 'Effects/Vignette',
12
+ {
13
+ blend: { options: BLEND_MODES, value: 'lighten' as const },
14
+ bottomLeft: { max: 1, min: 0, step: 0.01, value: 0 },
15
+ bottomRight: { max: 1, min: 0, step: 0.01, value: 0 },
16
+ color: { value: '#ffbd38' },
17
+ enabled: { value: true },
18
+ opacity: { max: 1, min: 0, step: 0.01, value: 0.22 },
19
+ size: { max: 1, min: 0, step: 0.01, value: 1 },
20
+ topLeft: { max: 1, min: 0, step: 0.01, value: 0.35 },
21
+ topRight: { max: 1, min: 0, step: 0.01, value: 0 }
22
+ },
23
+ { collapsed: true }
24
+ )
25
+
26
+ if (!c.enabled) return null
27
+
28
+ const rgb = hexToRgb(c.color)
29
+ const s = c.size * 60
30
+
31
+ const grad = (x: number, y: number, i: number) =>
32
+ i > 0 &&
33
+ `radial-gradient(ellipse at ${x}% ${y}%, rgba(${rgb},0) ${s}%, rgba(${rgb},${i}) 100%)`
34
+
35
+ const bg = [
36
+ grad(0, 0, c.topLeft),
37
+ grad(100, 0, c.topRight),
38
+ grad(0, 100, c.bottomLeft),
39
+ grad(100, 100, c.bottomRight)
40
+ ].filter(Boolean)
41
+
42
+ if (!bg.length) return null
43
+
44
+ return (
45
+ <div
46
+ className={cn('h-full w-full', className)}
47
+ style={{
48
+ background: bg.join(', '),
49
+ mixBlendMode: c.blend,
50
+ opacity: c.opacity,
51
+ ...style
52
+ }}
53
+ />
54
+ )
55
+ }
56
+
57
+ interface VignetteProps {
58
+ className?: string
59
+ style?: React.CSSProperties
60
+ }