@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.
Files changed (84) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/index.html +1 -12
  3. package/package.json +1 -1
  4. package/src/app/app.tsx +46 -0
  5. package/src/{blog → app}/components/BaseLayout.tsx +2 -2
  6. package/src/{blog/pages → app/components}/cover/index.tsx +12 -13
  7. package/src/app/constants/index.ts +1 -0
  8. package/src/{blog → app}/context/bbcontext.tsx +2 -2
  9. package/src/{blog/hooks/use_loading.ts → app/hooks/use_global_loading.ts} +1 -1
  10. package/src/{blog → app}/hooks/use_paths.ts +14 -12
  11. package/src/{blog → app}/hooks/use_plugin_entries.ts +1 -3
  12. package/src/{blog → app}/index.tsx +2 -2
  13. package/src/{blog → app}/main.css +3 -3
  14. package/src/{blog → app}/swr.tsx +1 -1
  15. package/src/{blog → app}/utils/index.ts +1 -1
  16. package/src/core/components/SlotComp.tsx +2 -3
  17. package/src/core/hooks/useMiddlewareTransData.ts +12 -7
  18. package/src/core/hooks/useSlotComp.ts +8 -7
  19. package/src/core/hooks/use_plugins.ts +28 -15
  20. package/src/core/{pluginManager.ts → plugin-system/pluginManager.ts} +26 -43
  21. package/src/core/{pluginManifestService.ts → plugin-system/pluginManifestService.ts} +4 -4
  22. package/src/core/plugin-system/pluginStore.ts +190 -0
  23. package/src/core/{registry.ts → plugin-system/registry.ts} +1 -3
  24. package/src/core/plugin-system/services/coreService.ts +56 -0
  25. package/src/core/plugin-system/services/systemUIService.ts +159 -0
  26. package/src/core/shared-service/contract/ICoreService.ts +23 -0
  27. package/src/core/shared-service/contract/IUIService.ts +49 -0
  28. package/src/core/shared-service/service-proxy.ts +26 -0
  29. package/src/core/shared-service/service-registry.ts +52 -0
  30. package/src/index.tsx +3 -3
  31. package/src/plugins/blog/components/BlogSlotCom.tsx +27 -0
  32. package/src/plugins/blog/components/app.tsx +15 -0
  33. package/src/{blog → plugins/blog}/components/article/index.tsx +2 -2
  34. package/src/plugins/blog/constants/index.ts +1 -0
  35. package/src/plugins/blog/context/index.ts +7 -0
  36. package/src/plugins/blog/hooks/useMiddlewareTransData.ts +79 -0
  37. package/src/{blog → plugins/blog}/hooks/use_blog_scroll_pos_restoration.ts +4 -6
  38. package/src/plugins/blog/hooks/use_blog_slot_com.ts +27 -0
  39. package/src/{blog → plugins/blog}/hooks/use_posts.ts +11 -11
  40. package/src/plugins/blog/index.ts +61 -0
  41. package/src/{blog → plugins/blog}/pages/extensions/txt/article.tsx +5 -5
  42. package/src/{blog → plugins/blog}/pages/extensions/txt/index.tsx +6 -3
  43. package/src/plugins/blog/services/BlogUIService.ts +120 -0
  44. package/src/plugins/blog/services/IBlogUIService.ts +31 -0
  45. package/src/plugins/extra-cd/index.ts +13 -2
  46. package/src/plugins/extra-entry/index.ts +8 -4
  47. package/src/plugins/fx/index.ts +18 -5
  48. package/src/plugins/manifest.ts +8 -6
  49. package/src/plugins/now/hooks/use_streaming.ts +3 -1
  50. package/src/plugins/now/index.ts +17 -6
  51. package/src/plugins/sticker/const.ts +2 -2
  52. package/src/plugins/sticker/index.ts +18 -3
  53. package/src/plugins/store/components/storePage.tsx +15 -4
  54. package/src/plugins/store/context/index.ts +1 -0
  55. package/src/plugins/store/index.ts +18 -7
  56. package/src/plugins/xwy/index.ts +24 -19
  57. package/src/plugins/xwy/types/index.ts +0 -18
  58. package/src/types/hostApi.ts +3 -33
  59. package/src/types/plugin.ts +2 -0
  60. package/src/utils/index.tsx +12 -44
  61. package/tsconfig.json +0 -1
  62. package/vite.config.js +1 -1
  63. package/src/blog/app.tsx +0 -52
  64. package/src/blog/constants/index.ts +0 -1
  65. package/src/blog/constants/routes.ts +0 -20
  66. package/src/blog/hooks/index.ts +0 -2
  67. package/src/blog/hooks/use_blog_context.ts +0 -14
  68. package/src/blog/hooks/use_mouse_position.ts +0 -17
  69. package/src/blog/hooks/use_role.ts +0 -14
  70. package/src/blog/hooks/use_sticker.ts +0 -11
  71. package/src/blog/pages/index.tsx +0 -2
  72. package/src/blog/pages/login/index.tsx +0 -24
  73. package/src/blog/types/blog-context.ts +0 -6
  74. package/src/blog/types/font.ts +0 -4
  75. package/src/blog/types/glsl.d.ts +0 -8
  76. package/src/core/pluginStore.ts +0 -70
  77. /package/src/{blog/pages → app/components}/bot/index.tsx +0 -0
  78. /package/src/{blog → app}/context/global_loading_state_provider.tsx +0 -0
  79. /package/src/{blog → app}/context/global_routes_provider.tsx +0 -0
  80. /package/src/{blog → app}/hooks/use_pathname.ts +0 -0
  81. /package/src/{blog → app}/utils/fingerprints.ts +0 -0
  82. /package/src/core/{bbplugin.ts → plugin-system/bbplugin.ts} +0 -0
  83. /package/src/{blog → plugins/blog}/components/index.tsx +0 -0
  84. /package/src/{blog/types → types}/path.ts +0 -0
