@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
package/src/index.ts ADDED
@@ -0,0 +1,130 @@
1
+ export { AnimatedCount, useAnimatedCount } from './ui/components/animated-count'
2
+ export { AsciiSkeleton, Scramble as AsciiScramble } from './ui/components/ascii'
3
+ export { Badge } from './ui/components/badge'
4
+ export { BottomSheet } from './ui/components/bottom-sheet'
5
+ export { NousGirlBadge } from './ui/components/badges/nous-girl'
6
+ export { BlendMode, useBlendMode, withBlendMode } from './ui/components/blend-mode'
7
+ export type { BlendModeProps } from './ui/components/blend-mode'
8
+ export { Blink } from './ui/components/blink'
9
+ export { Button } from './ui/components/button'
10
+ export { Card, CardContent, CardDescription, CardHeader, CardTitle } from './ui/components/card'
11
+ export { Checkbox } from './ui/components/checkbox'
12
+ export { CommandBlock, CopyButton } from './ui/components/command-block'
13
+ export { ConfirmDialog } from './ui/components/confirm-dialog'
14
+ export { Cursor } from './ui/components/cursor'
15
+ export { DropdownMenu } from './ui/components/dropdown-menu'
16
+ export { FitText } from './ui/components/fit-text'
17
+ export { BarChart, LineChart } from './ui/components/graphs'
18
+ export { Cell, Grid } from './ui/components/grid'
19
+ export { HoverBg } from './ui/components/hover-bg'
20
+ export * as Icons from './ui/components/icons'
21
+ export { DiscordIcon } from './ui/components/icons/discord'
22
+ export { GitHubIcon } from './ui/components/icons/github'
23
+ export { ImageDistortion } from './ui/components/image-distortion'
24
+ export type { AutoPlayPattern } from './ui/components/image-distortion'
25
+ export { Input } from './ui/components/input'
26
+ export { Label } from './ui/components/label'
27
+ export { LevaClient } from './ui/components/leva-client'
28
+ export { ListItem } from './ui/components/list-item'
29
+ export {
30
+ Dialog,
31
+ DialogClose,
32
+ DialogContent,
33
+ DialogDescription,
34
+ DialogFooter,
35
+ DialogHeader,
36
+ DialogOverlay,
37
+ DialogPortal,
38
+ DialogTitle,
39
+ DialogTrigger
40
+ } from './ui/components/dialog'
41
+ export { FilterGroup, Segmented } from './ui/components/segmented'
42
+ export { Switch } from './ui/components/switch'
43
+ export { Tabs, TabsList, TabsTrigger } from './ui/components/tabs'
44
+ export { Poster } from './ui/components/poster'
45
+ export type {
46
+ PosterAspect,
47
+ PosterProps,
48
+ PosterVariant
49
+ } from './ui/components/poster'
50
+ export {
51
+ applyLens,
52
+ BLEND_MODES,
53
+ LENSES,
54
+ LENS_0,
55
+ LENS_5I,
56
+ lens0,
57
+ lens5i,
58
+ toggleLens,
59
+ $lightMode
60
+ } from './ui/components/overlays'
61
+ export {
62
+ Glitch,
63
+ Greys,
64
+ Lens,
65
+ Noise,
66
+ Overlays,
67
+ Vignette
68
+ } from './ui/components/overlays'
69
+ export type { LensPreset } from './ui/components/overlays'
70
+ export { Progress } from './ui/components/progress'
71
+ export { SceneCanvas } from './ui/components/scene-canvas'
72
+ export { Scramble } from './ui/components/scramble'
73
+ export { Select, SelectOption } from './ui/components/select'
74
+ export { SelectionSwitcher } from './ui/components/selection-switcher'
75
+ export { Separator } from './ui/components/separator'
76
+ export { Spinner } from './ui/components/spinner'
77
+ export { Stats } from './ui/components/stats'
78
+ export { TerminalDemo } from './ui/components/terminal-demo'
79
+ export type { TerminalDemoStep } from './ui/components/terminal-demo'
80
+ export { ThemeToggle } from './ui/components/theme-toggle'
81
+ export { TierCard } from './ui/components/tier-card'
82
+ export { Toast } from './ui/components/toast'
83
+ export type { TierCardPrice, TierCardProps } from './ui/components/tier-card'
84
+ export { TV } from './ui/components/tv'
85
+ export { Watchlist } from './ui/components/watchlist'
86
+
87
+ export { Typography } from './ui/components/typography'
88
+ export type { TypographyProps } from './ui/components/typography'
89
+ export { H1 } from './ui/components/typography/h1'
90
+ export { H2 } from './ui/components/typography/h2'
91
+ export { Legend } from './ui/components/typography/legend'
92
+ export { Small } from './ui/components/typography/small'
93
+
94
+ export { BasicPage } from './ui/basic-page'
95
+ export { Header } from './ui/header'
96
+ export type { HeaderLink, HeaderProps, HeaderSocial } from './ui/header'
97
+ export { Footer } from './ui/footer'
98
+ export type {
99
+ FooterGroup,
100
+ FooterLink,
101
+ FooterProps
102
+ } from './ui/footer'
103
+ export { Socials } from './ui/components/socials'
104
+ export type { SocialLink } from './ui/components/socials'
105
+ export { LayoutWrapper } from './ui/layout-wrapper'
106
+
107
+ export {
108
+ FONT_SANS,
109
+ FONT_MONO,
110
+ FONT_RULES_COMPRESSED,
111
+ FONT_RULES_EXPANDED,
112
+ FONT_MONDWEST
113
+ } from './fonts'
114
+
115
+ export { cn, clamp, smoothstep, hexToVec3, truncate, stripWpStyles } from './utils'
116
+ export { polyRef } from './utils'
117
+ export type { PolyComponent, PolyProps, PolyRef } from './utils'
118
+ export { hexToRgb, rgbToHex, colorDodge, colorMix } from './utils/color'
119
+
120
+ export { useBelowBreakpoint } from './hooks/use-below-breakpoint'
121
+ export { useCappedFrame } from './hooks/use-capped-frame'
122
+ export { useConfirmDelete } from './hooks/use-confirm-delete'
123
+ export { useCssVarDims } from './hooks/use-css-var-dims'
124
+ export { $gpuTier, useGpuTier } from './hooks/use-gpu-tier'
125
+ export {
126
+ useSmoothControls,
127
+ getControlAtom,
128
+ setControlValue
129
+ } from './hooks/use-smooth-controls'
130
+ export { useToast } from './hooks/use-toast'
@@ -0,0 +1,34 @@
1
+ import type { ReactNode } from 'react'
2
+
3
+ import { Cell, Grid } from './components/grid'
4
+ import { Progress } from './components/progress'
5
+ import { H1 } from './components/typography/h1'
6
+ import { Small } from './components/typography/small'
7
+
8
+ export function BasicPage({ children, subtitle, title }: BasicPageProps) {
9
+ return (
10
+ <>
11
+ <Grid>
12
+ <Cell>
13
+ <Progress value={0} />
14
+ </Cell>
15
+ </Grid>
16
+
17
+ <Grid className="lg:grid-cols-[max-content_1fr]">
18
+ <Cell className="-order-1">
19
+ <div className="sticky top-4 flex flex-col gap-4">
20
+ {title ? <H1 className="-mb-2 pr-10 opacity-90">{title}</H1> : null}
21
+ {subtitle ? <Small className="opacity-60">{subtitle}</Small> : null}
22
+ </div>
23
+ </Cell>
24
+
25
+ <Cell className="post bg-current/3">{children}</Cell>
26
+ </Grid>
27
+ </>
28
+ )
29
+ }
30
+
31
+ interface BasicPageProps extends React.PropsWithChildren {
32
+ subtitle?: string
33
+ title?: ReactNode
34
+ }
@@ -0,0 +1,4 @@
1
+ @import "tailwindcss";
2
+ @source "../";
3
+ @import "./fonts.css";
4
+ @import "./globals.css";
@@ -0,0 +1,67 @@
1
+ import type { Meta, StoryObj } from '@storybook/react-vite'
2
+ import { useState } from 'react'
3
+
4
+ import {
5
+ AnimatedCount,
6
+ useAnimatedCount
7
+ } from './animated-count'
8
+ import { Button } from './button'
9
+ import { Typography } from './typography'
10
+ import { Small } from './typography/small'
11
+
12
+ const meta = {
13
+ args: { damping: 1, duration: 1600, value: 84210 },
14
+ component: AnimatedCount,
15
+ title: 'Components/Feedback/AnimatedCount'
16
+ } satisfies Meta<typeof AnimatedCount>
17
+
18
+ export default meta
19
+
20
+ type Story = StoryObj<typeof meta>
21
+
22
+ export const Playground: Story = {
23
+ render: args => (
24
+ <Typography className="text-4xl font-bold tabular-nums" expanded>
25
+ <AnimatedCount {...args} />
26
+ </Typography>
27
+ )
28
+ }
29
+
30
+ function LiveCount() {
31
+ const ts = useState(() => new Date())[0]
32
+ const value = useAnimatedCount(1000, 12, ts)
33
+
34
+ return <AnimatedCount duration={500} value={value} />
35
+ }
36
+
37
+ export const Live: StoryObj = {
38
+ render: () => (
39
+ <div className="flex flex-col gap-2">
40
+ <Small className="opacity-40">Live ticker (rate = 12)</Small>
41
+
42
+ <Typography className="text-4xl font-bold tabular-nums" expanded>
43
+ <LiveCount />
44
+ </Typography>
45
+ </div>
46
+ )
47
+ }
48
+
49
+ export const Manual: StoryObj = {
50
+ render: () => {
51
+ const [value, setValue] = useState(100)
52
+
53
+ return (
54
+ <div className="flex flex-col gap-4">
55
+ <Typography className="text-4xl font-bold tabular-nums" expanded>
56
+ <AnimatedCount duration={800} value={value} />
57
+ </Typography>
58
+
59
+ <div className="flex gap-2">
60
+ <Button onClick={() => setValue(v => v + 1000)}>+1000</Button>
61
+ <Button onClick={() => setValue(v => v + 100)}>+100</Button>
62
+ <Button onClick={() => setValue(v => Math.max(0, v - 100))}>-100</Button>
63
+ </div>
64
+ </div>
65
+ )
66
+ }
67
+ }
@@ -0,0 +1,168 @@
1
+ 'use client'
2
+
3
+ import { useEffect, useRef, useState } from 'react'
4
+
5
+ const ease = (t: number) => (t < 0.5 ? 4 * t ** 3 : 1 - (-2 * t + 2) ** 3 / 2)
6
+ const easeD = (t: number) => (t < 0.5 ? 12 * t ** 2 : 6 * (-2 * t + 2) ** 2)
7
+
8
+ export function useAnimatedCount(
9
+ from: number,
10
+ rate: number,
11
+ ts = new Date(),
12
+ pausedAt?: Date
13
+ ) {
14
+ const [value, setValue] = useState(from)
15
+ const current = useRef(from)
16
+
17
+ useEffect(() => {
18
+ if (!rate) {
19
+ return
20
+ }
21
+
22
+ let raf: number
23
+ let prev = Date.now()
24
+ let last = current.current
25
+
26
+ const target = () =>
27
+ from +
28
+ Math.floor(
29
+ ((pausedAt
30
+ ? Math.max(0, pausedAt.getTime() - ts.getTime())
31
+ : Date.now() - ts.getTime()) /
32
+ 1e3) *
33
+ rate *
34
+ 0.9
35
+ )
36
+
37
+ const tick = () => {
38
+ const now = Date.now()
39
+ current.current +=
40
+ (target() - current.current) * Math.min(1, ((now - prev) / 1e3) * 3)
41
+ prev = now
42
+
43
+ const rounded = Math.round(current.current)
44
+
45
+ if (rounded !== last) {
46
+ last = rounded
47
+ setValue(rounded)
48
+ }
49
+
50
+ if (!pausedAt || Math.abs(current.current - target()) > 0.5) {
51
+ raf = requestAnimationFrame(tick)
52
+ }
53
+ }
54
+
55
+ raf = requestAnimationFrame(tick)
56
+
57
+ return () => cancelAnimationFrame(raf)
58
+ }, [from, ts, rate, pausedAt])
59
+
60
+ return value
61
+ }
62
+
63
+ export function AnimatedCount({
64
+ damping = 1,
65
+ duration,
66
+ pausedAt,
67
+ rate = 0,
68
+ value
69
+ }: Props) {
70
+ const id = useRef(Math.random().toString(36).slice(2, 9))
71
+ const prev = useRef(value)
72
+ const [display, setDisplay] = useState(value)
73
+ const [velocity, setVelocity] = useState(rate)
74
+
75
+ useEffect(() => {
76
+ if (!duration) {
77
+ prev.current = value
78
+ return
79
+ }
80
+
81
+ const start = prev.current
82
+ const delta = value - start
83
+ const dur = duration * damping
84
+ prev.current = value
85
+
86
+ if (!delta) {
87
+ setVelocity(0)
88
+ return
89
+ }
90
+
91
+ const t0 = performance.now()
92
+
93
+ const tick = (now: number) => {
94
+ const t = Math.min((now - t0) / dur, 1)
95
+ setDisplay(Math.round(start + delta * ease(t)))
96
+ setVelocity(Math.abs((delta * easeD(t)) / dur) * 1000)
97
+ t < 1 ? requestAnimationFrame(tick) : setVelocity(0)
98
+ }
99
+
100
+ const raf = requestAnimationFrame(tick)
101
+
102
+ return () => cancelAnimationFrame(raf)
103
+ }, [value, duration, rate, damping])
104
+
105
+ const digits = Math.round(duration ? display : value).toLocaleString().split('')
106
+ const v = duration ? velocity : rate
107
+ const paused = !duration && pausedAt
108
+
109
+ const blurred = new Set(
110
+ digits
111
+ .map((c, i) => {
112
+ if (!/\d/.test(c) || v <= 0 || paused) {
113
+ return -1
114
+ }
115
+
116
+ const pos =
117
+ digits.filter(x => /\d/.test(x)).length -
118
+ digits.slice(0, i + 1).filter(x => /\d/.test(x)).length
119
+
120
+ return (10 ** Math.max(0, pos) / v) * 1e3 < 500 ? i : -1
121
+ })
122
+ .filter(i => i >= 0)
123
+ )
124
+
125
+ return (
126
+ <>
127
+ <svg className="pointer-events-none absolute size-0">
128
+ <defs>
129
+ {digits.map((_, i) => (
130
+ <filter
131
+ id={`blur-${id.current}-${i}`}
132
+ key={i}
133
+ suppressHydrationWarning
134
+ >
135
+ <feGaussianBlur
136
+ stdDeviation={`0 ${blurred.has(i) ? 1 + [...blurred].indexOf(i) * 0.6 : 0}`}
137
+ />
138
+ </filter>
139
+ ))}
140
+ </defs>
141
+ </svg>
142
+
143
+ <span className="inline-flex tabular-nums">
144
+ {digits.map((c, i) => (
145
+ <span
146
+ className="inline-block text-center"
147
+ key={i}
148
+ style={{
149
+ filter: blurred.has(i) ? `url(#blur-${id.current}-${i})` : 'none',
150
+ width: c === ',' ? '0.4ch' : '1ch'
151
+ }}
152
+ suppressHydrationWarning
153
+ >
154
+ {c}
155
+ </span>
156
+ ))}
157
+ </span>
158
+ </>
159
+ )
160
+ }
161
+
162
+ interface Props {
163
+ damping?: number
164
+ duration?: number
165
+ pausedAt?: Date
166
+ rate?: number
167
+ value: number
168
+ }
@@ -0,0 +1,30 @@
1
+ import type { Meta, StoryObj } from '@storybook/react-vite'
2
+
3
+ import { AsciiSkeleton, Scramble } from './ascii'
4
+ import { Typography } from './typography'
5
+
6
+ const meta = {
7
+ title: 'Components/Effects/Ascii'
8
+ } satisfies Meta
9
+
10
+ export default meta
11
+
12
+ type Story = StoryObj
13
+
14
+ export const RevealScramble: Story = {
15
+ render: () => (
16
+ <Typography className="text-lg" mono>
17
+ <Scramble delay={200} text="PSYCHE NETWORK" />
18
+ </Typography>
19
+ )
20
+ }
21
+
22
+ export const Skeleton: Story = {
23
+ render: () => (
24
+ <div className="flex flex-col gap-4">
25
+ <AsciiSkeleton cols={20} rows={1} />
26
+ <AsciiSkeleton cols={40} rows={3} />
27
+ <AsciiSkeleton cols={60} rows={5} speed={120} />
28
+ </div>
29
+ )
30
+ }
@@ -0,0 +1,110 @@
1
+ 'use client'
2
+
3
+ import { useEffect, useRef, useState } from 'react'
4
+
5
+ const BLOCK = '░▒▓█▄▀▐▌─│┌┐└┘├┤┬┴┼╌╎⣀⣤⣶⣿'
6
+ const ALPHA = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!@#$%&*'
7
+
8
+ const rand = (chars: string) => chars[(Math.random() * chars.length) | 0]
9
+
10
+ export function Scramble({
11
+ delay = 0,
12
+ text
13
+ }: {
14
+ delay?: number
15
+ text: string
16
+ }) {
17
+ const [display, setDisplay] = useState(text)
18
+ const iter = useRef(0)
19
+
20
+ useEffect(() => {
21
+ iter.current = 0
22
+
23
+ const timeout = setTimeout(() => {
24
+ const interval = setInterval(() => {
25
+ setDisplay(
26
+ text
27
+ .split('')
28
+ .map((c, i) =>
29
+ c === ' ' ? ' ' : i < iter.current ? text[i] : rand(ALPHA)
30
+ )
31
+ .join('')
32
+ )
33
+
34
+ iter.current += 1 / 3
35
+
36
+ if (iter.current >= text.length) {
37
+ clearInterval(interval)
38
+ setDisplay(text)
39
+ }
40
+ }, 30)
41
+
42
+ return () => clearInterval(interval)
43
+ }, delay)
44
+
45
+ return () => clearTimeout(timeout)
46
+ }, [text, delay])
47
+
48
+ return <>{display}</>
49
+ }
50
+
51
+ export function AsciiSkeleton({
52
+ className = '',
53
+ cols = 12,
54
+ rows = 1,
55
+ speed = 80
56
+ }: {
57
+ className?: string
58
+ cols?: number
59
+ rows?: number
60
+ speed?: number
61
+ }) {
62
+ const ref = useRef<HTMLSpanElement>(null)
63
+
64
+ useEffect(() => {
65
+ const el = ref.current
66
+
67
+ if (!el) {
68
+ return
69
+ }
70
+
71
+ const total = cols * rows
72
+ let frame = 0
73
+
74
+ const tick = () => {
75
+ let text = ''
76
+
77
+ for (let i = 0; i < total; i++) {
78
+ const x = i % cols
79
+ const shimmer = (x - frame * 0.5 + cols * 2) % (cols * 2)
80
+
81
+ text += shimmer < 6 ? ' ' : rand(BLOCK)
82
+
83
+ if (rows > 1 && x === cols - 1) {
84
+ text += '\n'
85
+ }
86
+ }
87
+
88
+ el.textContent = text
89
+ frame++
90
+ }
91
+
92
+ tick()
93
+ const id = setInterval(tick, speed)
94
+
95
+ return () => clearInterval(id)
96
+ }, [cols, rows, speed])
97
+
98
+ return (
99
+ <span
100
+ aria-hidden
101
+ className={`inline-block leading-tight opacity-20 select-none ${className}`}
102
+ ref={ref}
103
+ style={{
104
+ fontFamily: 'monospace',
105
+ fontSize: 'inherit',
106
+ letterSpacing: '0.05em'
107
+ }}
108
+ />
109
+ )
110
+ }
@@ -0,0 +1,31 @@
1
+ import type { Meta, StoryObj } from '@storybook/react-vite'
2
+
3
+ import { Badge } from './badge'
4
+ import { NousGirlBadge } from './badges/nous-girl'
5
+
6
+ const meta = {
7
+ args: { children: 'LIVE' },
8
+ component: Badge,
9
+ title: 'Components/Feedback/Badge'
10
+ } satisfies Meta<typeof Badge>
11
+
12
+ export default meta
13
+
14
+ type Story = StoryObj<typeof meta>
15
+
16
+ export const Default: Story = {}
17
+
18
+ export const Row: Story = {
19
+ render: () => (
20
+ <div className="flex items-center gap-3">
21
+ <Badge>2.47</Badge>
22
+ <Badge>LIVE</Badge>
23
+ <Badge>14B</Badge>
24
+ <Badge>DEMO</Badge>
25
+ </div>
26
+ )
27
+ }
28
+
29
+ export const NousGirl: StoryObj = {
30
+ render: () => <NousGirlBadge className="h-20 w-auto" />
31
+ }
@@ -0,0 +1,60 @@
1
+ import { cn } from '../../utils'
2
+
3
+ import { BlendMode, type BlendModeProps } from './blend-mode'
4
+
5
+ const BASE_CN =
6
+ 'inline-flex items-center font-compressed text-display px-2 py-1 leading-none tracking-[0.2em]'
7
+
8
+ const TONE_CLASSES: Record<Exclude<Tone, 'default'>, string> = {
9
+ destructive:
10
+ 'border border-destructive/30 bg-destructive/15 text-destructive',
11
+ outline: 'border border-midground/30 bg-transparent text-midground/80',
12
+ secondary: 'border border-midground/15 bg-midground/8 text-midground',
13
+ success: 'border border-success/30 bg-success/15 text-success',
14
+ warning: 'border border-warning/30 bg-warning/15 text-warning'
15
+ }
16
+
17
+ export const Badge = ({
18
+ className,
19
+ style,
20
+ tone = 'default',
21
+ ...props
22
+ }: BadgeProps) => {
23
+ // `tone="default"` keeps the original Lens-aware BlendMode treatment so
24
+ // existing brand-style consumers (nousnet-web etc.) render unchanged.
25
+ if (tone === 'default') {
26
+ return (
27
+ <BlendMode
28
+ as="span"
29
+ background="mg/0.075"
30
+ className={cn(BASE_CN, className)}
31
+ color="mg"
32
+ style={{ opacity: 'var(--midground-alpha)', ...style }}
33
+ {...(props as BlendModeProps<'span'>)}
34
+ />
35
+ )
36
+ }
37
+
38
+ // Semantic tones bypass BlendMode and use --color-* tokens directly so
39
+ // red stays red and green stays green regardless of the active Lens.
40
+ return (
41
+ <span
42
+ className={cn(BASE_CN, TONE_CLASSES[tone], className)}
43
+ style={style}
44
+ {...(props as React.HTMLAttributes<HTMLSpanElement>)}
45
+ />
46
+ )
47
+ }
48
+
49
+ type Tone =
50
+ | 'default'
51
+ | 'destructive'
52
+ | 'outline'
53
+ | 'secondary'
54
+ | 'success'
55
+ | 'warning'
56
+
57
+ interface BadgeProps
58
+ extends Omit<React.HTMLAttributes<HTMLSpanElement>, 'color'> {
59
+ tone?: Tone
60
+ }