@bbki.ng/site 5.6.7 → 5.8.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.
- package/CHANGELOG.md +12 -0
- package/index.html +1 -12
- package/package.json +1 -1
- package/src/app/app.tsx +46 -0
- package/src/{blog → app}/components/BaseLayout.tsx +2 -2
- package/src/{blog/pages → app/components}/cover/index.tsx +12 -13
- package/src/app/constants/index.ts +1 -0
- package/src/{blog → app}/context/bbcontext.tsx +2 -2
- package/src/{blog/hooks/use_loading.ts → app/hooks/use_global_loading.ts} +1 -1
- package/src/{blog → app}/hooks/use_paths.ts +14 -12
- package/src/{blog → app}/hooks/use_plugin_entries.ts +1 -3
- package/src/{blog → app}/index.tsx +2 -2
- package/src/{blog → app}/main.css +3 -3
- package/src/{blog → app}/swr.tsx +1 -1
- package/src/{blog → app}/utils/index.ts +1 -1
- package/src/core/components/SlotComp.tsx +2 -3
- package/src/core/hooks/useMiddlewareTransData.ts +12 -7
- package/src/core/hooks/useSlotComp.ts +8 -7
- package/src/core/hooks/use_plugins.ts +28 -15
- package/src/core/{pluginManager.ts → plugin-system/pluginManager.ts} +26 -43
- package/src/core/{pluginManifestService.ts → plugin-system/pluginManifestService.ts} +4 -4
- package/src/core/plugin-system/pluginStore.ts +190 -0
- package/src/core/{registry.ts → plugin-system/registry.ts} +1 -3
- package/src/core/plugin-system/services/coreService.ts +56 -0
- package/src/core/plugin-system/services/systemUIService.ts +159 -0
- package/src/core/shared-service/contract/ICoreService.ts +23 -0
- package/src/core/shared-service/contract/IUIService.ts +49 -0
- package/src/core/shared-service/service-proxy.ts +26 -0
- package/src/core/shared-service/service-registry.ts +52 -0
- package/src/index.tsx +3 -3
- package/src/plugins/blog/components/BlogSlotCom.tsx +27 -0
- package/src/plugins/blog/components/app.tsx +15 -0
- package/src/{blog → plugins/blog}/components/article/index.tsx +2 -2
- package/src/plugins/blog/constants/index.ts +1 -0
- package/src/plugins/blog/context/index.ts +7 -0
- package/src/plugins/blog/hooks/useMiddlewareTransData.ts +79 -0
- package/src/{blog → plugins/blog}/hooks/use_blog_scroll_pos_restoration.ts +4 -6
- package/src/plugins/blog/hooks/use_blog_slot_com.ts +27 -0
- package/src/{blog → plugins/blog}/hooks/use_posts.ts +11 -11
- package/src/plugins/blog/index.ts +61 -0
- package/src/{blog → plugins/blog}/pages/extensions/txt/article.tsx +5 -5
- package/src/{blog → plugins/blog}/pages/extensions/txt/index.tsx +6 -3
- package/src/plugins/blog/services/BlogUIService.ts +120 -0
- package/src/plugins/blog/services/IBlogUIService.ts +31 -0
- package/src/plugins/extra-cd/index.ts +13 -2
- package/src/plugins/extra-entry/index.ts +8 -4
- package/src/plugins/fx/index.ts +18 -5
- package/src/plugins/manifest.ts +8 -6
- package/src/plugins/now/hooks/use_streaming.ts +3 -1
- package/src/plugins/now/index.ts +17 -6
- package/src/plugins/sticker/const.ts +2 -2
- package/src/plugins/sticker/index.ts +18 -3
- package/src/plugins/store/components/storePage.tsx +15 -4
- package/src/plugins/store/context/index.ts +1 -0
- package/src/plugins/store/index.ts +18 -7
- package/src/plugins/xwy/index.ts +24 -19
- package/src/plugins/xwy/types/index.ts +0 -18
- package/src/types/hostApi.ts +3 -33
- package/src/types/plugin.ts +2 -0
- package/src/utils/index.tsx +12 -44
- package/tsconfig.json +0 -1
- package/vite.config.js +1 -1
- package/src/blog/app.tsx +0 -52
- package/src/blog/constants/index.ts +0 -1
- package/src/blog/constants/routes.ts +0 -20
- package/src/blog/hooks/index.ts +0 -2
- package/src/blog/hooks/use_blog_context.ts +0 -14
- package/src/blog/hooks/use_mouse_position.ts +0 -17
- package/src/blog/hooks/use_role.ts +0 -14
- package/src/blog/hooks/use_sticker.ts +0 -11
- package/src/blog/pages/index.tsx +0 -2
- package/src/blog/pages/login/index.tsx +0 -24
- package/src/blog/types/blog-context.ts +0 -6
- package/src/blog/types/font.ts +0 -4
- package/src/blog/types/glsl.d.ts +0 -8
- package/src/core/pluginStore.ts +0 -70
- /package/src/{blog/pages → app/components}/bot/index.tsx +0 -0
- /package/src/{blog → app}/context/global_loading_state_provider.tsx +0 -0
- /package/src/{blog → app}/context/global_routes_provider.tsx +0 -0
- /package/src/{blog → app}/hooks/use_pathname.ts +0 -0
- /package/src/{blog → app}/utils/fingerprints.ts +0 -0
- /package/src/core/{bbplugin.ts → plugin-system/bbplugin.ts} +0 -0
- /package/src/{blog → plugins/blog}/components/index.tsx +0 -0
- /package/src/{blog/types → types}/path.ts +0 -0
package/CHANGELOG.md
CHANGED
package/index.html
CHANGED
|
@@ -13,17 +13,6 @@
|
|
|
13
13
|
<link rel="icon" type="image/png" sizes="16x16" href="/favicon-16x16.png" />
|
|
14
14
|
<link rel="mask-icon" href="/favicon.svg" color="#FFFFFF" />
|
|
15
15
|
<meta name="theme-color" content="#ffffff" />
|
|
16
|
-
<link rel="preconnect" href="https://fonts.googleapis.com" />
|
|
17
|
-
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
|
|
18
|
-
<!-- <link
|
|
19
|
-
href="/google-font.css"
|
|
20
|
-
rel="stylesheet"
|
|
21
|
-
media="print"
|
|
22
|
-
onload="
|
|
23
|
-
this.media = 'all';
|
|
24
|
-
this.onload = null;
|
|
25
|
-
"
|
|
26
|
-
/> -->
|
|
27
16
|
<style>
|
|
28
17
|
bb-img:not(:defined) {
|
|
29
18
|
display: block;
|
|
@@ -45,7 +34,7 @@
|
|
|
45
34
|
</head>
|
|
46
35
|
|
|
47
36
|
<body class="h-full m-0 flex flex-col">
|
|
48
|
-
<div id="
|
|
37
|
+
<div id="app" class="grow no-scrollbar font-mono"></div>
|
|
49
38
|
<script type="module" src="/src/index.tsx"></script>
|
|
50
39
|
</body>
|
|
51
40
|
</html>
|
package/package.json
CHANGED
package/src/app/app.tsx
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { Route, Routes } from 'react-router-dom';
|
|
3
|
+
|
|
4
|
+
import { BotRedirect } from '#/app/components/bot';
|
|
5
|
+
import { BBContext } from '#/app/context/bbcontext';
|
|
6
|
+
import { Slot } from '#/core/components/SlotComp';
|
|
7
|
+
import { usePlugins } from '#/core/hooks/use_plugins';
|
|
8
|
+
import { Cover } from '#/app/components/cover';
|
|
9
|
+
import { SWR } from '#/app/swr';
|
|
10
|
+
import { usePluginEntries } from '#/app/hooks/use_plugin_entries';
|
|
11
|
+
import { BaseLayout } from '#/app/components/BaseLayout';
|
|
12
|
+
|
|
13
|
+
const AppRoutes = () => {
|
|
14
|
+
usePlugins();
|
|
15
|
+
|
|
16
|
+
const pluginEntries = usePluginEntries();
|
|
17
|
+
|
|
18
|
+
return (
|
|
19
|
+
<Routes>
|
|
20
|
+
<Route path="/" element={<BaseLayout />}>
|
|
21
|
+
<Route index element={<Cover />} />
|
|
22
|
+
<Route path="bot" element={<BotRedirect />} />
|
|
23
|
+
{pluginEntries?.map(route => (
|
|
24
|
+
<Route
|
|
25
|
+
key={route.path}
|
|
26
|
+
path={`${route.path}/*`}
|
|
27
|
+
element={<Slot name="route" data={route} />}
|
|
28
|
+
/>
|
|
29
|
+
))}
|
|
30
|
+
<Route path="*" element={null} />
|
|
31
|
+
</Route>
|
|
32
|
+
</Routes>
|
|
33
|
+
);
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
export const App = () => {
|
|
37
|
+
return (
|
|
38
|
+
<SWR>
|
|
39
|
+
<BBContext>
|
|
40
|
+
<AppRoutes />
|
|
41
|
+
</BBContext>
|
|
42
|
+
</SWR>
|
|
43
|
+
);
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
export default App;
|
|
@@ -3,8 +3,8 @@ import { useNavigate, Outlet } from 'react-router-dom';
|
|
|
3
3
|
import { Logo, Nav, Page, Grid, ErrorBoundary, Container } from '@bbki.ng/ui';
|
|
4
4
|
|
|
5
5
|
import { Slot } from '#/core/components/SlotComp';
|
|
6
|
-
import { usePaths } from '
|
|
7
|
-
import { GlobalLoadingContext } from '
|
|
6
|
+
import { usePaths } from '#/app/hooks/use_paths';
|
|
7
|
+
import { GlobalLoadingContext } from '#/app/context/global_loading_state_provider';
|
|
8
8
|
import { useMiddlewareTransformedData } from '#/core/hooks/useMiddlewareTransData';
|
|
9
9
|
|
|
10
10
|
export const BaseLayout = () => {
|
|
@@ -1,20 +1,19 @@
|
|
|
1
|
-
import React, { useEffect
|
|
2
|
-
import { LinkProps } from '@bbki.ng/ui';
|
|
1
|
+
import React, { useEffect } from 'react';
|
|
2
|
+
import { LinkProps, LinkListProps, LinkList } from '@bbki.ng/ui';
|
|
3
3
|
|
|
4
|
-
import { CenterLinkList } from '@/components';
|
|
5
4
|
import { useMiddlewareRunner } from '#/core/hooks';
|
|
6
5
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
{
|
|
11
|
-
|
|
12
|
-
children: 'cd ./blog',
|
|
13
|
-
},
|
|
14
|
-
],
|
|
15
|
-
[]
|
|
6
|
+
const CenterLinkList = (props: LinkListProps) => {
|
|
7
|
+
return (
|
|
8
|
+
<div className="flex justify-center relative h-full">
|
|
9
|
+
<LinkList {...props} />
|
|
10
|
+
</div>
|
|
16
11
|
);
|
|
12
|
+
};
|
|
17
13
|
|
|
14
|
+
const baseEntries: Array<LinkProps> = [];
|
|
15
|
+
|
|
16
|
+
export const Cover = (_: { className?: string }) => {
|
|
18
17
|
const [entries, setEntries] = React.useState<Array<LinkProps>>(baseEntries);
|
|
19
18
|
|
|
20
19
|
const { run } = useMiddlewareRunner<Array<LinkProps>>({
|
|
@@ -26,7 +25,7 @@ export const Cover = (_: { className?: string }) => {
|
|
|
26
25
|
|
|
27
26
|
useEffect(() => {
|
|
28
27
|
run(baseEntries).then(setEntries);
|
|
29
|
-
}, [
|
|
28
|
+
}, [run]);
|
|
30
29
|
|
|
31
30
|
return (
|
|
32
31
|
<>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export const API_ENDPOINT = 'https://cf.bbki.ng';
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import React, { ReactNode } from 'react';
|
|
2
2
|
|
|
3
|
-
import { GlobalLoadingStateProvider } from '
|
|
4
|
-
import { GlobalRoutesProvider } from '
|
|
3
|
+
import { GlobalLoadingStateProvider } from '#/app/context/global_loading_state_provider';
|
|
4
|
+
import { GlobalRoutesProvider } from '#/app/context/global_routes_provider';
|
|
5
5
|
|
|
6
6
|
export const BBContext = (props: { children: ReactNode }) => {
|
|
7
7
|
return (
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { useCallback, useContext, useEffect } from 'react';
|
|
2
2
|
|
|
3
|
-
import { GlobalLoadingContext } from '
|
|
3
|
+
import { GlobalLoadingContext } from '#/app/context/global_loading_state_provider';
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
6
|
* Hook to register a loading state with the global loading context.
|
|
@@ -1,21 +1,23 @@
|
|
|
1
|
-
import { useLocation } from 'react-router-dom';
|
|
2
1
|
import { useMemo } from 'react';
|
|
3
2
|
|
|
4
|
-
import { usePathName } from '
|
|
5
|
-
import { pathObj } from '
|
|
3
|
+
import { usePathName } from '#/app/hooks/use_pathname';
|
|
4
|
+
import { pathObj } from '#/types/path';
|
|
6
5
|
|
|
7
6
|
export const usePaths = (): pathObj[] => {
|
|
8
7
|
const pathname = usePathName();
|
|
9
|
-
const { pathname: locationPathname } = useLocation();
|
|
10
8
|
|
|
11
|
-
const pathNameArr = pathname.split('/');
|
|
9
|
+
const pathNameArr = useMemo(() => pathname.split('/'), [pathname]);
|
|
12
10
|
|
|
13
|
-
const pathsArr: string[] =
|
|
14
|
-
|
|
15
|
-
.
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
11
|
+
const pathsArr: string[] = useMemo(
|
|
12
|
+
() =>
|
|
13
|
+
pathNameArr.map((p: string, index: number) => {
|
|
14
|
+
return pathNameArr
|
|
15
|
+
.slice(0, index + 1)
|
|
16
|
+
.join('/')
|
|
17
|
+
.replace(/^$/, '/');
|
|
18
|
+
}),
|
|
19
|
+
[pathNameArr]
|
|
20
|
+
);
|
|
19
21
|
|
|
20
22
|
const result = useMemo(() => {
|
|
21
23
|
return pathsArr.map((path, index) => {
|
|
@@ -28,7 +30,7 @@ export const usePaths = (): pathObj[] => {
|
|
|
28
30
|
path,
|
|
29
31
|
};
|
|
30
32
|
});
|
|
31
|
-
}, [
|
|
33
|
+
}, [pathNameArr, pathsArr]);
|
|
32
34
|
|
|
33
35
|
if (pathname === '/') {
|
|
34
36
|
return [{ name: '~' }];
|
|
@@ -3,7 +3,7 @@ import { type PathRouteProps } from 'react-router-dom';
|
|
|
3
3
|
|
|
4
4
|
import { useMiddlewareRunner } from '#/core/hooks';
|
|
5
5
|
|
|
6
|
-
const protectedRoutesSet = new Set<string>(['/bot', '/', '
|
|
6
|
+
const protectedRoutesSet = new Set<string>(['/bot', '/', 'default']);
|
|
7
7
|
|
|
8
8
|
type PluginRoute = Omit<PathRouteProps, 'element'>;
|
|
9
9
|
|
|
@@ -35,7 +35,5 @@ export const usePluginEntries = () => {
|
|
|
35
35
|
return false;
|
|
36
36
|
}) as Array<PluginRoute>;
|
|
37
37
|
|
|
38
|
-
console.log('safe plugin routes:', safeRoutes);
|
|
39
|
-
|
|
40
38
|
return safeRoutes ?? [];
|
|
41
39
|
};
|
|
@@ -2,10 +2,10 @@ import React from 'react';
|
|
|
2
2
|
import { createRoot } from 'react-dom/client';
|
|
3
3
|
import { BrowserRouter as Router } from 'react-router-dom';
|
|
4
4
|
|
|
5
|
-
import App from './app';
|
|
5
|
+
import { App } from './app';
|
|
6
6
|
import './main.css';
|
|
7
7
|
|
|
8
|
-
export const
|
|
8
|
+
export const RenderApp = (ele: Element) => {
|
|
9
9
|
const root = createRoot(ele);
|
|
10
10
|
root.render(
|
|
11
11
|
<React.StrictMode>
|
|
@@ -58,7 +58,7 @@ body {
|
|
|
58
58
|
}
|
|
59
59
|
|
|
60
60
|
body,
|
|
61
|
-
#
|
|
61
|
+
#app {
|
|
62
62
|
height: 100lvh;
|
|
63
63
|
}
|
|
64
64
|
|
|
@@ -77,7 +77,7 @@ a {
|
|
|
77
77
|
}
|
|
78
78
|
}
|
|
79
79
|
|
|
80
|
-
#
|
|
80
|
+
#app {
|
|
81
81
|
overflow: auto;
|
|
82
82
|
-ms-overflow-style: none; /* Internet Explorer 10+ */
|
|
83
83
|
scrollbar-width: none;
|
|
@@ -86,7 +86,7 @@ a {
|
|
|
86
86
|
box-sizing: border-box;
|
|
87
87
|
}
|
|
88
88
|
|
|
89
|
-
#
|
|
89
|
+
#app::-webkit-scrollbar {
|
|
90
90
|
display: none;
|
|
91
91
|
}
|
|
92
92
|
|
package/src/{blog → app}/swr.tsx
RENAMED
|
@@ -1,11 +1,10 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
|
|
3
|
-
import { SlotName } from '#/types/slots';
|
|
4
|
-
|
|
5
3
|
import { useSlotComp } from '../hooks/useSlotComp';
|
|
4
|
+
import { SystemSlotName } from '../shared-service/contract/IUIService';
|
|
6
5
|
|
|
7
6
|
export interface ISlotProps {
|
|
8
|
-
name:
|
|
7
|
+
name: SystemSlotName;
|
|
9
8
|
data?: unknown;
|
|
10
9
|
|
|
11
10
|
placeholder?: React.ReactNode;
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { useState, useEffect, useCallback, useRef } from 'react';
|
|
2
2
|
|
|
3
|
-
import {
|
|
4
|
-
import
|
|
3
|
+
import { SystemUIService } from '../plugin-system/services/systemUIService';
|
|
4
|
+
import { SystemDataHookPoint } from '../shared-service/contract/IUIService';
|
|
5
5
|
|
|
6
6
|
export interface UseMiddlewareTransDataOptions {
|
|
7
|
-
hookPoint:
|
|
7
|
+
hookPoint: SystemDataHookPoint;
|
|
8
8
|
onMiddlewareChange?: () => void; // 通知外部,由外部决定是否 run
|
|
9
9
|
}
|
|
10
10
|
|
|
@@ -26,7 +26,7 @@ export function useMiddlewareRunner<T>({
|
|
|
26
26
|
setLoading(true);
|
|
27
27
|
setError(null);
|
|
28
28
|
try {
|
|
29
|
-
const result = await
|
|
29
|
+
const result = await SystemUIService.getInstance().runMiddlewares(hookPoint, inputData);
|
|
30
30
|
return result;
|
|
31
31
|
} catch (err) {
|
|
32
32
|
const error = err instanceof Error ? err : new Error(String(err));
|
|
@@ -41,8 +41,10 @@ export function useMiddlewareRunner<T>({
|
|
|
41
41
|
|
|
42
42
|
// 仅通知,不执行
|
|
43
43
|
useEffect(() => {
|
|
44
|
-
const unsubscribe =
|
|
45
|
-
|
|
44
|
+
const unsubscribe = SystemUIService.getInstance().subscribeMiddlewareChange(hook => {
|
|
45
|
+
if (hook === hookPoint) {
|
|
46
|
+
onMiddlewareChange?.();
|
|
47
|
+
}
|
|
46
48
|
});
|
|
47
49
|
|
|
48
50
|
return () => {
|
|
@@ -53,7 +55,10 @@ export function useMiddlewareRunner<T>({
|
|
|
53
55
|
return { loading, error, run };
|
|
54
56
|
}
|
|
55
57
|
|
|
56
|
-
export const useMiddlewareTransformedData = <T>(
|
|
58
|
+
export const useMiddlewareTransformedData = <T>(
|
|
59
|
+
hookPoint: SystemDataHookPoint,
|
|
60
|
+
defaultValue: T
|
|
61
|
+
) => {
|
|
57
62
|
const [result, setResult] = useState<T>(defaultValue);
|
|
58
63
|
|
|
59
64
|
const runRef = useRef<(input: T) => Promise<T>>(() => Promise.resolve(defaultValue));
|
|
@@ -1,19 +1,20 @@
|
|
|
1
1
|
import React, { useEffect, useState } from 'react';
|
|
2
2
|
|
|
3
|
-
import { IComPropsRegisteredToSlot
|
|
3
|
+
import { IComPropsRegisteredToSlot } from '#/types/slots';
|
|
4
4
|
|
|
5
|
-
import {
|
|
5
|
+
import { SystemUIService } from '../plugin-system/services/systemUIService';
|
|
6
|
+
import { SystemSlotName } from '../shared-service/contract/IUIService';
|
|
6
7
|
|
|
7
|
-
export const useSlotComp = (slotName:
|
|
8
|
+
export const useSlotComp = (slotName: SystemSlotName) => {
|
|
8
9
|
const [components, setComponents] = useState<React.ComponentType<IComPropsRegisteredToSlot>[]>(
|
|
9
|
-
() =>
|
|
10
|
+
() => SystemUIService.getInstance().getComponents(slotName)
|
|
10
11
|
);
|
|
11
12
|
|
|
12
13
|
useEffect(() => {
|
|
13
|
-
setComponents(
|
|
14
|
+
setComponents(SystemUIService.getInstance().getComponents(slotName));
|
|
14
15
|
|
|
15
|
-
const unsubscribe =
|
|
16
|
-
const comps =
|
|
16
|
+
const unsubscribe = SystemUIService.getInstance().subscribeSlotChange(() => {
|
|
17
|
+
const comps = SystemUIService.getInstance().getComponents(slotName);
|
|
17
18
|
setComponents(comps);
|
|
18
19
|
});
|
|
19
20
|
|
|
@@ -1,26 +1,28 @@
|
|
|
1
1
|
import { useEffect, useMemo, useState } from 'react';
|
|
2
2
|
|
|
3
|
-
import { useGlobalLoading } from '
|
|
4
|
-
import { pluginManager } from '#/core/pluginManager';
|
|
3
|
+
import { useGlobalLoading } from '#/app/hooks/use_global_loading';
|
|
4
|
+
import { pluginManager } from '#/core/plugin-system/pluginManager';
|
|
5
5
|
import { PluginID } from '#/types/plugin';
|
|
6
6
|
|
|
7
|
-
import { PluginManifestService } from '../pluginManifestService';
|
|
8
|
-
import { PluginStore } from '../pluginStore';
|
|
7
|
+
import { PluginManifestService } from '../plugin-system/pluginManifestService';
|
|
8
|
+
import { PluginStore } from '../plugin-system/pluginStore';
|
|
9
|
+
import { CoreService } from '../plugin-system/services/coreService';
|
|
9
10
|
|
|
10
11
|
const usePluginsLoading = () => {
|
|
11
12
|
const { setGlobalLoading, isLoading } = useGlobalLoading();
|
|
12
13
|
|
|
13
14
|
useEffect(() => {
|
|
14
|
-
|
|
15
|
+
CoreService.getInstance().setSiteLoading(isLoading);
|
|
15
16
|
}, [isLoading]);
|
|
16
17
|
|
|
17
18
|
useEffect(() => {
|
|
18
19
|
let unregister: (() => void) | undefined;
|
|
19
|
-
const
|
|
20
|
+
const unsubscribe = CoreService.getInstance().subscribePluginLoading(payload => {
|
|
20
21
|
unregister = setGlobalLoading(payload.id, payload.loading);
|
|
21
22
|
});
|
|
23
|
+
|
|
22
24
|
return () => {
|
|
23
|
-
|
|
25
|
+
unsubscribe();
|
|
24
26
|
unregister?.();
|
|
25
27
|
};
|
|
26
28
|
}, [setGlobalLoading]);
|
|
@@ -56,17 +58,28 @@ export const usePlugins = () => {
|
|
|
56
58
|
if (cancelled) {
|
|
57
59
|
return;
|
|
58
60
|
}
|
|
59
|
-
|
|
60
|
-
.
|
|
61
|
+
const loadNext = (index: number) => {
|
|
62
|
+
if (cancelled || index >= pluginIds.length) {
|
|
61
63
|
if (!cancelled) {
|
|
62
64
|
setDone(true);
|
|
63
65
|
}
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
66
|
+
return;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
const id = pluginIds[index];
|
|
70
|
+
pluginManager
|
|
71
|
+
.loadPlugin(id)
|
|
72
|
+
.then(() => loadNext(index + 1))
|
|
73
|
+
.catch(error => {
|
|
74
|
+
if (!cancelled) {
|
|
75
|
+
console.error(`Error loading plugin ${id}:`, error);
|
|
76
|
+
// 即使某个插件加载失败,也继续加载下一个
|
|
77
|
+
loadNext(index + 1);
|
|
78
|
+
}
|
|
79
|
+
});
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
loadNext(0);
|
|
70
83
|
})
|
|
71
84
|
.catch(error => {
|
|
72
85
|
if (!cancelled) {
|
|
@@ -1,23 +1,28 @@
|
|
|
1
|
-
import React from 'react';
|
|
2
|
-
|
|
3
1
|
import { IHostContext } from '#/types/hostApi';
|
|
4
|
-
import type { SlotName, HookPoint, IComPropsRegisteredToSlot } from '#/types/slots';
|
|
5
2
|
import { IPlugin, PluginEvents, PluginID, PluginPerm } from '#/types/plugin';
|
|
6
|
-
import { getStableDeviceId } from '@/utils/fingerprints';
|
|
7
|
-
import { cfApiFetcher } from '@/utils';
|
|
8
3
|
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
11
|
-
import {
|
|
4
|
+
import { createEventBus } from '../utils/eventBus';
|
|
5
|
+
import { AdminPluginIDSet } from '../const';
|
|
6
|
+
import { ServiceRegistry } from '../shared-service/service-registry';
|
|
7
|
+
|
|
12
8
|
import { PluginStore } from './pluginStore';
|
|
9
|
+
import { CoreService } from './services/coreService';
|
|
10
|
+
import { SystemUIService } from './services/systemUIService';
|
|
13
11
|
|
|
14
|
-
const pluginModules = import.meta.glob('
|
|
12
|
+
const pluginModules = import.meta.glob('../../plugins/*/index.ts');
|
|
15
13
|
|
|
16
14
|
class PluginManager {
|
|
17
15
|
private activePlugins: Map<string, IPlugin> = new Map();
|
|
18
16
|
|
|
19
17
|
private bus = createEventBus<PluginEvents>();
|
|
20
18
|
|
|
19
|
+
private constructor() {
|
|
20
|
+
ServiceRegistry.getInstance().register('core:baseService', CoreService.getInstance());
|
|
21
|
+
ServiceRegistry.getInstance().register('core:uiService', SystemUIService.getInstance());
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
private static instance: PluginManager;
|
|
25
|
+
|
|
21
26
|
private createHostContext(perm: PluginPerm = 'guest'): IHostContext {
|
|
22
27
|
const adminCtx =
|
|
23
28
|
perm === 'admin'
|
|
@@ -27,42 +32,19 @@ class PluginManager {
|
|
|
27
32
|
: {};
|
|
28
33
|
return {
|
|
29
34
|
...adminCtx,
|
|
30
|
-
|
|
31
|
-
fetch: cfApiFetcher,
|
|
32
|
-
setLoading: (id: PluginID, loading: boolean) => {
|
|
33
|
-
this.bus.emit('plugin:loading:changed', { id, loading });
|
|
34
|
-
},
|
|
35
|
-
onLoadingChanged: (listener: (payload: PluginEvents['site:loading:changed']) => void) => {
|
|
36
|
-
return this.bus.on('site:loading:changed', listener);
|
|
37
|
-
},
|
|
38
|
-
registerMiddleware: <T>(
|
|
39
|
-
point: HookPoint,
|
|
40
|
-
fn: (data: T) => Promise<T> | T,
|
|
41
|
-
pluginId: string,
|
|
42
|
-
weight = 0
|
|
43
|
-
) => {
|
|
44
|
-
registry.registerMiddleware(point, fn, pluginId, weight);
|
|
45
|
-
},
|
|
46
|
-
getDeviceId: getStableDeviceId,
|
|
47
|
-
getVersionHash: () => {
|
|
48
|
-
const hashStr: string =
|
|
49
|
-
typeof GLOBAL_COMMIT_HASH === 'string' ? GLOBAL_COMMIT_HASH : '0000000';
|
|
50
|
-
return hashStr;
|
|
51
|
-
},
|
|
52
|
-
registerSlot: (
|
|
53
|
-
slotName: SlotName,
|
|
54
|
-
component: React.ComponentType<IComPropsRegisteredToSlot>,
|
|
55
|
-
pluginId: string,
|
|
56
|
-
weight = 0
|
|
57
|
-
) => {
|
|
58
|
-
registry.registerComponent(slotName, component, pluginId, weight);
|
|
59
|
-
},
|
|
60
|
-
},
|
|
35
|
+
service: ServiceRegistry.getInstance(),
|
|
61
36
|
};
|
|
62
37
|
}
|
|
63
38
|
|
|
64
39
|
private loading = new Set<string>();
|
|
65
40
|
|
|
41
|
+
static getInstance() {
|
|
42
|
+
if (!PluginManager.instance) {
|
|
43
|
+
PluginManager.instance = new PluginManager();
|
|
44
|
+
}
|
|
45
|
+
return PluginManager.instance;
|
|
46
|
+
}
|
|
47
|
+
|
|
66
48
|
notifySiteLoadingChanged(loading: boolean) {
|
|
67
49
|
this.bus.emit('site:loading:changed', loading);
|
|
68
50
|
}
|
|
@@ -87,7 +69,7 @@ class PluginManager {
|
|
|
87
69
|
this.loading.add(pluginId);
|
|
88
70
|
this.bus.emit('plugin:loading:changed', { id: pluginId, loading: true });
|
|
89
71
|
|
|
90
|
-
const modulePath =
|
|
72
|
+
const modulePath = `../../plugins/${pluginId}/index.ts`;
|
|
91
73
|
const moduleLoader = pluginModules[modulePath];
|
|
92
74
|
if (!moduleLoader) {
|
|
93
75
|
console.error(`Plugin ${pluginId} not found in registry`);
|
|
@@ -139,8 +121,9 @@ class PluginManager {
|
|
|
139
121
|
|
|
140
122
|
this.activePlugins.delete(pluginId);
|
|
141
123
|
|
|
142
|
-
|
|
124
|
+
SystemUIService.getInstance().unregisterAllByPluginId(plugin.id);
|
|
125
|
+
CoreService.getInstance().notifyPluginUninstall(plugin.id);
|
|
143
126
|
}
|
|
144
127
|
}
|
|
145
128
|
|
|
146
|
-
export const pluginManager =
|
|
129
|
+
export const pluginManager = PluginManager.getInstance();
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { cfApiFetcher } from '
|
|
1
|
+
import { cfApiFetcher } from '#/app/utils';
|
|
2
2
|
import { FALLBACK_MANIFEST } from '#/plugins/manifest';
|
|
3
3
|
import { IPluginManifestEntry, PluginID } from '#/types/plugin';
|
|
4
4
|
|
|
@@ -11,6 +11,7 @@ const KnownPluginIDSet = new Set<string>([
|
|
|
11
11
|
'now',
|
|
12
12
|
'default',
|
|
13
13
|
'fx',
|
|
14
|
+
'blog',
|
|
14
15
|
]);
|
|
15
16
|
|
|
16
17
|
interface PluginsApiResponse {
|
|
@@ -22,7 +23,7 @@ interface PluginsApiResponse {
|
|
|
22
23
|
description?: string;
|
|
23
24
|
perm?: string;
|
|
24
25
|
icon?: string;
|
|
25
|
-
dependencies?:
|
|
26
|
+
dependencies?: Array<PluginID>;
|
|
26
27
|
}>;
|
|
27
28
|
}
|
|
28
29
|
|
|
@@ -65,8 +66,7 @@ class PluginManifestService {
|
|
|
65
66
|
let dependencies: PluginID[] | undefined;
|
|
66
67
|
if (item.dependencies) {
|
|
67
68
|
try {
|
|
68
|
-
|
|
69
|
-
dependencies = parsed.filter((id): id is PluginID => KnownPluginIDSet.has(id));
|
|
69
|
+
dependencies = item.dependencies.filter(p => KnownPluginIDSet.has(p));
|
|
70
70
|
} catch {
|
|
71
71
|
console.warn(
|
|
72
72
|
`[PluginManifestService] Failed to parse dependencies for plugin: ${item.id}`
|