@bbki.ng/site 5.8.11 → 6.0.1

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 (93) hide show
  1. package/CHANGELOG.md +30 -0
  2. package/package.json +6 -3
  3. package/src/app/app.tsx +8 -7
  4. package/src/app/components/BaseLayout.tsx +6 -4
  5. package/src/app/components/cover/index.tsx +9 -5
  6. package/src/app/context/bbcontext.tsx +1 -1
  7. package/src/app/index.tsx +18 -1
  8. package/src/types/hostApi.ts +1 -2
  9. package/src/app/context/global_loading_state_provider.tsx +0 -65
  10. package/src/app/hooks/use_global_loading.ts +0 -47
  11. package/src/app/hooks/use_plugin_entries.ts +0 -34
  12. package/src/core/components/SlotComp.tsx +0 -14
  13. package/src/core/const/index.ts +0 -3
  14. package/src/core/context/createPluginCtx.tsx +0 -60
  15. package/src/core/context/index.ts +0 -1
  16. package/src/core/hooks/index.ts +0 -6
  17. package/src/core/hooks/useMiddlewareTransData.ts +0 -13
  18. package/src/core/hooks/useSlotComp.ts +0 -4
  19. package/src/core/hooks/use_plugins.ts +0 -115
  20. package/src/core/plugin-system/bbplugin.ts +0 -15
  21. package/src/core/plugin-system/manifest.ts +0 -56
  22. package/src/core/plugin-system/pluginManager.ts +0 -126
  23. package/src/core/plugin-system/pluginManifestService.ts +0 -97
  24. package/src/core/plugin-system/pluginStore.ts +0 -200
  25. package/src/core/plugin-system/registry.ts +0 -17
  26. package/src/core/plugin-system/services/coreService.ts +0 -75
  27. package/src/core/plugin-system/services/systemUIService.ts +0 -64
  28. package/src/core/shared-service/contract/ICoreService.ts +0 -28
  29. package/src/core/shared-service/contract/IUIService.ts +0 -53
  30. package/src/core/shared-service/factory/createUIService.ts +0 -121
  31. package/src/core/shared-service/factory/createUIServiceReactKit.tsx +0 -149
  32. package/src/core/shared-service/service-proxy.ts +0 -28
  33. package/src/core/shared-service/service-registry.ts +0 -54
  34. package/src/core/utils/eventBus.ts +0 -29
  35. package/src/plugins/blog/components/BlogLink.tsx +0 -11
  36. package/src/plugins/blog/components/BlogSlotCom.tsx +0 -14
  37. package/src/plugins/blog/components/app.tsx +0 -15
  38. package/src/plugins/blog/components/article/index.tsx +0 -51
  39. package/src/plugins/blog/components/index.tsx +0 -10
  40. package/src/plugins/blog/constants/index.ts +0 -1
  41. package/src/plugins/blog/context/index.ts +0 -7
  42. package/src/plugins/blog/hooks/useMiddlewareTransData.ts +0 -14
  43. package/src/plugins/blog/hooks/use_blog_scroll_pos_restoration.ts +0 -89
  44. package/src/plugins/blog/hooks/use_blog_slot_com.ts +0 -5
  45. package/src/plugins/blog/hooks/use_posts.ts +0 -74
  46. package/src/plugins/blog/index.ts +0 -79
  47. package/src/plugins/blog/pages/extensions/txt/article.tsx +0 -42
  48. package/src/plugins/blog/pages/extensions/txt/index.tsx +0 -58
  49. package/src/plugins/blog/services/BlogUIService.ts +0 -27
  50. package/src/plugins/blog/services/IBlogUIService.ts +0 -16
  51. package/src/plugins/extra-cd/index.ts +0 -36
  52. package/src/plugins/extra-entry/components/page.tsx +0 -14
  53. package/src/plugins/extra-entry/index.ts +0 -28
  54. package/src/plugins/fx/components/index.tsx +0 -45
  55. package/src/plugins/fx/context/index.ts +0 -12
  56. package/src/plugins/fx/hooks/useTextEffects.ts +0 -39
  57. package/src/plugins/fx/index.ts +0 -53
  58. package/src/plugins/fx/services/FxService.ts +0 -47
  59. package/src/plugins/fx/services/IFxService.ts +0 -15
  60. package/src/plugins/notification/components/index.tsx +0 -26
  61. package/src/plugins/notification/index.ts +0 -26
  62. package/src/plugins/notification/services/INotificationService.ts +0 -24
  63. package/src/plugins/notification/services/NotificationService.ts +0 -29
  64. package/src/plugins/now/components/NowLink.tsx +0 -7
  65. package/src/plugins/now/components/index.tsx +0 -9
  66. package/src/plugins/now/components/streaming/arrow-down.tsx +0 -26
  67. package/src/plugins/now/components/streaming/index.tsx +0 -99
  68. package/src/plugins/now/components/streaming/useScrollBtnVisibility.ts +0 -28
  69. package/src/plugins/now/context/index.ts +0 -10
  70. package/src/plugins/now/hooks/use_streaming.ts +0 -88
  71. package/src/plugins/now/index.ts +0 -62
  72. package/src/plugins/now/utils/streaming.ts +0 -33
  73. package/src/plugins/sticker/components/StickerCom.tsx +0 -45
  74. package/src/plugins/sticker/const.ts +0 -76
  75. package/src/plugins/sticker/context.ts +0 -7
  76. package/src/plugins/sticker/index.ts +0 -43
  77. package/src/plugins/sticker/types.ts +0 -10
  78. package/src/plugins/store/components/ArrowCom.tsx +0 -34
  79. package/src/plugins/store/components/ArrowSvg.tsx +0 -1133
  80. package/src/plugins/store/components/storeIcon.tsx +0 -16
  81. package/src/plugins/store/components/storePage.tsx +0 -202
  82. package/src/plugins/store/context/index.ts +0 -15
  83. package/src/plugins/store/index.ts +0 -57
  84. package/src/plugins/store/utils/index.ts +0 -26
  85. package/src/plugins/version/index.ts +0 -28
  86. package/src/plugins/xwy/components/XwyLink.tsx +0 -7
  87. package/src/plugins/xwy/components/article.tsx +0 -13
  88. package/src/plugins/xwy/components/logo.tsx +0 -28
  89. package/src/plugins/xwy/const/index.ts +0 -28
  90. package/src/plugins/xwy/index.ts +0 -103
  91. package/src/plugins/xwy/transformers/index.ts +0 -67
  92. package/src/plugins/xwy/types/index.ts +0 -17
  93. package/src/plugins/xwy/utils/index.ts +0 -43