package/CHANGELOG.md CHANGED
@@ -1,5 +1,17 @@
1
1
  # @bbki.ng/site
2
2
 
3
+ ## 5.8.0
4
+
5
+ ### Minor Changes
6
+
7
+ - c920d33: add service registry
8
+
9
+ ## 5.7.0
10
+
11
+ ### Minor Changes
12
+
13
+ - 58170e5: blog as a plugin
14
+
3
15
  ## 5.6.7
4
16
 
5
17
  ### Patch Changes
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="blog" class="grow no-scrollbar font-mono"></div>
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bbki.ng/site",
3
- "version": "5.6.7",
3
+ "version": "5.8.0",
4
4
  "description": "code behind bbki.ng",
5
5
  "main": "index.js",
6
6
  "type": "module",
@@ -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 '@/hooks';
7
- import { GlobalLoadingContext } from '@/context/global_loading_state_provider';
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, useMemo } from 'react';
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
- export const Cover = (_: { className?: string }) => {
8
- const baseEntries: Array<LinkProps> = useMemo(
9
- () => [
10
- {
11
- to: '/blog',
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
- }, [baseEntries, run]);
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 '@/context/global_loading_state_provider';
4
- import { GlobalRoutesProvider } from '@/context/global_routes_provider';
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 '@/context/global_loading_state_provider';
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 '@/hooks/use_pathname';
5
- import { pathObj } from '@/types/path';
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[] = pathNameArr.map((p: string, index: number) => {
14
- return pathNameArr
15
- .slice(0, index + 1)
16
- .join('/')
17
- .replace(/^$/, '/');
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
- }, [locationPathname]);
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', '/', '/blog', '/blog/:title', 'default']);
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 RenderBlogInto = (ele: Element) => {
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
- #blog {
61
+ #app {
62
62
  height: 100lvh;
63
63
  }
64
64
 
@@ -77,7 +77,7 @@ a {
77
77
  }
78
78
  }
79
79
 
80
- #blog {
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
- #blog::-webkit-scrollbar {
89
+ #app::-webkit-scrollbar {
90
90
  display: none;
91
91
  }
