@bbki.ng/site 5.5.6 → 5.5.8

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.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,21 @@
1
1
  # @bbki.ng/site
2
2
 
3
+ ## 5.5.8
4
+
5
+ ### Patch Changes
6
+
7
+ - c279a11: update shader
8
+ - Updated dependencies [c279a11]
9
+ - @bbki.ng/ui@0.2.8
10
+
11
+ ## 5.5.7
12
+
13
+ ### Patch Changes
14
+
15
+ - 339de15: refactor effect layer
16
+ - Updated dependencies [339de15]
17
+ - @bbki.ng/ui@0.2.7
18
+
3
19
  ## 5.5.6
4
20
 
5
21
  ### Patch Changes
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bbki.ng/site",
3
- "version": "5.5.6",
3
+ "version": "5.5.8",
4
4
  "description": "code behind bbki.ng",
5
5
  "main": "index.js",
6
6
  "type": "module",
@@ -14,7 +14,7 @@
14
14
  "react-dom": "^18.0.0",
15
15
  "react-router-dom": "6",
16
16
  "swr": "^2.2.5",
17
- "@bbki.ng/ui": "0.2.6"
17
+ "@bbki.ng/ui": "0.2.8"
18
18
  },
19
19
  "devDependencies": {
20
20
  "@eslint/compat": "^1.0.0",
@@ -1,16 +1,33 @@
1
- import React, { createContext, ReactNode } from 'react';
1
+ import React, { ReactNode, useContext, useMemo } from 'react';
2
2
 
3
- import { EffectLayer } from '@/components/effect-layer/EffectLayer';
3
+ import { EffectLayer, grain, paper, spiral, watermark, Effect } from '@bbki.ng/ui';
4
4
 
5
- const EffectContext = createContext<any>({});
5
+ import { GlobalLoadingContext } from '@/context/global_loading_state_provider';
6
+ import { useFingerprint } from '@/hooks/use_fingerprint';
7
+
8
+ const hashStr: string = typeof GLOBAL_COMMIT_HASH === 'string' ? GLOBAL_COMMIT_HASH : '0000000';
6
9
 
7
10
  export const EffectContextProvider = (props: { children: ReactNode }) => {
11
+ const { isLoading, isFontLoading } = useContext(GlobalLoadingContext);
12
+ const { deviceId } = useFingerprint();
13
+
14
+ const effects: Effect[] = useMemo(() => {
15
+ const wmLines = [hashStr];
16
+ if (deviceId) wmLines.push(deviceId);
17
+ wmLines.push('hello world');
18
+
19
+ return [
20
+ grain(),
21
+ paper(),
22
+ spiral({ active: isLoading || isFontLoading }),
23
+ watermark({ lines: wmLines }),
24
+ ];
25
+ }, [isLoading, isFontLoading, deviceId]);
26
+
8
27
  return (
9
- <EffectContext.Provider value={{}}>
10
- <>
11
- <EffectLayer />
12
- {props.children}
13
- </>
14
- </EffectContext.Provider>
28
+ <>
29
+ <EffectLayer effects={effects} />
30
+ {props.children}
31
+ </>
15
32
  );
16
33
  };
@@ -1,35 +0,0 @@
1
- import React from 'react';
2
- import { Canvas } from '@bbki.ng/ui';
3
- import frag from './main.frag';
4
- import vert from './shader.vert';
5
- import cls from 'classnames';
6
- import uniforms from './uniforms';
7
- import { useRender } from '@/components/effect-layer/hooks/useRender';
8
-
9
- export const EffectLayer = () => {
10
- const canvasDefaultCls = cls(
11
- 'fixed',
12
- 'top-0',
13
- 'left-0',
14
- 'h-full',
15
- 'h-dvh',
16
- 'pointer-events-none',
17
- 'w-full',
18
- 'z-999'
19
- );
20
-
21
- const { onRender } = useRender();
22
-
23
- return (
24
- <Canvas
25
- className={canvasDefaultCls}
26
- uniforms={uniforms}
27
- fragment={frag}
28
- vertex={vert}
29
- onRender={onRender}
30
- style={{
31
- height: '100lvh',
32
- }}
33
- />
34
- );
35
- };
@@ -1,18 +0,0 @@
1
- // Integer-based hash, stable on mediump float and immune to precision loss
2
- float rand(vec2 co) {
3
- vec2 seed = co * uResolution + fract(uProgress * vec2(12.453, 78.379));
4
- vec3 p = fract(vec3(seed.xyx) * vec3(443.897, 441.423, 437.195));
5
- p += dot(p, p.yzx + 19.19);
6
- return fract((p.x + p.y) * p.z);
7
- }
8
-
9
- vec4 randGrain(vec2 uv) {
10
- float n = rand(uv);
11
- float intensity = (n - 0.5) * 0.05;
12
- return vec4(vec3(0.0), abs(intensity) + 0.02);
13
- }
14
-
15
- void drawGrain(vec2 uv) {
16
- vec4 grain = randGrain(uv);
17
- gl_FragColor = grain + gl_FragColor * (1.0 - grain.a);
18
- }
@@ -1,50 +0,0 @@
1
- // Fine book paper texture — static, subtle fiber pattern
2
- // Simulates delicate diffuse micro-fiber scattering on white paper
3
-
4
- float paperHash(vec2 p) {
5
- vec3 p3 = fract(vec3(p.xyx) * vec3(443.897, 441.423, 437.195));
6
- p3 += dot(p3, p3.yzx + 19.19);
7
- return fract((p3.x + p3.y) * p3.z);
8
- }
9
-
10
- float paperNoise(vec2 uv) {
11
- vec2 i = floor(uv);
12
- vec2 f = fract(uv);
13
- // smooth interpolation
14
- vec2 u = f * f * (3.0 - 2.0 * f);
15
-
16
- float a = paperHash(i);
17
- float b = paperHash(i + vec2(1.0, 0.0));
18
- float c = paperHash(i + vec2(0.0, 1.0));
19
- float d = paperHash(i + vec2(1.0, 1.0));
20
-
21
- return mix(mix(a, b, u.x), mix(c, d, u.x), u.y);
22
- }
23
-
24
- // fBm with 5 octaves for dense, fine fiber detail
25
- float paperFBM(vec2 uv) {
26
- float value = 0.0;
27
- float amplitude = 0.5;
28
- for (int i = 0; i < 3; i++) {
29
- value += amplitude * paperNoise(uv);
30
- uv *= 2.5;
31
- amplitude *= 0.4;
32
- }
33
- return value;
34
- }
35
-
36
- void drawPaperTexture(vec2 uv) {
37
- // Scale to physical pixels so texture density stays consistent across screens
38
- vec2 texCoord = uv * uResolution / uDevicePixelRatio;
39
-
40
- // Single high-frequency FBM — fine, uniform, no visible banding
41
- float fiber = paperFBM(texCoord * 3.0);
42
-
43
- // abs() for full symmetric coverage; low multiplier as quiet base layer
44
- float alpha = abs(fiber - 0.5) * 0.05;
45
-
46
- // Soft neutral gray (premultiplied) instead of pure black — avoids dirty look
47
- float tone = 0.55;
48
- vec4 paper = vec4(vec3(tone * alpha), alpha);
49
- gl_FragColor = paper + gl_FragColor * (1.0 - alpha);
50
- }
@@ -1,99 +0,0 @@
1
- uniform float uLoading;
2
- uniform float uSpiralProgress;
3
- uniform float uSpiralOpacity;
4
-
5
- const float PI2 = 3.141592653589793 * 2.0;
6
-
7
- vec3 spiralCurve(float _percent) {
8
- const float _length = 0.3;
9
- const float radius = 0.056;
10
- float t = mod(_percent, 0.25) / 0.25;
11
- t = mod(_percent, 0.25) - (2.0 * (1.0 - t) * t * -0.0185 + t * t * 0.25);
12
- float x = _length * sin(PI2 * _percent);
13
- float y = radius * cos(PI2 * 3.0 * _percent);
14
-
15
- if (
16
- floor(_percent / 0.25) == 0.0
17
- || floor(_percent / 0.25) == 2.0
18
- ) {
19
- t = t * -1.0;
20
- }
21
- float z = radius * sin(PI2 * 2.0 * (_percent - t));
22
- return vec3(x, y, z);
23
- }
24
-
25
- vec3 rotateXVec3(vec3 v, float angle) {
26
- float c = cos(angle);
27
- float s = sin(angle);
28
- return vec3(
29
- v.x,
30
- v.y * c - v.z * s,
31
- v.y * s + v.z * c
32
- );
33
- }
34
-
35
- void drawSpiral(vec2 uv) {
36
- if (uLoading < 0.5) {
37
- return;
38
- }
39
-
40
- float aspect = uResolution.x / uResolution.y;
41
- vec2 center = vec2(0.5, 0.6);
42
- vec2 p = uv - center;
43
- p.x *= aspect;
44
-
45
- // Fixed visual size: spiral always appears ~120 CSS px wide, regardless of device.
46
- // After aspect correction, 1 curve unit = uResolution.y / scale physical pixels,
47
- // so spiral CSS width = 0.6 * cssHeight / scale. Solve for scale:
48
- float cssHeight = uResolution.y / uDevicePixelRatio;
49
- float targetCss = 80.0;
50
- float scale = 0.6 * cssHeight / targetCss;
51
- p *= scale;
52
-
53
- // early exit: spiral fits within radius ~0.35 in curve space
54
- if (length(p) > 0.5) {
55
- return;
56
- }
57
-
58
- float minDist = 1.0;
59
-
60
- const int SAMPLES = 80;
61
- vec2 prevProjected;
62
- for (int i = 0; i < SAMPLES; i++) {
63
- float pct = float(i) / float(SAMPLES);
64
- vec3 pos3d = spiralCurve(pct);
65
- vec3 rotated = rotateXVec3(pos3d, uSpiralProgress);
66
- vec2 projected = rotated.xy;
67
-
68
- if (i > 0) {
69
- // distance from point to line segment
70
- vec2 ab = projected - prevProjected;
71
- vec2 ap = p - prevProjected;
72
- float t = clamp(dot(ap, ab) / dot(ab, ab), 0.0, 1.0);
73
- vec2 closest = prevProjected + t * ab;
74
- float d = distance(p, closest);
75
- minDist = min(minDist, d);
76
- }
77
-
78
- prevProjected = projected;
79
- }
80
-
81
- // close the loop: connect last sample back to first
82
- vec3 firstPos = spiralCurve(0.0);
83
- vec3 firstRotated = rotateXVec3(firstPos, uSpiralProgress);
84
- vec2 firstProjected = firstRotated.xy;
85
- vec2 ab = firstProjected - prevProjected;
86
- vec2 ap = p - prevProjected;
87
- float tc = clamp(dot(ap, ab) / dot(ab, ab), 0.0, 1.0);
88
- vec2 closest = prevProjected + tc * ab;
89
- float dc = distance(p, closest);
90
- minDist = min(minDist, dc);
91
-
92
- // ~1.2 CSS px line width, device-independent
93
- float lineWidth = 1.2 * scale / cssHeight;
94
- float alpha = 1.0 - smoothstep(0.0, lineWidth, minDist);
95
-
96
- float spiralAlpha = alpha * 0.8 * uSpiralOpacity;
97
- gl_FragColor.rgb = mix(gl_FragColor.rgb, vec3(0.0), spiralAlpha);
98
- gl_FragColor.a = max(gl_FragColor.a, spiralAlpha);
99
- }
@@ -1,157 +0,0 @@
1
- // Bitmap-font watermark — 4×5 pixel hex glyphs (0-9, a-f)
2
- // Rendered at the bottom-left corner; meant to be drawn BEFORE grain
3
- // so that film-grain noise is composited on top of the text.
4
-
5
- uniform vec4 uHashChars; // hex digit values for chars 0–3 (each 0.0–15.0)
6
- uniform vec4 uHashChars2; // hex digit values for chars 4–6 (.w unused)
7
- uniform vec4 uFpChars1; // fingerprint chars 0–3
8
- uniform vec4 uFpChars2; // fingerprint chars 4–7
9
- uniform vec4 uFpChars3; // fingerprint chars 8–11
10
- uniform vec4 uFpChars4; // fingerprint chars 12–15
11
- uniform float uWatermarkHover; // 0.0 = idle (#f1f1f1), 1.0 = hovered (black)
12
-
13
- // Each glyph is a 4-wide × 5-tall bitmap packed into 20 bits of a float.
14
- // Bit layout (MSB → LSB): row0-col0 … row0-col3, row1-col0 … row4-col3
15
- // Row 0 = top of glyph. Requires highp for exact integer representation.
16
-
17
- highp float charBitmap(float c) {
18
- // .##. #..# #..# #..# .##.
19
- if (c < 0.5) return 432534.0; // 0
20
- // .#.. ##.. .#.. .#.. ###.
21
- if (c < 1.5) return 312398.0; // 1
22
- // .##. #..# ..#. .#.. ####
23
- if (c < 2.5) return 430671.0; // 2
24
- // ###. ...# .##. ...# ###.
25
- if (c < 3.5) return 923166.0; // 3
26
- // #..# #..# #### ...# ...#
27
- if (c < 4.5) return 630545.0; // 4
28
- // #### #... ###. ...# ###.
29
- if (c < 5.5) return 1019422.0; // 5
30
- // .##. #... ###. #..# .##.
31
- if (c < 6.5) return 429718.0; // 6
32
- // #### ...# ..#. .#.. .#..
33
- if (c < 7.5) return 987716.0; // 7
34
- // .##. #..# .##. #..# .##.
35
- if (c < 8.5) return 431766.0; // 8
36
- // .##. #..# .### ...# .##.
37
- if (c < 9.5) return 431894.0; // 9
38
- // .##. #..# #### #..# #..#
39
- if (c < 10.5) return 434073.0; // a
40
- // ###. #..# ###. #..# ###.
41
- if (c < 11.5) return 958110.0; // b
42
- // .### #... #... #... .###
43
- if (c < 12.5) return 493703.0; // c
44
- // ###. #..# #..# #..# ###.
45
- if (c < 13.5) return 956830.0; // d
46
- // #### #... ###. #... ####
47
- if (c < 14.5) return 1019535.0; // e
48
- // #### #... ###. #... #...
49
- return 1019528.0; // f
50
- }
51
-
52
- float sampleChar(float c, vec2 p) {
53
- // p in bitmap-pixel coords: x ∈ [0,4), y ∈ [0,5), origin = bottom-left
54
- if (p.x < 0.0 || p.x >= 4.0 || p.y < 0.0 || p.y >= 5.0) return 0.0;
55
-
56
- highp float bits = charBitmap(c);
57
- float col = floor(p.x); // 0 = left
58
- float row = floor(p.y); // 0 = bottom (screen coords)
59
- // Map to bit position: row 0 (bottom) → bits 3-0, row 4 (top) → bits 19-16
60
- highp float idx = row * 4.0 + (3.0 - col);
61
- return mod(floor(bits / exp2(idx)), 2.0);
62
- }
63
-
64
- float getHashChar(float i) {
65
- if (i < 0.5) return uHashChars.x;
66
- if (i < 1.5) return uHashChars.y;
67
- if (i < 2.5) return uHashChars.z;
68
- if (i < 3.5) return uHashChars.w;
69
- if (i < 4.5) return uHashChars2.x;
70
- if (i < 5.5) return uHashChars2.y;
71
- return uHashChars2.z;
72
- }
73
-
74
- float getFpChar(float i) {
75
- if (i < 0.5) return uFpChars1.x;
76
- if (i < 1.5) return uFpChars1.y;
77
- if (i < 2.5) return uFpChars1.z;
78
- if (i < 3.5) return uFpChars1.w;
79
- if (i < 4.5) return uFpChars2.x;
80
- if (i < 5.5) return uFpChars2.y;
81
- if (i < 6.5) return uFpChars2.z;
82
- if (i < 7.5) return uFpChars2.w;
83
- if (i < 8.5) return uFpChars3.x;
84
- if (i < 9.5) return uFpChars3.y;
85
- if (i < 10.5) return uFpChars3.z;
86
- if (i < 11.5) return uFpChars3.w;
87
- if (i < 12.5) return uFpChars4.x;
88
- if (i < 13.5) return uFpChars4.y;
89
- if (i < 14.5) return uFpChars4.z;
90
- return uFpChars4.w;
91
- }
92
-
93
- void drawWatermark(vec2 uv) {
94
- // Work in CSS (logical) pixels
95
- vec2 pixel = gl_FragCoord.xy / uDevicePixelRatio;
96
-
97
- // Layout constants
98
- float scale = 2.0; // each bitmap pixel = 2 CSS px
99
- float charW = 4.0 * scale; // 8 px per glyph
100
- float charH = 5.0 * scale; // 10 px per glyph (≈ 12 px visual)
101
- float gap = 3.0; // 3 px gap between glyphs
102
- float cellW = charW + gap; // 11 px per cell
103
-
104
- float padX = 16.0;
105
- float padY = 16.0;
106
- float lineGap = 6.0; // 6 px gap between lines
107
-
108
- // ----- Line 1 (bottom): Git hash - 7 chars -----
109
- {
110
- float nChars = 7.0;
111
- vec2 pos = pixel - vec2(padX, padY);
112
- float totalW = nChars * cellW - gap;
113
-
114
- if (pos.x >= 0.0 && pos.x < totalW && pos.y >= 0.0 && pos.y < charH) {
115
- float ci = floor(pos.x / cellW);
116
- if (ci < nChars) {
117
- float localX = pos.x - ci * cellW;
118
- if (localX < charW) {
119
- vec2 bp = vec2(localX, pos.y) / scale;
120
- float charCode = getHashChar(ci);
121
- float on = sampleChar(charCode, bp);
122
- if (on > 0.5) {
123
- float a = 0.4;
124
- vec3 col = mix(vec3(0.945), vec3(0.0), uWatermarkHover);
125
- gl_FragColor = vec4(col * a, a) + gl_FragColor * (1.0 - a);
126
- return;
127
- }
128
- }
129
- }
130
- }
131
- }
132
-
133
- // ----- Line 2 (above): Fingerprint - 16 chars -----
134
- {
135
- float nChars = 16.0;
136
- float fpPadY = padY + charH + lineGap;
137
- vec2 pos = pixel - vec2(padX, fpPadY);
138
- float totalW = nChars * cellW - gap;
139
-
140
- if (pos.x >= 0.0 && pos.x < totalW && pos.y >= 0.0 && pos.y < charH) {
141
- float ci = floor(pos.x / cellW);
142
- if (ci < nChars) {
143
- float localX = pos.x - ci * cellW;
144
- if (localX < charW) {
145
- vec2 bp = vec2(localX, pos.y) / scale;
146
- float charCode = getFpChar(ci);
147
- float on = sampleChar(charCode, bp);
148
- if (on > 0.5) {
149
- float a = 0.4;
150
- vec3 col = mix(vec3(0.945), vec3(0.0), uWatermarkHover);
151
- gl_FragColor = vec4(col * a, a) + gl_FragColor * (1.0 - a);
152
- }
153
- }
154
- }
155
- }
156
- }
157
- }
@@ -1,49 +0,0 @@
1
- import { useRef } from 'react';
2
- import { useFingerprint } from '@/hooks/use_fingerprint';
3
-
4
- export const useFingerprintUniforms = () => {
5
- const { deviceId, loading } = useFingerprint();
6
-
7
- const charsRef = useRef<number[] | null>(null);
8
- const appliedRef = useRef(false);
9
-
10
- if (!loading && deviceId && !charsRef.current) {
11
- const hash = deviceId;
12
- const chars = [...hash].map(c => parseInt(c, 16) || 0);
13
- while (chars.length < 16) chars.push(0);
14
- charsRef.current = chars;
15
- }
16
-
17
- const updateFingerprintUniforms = (inst: any) => {
18
- if (!inst?.uniforms || appliedRef.current || !charsRef.current) {
19
- return;
20
- }
21
-
22
- const chars = charsRef.current;
23
-
24
- // Mutate the existing arrays instead of replacing them
25
- inst.uniforms.uFpChars1.value[0] = chars[0];
26
- inst.uniforms.uFpChars1.value[1] = chars[1];
27
- inst.uniforms.uFpChars1.value[2] = chars[2];
28
- inst.uniforms.uFpChars1.value[3] = chars[3];
29
-
30
- inst.uniforms.uFpChars2.value[0] = chars[4];
31
- inst.uniforms.uFpChars2.value[1] = chars[5];
32
- inst.uniforms.uFpChars2.value[2] = chars[6];
33
- inst.uniforms.uFpChars2.value[3] = chars[7];
34
-
35
- inst.uniforms.uFpChars3.value[0] = chars[8];
36
- inst.uniforms.uFpChars3.value[1] = chars[9];
37
- inst.uniforms.uFpChars3.value[2] = chars[10];
38
- inst.uniforms.uFpChars3.value[3] = chars[11];
39
-
40
- inst.uniforms.uFpChars4.value[0] = chars[12];
41
- inst.uniforms.uFpChars4.value[1] = chars[13];
42
- inst.uniforms.uFpChars4.value[2] = chars[14];
43
- inst.uniforms.uFpChars4.value[3] = chars[15];
44
-
45
- appliedRef.current = true;
46
- };
47
-
48
- return { updateFingerprintUniforms };
49
- };
@@ -1,80 +0,0 @@
1
- import { useCallback, useContext, useRef } from 'react';
2
- import { useMousePosition } from '@/hooks/use_mouse_position';
3
- import { useResolution } from '@/components/effect-layer/hooks/useResolution';
4
- import { useWatermarkHover } from '@/components/effect-layer/hooks/useWatermarkHover';
5
- import { useFingerprintUniforms } from '@/components/effect-layer/hooks/useFingerprintUniforms';
6
- import { GlobalLoadingContext } from '@/context/global_loading_state_provider';
7
-
8
- const SPIRAL_ACCEL = 0.005;
9
- const SPIRAL_MAX_SPEED = 0.2;
10
- const FADE_SPEED = 0.04; // ~0→1 in 25 frames (~400ms at 60fps)
11
- const MIN_DURATION = 800;
12
-
13
- export const useRender = () => {
14
- const pos = useMousePosition();
15
- const resolution = useResolution();
16
- const { isLoading, isFontLoading } = useContext(GlobalLoadingContext);
17
- const loadingRef = useRef(false);
18
- loadingRef.current = isLoading || isFontLoading;
19
-
20
- const spiralSpeedRef = useRef(0);
21
- const spiralOpacityRef = useRef(0);
22
- const loadingStartRef = useRef(0);
23
- const wasLoadingRef = useRef(false);
24
-
25
- const { updateWatermarkHover } = useWatermarkHover();
26
- const { updateFingerprintUniforms } = useFingerprintUniforms();
27
-
28
- const onRender = useCallback((inst: any) => {
29
- if (inst == null) {
30
- return;
31
- }
32
-
33
- inst.uniforms.uResolution.value[0] = inst.gl.canvas.width;
34
- inst.uniforms.uResolution.value[1] = inst.gl.canvas.height;
35
-
36
- inst.uniforms.uMouse.value[0] = pos.current.x;
37
- inst.uniforms.uMouse.value[1] = pos.current.y;
38
-
39
- const now = performance.now();
40
- const loading = loadingRef.current;
41
-
42
- // track when loading starts
43
- if (loading && !wasLoadingRef.current) {
44
- loadingStartRef.current = now;
45
- }
46
- wasLoadingRef.current = loading;
47
-
48
- // determine if spiral should be visible (loading OR within min duration)
49
- const elapsed = now - loadingStartRef.current;
50
- const inMinDuration = !loading && loadingStartRef.current > 0 && elapsed < MIN_DURATION;
51
- const shouldShow = loading || inMinDuration;
52
-
53
- // fade in/out
54
- if (shouldShow) {
55
- spiralOpacityRef.current = Math.min(1, spiralOpacityRef.current + FADE_SPEED);
56
- } else {
57
- spiralOpacityRef.current = Math.max(0, spiralOpacityRef.current - FADE_SPEED);
58
- if (spiralOpacityRef.current === 0) {
59
- loadingStartRef.current = 0;
60
- }
61
- }
62
-
63
- inst.uniforms.uLoading.value[0] = spiralOpacityRef.current > 0 ? 1.0 : 0.0;
64
- inst.uniforms.uSpiralOpacity.value[0] = spiralOpacityRef.current;
65
-
66
- if (spiralOpacityRef.current > 0) {
67
- // accelerate to max speed before fade out
68
- const accel = inMinDuration ? SPIRAL_ACCEL * 4 : SPIRAL_ACCEL;
69
- spiralSpeedRef.current = Math.min(SPIRAL_MAX_SPEED, spiralSpeedRef.current + accel);
70
- } else {
71
- spiralSpeedRef.current = 0;
72
- }
73
- inst.uniforms.uSpiralProgress.value[0] += spiralSpeedRef.current;
74
-
75
- updateWatermarkHover(inst);
76
- updateFingerprintUniforms(inst);
77
- }, []);
78
-
79
- return { onRender };
80
- };
@@ -1,19 +0,0 @@
1
- import { useEffect, useRef } from 'react';
2
-
3
- export const useResolution = () => {
4
- const resolution = useRef<[number, number]>([window.innerWidth, window.innerHeight]);
5
-
6
- const updateResolution = () => {
7
- resolution.current = [window.innerWidth, window.innerHeight];
8
- };
9
-
10
- useEffect(() => {
11
- window.addEventListener('resize', updateResolution);
12
-
13
- return () => {
14
- window.removeEventListener('resize', updateResolution);
15
- };
16
- }, []);
17
-
18
- return resolution;
19
- };
@@ -1,63 +0,0 @@
1
- import { useRef } from 'react';
2
- import { useMousePosition } from '@/hooks/use_mouse_position';
3
-
4
- // Watermark layout constants (must match watermark.frag)
5
- const WM_PAD_X = 16;
6
- const WM_PAD_Y = 16;
7
- const WM_SCALE = 2;
8
- const WM_CHAR_W = 4 * WM_SCALE; // 8
9
- const WM_CHAR_H = 5 * WM_SCALE; // 10
10
- const WM_GAP = 3;
11
- const WM_CELL_W = WM_CHAR_W + WM_GAP; // 11
12
-
13
- // Git hash line (bottom): 7 chars
14
- const WM_N_CHARS = 7;
15
- const WM_TOTAL_W = WM_N_CHARS * WM_CELL_W - WM_GAP; // 74
16
-
17
- // Fingerprint line (above): 16 chars
18
- const FP_N_CHARS = 16;
19
- const FP_TOTAL_W = FP_N_CHARS * WM_CELL_W - WM_GAP; // 173
20
- const LINE_GAP = 6; // vertical gap between lines
21
-
22
- // Combined bounds
23
- const MAX_W = Math.max(WM_TOTAL_W, FP_TOTAL_W);
24
- const TOTAL_H = WM_CHAR_H * 2 + LINE_GAP; // 26
25
-
26
- const WM_HOVER_PADDING = 4; // extra hit-area padding (px)
27
- const WM_HOVER_SPEED = 0.06; // ~0→1 in 17 frames (~280ms at 60fps)
28
-
29
- export const useWatermarkHover = () => {
30
- const pos = useMousePosition();
31
- const hoverRef = useRef(0);
32
-
33
- const updateWatermarkHover = (inst: any) => {
34
- // Detect mouse inside watermark bounding box (CSS pixels)
35
- const viewportH = inst.gl.canvas.height / window.devicePixelRatio;
36
- const mx = pos.current.x;
37
- const my = pos.current.y;
38
- // Convert clientY (top-down) to GL-style bottom-up for comparison
39
- const myFromBottom = viewportH - my;
40
-
41
- // The fingerprint line is above the git hash line
42
- // Git hash starts at WM_PAD_Y from bottom
43
- // Fingerprint starts at WM_PAD_Y + WM_CHAR_H + LINE_GAP from bottom
44
- const fpPadY = WM_PAD_Y + WM_CHAR_H + LINE_GAP;
45
- const maxPadY = Math.max(WM_PAD_Y, fpPadY);
46
-
47
- const inWatermark =
48
- mx >= WM_PAD_X - WM_HOVER_PADDING &&
49
- mx <= WM_PAD_X + MAX_W + WM_HOVER_PADDING &&
50
- myFromBottom >= WM_PAD_Y - WM_HOVER_PADDING &&
51
- myFromBottom <= maxPadY + WM_CHAR_H + WM_HOVER_PADDING;
52
-
53
- if (inWatermark) {
54
- hoverRef.current = Math.min(1, hoverRef.current + WM_HOVER_SPEED);
55
- } else {
56
- hoverRef.current = Math.max(0, hoverRef.current - WM_HOVER_SPEED);
57
- }
58
-
59
- inst.uniforms.uWatermarkHover.value[0] = hoverRef.current;
60
- };
61
-
62
- return { updateWatermarkHover };
63
- };
@@ -1,27 +0,0 @@
1
- precision highp float;
2
-
3
- uniform vec2 uResolution;
4
- uniform float uProgress;
5
- uniform float uDevicePixelRatio;
6
-
7
- #define DefaultColor vec4(0.0, 0.0, 0.0, 0.0)
8
-
9
- #include "effects/grain.frag"
10
- #include "effects/paper.frag"
11
- #include "effects/spiral.frag"
12
- #include "effects/watermark.frag"
13
-
14
-
15
- void main() {
16
- vec2 uv = gl_FragCoord.xy / uResolution;
17
-
18
- gl_FragColor = DefaultColor;
19
-
20
- drawWatermark(uv);
21
-
22
- drawPaperTexture(uv);
23
-
24
- drawGrain(uv);
25
-
26
- drawSpiral(uv);
27
- }
@@ -1,9 +0,0 @@
1
- attribute vec3 aPosition;
2
-
3
- uniform mat4 uProjectionMatrix;
4
- uniform mat4 uModelMatrix;
5
- uniform mat4 uViewMatrix;
6
-
7
- void main(){
8
- gl_Position= uProjectionMatrix * uModelMatrix * uViewMatrix * vec4(aPosition, 1.);
9
- }
@@ -1,32 +0,0 @@
1
- uniform vec2 uMouse;
2
-
3
- vec4 sdfCircle(vec2 uv, float r, vec2 center) {
4
- float x = uv.x;
5
- float y = uv.y;
6
-
7
- float d = distance(vec2(x, y), center) - r;
8
-
9
- return d > 0. ? DefaultColor : vec4(1.0, 0.0, 1.0, 1.0);
10
- }
11
-
12
- vec4 sdfSquare(vec2 uv, vec4 rect) {
13
- float x = uv.x;
14
- float y = uv.y;
15
-
16
- float dx = max(abs(x - rect.x) - rect.z, 0.);
17
- float dy = max(abs(y - rect.y) - rect.w, 0.);
18
-
19
- float d = max(dx, dy);
20
-
21
- return d > 0. ? DefaultColor : vec4(1.0, 0.0, 1.0, 1.0);
22
-
23
- }
24
-
25
- void drawCircleUnderMouse(vec2 uv, float r) {
26
- // flip uMouse vertically
27
- vec2 normalizedMouse = vec2(uMouse.x, uResolution.y - uMouse.y) * uDevicePixelRatio / uResolution;
28
-
29
- // sdfCircle transition to sdfSquare
30
-
31
- // gl_FragColor = sdfSquare(uv, vec4(normalizedMouse, 0.1, 0.1));
32
- }
@@ -1,11 +0,0 @@
1
- float sdHeart(vec2 p)
2
- {
3
- p.x = abs(p.x);
4
-
5
- if( p.y+p.x>1.0 ) {
6
- return sqrt(dot2(p-vec2(0.25,0.75))) - sqrt(2.0)/4.0;
7
- }
8
-
9
- return sqrt(min(dot2(p-vec2(0.00,1.00)),
10
- dot2(p-0.5*max(p.x+p.y,0.0)))) * sign(p.x-p.y);
11
- }
@@ -1,14 +0,0 @@
1
- void cosCurve(vec2 uv) {
2
- float curve = 0.1 * cos((19.25 * uv.x + 3.141592654 * 0.5) + (2.0 * uProgress));
3
-
4
- vec2 p = uv - 0.5;
5
-
6
- float d = length(p);
7
-
8
- gl_FragColor = d > curve ? vec4(1.0) : vec4(0.0);
9
-
10
- }
11
-
12
- void spiral(vec2 uv) {
13
- cosCurve(uv);
14
- }
@@ -1,62 +0,0 @@
1
- const hashStr: string = typeof GLOBAL_COMMIT_HASH === 'string' ? GLOBAL_COMMIT_HASH : '0000000';
2
- const hc = [...hashStr].map(c => parseInt(c, 16) || 0);
3
-
4
- export default {
5
- uResolution: {
6
- type: 'vec2',
7
- value: [innerWidth, innerHeight],
8
- },
9
- uDevicePixelRatio: {
10
- type: 'float',
11
- value: [devicePixelRatio],
12
- },
13
- pi: {
14
- type: 'float',
15
- value: [Math.PI],
16
- },
17
- uMouse: {
18
- type: 'vec2',
19
- value: [0, 0],
20
- },
21
- uLoading: {
22
- type: 'float',
23
- value: [0.0],
24
- },
25
- uSpiralProgress: {
26
- type: 'float',
27
- value: [0.0],
28
- },
29
- uSpiralOpacity: {
30
- type: 'float',
31
- value: [0.0],
32
- },
33
- uHashChars: {
34
- type: 'vec4',
35
- value: [hc[0] ?? 0, hc[1] ?? 0, hc[2] ?? 0, hc[3] ?? 0],
36
- },
37
- uHashChars2: {
38
- type: 'vec4',
39
- value: [hc[4] ?? 0, hc[5] ?? 0, hc[6] ?? 0, 0],
40
- },
41
- // Fingerprint hash uniforms (16 characters, split into 4 vec4)
42
- uFpChars1: {
43
- type: 'vec4',
44
- value: [0, 0, 0, 0],
45
- },
46
- uFpChars2: {
47
- type: 'vec4',
48
- value: [0, 0, 0, 0],
49
- },
50
- uFpChars3: {
51
- type: 'vec4',
52
- value: [0, 0, 0, 0],
53
- },
54
- uFpChars4: {
55
- type: 'vec4',
56
- value: [0, 0, 0, 0],
57
- },
58
- uWatermarkHover: {
59
- type: 'float',
60
- value: [0.0],
61
- },
62
- };