@@ -1,16 +0,0 @@
1
- import { Link } from '@bbki.ng/ui';
2
-
3
- const CupIcon = () => (
4
- <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="16" height="16">
5
- <path d="M8.75 8h6.5a.75.75 0 0 1 .75.75v6.5a.75.75 0 0 1-.75.75h-6.5a.75.75 0 0 1-.75-.75v-6.5A.75.75 0 0 1 8.75 8Zm.75 6.5h5v-5h-5Z"></path>
6
- <path d="M15.25 1a.75.75 0 0 1 .75.75V4h2.25c.966 0 1.75.784 1.75 1.75V8h2.25a.75.75 0 0 1 0 1.5H20v5h2.25a.75.75 0 0 1 0 1.5H20v2.25A1.75 1.75 0 0 1 18.25 20H16v2.25a.75.75 0 0 1-1.5 0V20h-5v2.25a.75.75 0 0 1-1.5 0V20H5.75A1.75 1.75 0 0 1 4 18.25V16H1.75a.75.75 0 0 1 0-1.5H4v-5H1.75a.75.75 0 0 1 0-1.5H4V5.75C4 4.784 4.784 4 5.75 4H8V1.75a.75.75 0 0 1 1.5 0V4h5V1.75a.75.75 0 0 1 .75-.75Zm3 17.5a.25.25 0 0 0 .25-.25V5.75a.25.25 0 0 0-.25-.25H5.75a.25.25 0 0 0-.25.25v12.5c0 .138.112.25.25.25Z"></path>
7
- </svg>
8
- );
9
-
10
- export const StoreIcon = () => {
11
- return (
12
- <Link to="/store" className="flex items-center">
13
- <CupIcon />
14
- </Link>
15
- );
16
- };
@@ -1,202 +0,0 @@
1
- import React, { useCallback, useEffect, useState } from 'react';
2
- import classNames from 'classnames';
3
- import { Button, Table, Link } from '@bbki.ng/ui';
4
-
5
- import { IComPropsRegisteredToSlot } from '#/types/slots';
6
- import { IPluginStoreEntry, PluginID } from '#/types/plugin';
7
-
8
- import { StoreCtx } from '../context';
9
- import { pluginDepDescForHuman } from '../utils';
10
-
11
- // 空状态展示
12
- const EmptyState = () => (
13
- <div className="flex flex-col items-center justify-center p-8 text-content-secondary">
14
- <p className="text-sm">暂无可用插件</p>
15
- </div>
16
- );
17
-
18
- // 错误状态展示
19
- const ErrorState = ({ error, onRetry }: { error: Error; onRetry: () => void }) => (
20
- <div className="flex flex-col items-start justify-center p-8">
21
- <p className="text-content-danger text-sm mb-4">加载失败: {error.message}</p>
22
- <Button onClick={onRetry} size="sm">
23
- 重试
24
- </Button>
25
- </div>
26
- );
27
-
28
- export const StorePage = (_: IComPropsRegisteredToSlot) => {
29
- const { list, setLoading, isInstalled, install, uninstall, get, notificationService } =
30
- StoreCtx.useCtx();
31
- const [plugins, setPlugins] = useState<Array<IPluginStoreEntry>>([]);
32
- const [error, setError] = useState<Error | null>(null);
33
-
34
- useEffect(() => {
35
- let cancelled = false;
36
-
37
- const fetchPlugins = async () => {
38
- setLoading(true);
39
- setError(null);
40
-
41
- try {
42
- const pluginList = await list();
43
- if (!cancelled) {
44
- setPlugins(pluginList);
45
- }
46
- } catch (err) {
47
- if (!cancelled) {
48
- setError(err instanceof Error ? err : new Error('获取插件列表失败'));
49
- }
50
- } finally {
51
- if (!cancelled) {
52
- setLoading(false);
53
- }
54
- }
55
- };
56
-
57
- fetchPlugins();
58
-
59
- return () => {
60
- cancelled = true;
61
- };
62
- }, [list, setLoading]);
63
-
64
- const refreshList = useCallback(async () => {
65
- try {
66
- const pluginList = await list();
67
- setPlugins(pluginList);
68
- setError(null);
69
- } catch (err) {
70
- setError(err instanceof Error ? err : new Error('刷新列表失败'));
71
- }
72
- }, [list]);
73
-
74
- const handleInstall = useCallback(
75
- async (id: PluginID) => {
76
- setLoading(true);
77
- setError(null);
78
-
79
- try {
80
- const success = await install(id, true);
81
- if (success) {
82
- await refreshList();
83
- } else {
84
- notificationService.notify({
85
- message: `安装插件失败,依赖缺失或其他错误`,
86
- });
87
- }
88
- } catch (err) {
89
- notificationService.notify({
90
- message: `安装插件失败: ${err instanceof Error ? err.message : String(err)}`,
91
- });
92
- } finally {
93
- setLoading(false);
94
- }
95
- },
96
- [install, refreshList, setLoading, notificationService]
97
- );
98
-
99
- const handleUninstall = useCallback(
100
- async (id: PluginID) => {
101
- setLoading(true);
102
- setError(null);
103
-
104
- try {
105
- await uninstall(id);
106
- await refreshList();
107
- } catch (err) {
108
- setError(err instanceof Error ? err : new Error('卸载失败'));
109
- } finally {
110
- setLoading(false);
111
- }
112
- },
113
- [uninstall, refreshList, setLoading]
114
- );
115
-
116
- const headerRenderer = useCallback(() => {
117
- return (
118
- <>
119
- <Table.HCell>功能清单</Table.HCell>
120
- <Table.HCell style={{ width: 'unset', maxWidth: 'unset' }}>&nbsp;</Table.HCell>
121
- <Table.HCell style={{ textAlign: 'right' }}>&nbsp;</Table.HCell>
122
- </>
123
- );
124
- }, []);
125
-
126
- const rowRenderer = useCallback(
127
- (index: number) => {
128
- if (index < 0 || index >= plugins.length) {
129
- return null;
130
- }
131
-
132
- const plugin = plugins[index];
133
- const pluginInstalled = isInstalled(plugin.id);
134
-
135
- return (
136
- <>
137
- <Table.Cell>{plugin.name}</Table.Cell>
138
- <Table.Cell style={{ width: 'unset', maxWidth: 'unset' }}>
139
- {plugin.description}
140
- {plugin.dependencies && plugin.dependencies.length > 0 && (
141
- <div className="mt-4 text-xs text-content-secondary">
142
- {pluginDepDescForHuman(plugin, get)}
143
- </div>
144
- )}
145
- </Table.Cell>
146
- <Table.Cell style={{ textAlign: 'right' }}>
147
- {pluginInstalled ? (
148
- <Button
149
- variant="ghost"
150
- className="text-content-danger"
151
- onClick={() => handleUninstall(plugin.id)}
152
- >
153
- 卸载
154
- </Button>
155
- ) : (
156
- <Button color="primary" onClick={() => handleInstall(plugin.id)}>
157
- 安装
158
- </Button>
159
- )}
160
- </Table.Cell>
161
- </>
162
- );
163
- },
164
- [plugins, isInstalled, get, handleUninstall, handleInstall]
165
- );
166
-
167
- if (error) {
168
- return (
169
- <div className="prose">
170
- <ErrorState error={error} onRetry={refreshList} />
171
- </div>
172
- );
173
- }
174
-
175
- if (plugins.length === 0) {
176
- return (
177
- <div className="prose">
178
- <EmptyState />
179
- </div>
180
- );
181
- }
182
-
183
- return (
184
- <>
185
- <div className="prose">
186
- <Table
187
- className="w-full"
188
- rowCount={plugins.length}
189
- headerRenderer={headerRenderer}
190
- rowRenderer={rowRenderer}
191
- />
192
- </div>
193
- <Link
194
- className={classNames('w-fit relative transition-all duration-300 mt-16')}
195
- to="/"
196
- style={{ left: -4 }}
197
- >
198
- cd ..
199
- </Link>
200
- </>
201
- );
202
- };
@@ -1,15 +0,0 @@
1
- import { createPluginCtx } from '#/core/context';
2
- import { NotificationService } from '#/plugins/notification/services/NotificationService';
3
- import { IPluginStoreEntry, PluginID } from '#/types/plugin';
4
-
5
- export interface IStoreCtx {
6
- install: (pid: PluginID, manual?: boolean) => Promise<boolean>;
7
- uninstall: (pid: PluginID) => Promise<void>;
8
- isInstalled: (pid: PluginID) => boolean;
9
- list: () => Promise<Array<IPluginStoreEntry>> | Array<IPluginStoreEntry>;
10
- get: (pid: PluginID) => IPluginStoreEntry | undefined;
11
- setLoading: (loading: boolean) => void;
12
- notificationService: NotificationService | Record<string, never>;
13
- }
14
-
15
- export const StoreCtx = createPluginCtx<IStoreCtx>();
@@ -1,57 +0,0 @@
1
- import { BBPlugin } from '#/core/plugin-system/bbplugin';
2
- import { IHostContext } from '#/types/hostApi';
3
- import { PluginID } from '#/types/plugin';
4
-
5
- import { ArrowCom } from './components/ArrowCom';
6
- import { StoreIcon } from './components/storeIcon';
7
- import { StorePage } from './components/storePage';
8
- import { StoreCtx } from './context';
9
-
10
- export class StorePlugin extends BBPlugin {
11
- id: PluginID = 'store';
12
- override onInstall = async (ctx: IHostContext): Promise<void> => {
13
- const coreService = ctx.service.get('core:baseService');
14
- if (!coreService) {
15
- console.error('Core service not found. StorePlugin installation failed.');
16
- return;
17
- }
18
-
19
- const systemUIService = ctx.service.get('core:uiService');
20
- if (!systemUIService) {
21
- console.error('UI service not found. StorePlugin installation failed.');
22
- return;
23
- }
24
-
25
- systemUIService.registerSlot('nav-right', StoreIcon, this.id);
26
-
27
- systemUIService.registerSlot('centerEntryArea', ArrowCom, this.id);
28
-
29
- systemUIService.registerPluginEntry(
30
- {
31
- path: '/store',
32
- pageComponent: StoreCtx.withCtx(
33
- {
34
- install: ctx.store?.installPlugin || (async () => false),
35
- uninstall: ctx.store?.disablePlugin || (async () => {}),
36
- isInstalled: ctx.store?.isPluginInstalled || ((_: PluginID) => false),
37
- get: ctx.store?.getPlugin || ((_: PluginID) => undefined),
38
- list: async () => {
39
- coreService.setLoading('store', true);
40
- const all = Array.from((await ctx.store?.getAllPlugins()) || []);
41
- coreService.setLoading('store', false);
42
- return all.filter(({ id }) => id !== this.id);
43
- },
44
- notificationService: ctx.service.get('notification'),
45
- setLoading: (loading: boolean) => {
46
- coreService.setLoading('store', loading);
47
- },
48
- },
49
- StorePage
50
- ),
51
- },
52
- this.id
53
- );
54
- };
55
- }
56
-
57
- export default new StorePlugin();
@@ -1,26 +0,0 @@
1
- import { IPluginStoreEntry, PluginID } from '#/types/plugin';
2
-
3
- export const pluginDepDescForHuman = (
4
- plugin: IPluginStoreEntry,
5
- infoGetter: (id: PluginID) => IPluginStoreEntry | undefined
6
- ): string => {
7
- if (!plugin.dependencies || plugin.dependencies.length === 0) {
8
- return '';
9
- }
10
-
11
- const desc =
12
- plugin.dependencies && plugin.dependencies.length > 0
13
- ? plugin.dependencies
14
- .map(dep => {
15
- const depPlugin = infoGetter(dep);
16
- return depPlugin ? depPlugin.name : dep;
17
- })
18
- .join(', ')
19
- : '';
20
-
21
- if (!desc) {
22
- return '';
23
- }
24
-
25
- return `需要先安装${desc}。`;
26
- };
@@ -1,28 +0,0 @@
1
- import { BBPlugin } from '#/core/plugin-system/bbplugin';
2
- import { IHostContext } from '#/types/hostApi';
3
- import { PluginID } from '#/types/plugin';
4
-
5
- export class VersionPlugin extends BBPlugin {
6
- id: PluginID = 'version';
7
-
8
- private _clear = () => {};
9
-
10
- override onInstall?: ((ctx: IHostContext) => void | Promise<void>) | undefined = async ctx => {
11
- const fxService = ctx.service.get('fx:service');
12
-
13
- const hash = await ctx.service.get('core:baseService')?.getVersionHash();
14
-
15
- this._clear = fxService.drawText({
16
- text: `version: ${hash}`,
17
- color: '#f8babc',
18
- y: window.innerHeight - 50,
19
- x: 16,
20
- });
21
- };
22
-
23
- override onDestroy?: (() => void) | undefined = () => {
24
- this._clear?.();
25
- };
26
- }
27
-
28
- export default new VersionPlugin();
@@ -1,7 +0,0 @@
1
- import { Link } from '@bbki.ng/ui';
2
-
3
- export const XwyLink = (
4
- <Link to="/blog/小乌鸦合集" className="ml-[var(--toast-button-margin-start)]">
5
- 打开
6
- </Link>
7
- );
@@ -1,13 +0,0 @@
1
- import React from 'react';
2
-
3
- import { IComPropsRegisteredToSlot } from '#/types/slots';
4
-
5
- export const SpecialTitle = (props: IComPropsRegisteredToSlot) => {
6
- const title = props.data as string;
7
-
8
- if (title === '小乌鸦合集') {
9
- return <span className="font-xwy text-content-special text-4xl">{title}</span>;
10
- }
11
-
12
- return <>{title}</>;
13
- };
@@ -1,28 +0,0 @@
1
- import React from 'react';
2
- import { useLocation } from 'react-router-dom';
3
-
4
- import { IComPropsRegisteredToSlot } from '#/types/slots';
5
-
6
- export const Crows = () => {
7
- return (
8
- <svg width="48" height="48" viewBox="0 0 48 48" fill="none" xmlns="http://www.w3.org/2000/svg">
9
- <path
10
- 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"
11
- fill="#FBD1D2"
12
- />
13
- <circle cx="17.6178" cy="18.2688" r="0.995689" fill="white" />
14
- </svg>
15
- );
16
- };
17
-
18
- export const XwyLogo = ({ data }: IComPropsRegisteredToSlot) => {
19
- const location = useLocation();
20
-
21
- const defaultLogo = data as React.ReactNode;
22
-
23
- if (decodeURIComponent(location.pathname).includes('小乌鸦')) {
24
- return <Crows />;
25
- }
26
-
27
- return <>{defaultLogo}</>;
28
- };
@@ -1,28 +0,0 @@
1
- import { FontRule } from '../types';
2
-
3
- export const LOADING_CLASS = 'fonts-loading';
4
-
5
- export const FontRules: Array<FontRule> = [
6
- {
7
- match: '小乌鸦合集',
8
- fontFamily: 'xwy',
9
- fontConfig: {
10
- name: 'xwy',
11
- src: '/fonts/xwy.woff2',
12
- format: 'woff2',
13
- },
14
- extraCls: 'text-2xl w-[81px]',
15
- variant: 'special',
16
- },
17
- {
18
- match: '',
19
- fontFamily: 'xwy-icon',
20
- fontConfig: {
21
- name: 'xwy-icon',
22
- src: '/fonts/yao-icons.woff2',
23
- format: 'woff2',
24
- hideGlyphOnLoading: true,
25
- preload: true,
26
- },
27
- },
28
- ];
@@ -1,103 +0,0 @@
1
- import { IHostContext } from '#/types/hostApi';
2
- import { BBPlugin } from '#/core/plugin-system/bbplugin';
3
- import { PluginID } from '#/types/plugin';
4
-
5
- import { SpecialTitle } from './components/article';
6
- import { XwyLogo } from './components/logo';
7
- import { FontRules, LOADING_CLASS } from './const';
8
- import { loadFont } from './utils';
9
- import {
10
- transformBreadcrumbPaths,
11
- transformTitleList,
12
- transformPostContent,
13
- pinTitle,
14
- } from './transformers';
15
- import { XwyLink } from './components/XwyLink';
16
-
17
- class XwyPlugin extends BBPlugin {
18
- id: PluginID = 'xwy';
19
-
20
- private loadedFonts = new Set<string>();
21
-
22
- private _serviceRegistry?: IHostContext['service'];
23
-
24
- override onInstall = (ctx: IHostContext) => {
25
- // add font loading listener
26
- this._serviceRegistry = ctx.service;
27
- this.initFontLoadingListener();
28
-
29
- // 加载所有需要的字体文件
30
- FontRules.forEach(rule => {
31
- if (rule.fontConfig && !this.loadedFonts.has(rule.fontConfig.name)) {
32
- loadFont(rule.fontConfig);
33
- this.loadedFonts.add(rule.fontConfig.name);
34
- }
35
- });
36
-
37
- this.batchRegisterMiddlewares(ctx);
38
-
39
- // 注册logo插槽组件
40
- const systemUIService = ctx.service.get('core:uiService');
41
- systemUIService?.registerSlot('logo', XwyLogo, this.id);
42
-
43
- // 注册「小乌鸦合集」标题组件
44
- const blogUIService = ctx.service.get('blog:uiService');
45
- blogUIService?.registerSlot('blog:articleTitle', SpecialTitle, this.id);
46
- };
47
-
48
- private batchRegisterMiddlewares(ctx: IHostContext) {
49
- const blogUIService = ctx.service.get('blog:uiService');
50
- const systemUIService = ctx.service.get('core:uiService');
51
-
52
- if (!blogUIService || !systemUIService) {
53
- console.warn(`[${this.id}] Required UI services not found, skipping middleware registration`);
54
- return;
55
- }
56
-
57
- // 注册博客相关的中间件
58
- for (const transformer of [pinTitle, transformTitleList]) {
59
- blogUIService.registerMiddleware('blog:transformTitleList', transformer, this.id);
60
- }
61
- for (const transformer of [transformPostContent]) {
62
- blogUIService.registerMiddleware('blog:transformPostContent', transformer, this.id);
63
- }
64
-
65
- // 注册系统相关的中间件
66
- for (const transformer of [transformBreadcrumbPaths]) {
67
- systemUIService.registerMiddleware('transformBreadcrumbPath', transformer, this.id);
68
- }
69
- }
70
-
71
- override onManualInstall = () => {
72
- this.promptToOpen();
73
- };
74
-
75
- private promptToOpen = () => {
76
- const notificationService = this._serviceRegistry?.get('notification');
77
- notificationService?.notify({
78
- message: '小乌鸦安装成功.',
79
- action: XwyLink,
80
- });
81
- };
82
-
83
- private initFontLoadingListener() {
84
- // listen font loading status and add class to body for styling
85
- console.log('Initializing font loading listener');
86
- document.body.classList.add(LOADING_CLASS);
87
- document.fonts.ready.then(() => {
88
- document.body.classList.remove(LOADING_CLASS);
89
- });
90
- }
91
-
92
- override onDisable = async () => {
93
- this.loadedFonts.forEach(fontName => {
94
- const styleEl = document.getElementById(`font-${fontName}`);
95
- if (styleEl) {
96
- styleEl.remove();
97
- }
98
- });
99
- this.loadedFonts.clear();
100
- };
101
- }
102
-
103
- export default new XwyPlugin();
@@ -1,67 +0,0 @@
1
- import { PathObj } from '@bbki.ng/ui';
2
-
3
- import { TitleListItem } from '#/types/posts';
4
-
5
- import { FontRules } from '../const';
6
-
7
- export const transformPostContent = (content: string = '') => {
8
- // 在文章内容中替换特定关键词为特殊样式
9
- return content
10
- .replace(
11
- /小乌鸦/g,
12
- `<span class="font-xwy text-content-special text-[1.6rem] align-middle leading-[1.09]">小乌鸦</span>`
13
- )
14
- .replace(
15
- /公园/,
16
- `公园 <span class="font-xwy-icon text-[#679867] text-[1.6rem] align-middle leading-[1.09]">&#xE01A&#xE003</span>`
17
- );
18
- };
19
-
20
- export const pinTitle = (titleList: TitleListItem[]) => {
21
- const pinnedTitle = '小乌鸦合集';
22
- const index = titleList.findIndex(item => item.name === pinnedTitle);
23
- if (index > -1) {
24
- const [item] = titleList.splice(index, 1);
25
- titleList.unshift(item);
26
- }
27
- return titleList;
28
- };
29
-
30
- export const transformBreadcrumbPaths = (paths: PathObj[]) => {
31
- const result = paths.map(p => {
32
- if (p.name === '小乌鸦合集') {
33
- return {
34
- ...p,
35
- name: '小乌鸦合集',
36
- className: 'font-xwy text-content-special text-[1.2rem] align-middle relative top-[1px]',
37
- };
38
- }
39
-
40
- return p;
41
- });
42
-
43
- console.log('Transformed breadcrumb paths:', result);
44
-
45
- return result;
46
- };
47
-
48
- /**
49
- * 转换标题列表,为匹配的标题添加字体类名
50
- */
51
- export const transformTitleList = (titleList: TitleListItem[]): TitleListItem[] => {
52
- return titleList.map(item => {
53
- for (const rule of FontRules) {
54
- const match =
55
- typeof rule.match === 'string' ? item.name === rule.match : rule.match.test(item.name);
56
-
57
- if (match) {
58
- return {
59
- ...item,
60
- variant: rule.variant || 'default',
61
- className: `font-${rule.fontFamily} ${rule.extraCls || ''}`.trim(),
62
- };
63
- }
64
- }
65
- return item;
66
- });
67
- };
@@ -1,17 +0,0 @@
1
- export interface FontConfig {
2
- name: string;
3
- src: string;
4
- format: 'woff2' | 'woff' | 'ttf' | 'otf';
5
- fontWeight?: string;
6
- fontStyle?: string;
7
- hideGlyphOnLoading?: boolean;
8
- preload?: boolean;
9
- }
10
-
11
- export interface FontRule {
12
- match: string | RegExp;
13
- fontFamily: string;
14
- fontConfig?: FontConfig;
15
- extraCls?: string;
16
- variant?: 'default' | 'special';
17
- }
@@ -1,43 +0,0 @@
1
- import { LOADING_CLASS } from '../const';
2
- import { type FontConfig } from '../types';
3
-
4
- export const loadFont = (config: FontConfig) => {
5
- const styleId = `font-${config.name}`;
6
- if (document.getElementById(styleId)) return;
7
-
8
- const hideCssRule = config.hideGlyphOnLoading
9
- ? `
10
- .${LOADING_CLASS} .font-${config.name} {
11
- visibility: hidden;
12
- }
13
- `
14
- : '';
15
-
16
- const existingLink = document.querySelector(`link[rel="preload"][href="${config.src}"]`);
17
- if (config.preload && !existingLink) {
18
- const link = document.createElement('link');
19
- link.rel = 'preload';
20
- link.as = 'font';
21
- link.href = config.src;
22
- link.type = `font/${config.format}`;
23
- link.crossOrigin = 'anonymous';
24
- document.head.appendChild(link);
25
- }
26
-
27
- const style = document.createElement('style');
28
- style.id = styleId;
29
- style.textContent = `
30
- @font-face {
31
- font-family: '${config.name}';
32
- src: url('${config.src}') format('${config.format}');
33
- font-weight: ${config.fontWeight || 'normal'};
34
- font-style: ${config.fontStyle || 'normal'};
35
- font-display: block;
36
- }
37
- .font-${config.name} {
38
- font-family: '${config.name}', monospace;
39
- }
40
- ${hideCssRule}
41
- `;
42
- document.head.appendChild(style);
43
- };