@nous-research/ui 0.10.0 → 0.12.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 (60) hide show
  1. package/dist/hooks/use-gpu-tier.d.ts +15 -1
  2. package/dist/hooks/use-gpu-tier.d.ts.map +1 -1
  3. package/dist/hooks/use-gpu-tier.js +48 -18
  4. package/dist/hooks/use-gpu-tier.js.map +1 -1
  5. package/dist/index.d.ts +8 -2
  6. package/dist/index.d.ts.map +1 -1
  7. package/dist/index.js +5 -2
  8. package/dist/index.js.map +1 -1
  9. package/dist/ui/components/icons/discord.d.ts +3 -0
  10. package/dist/ui/components/icons/discord.d.ts.map +1 -0
  11. package/dist/ui/components/icons/discord.js +6 -0
  12. package/dist/ui/components/icons/discord.js.map +1 -0
  13. package/dist/ui/components/icons/github.d.ts +3 -0
  14. package/dist/ui/components/icons/github.d.ts.map +1 -0
  15. package/dist/ui/components/icons/github.js +6 -0
  16. package/dist/ui/components/icons/github.js.map +1 -0
  17. package/dist/ui/components/icons/index.d.ts +2 -0
  18. package/dist/ui/components/icons/index.d.ts.map +1 -1
  19. package/dist/ui/components/icons/index.js +2 -0
  20. package/dist/ui/components/icons/index.js.map +1 -1
  21. package/dist/ui/components/overlays/blend-modes.d.ts +2 -0
  22. package/dist/ui/components/overlays/blend-modes.d.ts.map +1 -0
  23. package/dist/ui/components/overlays/blend-modes.js +14 -0
  24. package/dist/ui/components/overlays/blend-modes.js.map +1 -0
  25. package/dist/ui/components/overlays/glitch.d.ts.map +1 -1
  26. package/dist/ui/components/overlays/glitch.js +16 -6
  27. package/dist/ui/components/overlays/glitch.js.map +1 -1
  28. package/dist/ui/components/overlays/greys.d.ts.map +1 -1
  29. package/dist/ui/components/overlays/greys.js +16 -6
  30. package/dist/ui/components/overlays/greys.js.map +1 -1
  31. package/dist/ui/components/overlays/index.d.ts +9 -53
  32. package/dist/ui/components/overlays/index.d.ts.map +1 -1
  33. package/dist/ui/components/overlays/index.js +9 -135
  34. package/dist/ui/components/overlays/index.js.map +1 -1
  35. package/dist/ui/components/overlays/lens-layers.d.ts +15 -0
  36. package/dist/ui/components/overlays/lens-layers.d.ts.map +1 -0
  37. package/dist/ui/components/overlays/lens-layers.js +68 -0
  38. package/dist/ui/components/overlays/lens-layers.js.map +1 -0
  39. package/dist/ui/components/overlays/lens.d.ts +45 -0
  40. package/dist/ui/components/overlays/lens.d.ts.map +1 -0
  41. package/dist/ui/components/overlays/lens.js +65 -0
  42. package/dist/ui/components/overlays/lens.js.map +1 -0
  43. package/dist/ui/components/overlays/noise.d.ts.map +1 -1
  44. package/dist/ui/components/overlays/noise.js +18 -7
  45. package/dist/ui/components/overlays/noise.js.map +1 -1
  46. package/dist/ui/components/overlays/vignette.js +1 -1
  47. package/dist/ui/components/overlays/vignette.js.map +1 -1
  48. package/dist/ui/components/socials.d.ts +21 -0
  49. package/dist/ui/components/socials.d.ts.map +1 -0
  50. package/dist/ui/components/socials.js +9 -0
  51. package/dist/ui/components/socials.js.map +1 -0
  52. package/dist/ui/footer.d.ts +18 -3
  53. package/dist/ui/footer.d.ts.map +1 -1
  54. package/dist/ui/footer.js +13 -3
  55. package/dist/ui/footer.js.map +1 -1
  56. package/dist/ui/header.d.ts +39 -3
  57. package/dist/ui/header.d.ts.map +1 -1
  58. package/dist/ui/header.js +58 -5
  59. package/dist/ui/header.js.map +1 -1
  60. package/package.json +3 -1
@@ -2,10 +2,10 @@
2
2
  import { jsx as _jsx } from "react/jsx-runtime";
3
3
  import { useEffect, useRef } from 'react';
4
4
  import * as THREE from 'three';
5
- import { useGpuTier } from '../../../hooks/use-gpu-tier';
5
+ import { $gpuTier, useGpuTier } from '../../../hooks/use-gpu-tier';
6
6
  import { useSmoothControls } from '../../../hooks/use-smooth-controls';
7
7
  import { cn, hexToVec3 } from '../../../utils';
