@chatbi-v/cli 1.0.8 → 1.0.9

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 (104) hide show
  1. package/dist/app/.env +8 -0
  2. package/dist/app/.env.hbs +8 -0
  3. package/dist/app/README.md.hbs +16 -0
  4. package/dist/app/chatbi.config.ts.hbs +60 -0
  5. package/dist/{default/apps/main → app}/index.html.hbs +3 -4
  6. package/dist/app/package.json.hbs +34 -0
  7. package/dist/app/src/App.tsx.hbs +92 -0
  8. package/dist/app/src/components/GlobalErrorBoundary.tsx.hbs +69 -0
  9. package/dist/app/src/components/GlobalSettingsModal.tsx.hbs +35 -0
  10. package/dist/app/src/components/LayoutSkeletons.tsx.hbs +79 -0
  11. package/dist/app/src/components/index.ts.hbs +2 -0
  12. package/dist/app/src/custom-antd.less.hbs +7 -0
  13. package/dist/app/src/features/settings/ConfigRenderStrategy.tsx.hbs +119 -0
  14. package/dist/app/src/features/settings/ExtensionSettings.tsx.hbs +52 -0
  15. package/dist/app/src/features/settings/PluginList.tsx.hbs +115 -0
  16. package/dist/app/src/features/settings/PluginSettings.tsx.hbs +123 -0
  17. package/dist/app/src/features/settings/SchemaSettingsRenderer.tsx.hbs +56 -0
  18. package/dist/app/src/hooks/useAppRoutes.ts.hbs +39 -0
  19. package/dist/app/src/hooks/usePluginLoader.ts.hbs +22 -0
  20. package/dist/app/src/hooks/usePluginSettings.ts.hbs +29 -0
  21. package/dist/app/src/hooks/useThemeSync.ts.hbs +108 -0
  22. package/dist/app/src/index.css.hbs +45 -0
  23. package/dist/app/src/layouts/BackgroundEffects.tsx.hbs +10 -0
  24. package/dist/app/src/layouts/MainContent.tsx.hbs +58 -0
  25. package/dist/app/src/layouts/SidebarNav.tsx.hbs +182 -0
  26. package/dist/app/src/main.tsx.hbs +43 -0
  27. package/dist/app/src/providers/AppProviders.tsx.hbs +36 -0
  28. package/dist/app/src/services/api/index.ts.hbs +37 -0
  29. package/dist/app/src/services/api/modules/auth.ts.hbs +18 -0
  30. package/dist/app/src/services/config-service.ts.hbs +48 -0
  31. package/dist/app/src/stores/storage-adapter.ts.hbs +29 -0
  32. package/dist/app/src/stores/useSessionStore.ts.hbs +22 -0
  33. package/dist/app/src/stores/useUIStore.ts.hbs +64 -0
  34. package/dist/app/tailwind.config.cjs.hbs +14 -0
  35. package/dist/app/tsconfig.json.hbs +26 -0
  36. package/dist/app/vite.config.ts.hbs +89 -0
  37. package/dist/index.js +5343 -4330
  38. package/dist/monorepo/.gitignore.hbs +3 -0
  39. package/dist/{default → monorepo}/package.json.hbs +4 -0
  40. package/dist/monorepo/pnpm-workspace.yaml.hbs +10 -0
  41. package/dist/{default → monorepo}/tsconfig.json.hbs +3 -1
  42. package/dist/{default/plugins/demo-plugin → plugin}/package.json.hbs +7 -2
  43. package/dist/plugin/src/index.tsx.hbs +90 -0
  44. package/dist/plugin/tsconfig.json.hbs +14 -0
  45. package/package.json +9 -4
  46. package/templates/app/.env.hbs +8 -0
  47. package/templates/app/README.md.hbs +16 -0
  48. package/templates/app/chatbi.config.ts.hbs +60 -0
  49. package/templates/app/index.html.hbs +12 -0
  50. package/templates/app/package.json.hbs +34 -0
  51. package/templates/app/postcss.config.cjs.hbs +6 -0
  52. package/templates/app/src/App.tsx.hbs +92 -0
  53. package/templates/app/src/components/GlobalErrorBoundary.tsx.hbs +69 -0
  54. package/templates/app/src/components/GlobalSettingsModal.tsx.hbs +35 -0
  55. package/templates/app/src/components/LayoutSkeletons.tsx.hbs +79 -0
  56. package/templates/app/src/components/index.ts.hbs +2 -0
  57. package/templates/app/src/custom-antd.less.hbs +7 -0
  58. package/templates/app/src/features/settings/ConfigRenderStrategy.tsx.hbs +119 -0
  59. package/templates/app/src/features/settings/ExtensionSettings.tsx.hbs +52 -0
  60. package/templates/app/src/features/settings/PluginList.tsx.hbs +115 -0
  61. package/templates/app/src/features/settings/PluginSettings.tsx.hbs +123 -0
  62. package/templates/app/src/features/settings/SchemaSettingsRenderer.tsx.hbs +56 -0
  63. package/templates/app/src/hooks/useAppRoutes.ts.hbs +39 -0
  64. package/templates/app/src/hooks/usePluginLoader.ts.hbs +22 -0
  65. package/templates/app/src/hooks/usePluginSettings.ts.hbs +29 -0
  66. package/templates/app/src/hooks/useThemeSync.ts.hbs +108 -0
  67. package/templates/app/src/index.css.hbs +45 -0
  68. package/templates/app/src/layouts/BackgroundEffects.tsx.hbs +10 -0
  69. package/templates/app/src/layouts/MainContent.tsx.hbs +58 -0
  70. package/templates/app/src/layouts/SidebarNav.tsx.hbs +182 -0
  71. package/templates/app/src/main.tsx.hbs +43 -0
  72. package/templates/app/src/providers/AppProviders.tsx.hbs +36 -0
  73. package/templates/app/src/services/api/index.ts.hbs +37 -0
  74. package/templates/app/src/services/api/modules/auth.ts.hbs +18 -0
  75. package/templates/app/src/services/config-service.ts.hbs +48 -0
  76. package/templates/app/src/stores/storage-adapter.ts.hbs +29 -0
  77. package/templates/app/src/stores/useSessionStore.ts.hbs +22 -0
  78. package/templates/app/src/stores/useUIStore.ts.hbs +64 -0
  79. package/templates/app/tailwind.config.cjs.hbs +14 -0
  80. package/templates/app/tsconfig.json.hbs +26 -0
  81. package/templates/app/vite.config.ts.hbs +89 -0
  82. package/templates/monorepo/.gitignore.hbs +3 -0
  83. package/templates/monorepo/README.md.hbs +30 -0
  84. package/templates/monorepo/package.json.hbs +38 -0
  85. package/templates/monorepo/pnpm-workspace.yaml.hbs +10 -0
  86. package/templates/monorepo/tsconfig.json.hbs +26 -0
  87. package/templates/plugin/package.json.hbs +23 -0
  88. package/templates/plugin/src/index.tsx.hbs +90 -0
  89. package/templates/plugin/tsconfig.json.hbs +14 -0
  90. package/dist/default/apps/main/package.json.hbs +0 -20
  91. package/dist/default/apps/main/src/App.tsx.hbs +0 -178
  92. package/dist/default/apps/main/src/components/NavIcon.tsx.hbs +0 -41
  93. package/dist/default/apps/main/src/hooks/usePluginLoader.ts.hbs +0 -25
  94. package/dist/default/apps/main/src/index.css.hbs +0 -8
  95. package/dist/default/apps/main/src/main.tsx.hbs +0 -13
  96. package/dist/default/apps/main/src/pages/Guide.tsx.hbs +0 -133
  97. package/dist/default/apps/main/tailwind.config.cjs.hbs +0 -17
  98. package/dist/default/apps/main/tsconfig.json.hbs +0 -10
  99. package/dist/default/apps/main/vite.config.ts.hbs +0 -16
  100. package/dist/default/plugins/demo-plugin/src/index.tsx.hbs +0 -44
  101. package/dist/default/plugins/demo-plugin/tsconfig.json.hbs +0 -10
  102. package/dist/default/pnpm-workspace.yaml.hbs +0 -3
  103. /package/dist/{default/apps/main → app}/postcss.config.cjs.hbs +0 -0
  104. /package/dist/{default → monorepo}/README.md.hbs +0 -0