92
92
 
@@ -1,7 +1,7 @@
1
1
  import React, { ReactNode } from 'react';
2
2
  import { SWRConfig } from 'swr';
3
3
 
4
- import { cfApiFetcher } from '@/utils';
4
+ import { cfApiFetcher } from '#/app/utils';
5
5
 
6
6
  export const SWR = (props: { children: ReactNode }) => {
7
7
  return (
@@ -1,4 +1,4 @@
1
- import { API_ENDPOINT } from '@/constants/routes';
1
+ import { API_ENDPOINT } from '#/app/constants';
2
2
 
3
3
  import { getStableDeviceId } from './fingerprints';
4
4
 
@@ -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: SlotName;
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 { registry } from '#/core/registry';
4
- import type { HookPoint } from '#/types/slots';
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: 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 registry.runMiddleware(hookPoint, inputData);
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 = registry.subscribeMiddleware(hookPoint, () => {
45
- onMiddlewareChange?.();
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>(hookPoint: HookPoint, defaultValue: 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, SlotName } from '#/types/slots';
3
+ import { IComPropsRegisteredToSlot } from '#/types/slots';
4
4
 
5
- import { registry } from '../registry';
5
+ import { SystemUIService } from '../plugin-system/services/systemUIService';
6
+ import { SystemSlotName } from '../shared-service/contract/IUIService';
6
7
 
7
- export const useSlotComp = (slotName: SlotName) => {
8
+ export const useSlotComp = (slotName: SystemSlotName) => {
8
9
  const [components, setComponents] = useState<React.ComponentType<IComPropsRegisteredToSlot>[]>(
9
- () => registry.getComponents(slotName)
10
+ () => SystemUIService.getInstance().getComponents(slotName)
10
11
  );
11
12
 
12
13
  useEffect(() => {
13
- setComponents(registry.getComponents(slotName));
14
+ setComponents(SystemUIService.getInstance().getComponents(slotName));
14
15
 
15
- const unsubscribe = registry.subscribe(() => {
16
- const comps = registry.getComponents(slotName);
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 '@/hooks';
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
- pluginManager.notifySiteLoadingChanged(isLoading);
15
+ CoreService.getInstance().setSiteLoading(isLoading);
15
16
  }, [isLoading]);
16
17
 
17
18
  useEffect(() => {
18
19
  let unregister: (() => void) | undefined;
19
- const unscribe = pluginManager.subscribePluginLoading(payload => {
20
+ const unsubscribe = CoreService.getInstance().subscribePluginLoading(payload => {
20
21
  unregister = setGlobalLoading(payload.id, payload.loading);
21
22
  });
23
+
22
24
  return () => {
23
- unscribe();
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
- Promise.all(pluginIds.map(id => pluginManager.loadPlugin(id)))
60
- .then(() => {
61
+ const loadNext = (index: number) => {
62
+ if (cancelled || index >= pluginIds.length) {
61
63
  if (!cancelled) {
62
64
  setDone(true);
63
65
  }
64
- })
65
- .catch(error => {
66
- if (!cancelled) {
67
- console.error('Error loading plugins:', error);
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 { registry } from './registry';
10
- import { createEventBus } from './utils/eventBus';
11
- import { AdminPluginIDSet } from './const';
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('../plugins/*/index.ts');
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
- api: {
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 = `../plugins/${pluginId}/index.ts`;
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
- registry.unregisterAllByPluginId(plugin.id);
124
+ SystemUIService.getInstance().unregisterAllByPluginId(plugin.id);
125
+ CoreService.getInstance().notifyPluginUninstall(plugin.id);
143
126
  }
144
127
  }
145
128
 
146
- export const pluginManager = new PluginManager();
129
+ export const pluginManager = PluginManager.getInstance();
@@ -1,4 +1,4 @@
1
- import { cfApiFetcher } from '@/utils';
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?: string;
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
- const parsed = JSON.parse(item.dependencies) as string[];
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}`