8
- import { BLEND_MODES } from '.';
8
+ import { BLEND_MODES } from './blend-modes';
9
9
  const vert = /*glsl*/ `
10
10
  varying vec2 vUv;
11
11
  void main() {
@@ -48,11 +48,22 @@ export function Noise({ className, style }) {
48
48
  if (!canvasRef.current || !enabled) {
49
49
  return;
50
50
  }
51
- const renderer = new THREE.WebGLRenderer({
52
- alpha: true,
53
- canvas: canvasRef.current,
54
- premultipliedAlpha: false
55
- });
51
+ let renderer;
52
+ try {
53
+ renderer = new THREE.WebGLRenderer({
54
+ alpha: true,
55
+ canvas: canvasRef.current,
56
+ premultipliedAlpha: false
57
+ });
58
+ }
59
+ catch {
60
+ // Belt-and-braces: the gpu-tier hook should already have downgraded
61
+ // to 0 before we render, but if a driver fails the renderer
62
+ // constructor anyway (e.g. context loss between probe and use), make
63
+ // sure no other overlay tries again.
64
+ $gpuTier.set(0);
65
+ return;
66
+ }
56
67
  renderer.setClearColor(0x000000, 0);
57
68
  const scene = new THREE.Scene();
58
69
  const camera = new THREE.OrthographicCamera(-1, 1, 1, -1, 0, 1);
@@ -1 +1 @@
1
- {"version":3,"file":"noise.js","sourceRoot":"","sources":["../../../../src/ui/components/overlays/noise.tsx"],"names":[],"mappings":"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","sourcesContent":["'use client'\n\nimport { useEffect, useRef } from 'react'\nimport * as THREE from 'three'\n\nimport { useGpuTier } from '../../../hooks/use-gpu-tier'\nimport { useSmoothControls } from '../../../hooks/use-smooth-controls'\nimport { cn, hexToVec3 } from '../../../utils'\n\nimport { BLEND_MODES } from '.'\n\nconst vert = /*glsl*/ `\n varying vec2 vUv;\n void main() {\n vUv = uv;\n gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);\n }\n`\n\nconst frag = /*glsl*/ `\n uniform vec2 uRes;\n uniform float uDpr, uSize, uDensity, uOpacity;\n uniform vec3 uColor;\n varying vec2 vUv;\n\n float hash(vec2 p) {\n vec3 p3 = fract(vec3(p.xyx) * 0.1031);\n p3 += dot(p3, p3.yzx + 33.33);\n return fract((p3.x + p3.y) * p3.z);\n }\n\n void main() {\n float n = hash(floor(vUv * uRes / (uSize * uDpr)));\n gl_FragColor = vec4(uColor, step(1.0 - uDensity, n)) * uOpacity;\n }\n`\n\nexport function Noise({ className, style }: NoiseProps) {\n const gpuTier = useGpuTier()\n\n const c = useSmoothControls(\n 'Effects/Noise',\n {\n blend: { options: BLEND_MODES, value: 'color-dodge' as const },\n color: { value: '#eaeaea' },\n density: { max: 1, min: 0, step: 0.01, value: 0.11 },\n enabled: { value: true },\n opacity: { max: 1, min: 0, step: 0.01, value: 0.55 },\n size: { max: 10, min: 0.1, step: 0.1, value: 1 }\n },\n { collapsed: true }\n )\n\n const canvasRef = useRef<HTMLCanvasElement>(null)\n const cRef = useRef(c)\n cRef.current = c\n\n const enabled = c.enabled && gpuTier > 0\n\n useEffect(() => {\n if (!canvasRef.current || !enabled) {\n return\n }\n\n const renderer = new THREE.WebGLRenderer({\n alpha: true,\n canvas: canvasRef.current,\n premultipliedAlpha: false\n })\n\n renderer.setClearColor(0x000000, 0)\n\n const scene = new THREE.Scene()\n const camera = new THREE.OrthographicCamera(-1, 1, 1, -1, 0, 1)\n const geo = new THREE.PlaneGeometry(2, 2)\n\n const mat = new THREE.ShaderMaterial({\n fragmentShader: frag,\n transparent: true,\n uniforms: {\n uColor: { value: hexToVec3(c.color) },\n uDensity: { value: c.density },\n uDpr: { value: 1 },\n uOpacity: { value: c.opacity },\n uRes: { value: new THREE.Vector2() },\n uSize: { value: c.size }\n },\n vertexShader: vert\n })\n\n scene.add(new THREE.Mesh(geo, mat))\n\n const resize = () => {\n renderer.setSize(innerWidth, innerHeight)\n renderer.setPixelRatio(devicePixelRatio)\n mat.uniforms.uRes.value.set(\n innerWidth * devicePixelRatio,\n innerHeight * devicePixelRatio\n )\n mat.uniforms.uDpr.value = devicePixelRatio\n }\n\n resize()\n window.addEventListener('resize', resize)\n\n let raf: number\n const interval = gpuTier === 1 ? 100 : 0\n\n const loop = () => {\n raf = interval\n ? (setTimeout(loop, interval) as unknown as number)\n : requestAnimationFrame(loop)\n\n const v = cRef.current\n mat.uniforms.uSize.value = v.size\n mat.uniforms.uDensity.value = v.density\n mat.uniforms.uColor.value = hexToVec3(v.color)\n mat.uniforms.uOpacity.value = v.opacity\n renderer.render(scene, camera)\n }\n\n loop()\n\n return () => {\n window.removeEventListener('resize', resize)\n\n if (interval) {\n clearTimeout(raf)\n } else {\n cancelAnimationFrame(raf)\n }\n\n mat.dispose()\n geo.dispose()\n renderer.dispose()\n }\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [enabled, gpuTier])\n\n if (!enabled) {\n return null\n }\n\n return (\n <canvas\n className={cn('h-full w-full', className)}\n ref={canvasRef}\n style={{ mixBlendMode: c.blend, ...style }}\n />\n )\n}\n\ninterface NoiseProps {\n className?: string\n style?: React.CSSProperties\n}\n"]}
1
+ {"version":3,"file":"noise.js","sourceRoot":"","sources":["../../../../src/ui/components/overlays/noise.tsx"],"names":[],"mappings":"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","sourcesContent":["'use client'\n\nimport { useEffect, useRef } from 'react'\nimport * as THREE from 'three'\n\nimport { $gpuTier, useGpuTier } from '../../../hooks/use-gpu-tier'\nimport { useSmoothControls } from '../../../hooks/use-smooth-controls'\nimport { cn, hexToVec3 } from '../../../utils'\n\nimport { BLEND_MODES } from './blend-modes'\n\nconst vert = /*glsl*/ `\n varying vec2 vUv;\n void main() {\n vUv = uv;\n gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);\n }\n`\n\nconst frag = /*glsl*/ `\n uniform vec2 uRes;\n uniform float uDpr, uSize, uDensity, uOpacity;\n uniform vec3 uColor;\n varying vec2 vUv;\n\n float hash(vec2 p) {\n vec3 p3 = fract(vec3(p.xyx) * 0.1031);\n p3 += dot(p3, p3.yzx + 33.33);\n return fract((p3.x + p3.y) * p3.z);\n }\n\n void main() {\n float n = hash(floor(vUv * uRes / (uSize * uDpr)));\n gl_FragColor = vec4(uColor, step(1.0 - uDensity, n)) * uOpacity;\n }\n`\n\nexport function Noise({ className, style }: NoiseProps) {\n const gpuTier = useGpuTier()\n\n const c = useSmoothControls(\n 'Effects/Noise',\n {\n blend: { options: BLEND_MODES, value: 'color-dodge' as const },\n color: { value: '#eaeaea' },\n density: { max: 1, min: 0, step: 0.01, value: 0.11 },\n enabled: { value: true },\n opacity: { max: 1, min: 0, step: 0.01, value: 0.55 },\n size: { max: 10, min: 0.1, step: 0.1, value: 1 }\n },\n { collapsed: true }\n )\n\n const canvasRef = useRef<HTMLCanvasElement>(null)\n const cRef = useRef(c)\n cRef.current = c\n\n const enabled = c.enabled && gpuTier > 0\n\n useEffect(() => {\n if (!canvasRef.current || !enabled) {\n return\n }\n\n let renderer: THREE.WebGLRenderer\n\n try {\n renderer = new THREE.WebGLRenderer({\n alpha: true,\n canvas: canvasRef.current,\n premultipliedAlpha: false\n })\n } catch {\n // Belt-and-braces: the gpu-tier hook should already have downgraded\n // to 0 before we render, but if a driver fails the renderer\n // constructor anyway (e.g. context loss between probe and use), make\n // sure no other overlay tries again.\n $gpuTier.set(0)\n\n return\n }\n\n renderer.setClearColor(0x000000, 0)\n\n const scene = new THREE.Scene()\n const camera = new THREE.OrthographicCamera(-1, 1, 1, -1, 0, 1)\n const geo = new THREE.PlaneGeometry(2, 2)\n\n const mat = new THREE.ShaderMaterial({\n fragmentShader: frag,\n transparent: true,\n uniforms: {\n uColor: { value: hexToVec3(c.color) },\n uDensity: { value: c.density },\n uDpr: { value: 1 },\n uOpacity: { value: c.opacity },\n uRes: { value: new THREE.Vector2() },\n uSize: { value: c.size }\n },\n vertexShader: vert\n })\n\n scene.add(new THREE.Mesh(geo, mat))\n\n const resize = () => {\n renderer.setSize(innerWidth, innerHeight)\n renderer.setPixelRatio(devicePixelRatio)\n mat.uniforms.uRes.value.set(\n innerWidth * devicePixelRatio,\n innerHeight * devicePixelRatio\n )\n mat.uniforms.uDpr.value = devicePixelRatio\n }\n\n resize()\n window.addEventListener('resize', resize)\n\n let raf: number\n const interval = gpuTier === 1 ? 100 : 0\n\n const loop = () => {\n raf = interval\n ? (setTimeout(loop, interval) as unknown as number)\n : requestAnimationFrame(loop)\n\n const v = cRef.current\n mat.uniforms.uSize.value = v.size\n mat.uniforms.uDensity.value = v.density\n mat.uniforms.uColor.value = hexToVec3(v.color)\n mat.uniforms.uOpacity.value = v.opacity\n renderer.render(scene, camera)\n }\n\n loop()\n\n return () => {\n window.removeEventListener('resize', resize)\n\n if (interval) {\n clearTimeout(raf)\n } else {\n cancelAnimationFrame(raf)\n }\n\n mat.dispose()\n geo.dispose()\n renderer.dispose()\n }\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [enabled, gpuTier])\n\n if (!enabled) {\n return null\n }\n\n return (\n <canvas\n className={cn('h-full w-full', className)}\n ref={canvasRef}\n style={{ mixBlendMode: c.blend, ...style }}\n />\n )\n}\n\ninterface NoiseProps {\n className?: string\n style?: React.CSSProperties\n}\n"]}
@@ -3,7 +3,7 @@ import { jsx as _jsx } from "react/jsx-runtime";
3
3
  import { useSmoothControls } from '../../../hooks/use-smooth-controls';
4
4
  import { cn } from '../../../utils';
5
5
  import { hexToRgb } from '../../../utils/color';
6
- import { BLEND_MODES } from '.';
6
+ import { BLEND_MODES } from './blend-modes';
7
7
  export function Vignette({ className, style }) {
8
8
  const c = useSmoothControls('Effects/Vignette', {
9
9
  blend: { options: BLEND_MODES, value: 'lighten' },
@@ -1 +1 @@
1
- {"version":3,"file":"vignette.js","sourceRoot":"","sources":["../../../../src/ui/components/overlays/vignette.tsx"],"names":[],"mappings":"AAAA,YAAY,CAAA;;AAEZ,OAAO,EAAE,iBAAiB,EAAE,MAAM,oCAAoC,CAAA;AACtE,OAAO,EAAE,EAAE,EAAE,MAAM,gBAAgB,CAAA;AACnC,OAAO,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAA;AAE/C,OAAO,EAAE,WAAW,EAAE,MAAM,GAAG,CAAA;AAE/B,MAAM,UAAU,QAAQ,CAAC,EAAE,SAAS,EAAE,KAAK,EAAiB;IAC1D,MAAM,CAAC,GAAG,iBAAiB,CACzB,kBAAkB,EAClB;QACE,KAAK,EAAE,EAAE,OAAO,EAAE,WAAW,EAAE,KAAK,EAAE,SAAkB,EAAE;QAC1D,UAAU,EAAE,EAAE,GAAG,EAAE,CAAC,EAAE,GAAG,EAAE,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,EAAE;QACpD,WAAW,EAAE,EAAE,GAAG,EAAE,CAAC,EAAE,GAAG,EAAE,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,EAAE;QACrD,KAAK,EAAE,EAAE,KAAK,EAAE,SAAS,EAAE;QAC3B,OAAO,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE;QACxB,OAAO,EAAE,EAAE,GAAG,EAAE,CAAC,EAAE,GAAG,EAAE,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE;QACpD,IAAI,EAAE,EAAE,GAAG,EAAE,CAAC,EAAE,GAAG,EAAE,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,EAAE;QAC9C,OAAO,EAAE,EAAE,GAAG,EAAE,CAAC,EAAE,GAAG,EAAE,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE;QACpD,QAAQ,EAAE,EAAE,GAAG,EAAE,CAAC,EAAE,GAAG,EAAE,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,EAAE;KACnD,EACD,EAAE,SAAS,EAAE,IAAI,EAAE,CACpB,CAAA;IAED,IAAI,CAAC,CAAC,CAAC,OAAO;QAAE,OAAO,IAAI,CAAA;IAE3B,MAAM,GAAG,GAAG,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,CAAA;IAC7B,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,GAAG,EAAE,CAAA;IAErB,MAAM,IAAI,GAAG,CAAC,CAAS,EAAE,CAAS,EAAE,CAAS,EAAE,EAAE,CAC/C,CAAC,GAAG,CAAC;QACL,8BAA8B,CAAC,KAAK,CAAC,WAAW,GAAG,OAAO,CAAC,WAAW,GAAG,IAAI,CAAC,SAAS,CAAA;IAEzF,MAAM,EAAE,GAAG;QACT,IAAI,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,OAAO,CAAC;QACrB,IAAI,CAAC,GAAG,EAAE,CAAC,EAAE,CAAC,CAAC,QAAQ,CAAC;QACxB,IAAI,CAAC,CAAC,EAAE,GAAG,EAAE,CAAC,CAAC,UAAU,CAAC;QAC1B,IAAI,CAAC,GAAG,EAAE,GAAG,EAAE,CAAC,CAAC,WAAW,CAAC;KAC9B,CAAC,MAAM,CAAC,OAAO,CAAC,CAAA;IAEjB,IAAI,CAAC,EAAE,CAAC,MAAM;QAAE,OAAO,IAAI,CAAA;IAE3B,OAAO,CACL,cACE,SAAS,EAAE,EAAE,CAAC,eAAe,EAAE,SAAS,CAAC,EACzC,KAAK,EAAE;YACL,UAAU,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC;YACzB,YAAY,EAAE,CAAC,CAAC,KAAK;YACrB,OAAO,EAAE,CAAC,CAAC,OAAO;YAClB,GAAG,KAAK;SACT,GACD,CACH,CAAA;AACH,CAAC","sourcesContent":["'use client'\n\nimport { useSmoothControls } from '../../../hooks/use-smooth-controls'\nimport { cn } from '../../../utils'\nimport { hexToRgb } from '../../../utils/color'\n\nimport { BLEND_MODES } from '.'\n\nexport function Vignette({ className, style }: VignetteProps) {\n const c = useSmoothControls(\n 'Effects/Vignette',\n {\n blend: { options: BLEND_MODES, value: 'lighten' as const },\n bottomLeft: { max: 1, min: 0, step: 0.01, value: 0 },\n bottomRight: { max: 1, min: 0, step: 0.01, value: 0 },\n color: { value: '#ffbd38' },\n enabled: { value: true },\n opacity: { max: 1, min: 0, step: 0.01, value: 0.22 },\n size: { max: 1, min: 0, step: 0.01, value: 1 },\n topLeft: { max: 1, min: 0, step: 0.01, value: 0.35 },\n topRight: { max: 1, min: 0, step: 0.01, value: 0 }\n },\n { collapsed: true }\n )\n\n if (!c.enabled) return null\n\n const rgb = hexToRgb(c.color)\n const s = c.size * 60\n\n const grad = (x: number, y: number, i: number) =>\n i > 0 &&\n `radial-gradient(ellipse at ${x}% ${y}%, rgba(${rgb},0) ${s}%, rgba(${rgb},${i}) 100%)`\n\n const bg = [\n grad(0, 0, c.topLeft),\n grad(100, 0, c.topRight),\n grad(0, 100, c.bottomLeft),\n grad(100, 100, c.bottomRight)\n ].filter(Boolean)\n\n if (!bg.length) return null\n\n return (\n <div\n className={cn('h-full w-full', className)}\n style={{\n background: bg.join(', '),\n mixBlendMode: c.blend,\n opacity: c.opacity,\n ...style\n }}\n />\n )\n}\n\ninterface VignetteProps {\n className?: string\n style?: React.CSSProperties\n}\n"]}
1
+ {"version":3,"file":"vignette.js","sourceRoot":"","sources":["../../../../src/ui/components/overlays/vignette.tsx"],"names":[],"mappings":"AAAA,YAAY,CAAA;;AAEZ,OAAO,EAAE,iBAAiB,EAAE,MAAM,oCAAoC,CAAA;AACtE,OAAO,EAAE,EAAE,EAAE,MAAM,gBAAgB,CAAA;AACnC,OAAO,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAA;AAE/C,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAA;AAE3C,MAAM,UAAU,QAAQ,CAAC,EAAE,SAAS,EAAE,KAAK,EAAiB;IAC1D,MAAM,CAAC,GAAG,iBAAiB,CACzB,kBAAkB,EAClB;QACE,KAAK,EAAE,EAAE,OAAO,EAAE,WAAW,EAAE,KAAK,EAAE,SAAkB,EAAE;QAC1D,UAAU,EAAE,EAAE,GAAG,EAAE,CAAC,EAAE,GAAG,EAAE,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,EAAE;QACpD,WAAW,EAAE,EAAE,GAAG,EAAE,CAAC,EAAE,GAAG,EAAE,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,EAAE;QACrD,KAAK,EAAE,EAAE,KAAK,EAAE,SAAS,EAAE;QAC3B,OAAO,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE;QACxB,OAAO,EAAE,EAAE,GAAG,EAAE,CAAC,EAAE,GAAG,EAAE,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE;QACpD,IAAI,EAAE,EAAE,GAAG,EAAE,CAAC,EAAE,GAAG,EAAE,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,EAAE;QAC9C,OAAO,EAAE,EAAE,GAAG,EAAE,CAAC,EAAE,GAAG,EAAE,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE;QACpD,QAAQ,EAAE,EAAE,GAAG,EAAE,CAAC,EAAE,GAAG,EAAE,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,EAAE;KACnD,EACD,EAAE,SAAS,EAAE,IAAI,EAAE,CACpB,CAAA;IAED,IAAI,CAAC,CAAC,CAAC,OAAO;QAAE,OAAO,IAAI,CAAA;IAE3B,MAAM,GAAG,GAAG,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,CAAA;IAC7B,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,GAAG,EAAE,CAAA;IAErB,MAAM,IAAI,GAAG,CAAC,CAAS,EAAE,CAAS,EAAE,CAAS,EAAE,EAAE,CAC/C,CAAC,GAAG,CAAC;QACL,8BAA8B,CAAC,KAAK,CAAC,WAAW,GAAG,OAAO,CAAC,WAAW,GAAG,IAAI,CAAC,SAAS,CAAA;IAEzF,MAAM,EAAE,GAAG;QACT,IAAI,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,OAAO,CAAC;QACrB,IAAI,CAAC,GAAG,EAAE,CAAC,EAAE,CAAC,CAAC,QAAQ,CAAC;QACxB,IAAI,CAAC,CAAC,EAAE,GAAG,EAAE,CAAC,CAAC,UAAU,CAAC;QAC1B,IAAI,CAAC,GAAG,EAAE,GAAG,EAAE,CAAC,CAAC,WAAW,CAAC;KAC9B,CAAC,MAAM,CAAC,OAAO,CAAC,CAAA;IAEjB,IAAI,CAAC,EAAE,CAAC,MAAM;QAAE,OAAO,IAAI,CAAA;IAE3B,OAAO,CACL,cACE,SAAS,EAAE,EAAE,CAAC,eAAe,EAAE,SAAS,CAAC,EACzC,KAAK,EAAE;YACL,UAAU,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC;YACzB,YAAY,EAAE,CAAC,CAAC,KAAK;YACrB,OAAO,EAAE,CAAC,CAAC,OAAO;YAClB,GAAG,KAAK;SACT,GACD,CACH,CAAA;AACH,CAAC","sourcesContent":["'use client'\n\nimport { useSmoothControls } from '../../../hooks/use-smooth-controls'\nimport { cn } from '../../../utils'\nimport { hexToRgb } from '../../../utils/color'\n\nimport { BLEND_MODES } from './blend-modes'\n\nexport function Vignette({ className, style }: VignetteProps) {\n const c = useSmoothControls(\n 'Effects/Vignette',\n {\n blend: { options: BLEND_MODES, value: 'lighten' as const },\n bottomLeft: { max: 1, min: 0, step: 0.01, value: 0 },\n bottomRight: { max: 1, min: 0, step: 0.01, value: 0 },\n color: { value: '#ffbd38' },\n enabled: { value: true },\n opacity: { max: 1, min: 0, step: 0.01, value: 0.22 },\n size: { max: 1, min: 0, step: 0.01, value: 1 },\n topLeft: { max: 1, min: 0, step: 0.01, value: 0.35 },\n topRight: { max: 1, min: 0, step: 0.01, value: 0 }\n },\n { collapsed: true }\n )\n\n if (!c.enabled) return null\n\n const rgb = hexToRgb(c.color)\n const s = c.size * 60\n\n const grad = (x: number, y: number, i: number) =>\n i > 0 &&\n `radial-gradient(ellipse at ${x}% ${y}%, rgba(${rgb},0) ${s}%, rgba(${rgb},${i}) 100%)`\n\n const bg = [\n grad(0, 0, c.topLeft),\n grad(100, 0, c.topRight),\n grad(0, 100, c.bottomLeft),\n grad(100, 100, c.bottomRight)\n ].filter(Boolean)\n\n if (!bg.length) return null\n\n return (\n <div\n className={cn('h-full w-full', className)}\n style={{\n background: bg.join(', '),\n mixBlendMode: c.blend,\n opacity: c.opacity,\n ...style\n }}\n />\n )\n}\n\ninterface VignetteProps {\n className?: string\n style?: React.CSSProperties\n}\n"]}
@@ -0,0 +1,21 @@
1
+ export declare function Socials({ className, items, onNavigate, ...rest }: SocialsProps): import("react/jsx-runtime").JSX.Element;
2
+ export interface SocialLink {
3
+ external?: boolean;
4
+ href: string;
5
+ icon: React.ComponentType<{
6
+ className?: string;
7
+ }>;
8
+ label: string;
9
+ onClick?: React.MouseEventHandler;
10
+ }
11
+ interface SocialsProps extends React.HTMLAttributes<HTMLDivElement> {
12
+ items: SocialLink[];
13
+ /**
14
+ * Called *in addition* to each link's `onClick` after a click — useful in
15
+ * mobile drawer / dialog contexts where clicking a link should also close
16
+ * the surrounding overlay.
17
+ */
18
+ onNavigate?: () => void;
19
+ }
20
+ export {};
21
+ //# sourceMappingURL=socials.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"socials.d.ts","sourceRoot":"","sources":["../../../src/ui/components/socials.tsx"],"names":[],"mappings":"AAEA,wBAAgB,OAAO,CAAC,EAAE,SAAS,EAAE,KAAK,EAAE,UAAU,EAAE,GAAG,IAAI,EAAE,EAAE,YAAY,2CAqB9E;AAED,MAAM,WAAW,UAAU;IACzB,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB,IAAI,EAAE,MAAM,CAAA;IACZ,IAAI,EAAE,KAAK,CAAC,aAAa,CAAC;QAAE,SAAS,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,CAAA;IACjD,KAAK,EAAE,MAAM,CAAA;IACb,OAAO,CAAC,EAAE,KAAK,CAAC,iBAAiB,CAAA;CAClC;AAED,UAAU,YAAa,SAAQ,KAAK,CAAC,cAAc,CAAC,cAAc,CAAC;IACjE,KAAK,EAAE,UAAU,EAAE,CAAA;IACnB;;;;OAIG;IACH,UAAU,CAAC,EAAE,MAAM,IAAI,CAAA;CACxB"}
@@ -0,0 +1,9 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { cn } from '../../utils';
3
+ export function Socials({ className, items, onNavigate, ...rest }) {
4
+ return (_jsx("div", { className: cn('flex items-center gap-3', className), ...rest, children: items.map(({ external = true, href, icon: Icon, label, onClick }) => (_jsx("a", { className: "opacity-60 transition-opacity hover:opacity-100", href: href, onClick: e => {
5
+ onClick?.(e);
6
+ onNavigate?.();
7
+ }, rel: external ? 'noopener noreferrer' : undefined, target: external ? '_blank' : undefined, title: label, children: _jsx(Icon, {}) }, label))) }));
8
+ }
9
+ //# sourceMappingURL=socials.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"socials.js","sourceRoot":"","sources":["../../../src/ui/components/socials.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAE,EAAE,EAAE,MAAM,aAAa,CAAA;AAEhC,MAAM,UAAU,OAAO,CAAC,EAAE,SAAS,EAAE,KAAK,EAAE,UAAU,EAAE,GAAG,IAAI,EAAgB;IAC7E,OAAO,CACL,cAAK,SAAS,EAAE,EAAE,CAAC,yBAAyB,EAAE,SAAS,CAAC,KAAM,IAAI,YAC/D,KAAK,CAAC,GAAG,CAAC,CAAC,EAAE,QAAQ,GAAG,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,OAAO,EAAE,EAAE,EAAE,CAAC,CACpE,YACE,SAAS,EAAC,iDAAiD,EAC3D,IAAI,EAAE,IAAI,EAEV,OAAO,EAAE,CAAC,CAAC,EAAE;gBACX,OAAO,EAAE,CAAC,CAAC,CAAC,CAAA;gBACZ,UAAU,EAAE,EAAE,CAAA;YAChB,CAAC,EACD,GAAG,EAAE,QAAQ,CAAC,CAAC,CAAC,qBAAqB,CAAC,CAAC,CAAC,SAAS,EACjD,MAAM,EAAE,QAAQ,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,SAAS,EACvC,KAAK,EAAE,KAAK,YAEZ,KAAC,IAAI,KAAG,IATH,KAAK,CAUR,CACL,CAAC,GACE,CACP,CAAA;AACH,CAAC","sourcesContent":["import { cn } from '../../utils'\n\nexport function Socials({ className, items, onNavigate, ...rest }: SocialsProps) {\n return (\n <div className={cn('flex items-center gap-3', className)} {...rest}>\n {items.map(({ external = true, href, icon: Icon, label, onClick }) => (\n <a\n className=\"opacity-60 transition-opacity hover:opacity-100\"\n href={href}\n key={label}\n onClick={e => {\n onClick?.(e)\n onNavigate?.()\n }}\n rel={external ? 'noopener noreferrer' : undefined}\n target={external ? '_blank' : undefined}\n title={label}\n >\n <Icon />\n </a>\n ))}\n </div>\n )\n}\n\nexport interface SocialLink {\n external?: boolean\n href: string\n icon: React.ComponentType<{ className?: string }>\n label: string\n onClick?: React.MouseEventHandler\n}\n\ninterface SocialsProps extends React.HTMLAttributes<HTMLDivElement> {\n items: SocialLink[]\n /**\n * Called *in addition* to each link's `onClick` after a click — useful in\n * mobile drawer / dialog contexts where clicking a link should also close\n * the surrounding overlay.\n */\n onNavigate?: () => void\n}\n"]}
@@ -1,6 +1,21 @@
1
- export declare function Footer({ LinkComponent }: FooterProps): import("react/jsx-runtime").JSX.Element;
2
- interface FooterProps {
1
+ import { type SocialLink } from './components/socials';
2
+ export declare function Footer({ className, groups, LinkComponent, socials, socialsLabel, style, themeLabel, themeToggle }: FooterProps): import("react/jsx-runtime").JSX.Element;
3
+ export interface FooterGroup {
4
+ label: string;
5
+ links: (FooterLink | string)[];
6
+ }
7
+ export interface FooterLink {
8
+ href: string;
9
+ label: string;
10
+ }
11
+ export interface FooterProps {
12
+ className?: string;
13
+ groups?: FooterGroup[];
3
14
  LinkComponent?: React.ElementType;
15
+ socials?: SocialLink[];
16
+ socialsLabel?: string;
17
+ style?: React.CSSProperties;
18
+ themeLabel?: string;
19
+ themeToggle?: boolean;
4
20
  }
5
- export {};
6
21
  //# sourceMappingURL=footer.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"footer.d.ts","sourceRoot":"","sources":["../../src/ui/footer.tsx"],"names":[],"mappings":"AAeA,wBAAgB,MAAM,CAAC,EAAE,aAAmB,EAAE,EAAE,WAAW,2CA8B1D;AAED,UAAU,WAAW;IACnB,aAAa,CAAC,EAAE,KAAK,CAAC,WAAW,CAAA;CAClC"}
1
+ {"version":3,"file":"footer.d.ts","sourceRoot":"","sources":["../../src/ui/footer.tsx"],"names":[],"mappings":"AAMA,OAAO,EAAW,KAAK,UAAU,EAAE,MAAM,sBAAsB,CAAA;AAW/D,wBAAgB,MAAM,CAAC,EACrB,SAAS,EACT,MAAuB,EACvB,aAAmB,EACnB,OAAO,EACP,YAAwB,EACxB,KAAK,EACL,UAAoB,EACpB,WAAmB,EACpB,EAAE,WAAW,2CA+Db;AAED,MAAM,WAAW,WAAW;IAC1B,KAAK,EAAE,MAAM,CAAA;IACb,KAAK,EAAE,CAAC,UAAU,GAAG,MAAM,CAAC,EAAE,CAAA;CAC/B;AAED,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,MAAM,CAAA;IACZ,KAAK,EAAE,MAAM,CAAA;CACd;AAED,MAAM,WAAW,WAAW;IAC1B,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,MAAM,CAAC,EAAE,WAAW,EAAE,CAAA;IACtB,aAAa,CAAC,EAAE,KAAK,CAAC,WAAW,CAAA;IACjC,OAAO,CAAC,EAAE,UAAU,EAAE,CAAA;IACtB,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,KAAK,CAAC,EAAE,KAAK,CAAC,aAAa,CAAA;IAC3B,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,WAAW,CAAC,EAAE,OAAO,CAAA;CACtB"}
package/dist/ui/footer.js CHANGED
@@ -3,16 +3,26 @@ import { jsxs as _jsxs, jsx as _jsx } from "react/jsx-runtime";
3
3
  import { useRef } from 'react';
4
4
  import { useCssVarDims } from '../hooks/use-css-var-dims';
5
5
  import { Cell, Grid } from './components/grid';
6
+ import { Socials } from './components/socials';
7
+ import { ThemeToggle } from './components/theme-toggle';
6
8
  import { Small } from './components/typography/small';
7
- const GROUPS = [
9
+ const DEFAULT_GROUPS = [
8
10
  { label: 'Product', links: ['Overview', 'Features', 'Pricing'] },
9
11
  { label: 'Resources', links: ['Docs', 'Blog', 'Support'] },
10
12
  { label: 'Company', links: ['About', 'Careers', 'Contact'] },
11
13
  { label: 'Legal', links: ['Privacy', 'Terms', 'License'] }
12
14
  ];
13
- export function Footer({ LinkComponent = 'a' }) {
15
+ export function Footer({ className, groups = DEFAULT_GROUPS, LinkComponent = 'a', socials, socialsLabel = 'Socials', style, themeLabel = 'Theme', themeToggle = false }) {
14
16
  const ref = useRef(null);
15
17
  useCssVarDims('footer', ref);
16
- return (_jsxs(Grid, { as: "footer", ref, children: [_jsx(Cell, { children: _jsxs(Small, { className: "opacity-50", children: ["\u00A9", new Date().getFullYear()] }) }), GROUPS.map(({ label, links }) => (_jsxs(Cell, { children: [_jsx(Small, { className: "opacity-50", children: label }), _jsx("nav", { className: "mt-3 flex flex-col gap-2", children: links.map(link => (_jsx(Small, { as: LinkComponent, className: "underline", href: `/${link.toLowerCase()}`, children: link }, link))) })] }, label)))] }));
18
+ const hasSocials = (socials?.length ?? 0) > 0;
19
+ const hasChrome = hasSocials || themeToggle;
20
+ return (_jsxs("footer", { className: className, ref: ref, style: style, children: [_jsxs(Grid, { children: [_jsx(Cell, { children: _jsxs(Small, { className: "opacity-50", children: ["\u00A9", new Date().getFullYear()] }) }), groups.map(({ label, links }) => (_jsxs(Cell, { children: [_jsx(Small, { className: "opacity-50", children: label }), _jsx("nav", { className: "mt-3 flex flex-col gap-2", children: links.map(link => {
21
+ const href = typeof link === 'string'
22
+ ? `/${link.toLowerCase()}`
23
+ : link.href;
24
+ const label = typeof link === 'string' ? link : link.label;
25
+ return (_jsx(Small, { as: LinkComponent, className: "underline", href: href, children: label }, label));
26
+ }) })] }, label)))] }), hasChrome && (_jsxs(Grid, { children: [hasSocials && (_jsxs(Cell, { className: "flex items-start justify-between", children: [_jsx(Small, { className: "opacity-50", children: socialsLabel }), _jsx(Socials, { items: socials })] })), themeToggle && (_jsxs(Cell, { className: "flex items-start justify-between", children: [_jsx(Small, { className: "opacity-50", children: themeLabel }), _jsx(ThemeToggle, {})] }))] }))] }));
17
27
  }
18
28
  //# sourceMappingURL=footer.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"footer.js","sourceRoot":"","sources":["../../src/ui/footer.tsx"],"names":[],"mappings":"AAAA,YAAY,CAAA;;AAEZ,OAAO,EAAE,MAAM,EAAE,MAAM,OAAO,CAAA;AAE9B,OAAO,EAAE,aAAa,EAAE,MAAM,2BAA2B,CAAA;AACzD,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,MAAM,mBAAmB,CAAA;AAC9C,OAAO,EAAE,KAAK,EAAE,MAAM,+BAA+B,CAAA;AAErD,MAAM,MAAM,GAAG;IACb,EAAE,KAAK,EAAE,SAAS,EAAE,KAAK,EAAE,CAAC,UAAU,EAAE,UAAU,EAAE,SAAS,CAAC,EAAE;IAChE,EAAE,KAAK,EAAE,WAAW,EAAE,KAAK,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,SAAS,CAAC,EAAE;IAC1D,EAAE,KAAK,EAAE,SAAS,EAAE,KAAK,EAAE,CAAC,OAAO,EAAE,SAAS,EAAE,SAAS,CAAC,EAAE;IAC5D,EAAE,KAAK,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC,SAAS,EAAE,OAAO,EAAE,SAAS,CAAC,EAAE;CAC3D,CAAA;AAED,MAAM,UAAU,MAAM,CAAC,EAAE,aAAa,GAAG,GAAG,EAAe;IACzD,MAAM,GAAG,GAAG,MAAM,CAAc,IAAI,CAAC,CAAA;IACrC,aAAa,CAAC,QAAQ,EAAE,GAAG,CAAC,CAAA;IAE5B,OAAO,CACL,MAAC,IAAI,IAAC,EAAE,EAAC,QAAQ,EAAO,GAAG,aACzB,KAAC,IAAI,cACH,MAAC,KAAK,IAAC,SAAS,EAAC,YAAY,uBAAQ,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,IAAS,GACjE,EAEN,MAAM,CAAC,GAAG,CAAC,CAAC,EAAE,KAAK,EAAE,KAAK,EAAE,EAAE,EAAE,CAAC,CAChC,MAAC,IAAI,eACH,KAAC,KAAK,IAAC,SAAS,EAAC,YAAY,YAAE,KAAK,GAAS,EAE7C,cAAK,SAAS,EAAC,0BAA0B,YACtC,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,CACjB,KAAC,KAAK,IACJ,EAAE,EAAE,aAAa,EACjB,SAAS,EAAC,WAAW,EACrB,IAAI,EAAE,IAAI,IAAI,CAAC,WAAW,EAAE,EAAE,YAG7B,IAAI,IAFA,IAAI,CAGH,CACT,CAAC,GACE,KAdG,KAAK,CAeT,CACR,CAAC,IACG,CACR,CAAA;AACH,CAAC","sourcesContent":["'use client'\n\nimport { useRef } from 'react'\n\nimport { useCssVarDims } from '../hooks/use-css-var-dims'\nimport { Cell, Grid } from './components/grid'\nimport { Small } from './components/typography/small'\n\nconst GROUPS = [\n { label: 'Product', links: ['Overview', 'Features', 'Pricing'] },\n { label: 'Resources', links: ['Docs', 'Blog', 'Support'] },\n { label: 'Company', links: ['About', 'Careers', 'Contact'] },\n { label: 'Legal', links: ['Privacy', 'Terms', 'License'] }\n]\n\nexport function Footer({ LinkComponent = 'a' }: FooterProps) {\n const ref = useRef<HTMLElement>(null)\n useCssVarDims('footer', ref)\n\n return (\n <Grid as=\"footer\" {...{ ref }}>\n <Cell>\n <Small className=\"opacity-50\">&copy;{new Date().getFullYear()}</Small>\n </Cell>\n\n {GROUPS.map(({ label, links }) => (\n <Cell key={label}>\n <Small className=\"opacity-50\">{label}</Small>\n\n <nav className=\"mt-3 flex flex-col gap-2\">\n {links.map(link => (\n <Small\n as={LinkComponent}\n className=\"underline\"\n href={`/${link.toLowerCase()}`}\n key={link}\n >\n {link}\n </Small>\n ))}\n </nav>\n </Cell>\n ))}\n </Grid>\n )\n}\n\ninterface FooterProps {\n LinkComponent?: React.ElementType\n}\n"]}
1
+ {"version":3,"file":"footer.js","sourceRoot":"","sources":["../../src/ui/footer.tsx"],"names":[],"mappings":"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","sourcesContent":["'use client'\n\nimport { useRef } from 'react'\n\nimport { useCssVarDims } from '../hooks/use-css-var-dims'\nimport { Cell, Grid } from './components/grid'\nimport { Socials, type SocialLink } from './components/socials'\nimport { ThemeToggle } from './components/theme-toggle'\nimport { Small } from './components/typography/small'\n\nconst DEFAULT_GROUPS: FooterGroup[] = [\n { label: 'Product', links: ['Overview', 'Features', 'Pricing'] },\n { label: 'Resources', links: ['Docs', 'Blog', 'Support'] },\n { label: 'Company', links: ['About', 'Careers', 'Contact'] },\n { label: 'Legal', links: ['Privacy', 'Terms', 'License'] }\n]\n\nexport function Footer({\n className,\n groups = DEFAULT_GROUPS,\n LinkComponent = 'a',\n socials,\n socialsLabel = 'Socials',\n style,\n themeLabel = 'Theme',\n themeToggle = false\n}: FooterProps) {\n const ref = useRef<HTMLElement>(null)\n useCssVarDims('footer', ref)\n\n const hasSocials = (socials?.length ?? 0) > 0\n const hasChrome = hasSocials || themeToggle\n\n return (\n <footer className={className} ref={ref} style={style}>\n <Grid>\n <Cell>\n <Small className=\"opacity-50\">&copy;{new Date().getFullYear()}</Small>\n </Cell>\n\n {groups.map(({ label, links }) => (\n <Cell key={label}>\n <Small className=\"opacity-50\">{label}</Small>\n\n <nav className=\"mt-3 flex flex-col gap-2\">\n {links.map(link => {\n const href = typeof link === 'string'\n ? `/${link.toLowerCase()}`\n : link.href\n\n const label = typeof link === 'string' ? link : link.label\n\n return (\n <Small\n as={LinkComponent}\n className=\"underline\"\n href={href}\n key={label}\n >\n {label}\n </Small>\n )\n })}\n </nav>\n </Cell>\n ))}\n </Grid>\n\n {hasChrome && (\n <Grid>\n {hasSocials && (\n <Cell className=\"flex items-start justify-between\">\n <Small className=\"opacity-50\">{socialsLabel}</Small>\n\n <Socials items={socials!} />\n </Cell>\n )}\n\n {themeToggle && (\n <Cell className=\"flex items-start justify-between\">\n <Small className=\"opacity-50\">{themeLabel}</Small>\n\n <ThemeToggle />\n </Cell>\n )}\n </Grid>\n )}\n </footer>\n )\n}\n\nexport interface FooterGroup {\n label: string\n links: (FooterLink | string)[]\n}\n\nexport interface FooterLink {\n href: string\n label: string\n}\n\nexport interface FooterProps {\n className?: string\n groups?: FooterGroup[]\n LinkComponent?: React.ElementType\n socials?: SocialLink[]\n socialsLabel?: string\n style?: React.CSSProperties\n themeLabel?: string\n themeToggle?: boolean\n}\n"]}
@@ -1,6 +1,42 @@
1
- export declare function Header({ LinkComponent }: HeaderProps): import("react/jsx-runtime").JSX.Element;
2
- interface HeaderProps {
1
+ import { type SocialLink } from './components/socials';
2
+ export declare function Header({ brand, brandHref, className, desktopGridStyle, links, LinkComponent, scramble: scrambleProp, socials, socialsLabel, style, themeLabel, themeToggle }: HeaderProps): import("react/jsx-runtime").JSX.Element;
3
+ export interface HeaderLink {
4
+ external?: boolean;
5
+ href: string;
6
+ label: string;
7
+ onClick?: React.MouseEventHandler;
8
+ }
9
+ export interface HeaderProps {
10
+ brand?: React.ReactNode;
11
+ brandHref?: string;
12
+ className?: string;
13
+ /**
14
+ * Inline `style` for the desktop `Grid` only — useful for overriding
15
+ * `grid-template-columns` (e.g. to align with a sidebar track) without
16
+ * affecting the mobile bar or drawer.
17
+ */
18
+ desktopGridStyle?: React.CSSProperties;
19
+ links?: HeaderLink[];
3
20
  LinkComponent?: React.ElementType;
21
+ /**
22
+ * Apply the hover-Scramble effect to nav link labels. Defaults to `true`,
23
+ * automatically suppressed on tier-0 GPUs and when the user has
24
+ * `prefers-reduced-motion: reduce`.
25
+ */
26
+ scramble?: boolean;
27
+ /**
28
+ * Optional socials shown in a trailing chrome cell on desktop and in the
29
+ * mobile drawer's chrome row. For nav-heavy products (≥ 5 links) prefer
30
+ * passing socials to `<Footer>` instead — the desktop `Grid` only ships
31
+ * column rules through `grid-cols-6`, so brand + many links + chrome can
32
+ * overflow.
33
+ */
34
+ socials?: SocialLink[];
35
+ socialsLabel?: string;
36
+ style?: React.CSSProperties;
37
+ themeLabel?: string;
38
+ themeToggle?: boolean;
4
39
  }
5
- export {};
40
+ /** @deprecated Use `SocialLink` from `@nous-research/ui`. Same shape. */
41
+ export type HeaderSocial = SocialLink;
6
42
  //# sourceMappingURL=header.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"header.d.ts","sourceRoot":"","sources":["../../src/ui/header.tsx"],"names":[],"mappings":"AAkBA,wBAAgB,MAAM,CAAC,EAAE,aAAmB,EAAE,EAAE,WAAW,2CA8B1D;AAED,UAAU,WAAW;IACnB,aAAa,CAAC,EAAE,KAAK,CAAC,WAAW,CAAA;CAClC"}
1
+ {"version":3,"file":"header.d.ts","sourceRoot":"","sources":["../../src/ui/header.tsx"],"names":[],"mappings":"AAcA,OAAO,EAAW,KAAK,UAAU,EAAE,MAAM,sBAAsB,CAAA;AAoB/D,wBAAgB,MAAM,CAAC,EACrB,KAAqB,EACrB,SAAe,EACf,SAAS,EACT,gBAAgB,EAChB,KAAqB,EACrB,aAAmB,EACnB,QAAQ,EAAE,YAAmB,EAC7B,OAAO,EACP,YAAwB,EACxB,KAAK,EACL,UAAoB,EACpB,WAAmB,EACpB,EAAE,WAAW,2CAmJb;AAsJD,MAAM,WAAW,UAAU;IACzB,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB,IAAI,EAAE,MAAM,CAAA;IACZ,KAAK,EAAE,MAAM,CAAA;IACb,OAAO,CAAC,EAAE,KAAK,CAAC,iBAAiB,CAAA;CAClC;AAED,MAAM,WAAW,WAAW;IAC1B,KAAK,CAAC,EAAE,KAAK,CAAC,SAAS,CAAA;IACvB,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB;;;;OAIG;IACH,gBAAgB,CAAC,EAAE,KAAK,CAAC,aAAa,CAAA;IACtC,KAAK,CAAC,EAAE,UAAU,EAAE,CAAA;IACpB,aAAa,CAAC,EAAE,KAAK,CAAC,WAAW,CAAA;IACjC;;;;OAIG;IACH,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB;;;;;;OAMG;IACH,OAAO,CAAC,EAAE,UAAU,EAAE,CAAA;IACtB,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,KAAK,CAAC,EAAE,KAAK,CAAC,aAAa,CAAA;IAC3B,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,WAAW,CAAC,EAAE,OAAO,CAAA;CACtB;AAED,yEAAyE;AACzE,MAAM,MAAM,YAAY,GAAG,UAAU,CAAA"}
package/dist/ui/header.js CHANGED
@@ -1,21 +1,74 @@
1
1
  'use client';
2
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
- import { useRef } from 'react';
2
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
3
+ import { AnimatePresence, motion } from 'motion/react';
4
+ import { createElement, useCallback, useRef, useState } from 'react';
4
5
  import { useCssVarDims } from '../hooks/use-css-var-dims';
6
+ import { useGpuTier } from '../hooks/use-gpu-tier';
7
+ import { cn } from '../utils';
5
8
  import { Blink } from './components/blink';
6
9
  import { Cell, Grid } from './components/grid';
7
10
  import { HoverBg } from './components/hover-bg';
11
+ import { HamburgerIcon } from './components/icons/hamburger';
12
+ import { Scramble } from './components/scramble';
13
+ import { Socials } from './components/socials';
14
+ import { ThemeToggle } from './components/theme-toggle';
8
15
  import { H2 } from './components/typography/h2';
9
16
  import { Small } from './components/typography/small';
10
- const NAV = [
17
+ const DEFAULT_BRAND = (_jsxs("hgroup", { className: "flex flex-col gap-2", children: [_jsx(Small, { children: "Nous" }), _jsx(H2, { children: "Research" })] }));
18
+ const DEFAULT_LINKS = [
11
19
  { href: '/projects', label: 'Projects' },
12
20
  { href: '/participants', label: 'Participants' },
13
21
  { href: '/provenance', label: 'Provenance' },
14
22
  { href: '/contribute', label: 'Contribute' }
15
23
  ];
16
- export function Header({ LinkComponent = 'a' }) {
24
+ export function Header({ brand = DEFAULT_BRAND, brandHref = '/', className, desktopGridStyle, links = DEFAULT_LINKS, LinkComponent = 'a', scramble: scrambleProp = true, socials, socialsLabel = 'Socials', style, themeLabel = 'Theme', themeToggle = false }) {
17
25
  const ref = useRef(null);
18
26
  useCssVarDims('header', ref);
19
- return (_jsxs(Grid, { as: "header", ref, children: [_jsx(Cell, { as: LinkComponent, href: "/", children: _jsxs("hgroup", { className: "flex flex-col gap-2", children: [_jsx(Small, { children: "Nous" }), _jsx(H2, { children: "Psyche" })] }) }), NAV.map(({ href, label }) => (_jsxs(Cell, { as: LinkComponent, className: "group relative", href: '/runs', children: [_jsxs(Small, { children: [label, _jsx(Blink, {})] }), _jsx(HoverBg, {})] }, href)))] }));
27
+ // Skip the hover-Scramble rAF loop on tier-0 devices (no GPU / software
28
+ // renderer / `prefers-reduced-motion: reduce`) regardless of the prop.
29
+ const gpuTier = useGpuTier();
30
+ const scramble = scrambleProp && gpuTier > 0;
31
+ const [open, setOpen] = useState(false);
32
+ const close = useCallback(() => setOpen(false), []);
33
+ const hasSocials = (socials?.length ?? 0) > 0;
34
+ const hasMobileChrome = themeToggle || hasSocials;
35
+ return (_jsxs("header", { className: className, ref: ref, style: style, children: [_jsxs(Grid, { className: "hidden border-t border-b lg:grid", style: desktopGridStyle, children: [_jsx(BrandCell, { brand: brand, href: brandHref, LinkComponent: LinkComponent }), links.map(link => (_jsx(NavCell, { link: link, LinkComponent: LinkComponent, scramble: scramble }, link.href))), hasSocials && (_jsxs(Cell, { className: "flex items-start justify-between", children: [_jsx(Small, { className: "opacity-50", children: socialsLabel }), _jsx(Socials, { items: socials })] })), themeToggle && (_jsxs(Cell, { className: "flex items-start justify-between", children: [_jsx(Small, { className: "opacity-50", children: themeLabel }), _jsx(ThemeToggle, {})] }))] }), _jsxs("div", { className: cn('flex items-center justify-between border border-current/20 p-4', 'lg:hidden'), children: [_jsx(BrandLink, { brand: brand, href: brandHref, LinkComponent: LinkComponent }), _jsxs("div", { className: "flex items-center gap-3", children: [themeToggle && _jsx(ThemeToggle, {}), _jsx("button", { "aria-label": open ? 'Close menu' : 'Open menu', className: "relative z-50 cursor-pointer bg-transparent p-2", onClick: () => setOpen(v => !v), type: "button", children: _jsx(HamburgerIcon, { open: open }) })] })] }), _jsx(AnimatePresence, { children: open && (_jsx(motion.div, { animate: { opacity: 1 }, className: cn('bg-background/95 fixed inset-0 z-50 flex flex-col backdrop-blur-sm', 'p-8 lg:hidden'), exit: { opacity: 0 }, initial: { opacity: 0 }, transition: { duration: 0.2 }, children: _jsxs("div", { className: "flex flex-col border border-current/20", children: [_jsxs("div", { className: "flex items-center justify-between border-b border-current/20 p-4", children: [_jsx(BrandLink, { brand: brand, href: brandHref, LinkComponent: LinkComponent, onClick: close }), _jsx("button", { "aria-label": "Close menu", className: "cursor-pointer bg-transparent p-2", onClick: close, type: "button", children: _jsx(HamburgerIcon, { open: true }) })] }), links.map(link => (_jsx(MobileNavLink, { link: link, LinkComponent: LinkComponent, onNavigate: close, scramble: scramble }, link.href))), hasMobileChrome && (_jsxs("div", { className: "flex items-center gap-3 border-b border-current/20 p-4", children: [hasSocials && (_jsxs(_Fragment, { children: [_jsx(Small, { className: "opacity-50", children: socialsLabel }), _jsx(Socials, { items: socials, onNavigate: close })] })), themeToggle && hasSocials && _jsx("span", { className: "flex-1" }), themeToggle && (_jsxs(_Fragment, { children: [_jsx(Small, { className: "opacity-50", children: themeLabel }), _jsx(ThemeToggle, {})] }))] }))] }) })) })] }));
20
36
  }
37
+ function BrandCell({ brand, href, LinkComponent }) {
38
+ return isExternal(href) ? (_jsx(Cell, { href: href, ...EXTERNAL_REL, as: "a", children: brand })) : (_jsx(Cell, { as: LinkComponent, href: href, children: brand }));
39
+ }
40
+ function BrandLink({ brand, href, LinkComponent, onClick }) {
41
+ if (isExternal(href)) {
42
+ return (_jsx("a", { href: href, onClick: onClick, ...EXTERNAL_REL, children: brand }));
43
+ }
44
+ return createElement(LinkComponent, { href, onClick }, brand);
45
+ }
46
+ function NavCell({ link, LinkComponent, scramble }) {
47
+ const ref = useRef(null);
48
+ const isExt = link.external ?? isExternal(link.href);
49
+ const inner = (_jsxs(_Fragment, { children: [_jsxs(Small, { children: [scramble ? (_jsx(Scramble, { target: ref, children: link.label })) : (link.label), _jsx(Blink, {})] }), _jsx(HoverBg, {})] }));
50
+ if (isExt) {
51
+ return (_jsx(Cell, { as: "a", className: "group relative cursor-pointer", href: link.href, onClick: link.onClick, ref: ref, ...EXTERNAL_REL, children: inner }));
52
+ }
53
+ return (_jsx(Cell, { as: LinkComponent, className: "group relative cursor-pointer", href: link.href, onClick: link.onClick, ref: ref, children: inner }));
54
+ }
55
+ function MobileNavLink({ link, LinkComponent, onNavigate, scramble }) {
56
+ const ref = useRef(null);
57
+ const isExt = link.external ?? isExternal(link.href);
58
+ const className = cn('group relative flex cursor-pointer items-center border-b border-current/20 p-4');
59
+ const onClick = (e) => {
60
+ link.onClick?.(e);
61
+ onNavigate();
62
+ };
63
+ const children = (_jsxs(_Fragment, { children: [_jsxs(Small, { children: [scramble ? (_jsx(Scramble, { target: ref, children: link.label })) : (link.label), _jsx(Blink, {})] }), _jsx(HoverBg, {})] }));
64
+ if (isExt) {
65
+ return (_jsx("a", { className: className, href: link.href, onClick: onClick, ref: ref, ...EXTERNAL_REL, children: children }));
66
+ }
67
+ return createElement(LinkComponent, { className, href: link.href, onClick, ref }, children);
68
+ }
69
+ const EXTERNAL_REL = {
70
+ rel: 'noopener noreferrer',
71
+ target: '_blank'
72
+ };
73
+ const isExternal = (href) => /^(https?:|mailto:|tel:)/i.test(href);
21
74
  //# sourceMappingURL=header.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"header.js","sourceRoot":"","sources":["../../src/ui/header.tsx"],"names":[],"mappings":"AAAA,YAAY,CAAA;;AAEZ,OAAO,EAAE,MAAM,EAAE,MAAM,OAAO,CAAA;AAE9B,OAAO,EAAE,aAAa,EAAE,MAAM,2BAA2B,CAAA;AACzD,OAAO,EAAE,KAAK,EAAE,MAAM,oBAAoB,CAAA;AAC1C,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,MAAM,mBAAmB,CAAA;AAC9C,OAAO,EAAE,OAAO,EAAE,MAAM,uBAAuB,CAAA;AAC/C,OAAO,EAAE,EAAE,EAAE,MAAM,4BAA4B,CAAA;AAC/C,OAAO,EAAE,KAAK,EAAE,MAAM,+BAA+B,CAAA;AAErD,MAAM,GAAG,GAAG;IACV,EAAE,IAAI,EAAE,WAAW,EAAE,KAAK,EAAE,UAAU,EAAE;IACxC,EAAE,IAAI,EAAE,eAAe,EAAE,KAAK,EAAE,cAAc,EAAE;IAChD,EAAE,IAAI,EAAE,aAAa,EAAE,KAAK,EAAE,YAAY,EAAE;IAC5C,EAAE,IAAI,EAAE,aAAa,EAAE,KAAK,EAAE,YAAY,EAAE;CAC7C,CAAA;AAED,MAAM,UAAU,MAAM,CAAC,EAAE,aAAa,GAAG,GAAG,EAAe;IACzD,MAAM,GAAG,GAAG,MAAM,CAAc,IAAI,CAAC,CAAA;IACrC,aAAa,CAAC,QAAQ,EAAE,GAAG,CAAC,CAAA;IAE5B,OAAO,CACL,MAAC,IAAI,IAAC,EAAE,EAAC,QAAQ,EAAO,GAAG,aACzB,KAAC,IAAI,IAAC,EAAE,EAAE,aAAa,EAAE,IAAI,EAAC,GAAG,YAC/B,kBAAQ,SAAS,EAAC,qBAAqB,aACrC,KAAC,KAAK,uBAAa,EACnB,KAAC,EAAE,yBAAY,IACR,GACJ,EAEN,GAAG,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,EAAE,KAAK,EAAE,EAAE,EAAE,CAAC,CAC5B,MAAC,IAAI,IACH,EAAE,EAAE,aAAa,EACjB,SAAS,EAAC,gBAAgB,EAEpB,IAAI,EAAE,OAAO,aAEnB,MAAC,KAAK,eACH,KAAK,EACN,KAAC,KAAK,KAAG,IACH,EAER,KAAC,OAAO,KAAG,KARN,IAAI,CASJ,CACR,CAAC,IACG,CACR,CAAA;AACH,CAAC","sourcesContent":["'use client'\n\nimport { useRef } from 'react'\n\nimport { useCssVarDims } from '../hooks/use-css-var-dims'\nimport { Blink } from './components/blink'\nimport { Cell, Grid } from './components/grid'\nimport { HoverBg } from './components/hover-bg'\nimport { H2 } from './components/typography/h2'\nimport { Small } from './components/typography/small'\n\nconst NAV = [\n { href: '/projects', label: 'Projects' },\n { href: '/participants', label: 'Participants' },\n { href: '/provenance', label: 'Provenance' },\n { href: '/contribute', label: 'Contribute' }\n]\n\nexport function Header({ LinkComponent = 'a' }: HeaderProps) {\n const ref = useRef<HTMLElement>(null)\n useCssVarDims('header', ref)\n\n return (\n <Grid as=\"header\" {...{ ref }}>\n <Cell as={LinkComponent} href=\"/\">\n <hgroup className=\"flex flex-col gap-2\">\n <Small>Nous</Small>\n <H2>Psyche</H2>\n </hgroup>\n </Cell>\n\n {NAV.map(({ href, label }) => (\n <Cell\n as={LinkComponent}\n className=\"group relative\"\n key={href}\n {...{ href: '/runs' }}\n >\n <Small>\n {label}\n <Blink />\n </Small>\n\n <HoverBg />\n </Cell>\n ))}\n </Grid>\n )\n}\n\ninterface HeaderProps {\n LinkComponent?: React.ElementType\n}\n"]}
1
+ {"version":3,"file":"header.js","sourceRoot":"","sources":["../../src/ui/header.tsx"],"names":[],"mappings":"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","sourcesContent":["'use client'\n\nimport { AnimatePresence, motion } from 'motion/react'\nimport { createElement, useCallback, useRef, useState } from 'react'\n\nimport { useCssVarDims } from '../hooks/use-css-var-dims'\nimport { useGpuTier } from '../hooks/use-gpu-tier'\nimport { cn } from '../utils'\n\nimport { Blink } from './components/blink'\nimport { Cell, Grid } from './components/grid'\nimport { HoverBg } from './components/hover-bg'\nimport { HamburgerIcon } from './components/icons/hamburger'\nimport { Scramble } from './components/scramble'\nimport { Socials, type SocialLink } from './components/socials'\nimport { ThemeToggle } from './components/theme-toggle'\nimport { H2 } from './components/typography/h2'\nimport { Small } from './components/typography/small'\n\nconst DEFAULT_BRAND = (\n <hgroup className=\"flex flex-col gap-2\">\n <Small>Nous</Small>\n\n <H2>Research</H2>\n </hgroup>\n)\n\nconst DEFAULT_LINKS: HeaderLink[] = [\n { href: '/projects', label: 'Projects' },\n { href: '/participants', label: 'Participants' },\n { href: '/provenance', label: 'Provenance' },\n { href: '/contribute', label: 'Contribute' }\n]\n\nexport function Header({\n brand = DEFAULT_BRAND,\n brandHref = '/',\n className,\n desktopGridStyle,\n links = DEFAULT_LINKS,\n LinkComponent = 'a',\n scramble: scrambleProp = true,\n socials,\n socialsLabel = 'Socials',\n style,\n themeLabel = 'Theme',\n themeToggle = false\n}: HeaderProps) {\n const ref = useRef<HTMLElement>(null)\n useCssVarDims('header', ref)\n\n // Skip the hover-Scramble rAF loop on tier-0 devices (no GPU / software\n // renderer / `prefers-reduced-motion: reduce`) regardless of the prop.\n const gpuTier = useGpuTier()\n const scramble = scrambleProp && gpuTier > 0\n\n const [open, setOpen] = useState(false)\n const close = useCallback(() => setOpen(false), [])\n\n const hasSocials = (socials?.length ?? 0) > 0\n const hasMobileChrome = themeToggle || hasSocials\n\n return (\n <header className={className} ref={ref} style={style}>\n <Grid\n className=\"hidden border-t border-b lg:grid\"\n style={desktopGridStyle}\n >\n <BrandCell\n brand={brand}\n href={brandHref}\n LinkComponent={LinkComponent}\n />\n\n {links.map(link => (\n <NavCell\n key={link.href}\n link={link}\n LinkComponent={LinkComponent}\n scramble={scramble}\n />\n ))}\n\n {hasSocials && (\n <Cell className=\"flex items-start justify-between\">\n <Small className=\"opacity-50\">{socialsLabel}</Small>\n\n <Socials items={socials!} />\n </Cell>\n )}\n\n {themeToggle && (\n <Cell className=\"flex items-start justify-between\">\n <Small className=\"opacity-50\">{themeLabel}</Small>\n\n <ThemeToggle />\n </Cell>\n )}\n </Grid>\n\n <div\n className={cn(\n 'flex items-center justify-between border border-current/20 p-4',\n 'lg:hidden'\n )}\n >\n <BrandLink\n brand={brand}\n href={brandHref}\n LinkComponent={LinkComponent}\n />\n\n <div className=\"flex items-center gap-3\">\n {themeToggle && <ThemeToggle />}\n\n <button\n aria-label={open ? 'Close menu' : 'Open menu'}\n className=\"relative z-50 cursor-pointer bg-transparent p-2\"\n onClick={() => setOpen(v => !v)}\n type=\"button\"\n >\n <HamburgerIcon open={open} />\n </button>\n </div>\n </div>\n\n <AnimatePresence>\n {open && (\n <motion.div\n animate={{ opacity: 1 }}\n className={cn(\n 'bg-background/95 fixed inset-0 z-50 flex flex-col backdrop-blur-sm',\n 'p-8 lg:hidden'\n )}\n exit={{ opacity: 0 }}\n initial={{ opacity: 0 }}\n transition={{ duration: 0.2 }}\n >\n <div className=\"flex flex-col border border-current/20\">\n <div className=\"flex items-center justify-between border-b border-current/20 p-4\">\n <BrandLink\n brand={brand}\n href={brandHref}\n LinkComponent={LinkComponent}\n onClick={close}\n />\n\n <button\n aria-label=\"Close menu\"\n className=\"cursor-pointer bg-transparent p-2\"\n onClick={close}\n type=\"button\"\n >\n <HamburgerIcon open />\n </button>\n </div>\n\n {links.map(link => (\n <MobileNavLink\n key={link.href}\n link={link}\n LinkComponent={LinkComponent}\n onNavigate={close}\n scramble={scramble}\n />\n ))}\n\n {hasMobileChrome && (\n <div className=\"flex items-center gap-3 border-b border-current/20 p-4\">\n {hasSocials && (\n <>\n <Small className=\"opacity-50\">{socialsLabel}</Small>\n\n <Socials items={socials!} onNavigate={close} />\n </>\n )}\n\n {themeToggle && hasSocials && <span className=\"flex-1\" />}\n\n {themeToggle && (\n <>\n <Small className=\"opacity-50\">{themeLabel}</Small>\n\n <ThemeToggle />\n </>\n )}\n </div>\n )}\n </div>\n </motion.div>\n )}\n </AnimatePresence>\n </header>\n )\n}\n\nfunction BrandCell({ brand, href, LinkComponent }: BrandSlotProps) {\n return isExternal(href) ? (\n <Cell href={href} {...EXTERNAL_REL} as=\"a\">\n {brand}\n </Cell>\n ) : (\n <Cell as={LinkComponent} href={href}>\n {brand}\n </Cell>\n )\n}\n\nfunction BrandLink({ brand, href, LinkComponent, onClick }: BrandLinkProps) {\n if (isExternal(href)) {\n return (\n <a href={href} onClick={onClick} {...EXTERNAL_REL}>\n {brand}\n </a>\n )\n }\n\n return createElement(\n LinkComponent,\n { href, onClick } as Record<string, unknown>,\n brand\n )\n}\n\nfunction NavCell({ link, LinkComponent, scramble }: NavCellProps) {\n const ref = useRef<HTMLAnchorElement>(null)\n const isExt = link.external ?? isExternal(link.href)\n\n const inner = (\n <>\n <Small>\n {scramble ? (\n <Scramble target={ref}>{link.label}</Scramble>\n ) : (\n link.label\n )}\n\n <Blink />\n </Small>\n\n <HoverBg />\n </>\n )\n\n if (isExt) {\n return (\n <Cell\n as=\"a\"\n className=\"group relative cursor-pointer\"\n href={link.href}\n onClick={link.onClick}\n ref={ref}\n {...EXTERNAL_REL}\n >\n {inner}\n </Cell>\n )\n }\n\n return (\n <Cell\n as={LinkComponent}\n className=\"group relative cursor-pointer\"\n href={link.href}\n onClick={link.onClick}\n ref={ref}\n >\n {inner}\n </Cell>\n )\n}\n\nfunction MobileNavLink({\n link,\n LinkComponent,\n onNavigate,\n scramble\n}: MobileNavLinkProps) {\n const ref = useRef<HTMLAnchorElement>(null)\n const isExt = link.external ?? isExternal(link.href)\n\n const className = cn(\n 'group relative flex cursor-pointer items-center border-b border-current/20 p-4'\n )\n\n const onClick = (e: React.MouseEvent<HTMLAnchorElement>) => {\n link.onClick?.(e)\n onNavigate()\n }\n\n const children = (\n <>\n <Small>\n {scramble ? (\n <Scramble target={ref}>{link.label}</Scramble>\n ) : (\n link.label\n )}\n\n <Blink />\n </Small>\n\n <HoverBg />\n </>\n )\n\n if (isExt) {\n return (\n <a\n className={className}\n href={link.href}\n onClick={onClick}\n ref={ref}\n {...EXTERNAL_REL}\n >\n {children}\n </a>\n )\n }\n\n return createElement(\n LinkComponent,\n { className, href: link.href, onClick, ref } as Record<string, unknown>,\n children\n )\n}\n\nconst EXTERNAL_REL = {\n rel: 'noopener noreferrer',\n target: '_blank'\n} as const\n\nconst isExternal = (href: string) => /^(https?:|mailto:|tel:)/i.test(href)\n\ninterface BrandLinkProps extends BrandSlotProps {\n onClick?: React.MouseEventHandler\n}\n\ninterface BrandSlotProps {\n brand: React.ReactNode\n href: string\n LinkComponent: React.ElementType\n}\n\nexport interface HeaderLink {\n external?: boolean\n href: string\n label: string\n onClick?: React.MouseEventHandler\n}\n\nexport interface HeaderProps {\n brand?: React.ReactNode\n brandHref?: string\n className?: string\n /**\n * Inline `style` for the desktop `Grid` only — useful for overriding\n * `grid-template-columns` (e.g. to align with a sidebar track) without\n * affecting the mobile bar or drawer.\n */\n desktopGridStyle?: React.CSSProperties\n links?: HeaderLink[]\n LinkComponent?: React.ElementType\n /**\n * Apply the hover-Scramble effect to nav link labels. Defaults to `true`,\n * automatically suppressed on tier-0 GPUs and when the user has\n * `prefers-reduced-motion: reduce`.\n */\n scramble?: boolean\n /**\n * Optional socials shown in a trailing chrome cell on desktop and in the\n * mobile drawer's chrome row. For nav-heavy products (≥ 5 links) prefer\n * passing socials to `<Footer>` instead — the desktop `Grid` only ships\n * column rules through `grid-cols-6`, so brand + many links + chrome can\n * overflow.\n */\n socials?: SocialLink[]\n socialsLabel?: string\n style?: React.CSSProperties\n themeLabel?: string\n themeToggle?: boolean\n}\n\n/** @deprecated Use `SocialLink` from `@nous-research/ui`. Same shape. */\nexport type HeaderSocial = SocialLink\n\ninterface MobileNavLinkProps {\n link: HeaderLink\n LinkComponent: React.ElementType\n onNavigate: () => void\n scramble: boolean\n}\n\ninterface NavCellProps {\n link: HeaderLink\n LinkComponent: React.ElementType\n scramble: boolean\n}\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nous-research/ui",
3
- "version": "0.10.0",
3
+ "version": "0.12.0",
4
4
  "type": "module",
5
5
  "license": "MIT",
6
6
  "sideEffects": [
@@ -62,6 +62,7 @@
62
62
  "@vitejs/plugin-react": "^5.0.0",
63
63
  "gsap": "^3.13.0",
64
64
  "leva": "^0.10.1",
65
+ "motion": "^12.38.0",
65
66
  "storybook": "^10.3.1",
66
67
  "tailwindcss": "^4",
67
68
  "three": "^0.180.0",
@@ -73,6 +74,7 @@
73
74
  "@react-three/fiber": "^9.4.0",
74
75
  "gsap": "^3.13.0",
75
76
  "leva": "^0.10.1",
77
+ "motion": "^12.38.0",
76
78
  "react": "^19.0.0",
77
79
  "react-dom": "^19.0.0",
78
80
  "three": "^0.180.0"