package/dist/app/.env ADDED
@@ -0,0 +1,8 @@
1
+ # API Base URL
2
+ VITE_API_BASE_URL=/bron-chat-bi
3
+
4
+ # 是否开启 Mock 模式 (能力开关,需配合 url mock=true 使用)
5
+ VITE_USE_MOCK=true
6
+
7
+ # API Timeout (ms)
8
+ VITE_API_TIMEOUT=10000
@@ -0,0 +1,8 @@
1
+ # API Base URL
2
+ VITE_API_BASE_URL=/bron-chat-bi
3
+
4
+ # 是否开启 Mock 模式 (能力开关,需配合 url mock=true 使用)
5
+ VITE_USE_MOCK=true
6
+
7
+ # API Timeout (ms)
8
+ VITE_API_TIMEOUT=10000
@@ -0,0 +1,16 @@
1
+ # @chatbi/main
2
+
3
+ ChatBI 主应用,基于 React 和 Vite 构建。
4
+
5
+ ## 功能
6
+
7
+ - **会话管理**: 支持会话列表、搜索、历史记录。
8
+ - **插件集成**: 集成 Chat 和 Scene 插件。
9
+ - **主题切换**: 支持深色/浅色模式。
10
+ - **布局管理**: 响应式布局,支持侧边栏折叠。
11
+
12
+ ## 启动
13
+
14
+ ```bash
15
+ pnpm dev
16
+ ```
@@ -0,0 +1,60 @@
1
+ /**
2
+ * ChatBI 应用配置文件
3
+ * @description 定义应用的全局配置和插件参数
4
+ */
5
+
6
+ import { DiscoveryRule } from '@chatbi-v/core';
7
+
8
+ interface ChatBIConfig {
9
+ /**
10
+ * 插件配置表
11
+ * Key: 插件 ID (通常是包名,如 @chatbi-v/plugin-chat)
12
+ * Value: 插件配置项
13
+ */
14
+ plugins: Record<string, any>;
15
+ system: {
16
+ title: string;
17
+ version: string;
18
+ language: string;
19
+ /** 插件发现规则 */
20
+ discoveryRules: DiscoveryRule[];
21
+ };
22
+ }
23
+
24
+ export const chatbiConfig: ChatBIConfig = {
25
+ /**
26
+ * 插件配置中心
27
+ * 这里只定义插件的"运行参数",插件的加载已由 AutoLoader 自动处理
28
+ */
29
+ plugins: {
30
+ '@chatbi-v/plugin-theme-manager': {
31
+ defaultTheme: '{{theme}}-light',
32
+ defaultLayout: 'standard',
33
+ },
34
+ '@chatbi-v/plugin-layout-transform': {},
35
+ '@chatbi-v/plugin-system-monitor': {},
36
+ },
37
+ /**
38
+ * 系统配置
39
+ */
40
+ system: {
41
+ title: '{{projectName}}',
42
+ version: '1.0.0',
43
+ language: 'zh-CN',
44
+ /** 插件发现规则 */
45
+ discoveryRules: [
46
+ {
47
+ pattern: '@chatbi-plugins/*/src/index.{ts,tsx}',
48
+ pathSegment: 'plugins',
49
+ idPrefix: '@chatbi-v/plugin',
50
+ alias: '@chatbi-plugins',
51
+ },
52
+ {
53
+ pattern: '@chatbi-apps/*/src/index.{ts,tsx}',
54
+ pathSegment: 'apps',
55
+ idPrefix: '@chatbi-v/app',
56
+ alias: '@chatbi-apps',
57
+ },
58
+ ],
59
+ },
60
+ };
@@ -1,10 +1,9 @@
1
- <!DOCTYPE html>
2
- <html lang="en">
1
+ <!doctype html>
2
+ <html lang="zh-CN">
3
3
  <head>
