@chatbi-v/cli 1.0.7 → 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 (117) 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/{templates/default/apps/main → dist/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 +5662 -4194
  38. package/{templates/default → dist/monorepo}/package.json.hbs +5 -0
  39. package/dist/monorepo/pnpm-workspace.yaml.hbs +10 -0
  40. package/dist/{default → monorepo}/tsconfig.json.hbs +3 -1
  41. package/{templates/default/plugins/demo-plugin → dist/plugin}/package.json.hbs +9 -3
  42. package/dist/plugin/src/index.tsx.hbs +90 -0
  43. package/dist/plugin/tsconfig.json.hbs +14 -0
  44. package/package.json +18 -6
  45. package/templates/app/.env.hbs +8 -0
  46. package/templates/app/README.md.hbs +16 -0
  47. package/templates/app/chatbi.config.ts.hbs +60 -0
  48. package/{dist/default/apps/main → templates/app}/index.html.hbs +3 -4
  49. package/templates/app/package.json.hbs +34 -0
  50. package/templates/app/src/App.tsx.hbs +92 -0
  51. package/templates/app/src/components/GlobalErrorBoundary.tsx.hbs +69 -0
  52. package/templates/app/src/components/GlobalSettingsModal.tsx.hbs +35 -0
  53. package/templates/app/src/components/LayoutSkeletons.tsx.hbs +79 -0
  54. package/templates/app/src/components/index.ts.hbs +2 -0
  55. package/templates/app/src/custom-antd.less.hbs +7 -0
  56. package/templates/app/src/features/settings/ConfigRenderStrategy.tsx.hbs +119 -0
  57. package/templates/app/src/features/settings/ExtensionSettings.tsx.hbs +52 -0
  58. package/templates/app/src/features/settings/PluginList.tsx.hbs +115 -0
  59. package/templates/app/src/features/settings/PluginSettings.tsx.hbs +123 -0
  60. package/templates/app/src/features/settings/SchemaSettingsRenderer.tsx.hbs +56 -0
  61. package/templates/app/src/hooks/useAppRoutes.ts.hbs +39 -0
  62. package/templates/app/src/hooks/usePluginLoader.ts.hbs +22 -0
  63. package/templates/app/src/hooks/usePluginSettings.ts.hbs +29 -0
  64. package/templates/app/src/hooks/useThemeSync.ts.hbs +108 -0
  65. package/templates/app/src/index.css.hbs +45 -0
  66. package/templates/app/src/layouts/BackgroundEffects.tsx.hbs +10 -0
  67. package/templates/app/src/layouts/MainContent.tsx.hbs +58 -0
  68. package/templates/app/src/layouts/SidebarNav.tsx.hbs +182 -0
  69. package/templates/app/src/main.tsx.hbs +43 -0
  70. package/templates/app/src/providers/AppProviders.tsx.hbs +36 -0
  71. package/templates/app/src/services/api/index.ts.hbs +37 -0
  72. package/templates/app/src/services/api/modules/auth.ts.hbs +18 -0
  73. package/templates/app/src/services/config-service.ts.hbs +48 -0
  74. package/templates/app/src/stores/storage-adapter.ts.hbs +29 -0
  75. package/templates/app/src/stores/useSessionStore.ts.hbs +22 -0
  76. package/templates/app/src/stores/useUIStore.ts.hbs +64 -0
  77. package/templates/app/tailwind.config.cjs.hbs +14 -0
  78. package/templates/app/tsconfig.json.hbs +26 -0
  79. package/templates/app/vite.config.ts.hbs +89 -0
  80. package/templates/monorepo/.gitignore.hbs +3 -0
  81. package/{dist/default → templates/monorepo}/package.json.hbs +5 -0
  82. package/templates/monorepo/pnpm-workspace.yaml.hbs +10 -0
  83. package/templates/{default → monorepo}/tsconfig.json.hbs +3 -1
  84. package/{dist/default/plugins/demo-plugin → templates/plugin}/package.json.hbs +9 -3
  85. package/templates/plugin/src/index.tsx.hbs +90 -0
  86. package/templates/plugin/tsconfig.json.hbs +14 -0
  87. package/dist/default/apps/main/package.json.hbs +0 -20
  88. package/dist/default/apps/main/src/App.tsx.hbs +0 -162
  89. package/dist/default/apps/main/src/components/NavIcon.tsx.hbs +0 -41
  90. package/dist/default/apps/main/src/hooks/usePluginLoader.ts.hbs +0 -25
  91. package/dist/default/apps/main/src/index.css.hbs +0 -8
  92. package/dist/default/apps/main/src/main.tsx.hbs +0 -13
  93. package/dist/default/apps/main/src/pages/Guide.tsx.hbs +0 -133
  94. package/dist/default/apps/main/tailwind.config.cjs.hbs +0 -17
  95. package/dist/default/apps/main/tsconfig.json.hbs +0 -10
  96. package/dist/default/apps/main/vite.config.ts.hbs +0 -16
  97. package/dist/default/plugins/demo-plugin/src/index.tsx.hbs +0 -44
  98. package/dist/default/plugins/demo-plugin/tsconfig.json.hbs +0 -10
  99. package/dist/default/pnpm-workspace.yaml.hbs +0 -3
  100. package/templates/default/apps/main/package.json.hbs +0 -20
  101. package/templates/default/apps/main/src/App.tsx.hbs +0 -162
  102. package/templates/default/apps/main/src/components/NavIcon.tsx.hbs +0 -41
  103. package/templates/default/apps/main/src/hooks/usePluginLoader.ts.hbs +0 -25
  104. package/templates/default/apps/main/src/index.css.hbs +0 -8
  105. package/templates/default/apps/main/src/main.tsx.hbs +0 -13
  106. package/templates/default/apps/main/src/pages/Guide.tsx.hbs +0 -133
  107. package/templates/default/apps/main/tailwind.config.cjs.hbs +0 -17
  108. package/templates/default/apps/main/tsconfig.json.hbs +0 -10
  109. package/templates/default/apps/main/vite.config.ts.hbs +0 -16
  110. package/templates/default/plugins/demo-plugin/src/index.tsx.hbs +0 -44
  111. package/templates/default/plugins/demo-plugin/tsconfig.json.hbs +0 -10
  112. package/templates/default/pnpm-workspace.yaml.hbs +0 -3
  113. /package/dist/{default/apps/main → app}/postcss.config.cjs.hbs +0 -0
  114. /package/{templates/default → dist/monorepo}/.gitignore.hbs +0 -0
  115. /package/dist/{default → monorepo}/README.md.hbs +0 -0
  116. /package/templates/{default/apps/main → app}/postcss.config.cjs.hbs +0 -0
  117. /package/templates/{default → monorepo}/README.md.hbs +0 -0
@@ -0,0 +1,45 @@
1
+ @import 'antd/dist/reset.css';
2
+ @import './custom-antd.less'; /* 注入变量 */
3
+
4
+ @tailwind base;
5
+ @tailwind components;
6
+ @tailwind utilities;
7
+
8
+ @layer base {
9
+ :root {
10
+ /* Default Dark Theme (Slate/Indigo) */
11
+ --color-background: 15 23 42; /* slate-900 #0f172a */
12
+ --color-surface: 30 41 59; /* slate-800 #1e293b */
13
+ --color-primary: 99 102 241; /* indigo-500 #6366f1 */
14
+ --color-secondary: 168 85 247; /* purple-500 #a855f7 */
15
+ --color-accent: 34 211 238; /* cyan-400 #22d3ee */
16
+ --color-text-main: 226 232 240; /* slate-200 */
17
+ --color-text-muted: 148 163 184; /* slate-400 */
18
+ --color-border: 51 65 85; /* slate-700 */
19
+
20
+ /* Layout Variables Default (Compact) */
21
+ --layout-sidebar-width: 240px;
22
+ --layout-content-padding: 16px;
23
+ --layout-gap: 8px;
24
+ --layout-font-scale: 1;
25
+ }
26
+
27
+ [data-theme='light'] {
28
+ /* Light Theme */
29
+ --color-background: 248 250 252; /* slate-50 */
30
+ --color-surface: 255 255 255; /* white */
31
+ --color-primary: 79 70 229; /* indigo-600 */
32
+ --color-secondary: 147 51 234; /* purple-600 */
33
+ --color-accent: 6 182 212; /* cyan-500 */
34
+ --color-text-main: 15 23 42; /* slate-900 */
35
+ --color-text-muted: 100 116 139; /* slate-500 */
36
+ --color-border: 226 232 240; /* slate-200 */
37
+ }
38
+
39
+ body {
40
+ @apply bg-background text-text-main overflow-hidden antialiased;
41
+ }
42
+ }
43
+
44
+ @layer utilities {
45
+ }
@@ -0,0 +1,10 @@
1
+ import React from 'react';
2
+
3
+ export const BackgroundEffects: React.FC = () => {
4
+ return (
5
+ <div className="fixed top-0 left-0 w-full h-full overflow-hidden pointer-events-none z-0">
6
+ <div className="absolute top-[-20%] left-[-10%] w-[50%] h-[50%] bg-primary/10 rounded-full blur-[120px]" />
7
+ <div className="absolute bottom-[-20%] right-[-10%] w-[40%] h-[40%] bg-secondary/10 rounded-full blur-[100px]" />
8
+ </div>
9
+ );
10
+ };
@@ -0,0 +1,58 @@
1
+ import { PluginSlot, Slot, StatusBarItemSkeleton } from '@chatbi-v/core';
2
+ import React from 'react';
3
+ import { Navigate, Route, Routes } from 'react-router-dom';
4
+
5
+ import { ContentSkeleton } from '../components/LayoutSkeletons';
6
+ import { useAppRoutes } from '../hooks/useAppRoutes';
7
+
8
+ interface MainContentProps {
9
+ pluginsLoaded: boolean;
10
+ sharedProps: any;
11
+ }
12
+
13
+ export const MainContent: React.FC<MainContentProps> = React.memo(({
14
+ pluginsLoaded,
15
+ sharedProps,
16
+ }) => {
17
+ const { routes, defaultRoute } = useAppRoutes();
18
+
19
+ return (
20
+ <main className="flex-1 flex flex-col relative z-10 overflow-hidden bg-white/50 dark:bg-black/20 backdrop-blur-sm">
21
+ {!pluginsLoaded ? (
22
+ <ContentSkeleton />
23
+ ) : (
24
+ <>
25
+ <div className="flex-1 relative overflow-hidden min-h-0">
26
+ <Routes>
27
+ {routes.map((route) => (
28
+ <Route
29
+ key={route.path}
30
+ path={route.path}
31
+ element={<route.component {...sharedProps} />}
32
+ />
33
+ ))}
34
+
35
+ {/* 默认路由重定向 */}
36
+ {defaultRoute ? (
37
+ <Route path="/" element={<Navigate to={defaultRoute} replace />} />
38
+ ) : (
39
+ <Route path="/" element={<div className="flex h-full items-center justify-center text-slate-500">请在设置中启用至少一个业务插件</div>} />
40
+ )}
41
+
42
+ {/* 404 / Catch-all: 重定向到默认路由 */}
43
+ <Route path="*" element={
44
+ defaultRoute ? <Navigate to={defaultRoute} replace /> : <Navigate to="/" replace />
45
+ } />
46
+ </Routes>
47
+ </div>
48
+
49
+ {/* Status Bar Slot */}
50
+ <div className="h-6 bg-slate-100 dark:bg-slate-900 border-t border-slate-200 dark:border-white/5 flex items-center px-2 text-xs text-slate-500 gap-4">
51
+ <div className="flex-1">Ready</div>
52
+ <PluginSlot slot={Slot.StatusBar} skeleton={<StatusBarItemSkeleton />} />
53
+ </div>
54
+ </>
55
+ )}
56
+ </main>
57
+ );
58
+ });
@@ -0,0 +1,182 @@
1
+ import {
2
+ MenuFoldOutlined,
3
+ MenuUnfoldOutlined,
4
+ SettingOutlined,
5
+ ThunderboltOutlined,
6
+ } from '@ant-design/icons';
7
+ import { AvatarSkeleton,type PluginExtension, pluginManager, PluginSlot, SidebarIconSkeleton, Slot } from '@chatbi-v/core';
8
+ import React from 'react';
9
+ import { useNavigate } from 'react-router-dom';
10
+
11
+ import { chatbiConfig } from '../../chatbi.config';
12
+ import { NavSkeleton } from '../components/LayoutSkeletons';
13
+ import { useUIStore } from '../stores/useUIStore';
14
+
15
+ // 导航图标组件
16
+ export const NavIcon = React.memo(({
17
+ icon,
18
+ active,
19
+ onClick,
20
+ title,
21
+ label,
22
+ expanded,
23
+ }: {
24
+ icon: React.ReactNode;
25
+ active?: boolean;
26
+ onClick?: () => void;
27
+ title?: string;
28
+ label?: string;
29
+ expanded?: boolean;
30
+ }) => (
31
+ <div
32
+ onClick={onClick}
33
+ title={title}
34
+ className={`flex items-center cursor-pointer transition-all duration-300 relative group
35
+ ${expanded ? 'w-full' : 'w-12'} px-3 h-11 rounded-xl
36
+ ${
37
+ active
38
+ ? 'bg-primary/15 text-primary font-semibold shadow-sm ring-1 ring-primary/20 dark:bg-primary/20'
39
+ : 'text-slate-500 dark:text-slate-400 hover:bg-slate-100 dark:hover:bg-white/10 hover:text-slate-900 dark:hover:text-white hover:scale-105 active:scale-95'
40
+ }`}
41
+ >
42
+ <div className="w-6 h-6 flex items-center justify-center shrink-0">
43
+ <div className={`text-xl transition-transform duration-300 ${!expanded && active ? 'scale-110' : ''}`}>{icon}</div>
44
+ </div>
45
+
46
+ <div className={`overflow-hidden whitespace-nowrap transition-all duration-300 ease-in-out ${
47
+ expanded ? 'w-auto opacity-100 ml-3' : 'w-0 opacity-0 ml-0'
48
+ }`}>
49
+ <span className="text-sm block">
50
+ {label}
51
+ </span>
52
+ </div>
53
+
54
+ </div>
55
+ ));
56
+
57
+ interface SidebarNavProps {
58
+ activeTab: string;
59
+ pluginsLoaded: boolean;
60
+ }
61
+
62
+ export const SidebarNav: React.FC<SidebarNavProps> = React.memo(({
63
+ activeTab,
64
+ pluginsLoaded,
65
+ }) => {
66
+ const navigate = useNavigate();
67
+ const {
68
+ collapsed, setCollapsed,
69
+ setIsSettingsOpen, isMaximized
70
+ } = useUIStore();
71
+
72
+ const renderSidebarBottomItem = React.useCallback(({ key, extension }: { key: string, extension: PluginExtension }) => {
73
+ const Component = extension.component;
74
+ return (
75
+ <Component key={key} collapsed={collapsed} />
76
+ );
77
+ }, [collapsed]);
78
+
79
+ const renderSidebarSystemItem = React.useCallback(({ key, extension }: { key: string, extension: PluginExtension }) => {
80
+ const Component = extension.component;
81
+ return (
82
+ <div key={key} className={`flex items-center gap-3 transition-all duration-300 relative group
83
+ ${collapsed ? 'w-12 justify-center' : 'w-full px-4'} rounded-xl`}
84
+ >
85
+ {Component ? <Component collapsed={collapsed} /> : null}
86
+ </div>
87
+ );
88
+ }, [collapsed]);
89
+
90
+ return (
91
+ <nav 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)]
92
+ border-r border-slate-200/60 dark:border-white/10 backdrop-blur-xl bg-white/80 dark:bg-slate-900/80
93
+ ${isMaximized ? 'w-0 overflow-hidden opacity-0 p-0 border-none' : (collapsed ? 'w-[72px]' : 'w-[var(--layout-sidebar-width)]')}
94
+ h-screen
95
+ `}>
96
+ {!pluginsLoaded ? (
97
+ <NavSkeleton />
98
+ ) : (
99
+ <>
100
+ {/* 品牌标识 */}
101
+ <div className={`flex items-center gap-3 mb-6 transition-all duration-300 ${!collapsed ? 'w-full px-5' : 'justify-center'}`}>
102
+ <div className="w-12 h-12 rounded-2xl bg-gradient-to-tr from-primary to-secondary flex items-center justify-center shadow-lg shadow-primary/20 shrink-0 group cursor-pointer hover:scale-105 transition-transform duration-300">
103
+ <ThunderboltOutlined className="text-2xl text-white" />
104
+ </div>
105
+ {!collapsed && (
106
+ <div className="flex flex-col animate-in fade-in slide-in-from-left-4 duration-500">
107
+ <span className="font-bold text-xl leading-tight tracking-tight text-slate-800 dark:text-slate-100">{chatbiConfig.system.title}</span>
108
+ </div>
109
+ )}
110
+ </div>
111
+
112
+ {/* 导航项 */}
113
+ <div className="flex-1 w-full flex flex-col gap-[var(--layout-gap)] overflow-y-auto no-scrollbar items-center transition-all duration-300 px-3 pt-2">
114
+ <PluginSlot
115
+ slot={Slot.Sidebar}
116
+ className="flex flex-col gap-[var(--layout-gap)] w-full items-center"
117
+ skeleton={
118
+ <div className="flex flex-col gap-[var(--layout-gap)] w-full items-center">
119
+ {[1, 2, 3].map(i => <SidebarIconSkeleton key={i} />)}
120
+ </div>
121
+ }
122
+ renderItem={({ key, extension }: { key: string, extension: PluginExtension }) => (
123
+ <NavIcon
124
+ key={key}
125
+ icon={extension.meta?.icon}
126
+ label={extension.meta?.label}
127
+ active={
128
+ // Support multi-level route matching
129
+ // e.g., activeTab='scene/10/tables' should match path='/scene'
130
+ // Remove leading slash for consistency
131
+ activeTab === (extension.meta?.path || extension.meta?.key)?.replace(/^\//, '') ||
132
+ activeTab.startsWith(((extension.meta?.path || extension.meta?.key)?.replace(/^\//, '') || '') + '/')
133
+ }
134
+ onClick={() => navigate(extension.meta?.path || '/')}
135
+ expanded={!collapsed}
136
+ />
137
+ )}
138
+ />
139
+ </div>
140
+
141
+ {/* 底部操作区 */}
142
+ <div className="w-full flex flex-col gap-[var(--layout-gap)] mt-auto mb-2 items-center transition-all duration-300 px-3">
143
+ <PluginSlot
144
+ slot={Slot.SidebarBottom}
145
+ className="w-full flex flex-col gap-[var(--layout-gap)]"
146
+ skeleton={<SidebarIconSkeleton />}
147
+ renderItem={renderSidebarBottomItem}
148
+ />
149
+ <NavIcon
150
+ icon={<SettingOutlined />}
151
+ onClick={() => setIsSettingsOpen(true)}
152
+ title="系统设置"
153
+ label="设置"
154
+ expanded={!collapsed}
155
+ />
156
+
157
+ <div className={`h-px bg-slate-200 dark:bg-white/10 my-2 transition-all duration-300 ${collapsed ? 'w-8' : 'w-full'}`} />
158
+
159
+ <PluginSlot
160
+ slot={Slot.SidebarSystem}
161
+ className={`w-full transition-all duration-300 ${collapsed ? 'flex justify-center' : ''}`}
162
+ skeleton={
163
+ <div className={`flex items-center gap-3 transition-all duration-300 ${collapsed ? 'w-12 justify-center' : 'w-full px-4'}`}>
164
+ <AvatarSkeleton />
165
+ </div>
166
+ }
167
+ renderItem={renderSidebarSystemItem}
168
+ />
169
+
170
+ <NavIcon
171
+ icon={collapsed ? <MenuUnfoldOutlined /> : <MenuFoldOutlined />}
172
+ onClick={() => setCollapsed(!collapsed)}
173
+ title={collapsed ? "展开侧边栏" : "折叠侧边栏"}
174
+ label={collapsed ? "展开" : "折叠"}
175
+ expanded={!collapsed}
176
+ />
177
+ </div>
178
+ </>
179
+ )}
180
+ </nav>
181
+ );
182
+ });
@@ -0,0 +1,43 @@
1
+ import 'antd/dist/reset.css'
2
+ import './index.css'
3
+
4
+ import React from 'react'
5
+ import ReactDOM from 'react-dom/client'
6
+ import { HashRouter } from 'react-router-dom'
7
+
8
+ import App from './App'
9
+ import { GlobalErrorBoundary } from './components'
10
+ import { api } from './services/api'
11
+
12
+ // 挂载全局 API 实例供插件使用
13
+ // 插件可以通过 window.chatbi.api 访问核心请求实例
14
+ declare global {
15
+ interface Window {
16
+ chatbi: {
17
+ api: typeof api;
18
+ };
19
+ }
20
+ }
21
+
22
+ window.chatbi = {
23
+ api
24
+ };
25
+
26
+ // API Mock 策略说明:
27
+ // Mock 逻辑已移至 services/api/index.ts 中统一处理
28
+ // 开启方式:
29
+ // 1. URL 参数: ?mock=true (优先级最高)
30
+ // 2. 环境变量: VITE_USE_MOCK=true (在 .env.development 中配置)
31
+
32
+ // 应用启动
33
+ ReactDOM.createRoot(document.getElementById('root')!).render(
34
+ <React.StrictMode>
35
+ {/* 全局错误边界,捕获渲染过程中的未处理异常 */}
36
+ <GlobalErrorBoundary>
37
+ {/* 路由模式:默认使用 HashRouter,如果需要 history 模式请改为 BrowserRouter */}
38
+ <HashRouter>
39
+ <App />
40
+ </HashRouter>
41
+ </GlobalErrorBoundary>
42
+ </React.StrictMode>,
43
+ )
@@ -0,0 +1,36 @@
1
+ import { XProvider } from '@ant-design/x';
2
+ import { ApiProvider, pluginManager, PluginProvider } from '@chatbi-v/core';
3
+ import { App as AntdApp, ConfigProvider } from 'antd';
4
+ import React, { ReactNode } from 'react';
5
+
6
+ import { useThemeSync } from '../hooks/useThemeSync';
7
+ import { api } from '../services/api';
8
+
9
+ interface AppProvidersProps {
10
+ children: ReactNode;
11
+ }
12
+
13
+ /**
14
+ * 应用全局 Providers
15
+ * @description 封装所有全局 Context Provider,包括 API, Plugin, Theme, Antd 等
16
+ * @note 使用 reduceRight 组合 Provider,避免嵌套地狱 (Provider Hell)
17
+ */
18
+ export const AppProviders: React.FC<AppProvidersProps> = ({ children }) => {
19
+ const themeConfig = useThemeSync();
20
+
21
+ // 定义 Provider 列表,顺序从外到内
22
+ const providers: Array<[React.ComponentType<any>, Record<string, any>]> = [
23
+ // 核心服务
24
+ [ApiProvider, { api }],
25
+ [PluginProvider, { manager: pluginManager }],
26
+ // UI 主题配置
27
+ [ConfigProvider, { theme: themeConfig }],
28
+ [AntdApp, {}],
29
+ [XProvider, { theme: themeConfig }],
30
+ ];
31
+
32
+ // 使用 reduceRight 从内向外包裹 children
33
+ return providers.reduceRight((acc, [Provider, props]) => {
34
+ return <Provider {...props}>{acc}</Provider>;
35
+ }, children);
36
+ };
@@ -0,0 +1,37 @@
1
+ import { ApiEngine, AxiosAdapter, createLogger, isMockMode } from '@chatbi-v/core';
2
+ import { MockAdapter } from '@chatbi-v/mocks';
3
+
4
+ const logger = createLogger('Api');
5
+
6
+ // 从环境变量获取配置
7
+ const baseURL = import.meta.env.VITE_API_BASE_URL || '/api';
8
+ const timeout = Number(import.meta.env.VITE_API_TIMEOUT) || 10000;
9
+
10
+ // 统一 Mock 模式决策
11
+ const useMock = isMockMode();
12
+
13
+ if (useMock) {
14
+ logger.info(`⚠️ Mock 模式已启用 (基于核心库 isMockMode 判断)`);
15
+ } else {
16
+ logger.debug(`正在以真实模式运行`);
17
+ }
18
+
19
+ // 创建适配器 (根据 Mock 模式切换)
20
+ const adapter = useMock
21
+ ? new MockAdapter(100) // 100ms 延迟模拟
22
+ : new AxiosAdapter(baseURL, timeout);
23
+
24
+ // 创建 API 引擎实例
25
+ export const api = new ApiEngine(adapter);
26
+
27
+ // 自动加载 ./modules 下的所有配置文件
28
+ // 使用 Vite 的 import.meta.glob 功能
29
+ const modules = import.meta.glob('./modules/*.ts', { eager: true });
30
+
31
+ Object.entries(modules).forEach(([path, module]: [string, any]) => {
32
+ // 从路径中提取模块名 (e.g., './modules/auth.ts' -> 'auth')
33
+ const name = path.match(/\.\/modules\/(.*)\.ts$/)?.[1];
34
+ if (name && module.default) {
35
+ api.register({ [name]: module.default });
36
+ }
37
+ });
@@ -0,0 +1,18 @@
1
+ export default {
2
+ login: {
3
+ url: '/auth/login',
4
+ method: 'POST',
5
+ desc: '用户登录',
6
+ mock: true,
7
+ mockData: {
8
+ code: 0,
9
+ message: 'success',
10
+ data: { token: 'mock-token-123', user: { id: 1, name: 'Admin' } },
11
+ },
12
+ },
13
+ logout: {
14
+ url: '/auth/logout',
15
+ method: 'POST',
16
+ desc: '用户登出',
17
+ },
18
+ };
@@ -0,0 +1,48 @@
1
+ import { pluginManager, dateUtils } from '@chatbi-v/core';
2
+
3
+ export const exportSystemConfig = () => {
4
+ const exportData: Record<string, any> = {
5
+ pluginStates: {},
6
+ configs: {},
7
+ timestamp: dateUtils.dayjs().toISOString(),
8
+ environment: {
9
+ userAgent: navigator.userAgent,
10
+ language: navigator.language,
11
+ },
12
+ };
13
+
14
+ // 1. Export Plugin States
15
+ const allPlugins = pluginManager.getPlugins();
16
+ allPlugins.forEach((p) => {
17
+ exportData.pluginStates[p.id] = pluginManager.getPluginState(p.id);
18
+ });
19
+
20
+ // 2. Export Configs (scan localStorage for plugin related keys)
21
+ // We look for keys starting with 'plugin:' and global config keys
22
+ const globalKeys = ['chatbi-theme', 'chatbi-layout'];
23
+
24
+ // Global configs
25
+ globalKeys.forEach(key => {
26
+ const val = localStorage.getItem(key);
27
+ if (val) exportData.configs[key] = val;
28
+ });
29
+
30
+ // Plugin specific configs and System configs
31
+ for (let i = 0; i < localStorage.length; i++) {
32
+ const key = localStorage.key(i);
33
+ if (key && (key.startsWith('plugin:') || key.startsWith('system:'))) {
34
+ exportData.configs[key] = localStorage.getItem(key);
35
+ }
36
+ }
37
+
38
+ // Create and download file
39
+ const blob = new Blob([JSON.stringify(exportData, null, 2)], { type: 'application/json' });
40
+ const url = URL.createObjectURL(blob);
41
+ const a = document.createElement('a');
42
+ a.href = url;
43
+ a.download = `chatbi-config-${dateUtils.formatDate()}.json`;
44
+ document.body.appendChild(a);
45
+ a.click();
46
+ document.body.removeChild(a);
47
+ URL.revokeObjectURL(url);
48
+ };
@@ -0,0 +1,29 @@
1
+ import { pluginManager } from '@chatbi-v/core';
2
+ import { StateStorage } from 'zustand/middleware';
3
+
4
+ export const createZustandStorage = (scope: 'system' | 'shared' = 'system'): StateStorage => {
5
+ return {
6
+ getItem: (name: string): string | null => {
7
+ // Lazy access to ensure pluginManager is ready (though it's a singleton)
8
+ const storageManager = pluginManager.getStorageManager();
9
+ const storage = scope === 'system'
10
+ ? storageManager.getSystemStorage()
11
+ : storageManager.getSharedStorage();
12
+ return storage.getItem(name);
13
+ },
14
+ setItem: (name: string, value: string): void => {
15
+ const storageManager = pluginManager.getStorageManager();
16
+ const storage = scope === 'system'
17
+ ? storageManager.getSystemStorage()
18
+ : storageManager.getSharedStorage();
19
+ storage.setItem(name, value);
20
+ },
21
+ removeItem: (name: string): void => {
22
+ const storageManager = pluginManager.getStorageManager();
23
+ const storage = scope === 'system'
24
+ ? storageManager.getSystemStorage()
25
+ : storageManager.getSharedStorage();
26
+ storage.removeItem(name);
27
+ },
28
+ };
29
+ };
@@ -0,0 +1,22 @@
1
+ import { create } from 'zustand';
2
+ import { createJSONStorage,persist } from 'zustand/middleware';
3
+
4
+ import { createZustandStorage } from './storage-adapter';
5
+
6
+ export interface SessionState {
7
+ activeSession: string | null;
8
+ setActiveSession: (id: string | null) => void;
9
+ }
10
+
11
+ export const useSessionStore = create<SessionState>()(
12
+ persist(
13
+ (set) => ({
14
+ activeSession: null,
15
+ setActiveSession: (id) => set({ activeSession: id }),
16
+ }),
17
+ {
18
+ name: 'chatbi-session-storage',
19
+ storage: createJSONStorage(() => createZustandStorage('system')),
20
+ }
21
+ )
22
+ );
@@ -0,0 +1,64 @@
1
+ import { create } from 'zustand';
2
+ import { createJSONStorage,persist } from 'zustand/middleware';
3
+
4
+ import { createZustandStorage } from './storage-adapter';
5
+
6
+ export interface UIState {
7
+ collapsed: boolean;
8
+ toggleCollapsed: () => void;
9
+ setCollapsed: (v: boolean) => void;
10
+
11
+ isRightPanelOpen: boolean;
12
+ toggleRightPanel: () => void;
13
+ setIsRightPanelOpen: (v: boolean) => void;
14
+
15
+ isMaximized: boolean;
16
+ toggleMaximized: () => void;
17
+ setIsMaximized: (v: boolean) => void;
18
+
19
+ isSettingsOpen: boolean;
20
+ setIsSettingsOpen: (v: boolean) => void;
21
+
22
+ currentThemeId: string;
23
+ themeMode: 'dark' | 'light';
24
+ setThemeId: (id: string) => void;
25
+ setThemeMode: (mode: 'dark' | 'light') => void;
26
+ toggleThemeMode: () => void;
27
+ }
28
+
29
+ export const useUIStore = create<UIState>()(
30
+ persist(
31
+ (set) => ({
32
+ collapsed: false,
33
+ toggleCollapsed: () => set((state) => ({ collapsed: !state.collapsed })),
34
+ setCollapsed: (v) => set({ collapsed: v }),
35
+
36
+ isRightPanelOpen: true,
37
+ toggleRightPanel: () => set((state) => ({ isRightPanelOpen: !state.isRightPanelOpen })),
38
+ setIsRightPanelOpen: (v) => set({ isRightPanelOpen: v }),
39
+
40
+ isMaximized: false,
41
+ toggleMaximized: () => set((state) => ({ isMaximized: !state.isMaximized })),
42
+ setIsMaximized: (v) => set({ isMaximized: v }),
43
+
44
+ isSettingsOpen: false,
45
+ setIsSettingsOpen: (v) => set({ isSettingsOpen: v }),
46
+
47
+ currentThemeId: 'default',
48
+ themeMode: 'dark',
49
+ setThemeId: (id) => set({ currentThemeId: id }),
50
+ setThemeMode: (mode) => set({ themeMode: mode }),
51
+ toggleThemeMode: () => set((state) => ({ themeMode: state.themeMode === 'light' ? 'dark' : 'light' })),
52
+ }),
53
+ {
54
+ name: 'chatbi-ui-storage',
55
+ storage: createJSONStorage(() => createZustandStorage('system')),
56
+ partialize: (state) => ({
57
+ currentThemeId: state.currentThemeId,
58
+ themeMode: state.themeMode,
59
+ collapsed: state.collapsed,
60
+ isRightPanelOpen: state.isRightPanelOpen
61
+ }),
62
+ }
63
+ )
64
+ );
@@ -0,0 +1,14 @@
1
+ /** @type {import('tailwindcss').Config} */
2
+ module.exports = {
3
+ presets: [
4
+ require('@chatbi-v/tailwind-config')
5
+ ],
6
+ darkMode: 'class',
7
+ content: [
8
+ './index.html',
9
+ './src/**/*.{ts,tsx}',
10
+ './.chatbi/core/src/**/*.{ts,tsx}',
11
+ './.chatbi/plugin-*/**/*.{ts,tsx}'
12
+ ],
13
+ plugins: []
14
+ }
@@ -0,0 +1,26 @@
1
+ {
2
+ "extends": "{{tsconfigPath}}",
3
+ "compilerOptions": {
4
+ "target": "ES2020",
5
+ "useDefineForClassFields": true,
6
+ "lib": ["ES2020", "DOM", "DOM.Iterable"],
7
+ "module": "ESNext",
8
+ "skipLibCheck": true,
9
+ "moduleResolution": "bundler",
10
+ "allowImportingTsExtensions": true,
11
+ "resolveJsonModule": true,
12
+ "isolatedModules": true,
13
+ "noEmit": true,
14
+ "declaration": false,
15
+ "declarationMap": false,
16
+ "composite": false,
17
+ "jsx": "react-jsx",
18
+ "types": ["vite/client", "node"],
19
+ "baseUrl": ".",
20
+ "paths": {
21
+ "@/*": ["./src/*"]
22
+ }
23
+ },
24
+ "include": ["src"],
25
+ "references": []
26
+ }