@bbki.ng/site 5.4.25 → 5.4.27
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 +18 -0
- package/package.json +3 -4
- package/src/blog/app.tsx +9 -14
- package/src/blog/components/Auth.tsx +5 -9
- package/src/blog/components/DelayFadeIn/DelayFadeIn.tsx +5 -5
- package/src/blog/components/Pochacco/xwy.tsx +2 -8
- package/src/blog/components/Spinner.tsx +2 -2
- package/src/blog/components/article/index.tsx +3 -3
- package/src/blog/components/effect-layer/EffectContextProvider.tsx +3 -2
- package/src/blog/components/effect-layer/EffectLayer.tsx +14 -14
- package/src/blog/components/effect-layer/effects/spiral.frag +93 -0
- package/src/blog/components/effect-layer/hooks/useRender.ts +61 -14
- package/src/blog/components/effect-layer/hooks/useResolution.ts +4 -7
- package/src/blog/components/effect-layer/main.frag +4 -0
- package/src/blog/components/effect-layer/uniforms.ts +19 -9
- package/src/blog/components/index.tsx +1 -5
- package/src/blog/components/my_suspense.tsx +4 -7
- package/src/blog/components/share/share-btn.tsx +3 -3
- package/src/blog/components/share/share-icon.tsx +1 -1
- package/src/blog/constants/routes.ts +1 -1
- package/src/blog/context/global_loading_state_provider.tsx +2 -8
- package/src/blog/context/global_routes_provider.tsx +5 -7
- package/src/blog/hooks/use_blog_scroll_pos_restoration.ts +9 -9
- package/src/blog/hooks/use_font_loading.ts +3 -3
- package/src/blog/hooks/use_mouse_position.ts +5 -5
- package/src/blog/hooks/use_pathname.ts +2 -2
- package/src/blog/hooks/use_paths.ts +8 -8
- package/src/blog/hooks/use_posts.ts +1 -0
- package/src/blog/main.css +4 -5
- package/src/blog/pages/bot/index.tsx +3 -3
- package/src/blog/pages/cover/index.tsx +2 -0
- package/src/blog/pages/extensions/txt/article.tsx +1 -1
- package/src/blog/pages/extensions/txt/index.tsx +2 -2
- package/src/blog/pages/index.tsx +2 -2
- package/src/blog/pages/streaming/arrow-down.tsx +24 -24
- package/src/blog/pages/streaming/index.tsx +4 -8
- package/src/blog/pages/streaming/useScrollBtnVisibility.ts +19 -19
- package/src/blog/types/articles.ts +1 -1
- package/src/blog/types/color.ts +13 -13
- package/src/blog/types/cusdis.ts +2 -2
- package/src/blog/types/font.ts +2 -2
- package/src/blog/types/oss.ts +5 -5
- package/src/blog/types/path.ts +1 -1
- package/src/blog/types/photo.ts +1 -1
- package/src/blog/utils/streaming.ts +16 -14
- package/src/index.tsx +2 -2
- package/tsconfig.json +1 -0
- package/vite.config.js +0 -1
- package/src/blog/components/with_wrapper/index.tsx +0 -35
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,23 @@
|
|
|
1
1
|
# @bbki.ng/site
|
|
2
2
|
|
|
3
|
+
## 5.4.27
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- 1ab4d7c: update animation
|
|
8
|
+
- Updated dependencies [1ab4d7c]
|
|
9
|
+
- @bbki.ng/ui@0.1.2
|
|
10
|
+
|
|
11
|
+
## 5.4.26
|
|
12
|
+
|
|
13
|
+
### Patch Changes
|
|
14
|
+
|
|
15
|
+
- 282af74: update ui components
|
|
16
|
+
- 558e40b: update compoennts
|
|
17
|
+
- Updated dependencies [282af74]
|
|
18
|
+
- Updated dependencies [558e40b]
|
|
19
|
+
- @bbki.ng/ui@0.1.1
|
|
20
|
+
|
|
3
21
|
## 5.4.25
|
|
4
22
|
|
|
5
23
|
### Patch Changes
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bbki.ng/site",
|
|
3
|
-
"version": "5.4.
|
|
3
|
+
"version": "5.4.27",
|
|
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/
|
|
17
|
+
"@bbki.ng/ui": "0.1.2"
|
|
18
18
|
},
|
|
19
19
|
"devDependencies": {
|
|
20
20
|
"@eslint/compat": "^1.0.0",
|
|
@@ -57,8 +57,7 @@
|
|
|
57
57
|
"vite-plugin-mdx": "^3.5.8",
|
|
58
58
|
"vite-plugin-pwa": "0.19",
|
|
59
59
|
"workbox-window": "^6.3.0",
|
|
60
|
-
"@bbki.ng/config": "1.0.6"
|
|
61
|
-
"@bbki.ng/stylebase": "3.1.7"
|
|
60
|
+
"@bbki.ng/config": "1.0.6"
|
|
62
61
|
},
|
|
63
62
|
"author": "bbbottle",
|
|
64
63
|
"license": "MIT",
|
package/src/blog/app.tsx
CHANGED
|
@@ -1,34 +1,23 @@
|
|
|
1
|
-
import React, { useContext
|
|
1
|
+
import React, { useContext } from 'react';
|
|
2
2
|
import { Outlet, Route, Routes } from 'react-router-dom';
|
|
3
|
-
import { Nav, NotFound, Page } from '@bbki.ng/components';
|
|
4
3
|
import { Cover, Streaming } from './pages';
|
|
4
|
+
import { Nav, NotFound, Page, Grid, ErrorBoundary } from '@bbki.ng/ui';
|
|
5
5
|
|
|
6
6
|
import ArticlePage from '@/pages/extensions/txt/article';
|
|
7
7
|
import Txt from '@/pages/extensions/txt';
|
|
8
8
|
|
|
9
9
|
import { usePaths } from '@/hooks';
|
|
10
|
-
import { Login } from '@/pages/login';
|
|
11
10
|
import { SWR } from '@/swr';
|
|
12
11
|
import { GlobalLoadingContext } from '@/context/global_loading_state_provider';
|
|
13
12
|
import { BotRedirect } from '@/pages/bot';
|
|
14
13
|
import { BBContext } from '@/context/bbcontext';
|
|
15
|
-
import { ThreeColLayout, ErrorBoundary } from '@bbki.ng/components';
|
|
16
14
|
import { useDynamicLogo } from './hooks/use_dynamic_logo';
|
|
17
|
-
import { EffectContextProvider } from './components/effect-layer/EffectContextProvider';
|
|
18
15
|
|
|
19
16
|
const Layout = () => {
|
|
20
17
|
const paths = usePaths();
|
|
21
18
|
const { isLoading } = useContext(GlobalLoadingContext);
|
|
22
19
|
const logo = useDynamicLogo();
|
|
23
20
|
|
|
24
|
-
const middleRenderer = useMemo(() => {
|
|
25
|
-
return () => (
|
|
26
|
-
<ErrorBoundary>
|
|
27
|
-
<Outlet />
|
|
28
|
-
</ErrorBoundary>
|
|
29
|
-
);
|
|
30
|
-
}, []);
|
|
31
|
-
|
|
32
21
|
return (
|
|
33
22
|
<Page
|
|
34
23
|
nav={
|
|
@@ -39,7 +28,13 @@ const Layout = () => {
|
|
|
39
28
|
customLogo={logo}
|
|
40
29
|
/>
|
|
41
30
|
}
|
|
42
|
-
main={
|
|
31
|
+
main={
|
|
32
|
+
<Grid leftAside={<div />}>
|
|
33
|
+
<ErrorBoundary>
|
|
34
|
+
<Outlet />
|
|
35
|
+
</ErrorBoundary>
|
|
36
|
+
</Grid>
|
|
37
|
+
}
|
|
43
38
|
/>
|
|
44
39
|
);
|
|
45
40
|
};
|
|
@@ -1,16 +1,12 @@
|
|
|
1
|
-
import { Role, useRole } from
|
|
2
|
-
import React from
|
|
3
|
-
import { Navigate } from
|
|
1
|
+
import { Role, useRole } from '@/hooks/use_role';
|
|
2
|
+
import React from 'react';
|
|
3
|
+
import { Navigate } from 'react-router-dom';
|
|
4
4
|
|
|
5
|
-
export const Auth = (props: {
|
|
6
|
-
children: any;
|
|
7
|
-
shouldRedirect?: boolean;
|
|
8
|
-
role?: Role[];
|
|
9
|
-
}) => {
|
|
5
|
+
export const Auth = (props: { children: any; shouldRedirect?: boolean; role?: Role[] }) => {
|
|
10
6
|
const myRole = useRole();
|
|
11
7
|
|
|
12
8
|
if (props.role && !props.role.includes(myRole)) {
|
|
13
|
-
return props.shouldRedirect ? <Navigate to={
|
|
9
|
+
return props.shouldRedirect ? <Navigate to={'/login'} /> : null;
|
|
14
10
|
}
|
|
15
11
|
|
|
16
12
|
return props.children;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import React, { useEffect } from
|
|
2
|
-
import cls from
|
|
1
|
+
import React, { useEffect } from 'react';
|
|
2
|
+
import cls from 'classnames';
|
|
3
3
|
|
|
4
4
|
export type DelayFadeInProps = {
|
|
5
5
|
children: any;
|
|
@@ -19,9 +19,9 @@ export const DelayFadeIn = (props: DelayFadeInProps) => {
|
|
|
19
19
|
};
|
|
20
20
|
}, []);
|
|
21
21
|
|
|
22
|
-
const className = cls(
|
|
23
|
-
|
|
24
|
-
|
|
22
|
+
const className = cls('transition-opacity', {
|
|
23
|
+
'opacity-100': show,
|
|
24
|
+
'opacity-0': !show,
|
|
25
25
|
});
|
|
26
26
|
|
|
27
27
|
return <div className={className}>{props.children}</div>;
|
|
@@ -1,14 +1,8 @@
|
|
|
1
|
-
import React from
|
|
1
|
+
import React from 'react';
|
|
2
2
|
|
|
3
3
|
export const Crows = () => {
|
|
4
4
|
return (
|
|
5
|
-
<svg
|
|
6
|
-
width="48"
|
|
7
|
-
height="48"
|
|
8
|
-
viewBox="0 0 48 48"
|
|
9
|
-
fill="none"
|
|
10
|
-
xmlns="http://www.w3.org/2000/svg"
|
|
11
|
-
>
|
|
5
|
+
<svg width="48" height="48" viewBox="0 0 48 48" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
12
6
|
<path
|
|
13
7
|
d="M12.821 17.5305C10.709 18.17 9.68345 19.4423 9.22624 20.1359C9.11159 20.3099 9.21615 20.5428 9.42038 20.5839L12.67 21.2381C12.8291 21.2702 12.9328 21.4275 12.9084 21.5879C11.3004 32.1653 21.5275 36.7547 28.6638 33.0597C28.7443 33.018 28.8408 33.0139 28.9245 33.0487C32.8032 34.6598 35.967 34.5662 37.8217 34.3099C38.131 34.2671 38.1505 33.841 37.855 33.7401C29.1343 30.7633 26.0152 24.5245 25.5144 18.8022C25.3835 17.3066 23.8172 13.2016 19.2675 13.0058C15.7934 12.8563 13.6137 15.6103 13.0319 17.325C12.9986 17.4231 12.9201 17.5004 12.821 17.5305Z"
|
|
14
8
|
fill="#FBD1D2"
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import React, { useContext, useEffect } from
|
|
2
|
-
import { GlobalLoadingContext } from
|
|
1
|
+
import React, { useContext, useEffect } from 'react';
|
|
2
|
+
import { GlobalLoadingContext } from '@/context/global_loading_state_provider';
|
|
3
3
|
|
|
4
4
|
export const Spinner = (props: { disableDotIndicator?: boolean }) => {
|
|
5
5
|
const { disableDotIndicator } = props;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import React, { ReactElement } from 'react';
|
|
2
|
-
import {
|
|
2
|
+
import { Button, Article } from '@bbki.ng/ui';
|
|
3
3
|
import { ROUTES } from '@/constants';
|
|
4
4
|
import classNames from 'classnames';
|
|
5
5
|
import { useNavigate } from 'react-router-dom';
|
|
@@ -30,7 +30,7 @@ export const ArticlePage = (props: ArticlePageProps) => {
|
|
|
30
30
|
title={title}
|
|
31
31
|
date={props.date}
|
|
32
32
|
description={description}
|
|
33
|
-
className={`${props.className}
|
|
33
|
+
className={`${props.className} mt-32`}
|
|
34
34
|
loading={false}
|
|
35
35
|
>
|
|
36
36
|
<article className={articleCls}>{props.children}</article>
|
|
@@ -41,7 +41,7 @@ export const ArticlePage = (props: ArticlePageProps) => {
|
|
|
41
41
|
<div className="">
|
|
42
42
|
<Button
|
|
43
43
|
className=""
|
|
44
|
-
|
|
44
|
+
type="button"
|
|
45
45
|
onClick={() => {
|
|
46
46
|
navgation(-1);
|
|
47
47
|
}}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import React, { createContext, ReactNode } from
|
|
2
|
-
|
|
1
|
+
import React, { createContext, ReactNode } from 'react';
|
|
2
|
+
|
|
3
|
+
import { EffectLayer } from '@/components/effect-layer/EffectLayer';
|
|
3
4
|
|
|
4
5
|
const EffectContext = createContext<any>({});
|
|
5
6
|
|
|
@@ -1,20 +1,20 @@
|
|
|
1
|
-
import React from
|
|
2
|
-
import { Canvas } from
|
|
3
|
-
import frag from
|
|
4
|
-
import vert from
|
|
5
|
-
import cls from
|
|
6
|
-
import uniforms from
|
|
7
|
-
import { useRender } from
|
|
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
8
|
|
|
9
9
|
export const EffectLayer = () => {
|
|
10
10
|
const canvasDefaultCls = cls(
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
11
|
+
'fixed',
|
|
12
|
+
'top-0',
|
|
13
|
+
'left-0',
|
|
14
|
+
'h-full',
|
|
15
|
+
'pointer-events-none',
|
|
16
|
+
'w-full',
|
|
17
|
+
'z-999'
|
|
18
18
|
);
|
|
19
19
|
|
|
20
20
|
const { onRender } = useRender();
|
|
@@ -0,0 +1,93 @@
|
|
|
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
|
+
float scale = 4.0;
|
|
46
|
+
p *= scale;
|
|
47
|
+
|
|
48
|
+
// early exit: spiral fits within radius ~0.35 in curve space
|
|
49
|
+
if (length(p) > 0.5) {
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
float minDist = 1.0;
|
|
54
|
+
|
|
55
|
+
const int SAMPLES = 80;
|
|
56
|
+
vec2 prevProjected;
|
|
57
|
+
for (int i = 0; i < SAMPLES; i++) {
|
|
58
|
+
float pct = float(i) / float(SAMPLES);
|
|
59
|
+
vec3 pos3d = spiralCurve(pct);
|
|
60
|
+
vec3 rotated = rotateXVec3(pos3d, uSpiralProgress);
|
|
61
|
+
vec2 projected = rotated.xy;
|
|
62
|
+
|
|
63
|
+
if (i > 0) {
|
|
64
|
+
// distance from point to line segment
|
|
65
|
+
vec2 ab = projected - prevProjected;
|
|
66
|
+
vec2 ap = p - prevProjected;
|
|
67
|
+
float t = clamp(dot(ap, ab) / dot(ab, ab), 0.0, 1.0);
|
|
68
|
+
vec2 closest = prevProjected + t * ab;
|
|
69
|
+
float d = distance(p, closest);
|
|
70
|
+
minDist = min(minDist, d);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
prevProjected = projected;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// close the loop: connect last sample back to first
|
|
77
|
+
vec3 firstPos = spiralCurve(0.0);
|
|
78
|
+
vec3 firstRotated = rotateXVec3(firstPos, uSpiralProgress);
|
|
79
|
+
vec2 firstProjected = firstRotated.xy;
|
|
80
|
+
vec2 ab = firstProjected - prevProjected;
|
|
81
|
+
vec2 ap = p - prevProjected;
|
|
82
|
+
float tc = clamp(dot(ap, ab) / dot(ab, ab), 0.0, 1.0);
|
|
83
|
+
vec2 closest = prevProjected + tc * ab;
|
|
84
|
+
float dc = distance(p, closest);
|
|
85
|
+
minDist = min(minDist, dc);
|
|
86
|
+
|
|
87
|
+
float lineWidth = 0.002;
|
|
88
|
+
float alpha = 1.0 - smoothstep(0.0, lineWidth, minDist);
|
|
89
|
+
|
|
90
|
+
float spiralAlpha = alpha * 0.8 * uSpiralOpacity;
|
|
91
|
+
gl_FragColor.rgb = mix(gl_FragColor.rgb, vec3(0.0), spiralAlpha);
|
|
92
|
+
gl_FragColor.a = max(gl_FragColor.a, spiralAlpha);
|
|
93
|
+
}
|
|
@@ -1,25 +1,72 @@
|
|
|
1
|
-
import { useCallback,
|
|
2
|
-
import { useMousePosition } from
|
|
3
|
-
import { useResolution } from
|
|
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 { GlobalLoadingContext } from '@/context/global_loading_state_provider';
|
|
5
|
+
|
|
6
|
+
const SPIRAL_ACCEL = 0.005;
|
|
7
|
+
const SPIRAL_MAX_SPEED = 0.2;
|
|
8
|
+
const FADE_SPEED = 0.04; // ~0→1 in 25 frames (~400ms at 60fps)
|
|
9
|
+
const MIN_DURATION = 800;
|
|
4
10
|
|
|
5
11
|
export const useRender = () => {
|
|
6
12
|
const pos = useMousePosition();
|
|
7
13
|
const resolution = useResolution();
|
|
14
|
+
const { isLoading, isFontLoading } = useContext(GlobalLoadingContext);
|
|
15
|
+
const loadingRef = useRef(false);
|
|
16
|
+
loadingRef.current = isLoading || isFontLoading;
|
|
17
|
+
|
|
18
|
+
const spiralSpeedRef = useRef(0);
|
|
19
|
+
const spiralOpacityRef = useRef(0);
|
|
20
|
+
const loadingStartRef = useRef(0);
|
|
21
|
+
const wasLoadingRef = useRef(false);
|
|
22
|
+
|
|
23
|
+
const onRender = useCallback((inst: any) => {
|
|
24
|
+
if (inst == null) {
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
inst.uniforms.uResolution.value[0] = inst.gl.canvas.width;
|
|
29
|
+
inst.uniforms.uResolution.value[1] = inst.gl.canvas.height;
|
|
30
|
+
|
|
31
|
+
inst.uniforms.uMouse.value[0] = pos.current.x;
|
|
32
|
+
inst.uniforms.uMouse.value[1] = pos.current.y;
|
|
33
|
+
|
|
34
|
+
const now = performance.now();
|
|
35
|
+
const loading = loadingRef.current;
|
|
36
|
+
|
|
37
|
+
// track when loading starts
|
|
38
|
+
if (loading && !wasLoadingRef.current) {
|
|
39
|
+
loadingStartRef.current = now;
|
|
40
|
+
}
|
|
41
|
+
wasLoadingRef.current = loading;
|
|
42
|
+
|
|
43
|
+
// determine if spiral should be visible (loading OR within min duration)
|
|
44
|
+
const elapsed = now - loadingStartRef.current;
|
|
45
|
+
const inMinDuration = !loading && loadingStartRef.current > 0 && elapsed < MIN_DURATION;
|
|
46
|
+
const shouldShow = loading || inMinDuration;
|
|
8
47
|
|
|
9
|
-
|
|
10
|
-
(
|
|
11
|
-
|
|
12
|
-
|
|
48
|
+
// fade in/out
|
|
49
|
+
if (shouldShow) {
|
|
50
|
+
spiralOpacityRef.current = Math.min(1, spiralOpacityRef.current + FADE_SPEED);
|
|
51
|
+
} else {
|
|
52
|
+
spiralOpacityRef.current = Math.max(0, spiralOpacityRef.current - FADE_SPEED);
|
|
53
|
+
if (spiralOpacityRef.current === 0) {
|
|
54
|
+
loadingStartRef.current = 0;
|
|
13
55
|
}
|
|
56
|
+
}
|
|
14
57
|
|
|
15
|
-
|
|
16
|
-
|
|
58
|
+
inst.uniforms.uLoading.value[0] = spiralOpacityRef.current > 0 ? 1.0 : 0.0;
|
|
59
|
+
inst.uniforms.uSpiralOpacity.value[0] = spiralOpacityRef.current;
|
|
17
60
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
61
|
+
if (spiralOpacityRef.current > 0) {
|
|
62
|
+
// accelerate to max speed before fade out
|
|
63
|
+
const accel = inMinDuration ? SPIRAL_ACCEL * 4 : SPIRAL_ACCEL;
|
|
64
|
+
spiralSpeedRef.current = Math.min(SPIRAL_MAX_SPEED, spiralSpeedRef.current + accel);
|
|
65
|
+
} else {
|
|
66
|
+
spiralSpeedRef.current = 0;
|
|
67
|
+
}
|
|
68
|
+
inst.uniforms.uSpiralProgress.value[0] += spiralSpeedRef.current;
|
|
69
|
+
}, []);
|
|
23
70
|
|
|
24
71
|
return { onRender };
|
|
25
72
|
};
|
|
@@ -1,20 +1,17 @@
|
|
|
1
|
-
import { useEffect, useRef } from
|
|
1
|
+
import { useEffect, useRef } from 'react';
|
|
2
2
|
|
|
3
3
|
export const useResolution = () => {
|
|
4
|
-
const resolution = useRef<[number, number]>([
|
|
5
|
-
window.innerWidth,
|
|
6
|
-
window.innerHeight,
|
|
7
|
-
]);
|
|
4
|
+
const resolution = useRef<[number, number]>([window.innerWidth, window.innerHeight]);
|
|
8
5
|
|
|
9
6
|
const updateResolution = () => {
|
|
10
7
|
resolution.current = [window.innerWidth, window.innerHeight];
|
|
11
8
|
};
|
|
12
9
|
|
|
13
10
|
useEffect(() => {
|
|
14
|
-
window.addEventListener(
|
|
11
|
+
window.addEventListener('resize', updateResolution);
|
|
15
12
|
|
|
16
13
|
return () => {
|
|
17
|
-
window.removeEventListener(
|
|
14
|
+
window.removeEventListener('resize', updateResolution);
|
|
18
15
|
};
|
|
19
16
|
}, []);
|
|
20
17
|
|
|
@@ -7,6 +7,7 @@ uniform float uDevicePixelRatio;
|
|
|
7
7
|
#define DefaultColor vec4(0.0, 0.0, 0.0, 0.0)
|
|
8
8
|
|
|
9
9
|
#include "effects/grain.frag"
|
|
10
|
+
#include "effects/spiral.frag"
|
|
10
11
|
//#include "shapes/circle.frag"
|
|
11
12
|
|
|
12
13
|
|
|
@@ -21,4 +22,7 @@ void main() {
|
|
|
21
22
|
|
|
22
23
|
// draw grain on nav
|
|
23
24
|
drawGrainOnNav(uv);
|
|
25
|
+
|
|
26
|
+
// draw loading spiral
|
|
27
|
+
drawSpiral(uv);
|
|
24
28
|
}
|
|
@@ -1,20 +1,30 @@
|
|
|
1
1
|
export default {
|
|
2
2
|
uResolution: {
|
|
3
|
-
type:
|
|
4
|
-
value: [
|
|
5
|
-
innerWidth, innerHeight,
|
|
6
|
-
],
|
|
3
|
+
type: 'vec2',
|
|
4
|
+
value: [innerWidth, innerHeight],
|
|
7
5
|
},
|
|
8
6
|
uDevicePixelRatio: {
|
|
9
|
-
type:
|
|
7
|
+
type: 'float',
|
|
10
8
|
value: [devicePixelRatio],
|
|
11
9
|
},
|
|
12
10
|
pi: {
|
|
13
|
-
type:
|
|
11
|
+
type: 'float',
|
|
14
12
|
value: [Math.PI],
|
|
15
13
|
},
|
|
16
14
|
uMouse: {
|
|
17
|
-
type:
|
|
15
|
+
type: 'vec2',
|
|
18
16
|
value: [0, 0],
|
|
19
|
-
}
|
|
20
|
-
|
|
17
|
+
},
|
|
18
|
+
uLoading: {
|
|
19
|
+
type: 'float',
|
|
20
|
+
value: [0.0],
|
|
21
|
+
},
|
|
22
|
+
uSpiralProgress: {
|
|
23
|
+
type: 'float',
|
|
24
|
+
value: [0.0],
|
|
25
|
+
},
|
|
26
|
+
uSpiralOpacity: {
|
|
27
|
+
type: 'float',
|
|
28
|
+
value: [0.0],
|
|
29
|
+
},
|
|
30
|
+
};
|
|
@@ -1,8 +1,5 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
-
import { LinkList } from '@bbki.ng/
|
|
3
|
-
import { BlurCover } from '@bbki.ng/components';
|
|
4
|
-
|
|
5
|
-
export { withArticleWrapper } from './with_wrapper';
|
|
2
|
+
import { LinkList } from '@bbki.ng/ui';
|
|
6
3
|
|
|
7
4
|
export { MySuspense } from './my_suspense';
|
|
8
5
|
|
|
@@ -10,7 +7,6 @@ export const CenterLinkList = (props: any) => {
|
|
|
10
7
|
return (
|
|
11
8
|
<div className="flex justify-center relative p-16 h-full">
|
|
12
9
|
<LinkList {...props} />
|
|
13
|
-
<BlurCover status={props.loading ? 'show' : 'silent'} />
|
|
14
10
|
</div>
|
|
15
11
|
);
|
|
16
12
|
};
|
|
@@ -1,11 +1,8 @@
|
|
|
1
|
-
import React, { ReactElement, ReactNode, Suspense } from
|
|
2
|
-
import { ErrorBoundary } from
|
|
3
|
-
import { Spinner } from
|
|
1
|
+
import React, { ReactElement, ReactNode, Suspense } from 'react';
|
|
2
|
+
import { ErrorBoundary } from '@bbki.ng/ui';
|
|
3
|
+
import { Spinner } from './Spinner';
|
|
4
4
|
|
|
5
|
-
export const MySuspense = (props: {
|
|
6
|
-
children: ReactNode;
|
|
7
|
-
fallback?: ReactElement;
|
|
8
|
-
}) => {
|
|
5
|
+
export const MySuspense = (props: { children: ReactNode; fallback?: ReactElement }) => {
|
|
9
6
|
return (
|
|
10
7
|
<ErrorBoundary>
|
|
11
8
|
<Suspense fallback={<Spinner />}>{props.children}</Suspense>
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
-
import { Button
|
|
2
|
+
import { Button } from '@bbki.ng/ui';
|
|
3
3
|
import { ShareIcon } from './share-icon';
|
|
4
4
|
|
|
5
5
|
export const ShareBtn = ({ shareInfo }: { shareInfo: ShareData }) => {
|
|
@@ -17,9 +17,9 @@ export const ShareBtn = ({ shareInfo }: { shareInfo: ShareData }) => {
|
|
|
17
17
|
|
|
18
18
|
return (
|
|
19
19
|
<Button
|
|
20
|
-
size="
|
|
20
|
+
size="sm"
|
|
21
21
|
className="text-gray-400 hover:text-gray-600 transition-colors ease-in duration-200"
|
|
22
|
-
|
|
22
|
+
variant="ghost"
|
|
23
23
|
onClick={handleShare}
|
|
24
24
|
>
|
|
25
25
|
<ShareIcon />
|
|
@@ -13,7 +13,7 @@ export const ROUTE_NAME = {
|
|
|
13
13
|
unknown: '未知',
|
|
14
14
|
};
|
|
15
15
|
|
|
16
|
-
export const GITHUB_REPO_ADDRESS = 'https://github.com/bbbottle/bottle/tree/main/
|
|
16
|
+
export const GITHUB_REPO_ADDRESS = 'https://github.com/bbbottle/bottle/tree/main/apps/site';
|
|
17
17
|
export const API_ENDPOINT = 'https://cf.bbki.ng';
|
|
18
18
|
export const API = {
|
|
19
19
|
POSTS: 'posts',
|
|
@@ -1,11 +1,5 @@
|
|
|
1
|
-
import React, {
|
|
2
|
-
|
|
3
|
-
ReactNode,
|
|
4
|
-
useState,
|
|
5
|
-
Dispatch,
|
|
6
|
-
SetStateAction,
|
|
7
|
-
} from "react";
|
|
8
|
-
import { useFontLoading } from "@/hooks/use_font_loading";
|
|
1
|
+
import React, { createContext, ReactNode, useState, Dispatch, SetStateAction } from 'react';
|
|
2
|
+
import { useFontLoading } from '@/hooks/use_font_loading';
|
|
9
3
|
|
|
10
4
|
type LoadingContext = {
|
|
11
5
|
isLoading: boolean;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import React, { createContext, ReactNode, useState } from
|
|
1
|
+
import React, { createContext, ReactNode, useState } from 'react';
|
|
2
2
|
|
|
3
3
|
type routeInfo = {
|
|
4
4
|
name: string;
|
|
@@ -18,14 +18,14 @@ export const GlobalRoutesContext = createContext<GlobalRoutesContextType>({
|
|
|
18
18
|
});
|
|
19
19
|
|
|
20
20
|
export const GlobalRoutesProvider = (props: { children: ReactNode }) => {
|
|
21
|
-
const [routesMap, setRoutesMap] = useState<{[key: string]: routeInfo}>({});
|
|
21
|
+
const [routesMap, setRoutesMap] = useState<{ [key: string]: routeInfo }>({});
|
|
22
22
|
|
|
23
23
|
const addGlobalRoute = (r: routeInfo) => {
|
|
24
|
-
setRoutesMap(
|
|
24
|
+
setRoutesMap(prev => ({ ...prev, [r.name]: r }));
|
|
25
25
|
};
|
|
26
26
|
|
|
27
27
|
const removeGlobalRoute = (routeName: string) => {
|
|
28
|
-
setRoutesMap(
|
|
28
|
+
setRoutesMap(prev => {
|
|
29
29
|
const copy = { ...prev };
|
|
30
30
|
delete copy[routeName];
|
|
31
31
|
return copy;
|
|
@@ -35,9 +35,7 @@ export const GlobalRoutesProvider = (props: { children: ReactNode }) => {
|
|
|
35
35
|
const globalRoutes = Object.values(routesMap);
|
|
36
36
|
|
|
37
37
|
return (
|
|
38
|
-
<GlobalRoutesContext.Provider
|
|
39
|
-
value={{ globalRoutes, addGlobalRoute, removeGlobalRoute }}
|
|
40
|
-
>
|
|
38
|
+
<GlobalRoutesContext.Provider value={{ globalRoutes, addGlobalRoute, removeGlobalRoute }}>
|
|
41
39
|
{props.children}
|
|
42
40
|
</GlobalRoutesContext.Provider>
|
|
43
41
|
);
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { useCallback, useEffect, useRef } from
|
|
2
|
-
import { useLocation } from
|
|
1
|
+
import { useCallback, useEffect, useRef } from 'react';
|
|
2
|
+
import { useLocation } from 'react-router-dom';
|
|
3
3
|
|
|
4
|
-
const SCROLL_STORAGE_KEY =
|
|
4
|
+
const SCROLL_STORAGE_KEY = 'div-scroll-positions';
|
|
5
5
|
|
|
6
6
|
function getScrollPositions(): Record<string, number> {
|
|
7
7
|
const stored = sessionStorage.getItem(SCROLL_STORAGE_KEY);
|
|
@@ -16,7 +16,7 @@ function saveScrollPosition(key: string, position: number) {
|
|
|
16
16
|
|
|
17
17
|
export function useBlogScrollReset() {
|
|
18
18
|
useEffect(() => {
|
|
19
|
-
const element = document.getElementById(
|
|
19
|
+
const element = document.getElementById('blog');
|
|
20
20
|
if (!element) return;
|
|
21
21
|
|
|
22
22
|
element.scrollTop = 0;
|
|
@@ -24,13 +24,13 @@ export function useBlogScrollReset() {
|
|
|
24
24
|
}
|
|
25
25
|
|
|
26
26
|
export function useBlogScroll() {
|
|
27
|
-
const element = document.getElementById(
|
|
27
|
+
const element = document.getElementById('blog');
|
|
28
28
|
|
|
29
29
|
const gotoTop = useCallback(() => {
|
|
30
30
|
if (!element) return;
|
|
31
31
|
|
|
32
32
|
const id = setTimeout(() => {
|
|
33
|
-
element.scrollTo({ top: 0, behavior:
|
|
33
|
+
element.scrollTo({ top: 0, behavior: 'smooth' });
|
|
34
34
|
}, 150);
|
|
35
35
|
|
|
36
36
|
return () => clearTimeout(id);
|
|
@@ -45,7 +45,7 @@ export function useBlogScrollRestoration(debounceMs: number = 100) {
|
|
|
45
45
|
const location = useLocation();
|
|
46
46
|
const isFirstRender = useRef(true);
|
|
47
47
|
const scrollTimeoutRef = useRef<number>();
|
|
48
|
-
const element = document.getElementById(
|
|
48
|
+
const element = document.getElementById('blog');
|
|
49
49
|
const scrollKey = `blog`;
|
|
50
50
|
|
|
51
51
|
// Restore scroll position on mount
|
|
@@ -82,10 +82,10 @@ export function useBlogScrollRestoration(debounceMs: number = 100) {
|
|
|
82
82
|
}, debounceMs);
|
|
83
83
|
};
|
|
84
84
|
|
|
85
|
-
element.addEventListener(
|
|
85
|
+
element.addEventListener('scroll', handleScroll, { passive: true });
|
|
86
86
|
|
|
87
87
|
return () => {
|
|
88
|
-
element.removeEventListener(
|
|
88
|
+
element.removeEventListener('scroll', handleScroll);
|
|
89
89
|
};
|
|
90
90
|
}, []);
|
|
91
91
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { useEffect, useState } from
|
|
2
|
-
import { changeFont } from
|
|
3
|
-
import { FontType } from
|
|
1
|
+
import { useEffect, useState } from 'react';
|
|
2
|
+
import { changeFont } from '@/utils';
|
|
3
|
+
import { FontType } from '@/types/font';
|
|
4
4
|
|
|
5
5
|
export const useFontLoading = () => {
|
|
6
6
|
const [isFontLoading, setIsFontLoading] = useState(false);
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import {useEffect, useRef} from
|
|
1
|
+
import { useEffect, useRef } from 'react';
|
|
2
2
|
|
|
3
3
|
export const useMousePosition = () => {
|
|
4
|
-
const posRef = useRef<{x: number
|
|
4
|
+
const posRef = useRef<{ x: number; y: number }>({ x: 0, y: 0 });
|
|
5
5
|
useEffect(() => {
|
|
6
6
|
const updateMousePosition = (e: MouseEvent) => {
|
|
7
7
|
posRef.current = {
|
|
@@ -9,9 +9,9 @@ export const useMousePosition = () => {
|
|
|
9
9
|
y: e.clientY,
|
|
10
10
|
};
|
|
11
11
|
};
|
|
12
|
-
window.addEventListener(
|
|
13
|
-
return () => window.removeEventListener(
|
|
12
|
+
window.addEventListener('mousemove', updateMousePosition);
|
|
13
|
+
return () => window.removeEventListener('mousemove', updateMousePosition);
|
|
14
14
|
}, []);
|
|
15
15
|
|
|
16
16
|
return posRef;
|
|
17
|
-
}
|
|
17
|
+
};
|
|
@@ -1,25 +1,25 @@
|
|
|
1
|
-
import { pathObj } from
|
|
2
|
-
import { usePathName } from
|
|
1
|
+
import { pathObj } from '@/types/path';
|
|
2
|
+
import { usePathName } from '@/hooks/use_pathname';
|
|
3
3
|
|
|
4
4
|
export const usePaths = (): pathObj[] => {
|
|
5
5
|
const pathname = usePathName();
|
|
6
6
|
|
|
7
|
-
if (pathname ===
|
|
8
|
-
return [{ name:
|
|
7
|
+
if (pathname === '/') {
|
|
8
|
+
return [{ name: '~' }];
|
|
9
9
|
}
|
|
10
10
|
|
|
11
|
-
const pathNameArr = pathname.split(
|
|
11
|
+
const pathNameArr = pathname.split('/');
|
|
12
12
|
|
|
13
13
|
const pathsArr: string[] = pathNameArr.map((p: string, index: number) => {
|
|
14
14
|
return pathNameArr
|
|
15
15
|
.slice(0, index + 1)
|
|
16
|
-
.join(
|
|
17
|
-
.replace(/^$/,
|
|
16
|
+
.join('/')
|
|
17
|
+
.replace(/^$/, '/');
|
|
18
18
|
});
|
|
19
19
|
|
|
20
20
|
return pathsArr.map((path, index) => {
|
|
21
21
|
const isLast = index === pathsArr.length - 1;
|
|
22
|
-
const name = decodeURIComponent(pathNameArr[index].replace(/^$/,
|
|
22
|
+
const name = decodeURIComponent(pathNameArr[index].replace(/^$/, '~'));
|
|
23
23
|
return isLast
|
|
24
24
|
? { name }
|
|
25
25
|
: {
|
package/src/blog/main.css
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
|
-
@import
|
|
2
|
-
@import
|
|
3
|
-
@source "../../node_modules/@bbki.ng/components/";
|
|
1
|
+
@import 'tailwindcss';
|
|
2
|
+
@import '@bbki.ng/ui/styles';
|
|
4
3
|
|
|
5
4
|
/*
|
|
6
5
|
The default border color has changed to `currentcolor` in Tailwind CSS v4,
|
|
@@ -40,7 +39,7 @@
|
|
|
40
39
|
|
|
41
40
|
* {
|
|
42
41
|
-webkit-user-drag: none;
|
|
43
|
-
-webkit-font-smoothing:
|
|
42
|
+
-webkit-font-smoothing: 'antialiased';
|
|
44
43
|
font-weight: 400 !important;
|
|
45
44
|
}
|
|
46
45
|
|
|
@@ -69,7 +68,7 @@ body {
|
|
|
69
68
|
}
|
|
70
69
|
|
|
71
70
|
.noto-serif {
|
|
72
|
-
font-family:
|
|
71
|
+
font-family: 'Menlo', 'Consolas', 'Noto Serif SC', monospace;
|
|
73
72
|
font-weight: 400;
|
|
74
73
|
font-style: normal;
|
|
75
74
|
}
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import { useEffect } from
|
|
2
|
-
import { useSearchParams } from
|
|
1
|
+
import { useEffect } from 'react';
|
|
2
|
+
import { useSearchParams } from 'react-router-dom';
|
|
3
3
|
|
|
4
4
|
export const BotRedirect = () => {
|
|
5
5
|
const [param] = useSearchParams();
|
|
6
6
|
|
|
7
|
-
const code = param.get(
|
|
7
|
+
const code = param.get('code');
|
|
8
8
|
|
|
9
9
|
useEffect(() => {
|
|
10
10
|
window.location.href = `https://t.me/BBKingsBot?start=${code}`;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
-
import { LinkProps, Button } from '@bbki.ng/
|
|
2
|
+
import { LinkProps, Button } from '@bbki.ng/ui';
|
|
3
3
|
import { usePosts } from '@/hooks/use_posts';
|
|
4
4
|
import { CenterLinkList } from '@/components';
|
|
5
5
|
import { useBlogScroll, useBlogScrollRestoration } from '@/hooks/use_blog_scroll_pos_restoration';
|
|
@@ -29,7 +29,7 @@ export default (props: TxtProps) => {
|
|
|
29
29
|
links={props.articleList || titleList}
|
|
30
30
|
loading={isLoading}
|
|
31
31
|
footer={
|
|
32
|
-
<Button onClick={gotoTop} className="mt-
|
|
32
|
+
<Button onClick={gotoTop} className="mt-32">
|
|
33
33
|
<svg
|
|
34
34
|
data-testid="geist-icon"
|
|
35
35
|
height="16"
|
package/src/blog/pages/index.tsx
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export { Cover } from
|
|
2
|
-
export { default as Streaming } from
|
|
1
|
+
export { Cover } from './cover';
|
|
2
|
+
export { default as Streaming } from './streaming';
|
|
@@ -1,26 +1,26 @@
|
|
|
1
|
-
import classNames from
|
|
2
|
-
import React from
|
|
1
|
+
import classNames from 'classnames';
|
|
2
|
+
import React from 'react';
|
|
3
3
|
|
|
4
4
|
export const ArrowDownIcon = ({ show }: { show: boolean }) => (
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
)
|
|
5
|
+
<svg
|
|
6
|
+
data-testid="geist-icon"
|
|
7
|
+
height="16"
|
|
8
|
+
stroke-linejoin="round"
|
|
9
|
+
viewBox="0 0 16 16"
|
|
10
|
+
width="16"
|
|
11
|
+
style={{
|
|
12
|
+
rotate: '180deg',
|
|
13
|
+
}}
|
|
14
|
+
className={classNames('transition-opacity duration-200 inline-block', {
|
|
15
|
+
'opacity-0': !show,
|
|
16
|
+
'opacity-100': show,
|
|
17
|
+
})}
|
|
18
|
+
>
|
|
19
|
+
<path
|
|
20
|
+
fill-rule="evenodd"
|
|
21
|
+
clip-rule="evenodd"
|
|
22
|
+
d="M8.70711 1.39644C8.31659 1.00592 7.68342 1.00592 7.2929 1.39644L2.21968 6.46966L1.68935 6.99999L2.75001 8.06065L3.28034 7.53032L7.25001 3.56065V14.25V15H8.75001V14.25V3.56065L12.7197 7.53032L13.25 8.06065L14.3107 6.99999L13.7803 6.46966L8.70711 1.39644Z"
|
|
23
|
+
fill="currentColor"
|
|
24
|
+
></path>
|
|
25
|
+
</svg>
|
|
26
|
+
);
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import React, { useEffect, useRef, useState } from 'react';
|
|
2
2
|
import { useStreaming, StreamingItem } from '@/hooks/use_streaming';
|
|
3
3
|
import { formatStreamingData } from '@/utils/streaming';
|
|
4
|
-
import {
|
|
4
|
+
import { Button, Panel } from '@bbki.ng/ui';
|
|
5
5
|
import { useScrollBtnVisibility } from './useScrollBtnVisibility';
|
|
6
6
|
import classNames from 'classnames';
|
|
7
7
|
import { ArrowDownIcon } from './arrow-down';
|
|
@@ -62,17 +62,13 @@ const Streaming = () => {
|
|
|
62
62
|
};
|
|
63
63
|
}, [isLoading, formattedData]);
|
|
64
64
|
|
|
65
|
-
if (isError) {
|
|
66
|
-
return <div className="p-8 text-center text-gray-500">加载失败</div>;
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
if (isLoading) {
|
|
65
|
+
if (isLoading || isError) {
|
|
70
66
|
return null;
|
|
71
67
|
}
|
|
72
68
|
|
|
73
69
|
return (
|
|
74
70
|
<>
|
|
75
|
-
<Panel className="
|
|
71
|
+
<Panel className="p-2.5! mt-32">
|
|
76
72
|
<bb-msg-history
|
|
77
73
|
// infinite
|
|
78
74
|
hide-scroll-bar
|
|
@@ -85,7 +81,7 @@ const Streaming = () => {
|
|
|
85
81
|
</Panel>
|
|
86
82
|
{scrolled ? (
|
|
87
83
|
<Button
|
|
88
|
-
className="mt-
|
|
84
|
+
className="mt-32"
|
|
89
85
|
transparent={!showScrollBtn}
|
|
90
86
|
onClick={() => {
|
|
91
87
|
bbMsgHistoryRef.current?.scrollToBottom();
|
|
@@ -1,27 +1,27 @@
|
|
|
1
|
-
import { useEffect, useState } from
|
|
2
|
-
import { BbMsgHistoryElement } from
|
|
1
|
+
import { useEffect, useState } from 'react';
|
|
2
|
+
import { BbMsgHistoryElement } from '.';
|
|
3
3
|
|
|
4
4
|
export const useScrollBtnVisibility = (ele: BbMsgHistoryElement) => {
|
|
5
|
-
|
|
5
|
+
const [visible, setVisible] = useState(false);
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
7
|
+
useEffect(() => {
|
|
8
|
+
if (!ele) {
|
|
9
|
+
return;
|
|
10
|
+
}
|
|
11
11
|
|
|
12
|
-
|
|
13
|
-
|
|
12
|
+
const handleShow = () => setVisible(true);
|
|
13
|
+
const handleHide = () => setVisible(false);
|
|
14
14
|
|
|
15
|
-
|
|
16
|
-
|
|
15
|
+
ele.addEventListener('bb-scrollbuttonshow', handleShow);
|
|
16
|
+
ele.addEventListener('bb-scrollbuttonhide', handleHide);
|
|
17
17
|
|
|
18
|
-
|
|
18
|
+
console.log('add event listeners');
|
|
19
19
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
20
|
+
return () => {
|
|
21
|
+
ele.removeEventListener('bb-scrollbuttonshow', handleShow);
|
|
22
|
+
ele.removeEventListener('bb-scrollbuttonhide', handleHide);
|
|
23
|
+
};
|
|
24
|
+
}, [ele]);
|
|
25
25
|
|
|
26
|
-
|
|
27
|
-
}
|
|
26
|
+
return visible;
|
|
27
|
+
};
|
package/src/blog/types/color.ts
CHANGED
|
@@ -1,21 +1,21 @@
|
|
|
1
1
|
export enum TextColors {
|
|
2
|
-
GRAY =
|
|
3
|
-
RED =
|
|
4
|
-
BLUE =
|
|
2
|
+
GRAY = 'text-gray-400',
|
|
3
|
+
RED = 'text-red-500',
|
|
4
|
+
BLUE = 'text-blue-600',
|
|
5
5
|
}
|
|
6
6
|
|
|
7
7
|
export enum BgColors {
|
|
8
|
-
WHITE_GRAY =
|
|
9
|
-
LIGHT_GRAY =
|
|
10
|
-
GRAY =
|
|
11
|
-
RED =
|
|
12
|
-
BLUE =
|
|
13
|
-
LIGHT_BLUE =
|
|
14
|
-
WHITE_BLUE =
|
|
8
|
+
WHITE_GRAY = 'bg-gray-50',
|
|
9
|
+
LIGHT_GRAY = 'bg-gray-100',
|
|
10
|
+
GRAY = 'bg-gray-400',
|
|
11
|
+
RED = 'bg-red-500',
|
|
12
|
+
BLUE = 'bg-blue-600',
|
|
13
|
+
LIGHT_BLUE = 'bg-blue-300',
|
|
14
|
+
WHITE_BLUE = 'bg-blue-100',
|
|
15
15
|
}
|
|
16
16
|
|
|
17
17
|
export enum HoveredTextColors {
|
|
18
|
-
GRAY =
|
|
19
|
-
RED =
|
|
20
|
-
BLUR =
|
|
18
|
+
GRAY = 'hover:bg-gray-100',
|
|
19
|
+
RED = 'hover:bg-red-100',
|
|
20
|
+
BLUR = 'hover:bg-blue-100',
|
|
21
21
|
}
|
package/src/blog/types/cusdis.ts
CHANGED
package/src/blog/types/font.ts
CHANGED
package/src/blog/types/oss.ts
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
export enum ossProcessType {
|
|
2
|
-
THUMBNAIL =
|
|
3
|
-
WEBP =
|
|
4
|
-
NULL =
|
|
5
|
-
oWEBP =
|
|
6
|
-
PROG =
|
|
2
|
+
THUMBNAIL = 'thumbnail',
|
|
3
|
+
WEBP = 'webp',
|
|
4
|
+
NULL = 'null',
|
|
5
|
+
oWEBP = 'owebp',
|
|
6
|
+
PROG = 'prog',
|
|
7
7
|
}
|
|
8
8
|
|
|
9
9
|
export interface UploadResult {
|
package/src/blog/types/path.ts
CHANGED
package/src/blog/types/photo.ts
CHANGED
|
@@ -1,27 +1,29 @@
|
|
|
1
|
-
import { StreamingItem } from
|
|
1
|
+
import { StreamingItem } from '@/hooks/use_streaming';
|
|
2
2
|
|
|
3
3
|
// Format: "author: content" (with optional timestamp)
|
|
4
4
|
// Timestamp format: YYYY-mm-dd HH:MM:SS
|
|
5
5
|
export const formatStreamingData = (items: StreamingItem[]): string => {
|
|
6
6
|
if (!items || items.length === 0) {
|
|
7
|
-
return
|
|
7
|
+
return '';
|
|
8
8
|
}
|
|
9
9
|
// Reverse to show oldest first (bb-msg-history appends to bottom)
|
|
10
10
|
return [...items]
|
|
11
11
|
.reverse()
|
|
12
|
-
.map(
|
|
12
|
+
.map(item => {
|
|
13
13
|
const time = item.createdAt
|
|
14
|
-
? new Date(item.createdAt)
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
14
|
+
? new Date(item.createdAt)
|
|
15
|
+
.toLocaleString('zh-CN', {
|
|
16
|
+
year: 'numeric',
|
|
17
|
+
month: '2-digit',
|
|
18
|
+
day: '2-digit',
|
|
19
|
+
hour: '2-digit',
|
|
20
|
+
minute: '2-digit',
|
|
21
|
+
second: '2-digit',
|
|
22
|
+
hour12: false,
|
|
23
|
+
})
|
|
24
|
+
.replace(/\//g, '-')
|
|
25
|
+
: '';
|
|
24
26
|
return time ? `[${time}] ${item.author}: ${item.content}` : `${item.author}: ${item.content}`;
|
|
25
27
|
})
|
|
26
|
-
.join(
|
|
28
|
+
.join('\n');
|
|
27
29
|
};
|
package/src/index.tsx
CHANGED
package/tsconfig.json
CHANGED
package/vite.config.js
CHANGED
|
@@ -1,35 +0,0 @@
|
|
|
1
|
-
import React from "react";
|
|
2
|
-
import { FunctionComponent } from "react";
|
|
3
|
-
import { ArticlePage } from "@/components/article";
|
|
4
|
-
import { ThreeColLayout, ErrorBoundary } from "@bbki.ng/components";
|
|
5
|
-
|
|
6
|
-
export const withArticleWrapper =
|
|
7
|
-
(Component: FunctionComponent<any>): FunctionComponent<any> =>
|
|
8
|
-
(props: any) => {
|
|
9
|
-
return (
|
|
10
|
-
<>
|
|
11
|
-
<ArticlePage {...props}>
|
|
12
|
-
<Component />
|
|
13
|
-
</ArticlePage>
|
|
14
|
-
</>
|
|
15
|
-
);
|
|
16
|
-
};
|
|
17
|
-
|
|
18
|
-
export const threeColWrapper =
|
|
19
|
-
<T extends object>(Component: any, catchError: boolean = true) =>
|
|
20
|
-
(props: T) => {
|
|
21
|
-
return (
|
|
22
|
-
<ThreeColLayout
|
|
23
|
-
middleRenderer={() => {
|
|
24
|
-
if (!catchError) {
|
|
25
|
-
return <Component {...props} />;
|
|
26
|
-
}
|
|
27
|
-
return (
|
|
28
|
-
<ErrorBoundary>
|
|
29
|
-
<Component {...props} />
|
|
30
|
-
</ErrorBoundary>
|
|
31
|
-
);
|
|
32
|
-
}}
|
|
33
|
-
/>
|
|
34
|
-
);
|
|
35
|
-
};
|