4
4
  <meta charset="UTF-8" />
5
- <link rel="icon" type="image/svg+xml" href="/vite.svg" />
6
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
7
- <title>ChatBI Business Plugin Host</title>
6
+ <title>智能分析平台</title>
8
7
  </head>
9
8
  <body>
10
9
  <div id="root"></div>
@@ -0,0 +1,34 @@
1
+ {
2
+ "name": "{{name}}",
3
+ "version": "0.0.1",
4
+ "type": "module",
5
+ "scripts": {
6
+ "dev": "vite",
7
+ "build": "tsc && vite build",
8
+ "preview": "vite preview",
9
+ "test": "vitest",
10
+ "sync": "chatbi-cli sync"
11
+ },
12
+ "dependencies": {
13
+ "@ant-design/icons": "^5.2.6",
14
+ "@ant-design/x": "^2.1.1",
15
+ "antd": "^5.29.3",
16
+ "react": "^18.3.1",
17
+ "react-dom": "^18.3.1",
18
+ "react-router-dom": "^7.10.1",
19
+ "zustand": "^5.0.9"
20
+ },
21
+ "devDependencies": {
22
+ "@types/node": "^20.0.0",
23
+ "@types/react": "^18.2.43",
24
+ "@types/react-dom": "^18.2.17",
25
+ "@vitejs/plugin-react": "^4.2.1",
26
+ "less": "^4.2.0",
27
+ "typescript": "^5.3.3",
28
+ "vite": "^5.0.8",
29
+ "vite-tsconfig-paths": "^4.3.1",
30
+ "tailwindcss": "^3.4.1",
31
+ "autoprefixer": "^10.4.17",
32
+ "postcss": "^8.4.35"
33
+ }
34
+ }
@@ -0,0 +1,92 @@
1
+ import React, { lazy, Suspense, useMemo } from 'react';
2
+ import { useLocation } from 'react-router-dom';
3
+
4
+ import { GlobalSettingsModal } from './components/GlobalSettingsModal';
5
+ import { ContentSkeleton, NavSkeleton } from './components/LayoutSkeletons';
6
+ import { usePluginLoader } from './hooks/usePluginLoader';
7
+ import { AppProviders } from './providers/AppProviders';
8
+ import { useSessionStore } from './stores/useSessionStore';
9
+ import { useUIStore } from './stores/useUIStore';
10
+
11
+ // Lazy load layouts
12
+ const BackgroundEffects = lazy(() => import('./layouts/BackgroundEffects').then(module => ({ default: module.BackgroundEffects })));
13
+ const MainContent = lazy(() => import('./layouts/MainContent').then(module => ({ default: module.MainContent })));
14
+ const SidebarNav = lazy(() => import('./layouts/SidebarNav').then(module => ({ default: module.SidebarNav })));
15
+
16
+ const App: React.FC = () => {
17
+ const location = useLocation();
18
+ const activeTab = location.pathname.replace(/^\//, '') || '';
19
+
20
+ // 1. 加载插件 (逻辑已封装)
21
+ const { pluginsLoaded } = usePluginLoader();
22
+
23
+ // 2. 获取 UI 状态
24
+ const { activeSession, setActiveSession } = useSessionStore();
25
+ const {
26
+ isRightPanelOpen, setIsRightPanelOpen,
27
+ isMaximized, setIsMaximized,
28
+ collapsed
29
+ } = useUIStore();
30
+
31
+ // 3. 构造共享 Props
32
+ const sharedProps = useMemo(() => ({
33
+ activeSession,
34
+ setActiveSession,
35
+ isRightPanelOpen,
36
+ setIsRightPanelOpen,
37
+ isMaximized,
38
+ setIsMaximized
39
+ }), [
40
+ activeSession,
41
+ setActiveSession,
42
+ isRightPanelOpen,
43
+ setIsRightPanelOpen,
44
+ isMaximized,
45
+ setIsMaximized
46
+ ]);
47
+
48
+ return (
49
+ <AppProviders>
50
+ <div className="flex h-screen w-screen bg-slate-50 dark:bg-slate-950 overflow-hidden text-slate-900 dark:text-slate-200 selection:bg-primary/30 font-sans transition-colors duration-300">
51
+
52
+ <Suspense fallback={null}>
53
+ <BackgroundEffects />
54
+ </Suspense>
55
+
56
+ {/* Sidebar */}
57
+ <Suspense fallback={
58
+ <div className={`relative z-20 flex flex-col items-center py-4 gap-[var(--layout-gap)] transition-all duration-500 ease-[cubic-bezier(0.2,0,0,1)]
59
+ border-r border-slate-200/60 dark:border-white/10 backdrop-blur-xl bg-white/80 dark:bg-slate-900/80
60
+ ${isMaximized ? 'w-0 overflow-hidden opacity-0 p-0 border-none' : (collapsed ? 'w-[72px]' : 'w-[var(--layout-sidebar-width)]')}
61
+ h-screen
62
+ `}>
63
+ <NavSkeleton />
64
+ </div>
65
+ }>
66
+ <SidebarNav
67
+ activeTab={activeTab}
68
+ pluginsLoaded={pluginsLoaded}
69
+ />
70
+ </Suspense>
71
+
72
+ {/* Main Content */}
73
+ <Suspense fallback={
74
+ <main className="flex-1 flex flex-col relative z-10 overflow-hidden bg-white/50 dark:bg-black/20 backdrop-blur-sm">
75
+ <ContentSkeleton />
76
+ </main>
77
+ }>
78
+ <MainContent
79
+ pluginsLoaded={pluginsLoaded}
80
+ sharedProps={sharedProps}
81
+ />
82
+ </Suspense>
83
+
84
+ {/* Global Settings */}
85
+ <GlobalSettingsModal />
86
+
87
+ </div>
88
+ </AppProviders>
89
+ );
90
+ };
91
+
92
+ export default App;
@@ -0,0 +1,69 @@
1
+ import { createLogger } from '@chatbi-v/core';
2
+ import { Button, Result, Typography } from 'antd';
3
+ import React, { Component, ErrorInfo, ReactNode } from 'react';
4
+
5
+ const { Paragraph, Text } = Typography;
6
+ const logger = createLogger('GlobalError');
7
+
8
+ interface Props {
9
+ children: ReactNode;
10
+ }
11
+
12
+ interface State {
13
+ hasError: boolean;
14
+ error: Error | null;
15
+ errorInfo: ErrorInfo | null;
16
+ }
17
+
18
+ export class GlobalErrorBoundary extends Component<Props, State> {
19
+ constructor(props: Props) {
20
+ super(props);
21
+ this.state = { hasError: false, error: null, errorInfo: null };
22
+ }
23
+
24
+ static getDerivedStateFromError(error: Error): State {
25
+ return { hasError: true, error, errorInfo: null };
26
+ }
27
+
28
+ componentDidCatch(error: Error, errorInfo: ErrorInfo) {
29
+ logger.error('Uncaught error:', error, errorInfo);
30
+ this.setState({ errorInfo });
31
+ // TODO: Report to logging service
32
+ }
33
+
34
+ handleReload = () => {
35
+ window.location.reload();
36
+ };
37
+
38
+ render() {
39
+ if (this.state.hasError) {
40
+ return (
41
+ <div className="flex items-center justify-center min-h-screen bg-gray-50 p-4">
42
+ <Result
43
+ status="500"
44
+ title="应用遇到了一些问题"
45
+ subTitle="抱歉,发生了未预期的错误,请尝试刷新页面。"
46
+ extra={[
47
+ <Button type="primary" key="reload" onClick={this.handleReload}>
48
+ 刷新页面
49
+ </Button>,
50
+ ]}
51
+ >
52
+ {import.meta.env.DEV && this.state.error && (
53
+ <div className="mt-8 text-left max-w-2xl bg-white p-4 rounded border border-gray-200 shadow-sm overflow-auto max-h-96">
54
+ <Paragraph>
55
+ <Text type="danger" strong>Error: {this.state.error.toString()}</Text>
56
+ </Paragraph>
57
+ <Paragraph className="mb-0">
58
+ <Text code className="text-xs">{this.state.errorInfo?.componentStack}</Text>
59
+ </Paragraph>
60
+ </div>
61
+ )}
62
+ </Result>
63
+ </div>
64
+ );
65
+ }
66
+
67
+ return this.props.children;
68
+ }
69
+ }
@@ -0,0 +1,35 @@
1
+ import { Modal, Skeleton } from 'antd';
2
+ import React, { lazy, Suspense } from 'react';
3
+
4
+ import { useUIStore } from '../stores/useUIStore';
5
+
6
+ // Lazy load
7
+ const PluginSettings = lazy(() => import('../features/settings/PluginSettings').then(module => ({ default: module.PluginSettings })));
8
+
9
+ /**
10
+ * 全局设置弹窗组件
11
+ */
12
+ export const GlobalSettingsModal: React.FC = () => {
13
+ const { isSettingsOpen, setIsSettingsOpen } = useUIStore();
14
+
15
+ return (
16
+ <Modal
17
+ title="设置"
18
+ open={isSettingsOpen}
19
+ onCancel={() => setIsSettingsOpen(false)}
20
+ footer={null}
21
+ width={800}
22
+ centered
23
+ className="settings-modal"
24
+ styles={{ body: { height: '600px', padding: 0, overflow: 'hidden' } }}
25
+ >
26
+ <Suspense fallback={
27
+ <div className="w-full h-full flex items-center justify-center">
28
+ <Skeleton active paragraph={{ rows: 10 }} />
29
+ </div>
30
+ }>
31
+ <PluginSettings />
32
+ </Suspense>
33
+ </Modal>
34
+ );
35
+ };
@@ -0,0 +1,79 @@
1
+ import { SidebarIconSkeleton } from '@chatbi-v/core';
2
+ import { Skeleton } from 'antd';
3
+ import React from 'react';
4
+
5
+ import { useUIStore } from '../stores/useUIStore';
6
+
7
+ export const NavSkeleton: React.FC = () => {
8
+ const { collapsed } = useUIStore();
9
+
10
+ return (
11
+ <div className={`flex flex-col items-center py-4 gap-[var(--layout-gap)] transition-all duration-300 w-full h-full`}>
12
+ {/* Brand Icon Skeleton */}
13
+ <div className={`flex items-center gap-3 mb-6 transition-all duration-300 ${!collapsed ? 'w-full px-5' : 'justify-center'}`}>
14
+ <div className="w-12 h-12 rounded-2xl bg-slate-200 dark:bg-white/10 shrink-0 animate-pulse" />
15
+ {!collapsed && (
16
+ <div className="flex flex-col gap-2 flex-1">
17
+ <div className="h-5 bg-slate-200 dark:bg-white/10 rounded w-20 animate-pulse" />
18
+ <div className="h-2 bg-slate-200 dark:bg-white/10 rounded w-12 animate-pulse" />
19
+ </div>
20
+ )}
21
+ </div>
22
+
23
+ {/* Nav Items Skeleton */}
24
+ <div className="flex-1 w-full flex flex-col gap-[var(--layout-gap)] items-center px-3 pt-2">
25
+ {[1, 2, 3, 4].map((i) => (
26
+ <SidebarIconSkeleton key={i} expanded={!collapsed} />
27
+ ))}
28
+ </div>
29
+
30
+ {/* Bottom Actions Skeleton */}
31
+ <div className="w-full flex flex-col gap-[var(--layout-gap)] mt-auto mb-2 items-center px-3">
32
+ <SidebarIconSkeleton expanded={!collapsed} />
33
+ <SidebarIconSkeleton expanded={!collapsed} />
34
+ <div className={`h-px bg-slate-200 dark:bg-white/10 my-2 transition-all duration-300 ${collapsed ? 'w-8' : 'w-full'}`} />
35
+ <div className={`flex items-center gap-3 transition-all duration-300 ${collapsed ? 'w-12 justify-center' : 'w-full px-4'}`}>
36
+ <div className="w-10 h-10 rounded-full bg-slate-200 dark:bg-white/10 animate-pulse" />
37
+ {!collapsed && <div className="h-4 bg-slate-200 dark:bg-white/10 rounded w-20 animate-pulse" />}
38
+ </div>
39
+ <SidebarIconSkeleton expanded={!collapsed} />
40
+ </div>
41
+ </div>
42
+ );
43
+ };
44
+
45
+ export const SidebarSkeleton: React.FC = () => {
46
+ return (
47
+ <div className="h-full w-full flex flex-col p-4 gap-4 animate-pulse">
48
+ {/* Header / Search */}
49
+ <div className="h-8 bg-slate-200 dark:bg-white/5 rounded-lg w-full" />
50
+
51
+ {/* List Items */}
52
+ <div className="flex-1 flex flex-col gap-3 mt-2">
53
+ {[1, 2, 3, 4, 5, 6].map((i) => (
54
+ <div key={i} className="flex flex-col gap-2 p-2">
55
+ <div className="h-4 bg-slate-200 dark:bg-white/5 rounded w-3/4" />
56
+ <div className="h-3 bg-slate-200 dark:bg-white/5 rounded w-1/2" />
57
+ </div>
58
+ ))}
59
+ </div>
60
+ </div>
61
+ );
62
+ };
63
+
64
+ export const ContentSkeleton: React.FC = () => {
65
+ return (
66
+ <div className="h-full w-full p-8 flex flex-col gap-6 animate-pulse">
67
+ {/* Header */}
68
+ <div className="h-10 bg-slate-200 dark:bg-white/5 rounded-xl w-1/3" />
69
+
70
+ {/* Content Blocks */}
71
+ <div className="flex-1 grid grid-cols-2 gap-6">
72
+ <div className="bg-slate-200 dark:bg-white/5 rounded-2xl h-full" />
73
+ <div className="bg-slate-200 dark:bg-white/5 rounded-2xl h-full" />
74
+ </div>
75
+
76
+ <div className="h-1/3 bg-slate-200 dark:bg-white/5 rounded-2xl w-full" />
77
+ </div>
78
+ );
79
+ };
@@ -0,0 +1,2 @@
1
+ export * from './GlobalErrorBoundary';
2
+ export * from './LayoutSkeletons';
@@ -0,0 +1,7 @@
1
+ /* src/custom-antd.less —— 会被 vite 注入,覆盖 antd 默认变量 */
2
+ @primary-color: #6366f1; // indigo-500 ← 与 tailwind 同步
3
+ @success-color: #22c55e; // green-500
4
+ @error-color: #ef4444; // red-500
5
+ @border-radius-base: 6px;
6
+ @font-size-base: 14px;
7
+ @line-height-base: 1.5715;
@@ -0,0 +1,119 @@
1
+ import type { PluginConfigItem } from '@chatbi-v/core';
2
+ import { Form,Input, InputNumber, Select, Switch } from 'antd';
3
+ import React from 'react';
4
+
5
+ /**
6
+ * 配置渲染策略接口
7
+ */
8
+ export type ConfigRenderer = (item: PluginConfigItem) => React.ReactNode;
9
+
10
+ /**
11
+ * 配置渲染策略工厂
12
+ * @description 使用策略模式管理不同类型的配置项渲染逻辑,便于扩展新的配置类型
13
+ */
14
+ class ConfigStrategyFactory {
15
+ private strategies: Map<string, ConfigRenderer> = new Map();
16
+
17
+ constructor() {
18
+ this.registerDefaults();
19
+ }
20
+
21
+ /**
22
+ * 注册默认的渲染策略
23
+ */
24
+ private registerDefaults() {
25
+ // Boolean -> Switch
26
+ this.register('boolean', (item) => (
27
+ <Form.Item
28
+ key={item.key}
29
+ label={item.label}
30
+ name={item.key}
31
+ tooltip={item.description}
32
+ extra={item.description}
33
+ valuePropName="checked"
34
+ >
35
+ <Switch />
36
+ </Form.Item>
37
+ ));
38
+
39
+ // Number -> InputNumber
40
+ this.register('number', (item) => (
41
+ <Form.Item
42
+ key={item.key}
43
+ label={item.label}
44
+ name={item.key}
45
+ tooltip={item.description}
46
+ extra={item.description}
47
+ >
48
+ <InputNumber min={item.min} max={item.max} className="w-full" />
49
+ </Form.Item>
50
+ ));
51
+
52
+ // Select -> Select
53
+ this.register('select', (item) => (
54
+ <Form.Item
55
+ key={item.key}
56
+ label={item.label}
57
+ name={item.key}
58
+ tooltip={item.description}
59
+ extra={item.description}
60
+ >
61
+ <Select options={item.options} mode={item.mode} />
62
+ </Form.Item>
63
+ ));
64
+
65
+ // String -> Input
66
+ this.register('string', (item) => (
67
+ <Form.Item
68
+ key={item.key}
69
+ label={item.label}
70
+ name={item.key}
71
+ tooltip={item.description}
72
+ extra={item.description}
73
+ >
74
+ <Input />
75
+ </Form.Item>
76
+ ));
77
+
78
+ // Default fallback
79
+ this.register('default', (item) => (
80
+ <Form.Item
81
+ key={item.key}
82
+ label={item.label}
83
+ name={item.key}
84
+ tooltip={item.description}
85
+ extra={item.description}
86
+ >
87
+ <Input />
88
+ </Form.Item>
89
+ ));
90
+ }
91
+
92
+ /**
93
+ * 注册自定义渲染策略
94
+ * @param type 配置类型
95
+ * @param renderer 渲染函数
96
+ */
97
+ register(type: string, renderer: ConfigRenderer) {
98
+ this.strategies.set(type, renderer);
99
+ }
100
+
101
+ /**
102
+ * 获取渲染策略
103
+ * @param type 配置类型
104
+ */
105
+ getRenderer(type: string): ConfigRenderer {
106
+ return this.strategies.get(type) || this.strategies.get('default')!;
107
+ }
108
+
109
+ /**
110
+ * 渲染配置项
111
+ * @param item 配置定义
112
+ */
113
+ render(item: PluginConfigItem): React.ReactNode {
114
+ const renderer = this.getRenderer(item.type);
115
+ return renderer(item);
116
+ }
117
+ }
118
+
119
+ export const configStrategyFactory = new ConfigStrategyFactory();
@@ -0,0 +1,52 @@
1
+ import { Plugin, PluginExtension, Slot } from '@chatbi-v/core';
2
+ import { Card, Divider, Empty } from 'antd';
3
+ import React from 'react';
4
+
5
+ import { SchemaSettingsRenderer } from './SchemaSettingsRenderer';
6
+
7
+ interface ExtensionSettingsProps {
8
+ plugin: Plugin;
9
+ }
10
+
11
+ export const ExtensionSettings: React.FC<ExtensionSettingsProps> = ({ plugin }) => {
12
+ const settingsExt = plugin.metadata.extensions?.find(e => e.slot === Slot.Settings);
13
+ const hasSchema = plugin.metadata.configuration && plugin.metadata.configuration.length > 0;
14
+
15
+ if (!settingsExt && !hasSchema) {
16
+ return (
17
+ <div className="h-full flex flex-col items-center justify-center">
18
+ <Empty description={<span style={{ color: 'rgb(var(--color-text-muted))' }}>该插件暂无配置项</span>} />
19
+ </div>
20
+ );
21
+ }
22
+
23
+ const title = settingsExt?.meta?.label || `${plugin.metadata.name} 配置`;
24
+
25
+ return (
26
+ <div className="max-w-3xl mx-auto">
27
+ <div className="mb-6">
28
+ <h2 className="text-xl font-medium mb-1" style={{ color: 'rgb(var(--color-text-main))' }}>{title}</h2>
29
+ <p className="text-sm" style={{ color: 'rgb(var(--color-text-muted))' }}>
30
+ {plugin.metadata.description}
31
+ </p>
32
+ </div>
33
+
34
+ <div
35
+ className="rounded-lg p-6 border transition-colors duration-200"
36
+ style={{
37
+ backgroundColor: 'rgb(var(--color-surface))',
38
+ borderColor: 'rgb(var(--color-border))',
39
+ }}
40
+ >
41
+ {/* 1. Custom Component Settings */}
42
+ {settingsExt && <settingsExt.component />}
43
+
44
+ {/* Divider if both exist */}
45
+ {settingsExt && hasSchema && <Divider style={{ borderColor: 'rgb(var(--color-border))', margin: '24px 0' }} />}
46
+
47
+ {/* 2. Schema Settings */}
48
+ {hasSchema && <SchemaSettingsRenderer plugin={plugin} />}
49
+ </div>
50
+ </div>
51
+ );
52
+ };