@zhin.js/console 1.0.21 → 1.0.23

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 (56) hide show
  1. package/CHANGELOG.md +20 -0
  2. package/README.md +4 -4
  3. package/client/components.json +17 -0
  4. package/client/index.html +1 -1
  5. package/client/src/components/PluginConfigForm/BasicFieldRenderers.tsx +89 -180
  6. package/client/src/components/PluginConfigForm/CollectionFieldRenderers.tsx +97 -200
  7. package/client/src/components/PluginConfigForm/CompositeFieldRenderers.tsx +31 -70
  8. package/client/src/components/PluginConfigForm/FieldRenderer.tsx +27 -77
  9. package/client/src/components/PluginConfigForm/NestedFieldRenderer.tsx +33 -53
  10. package/client/src/components/PluginConfigForm/index.tsx +71 -173
  11. package/client/src/components/ui/accordion.tsx +54 -0
  12. package/client/src/components/ui/alert.tsx +62 -0
  13. package/client/src/components/ui/avatar.tsx +41 -0
  14. package/client/src/components/ui/badge.tsx +32 -0
  15. package/client/src/components/ui/button.tsx +50 -0
  16. package/client/src/components/ui/card.tsx +50 -0
  17. package/client/src/components/ui/checkbox.tsx +25 -0
  18. package/client/src/components/ui/dialog.tsx +87 -0
  19. package/client/src/components/ui/dropdown-menu.tsx +97 -0
  20. package/client/src/components/ui/input.tsx +21 -0
  21. package/client/src/components/ui/scroll-area.tsx +43 -0
  22. package/client/src/components/ui/select.tsx +127 -0
  23. package/client/src/components/ui/separator.tsx +23 -0
  24. package/client/src/components/ui/skeleton.tsx +12 -0
  25. package/client/src/components/ui/switch.tsx +26 -0
  26. package/client/src/components/ui/tabs.tsx +52 -0
  27. package/client/src/components/ui/textarea.tsx +20 -0
  28. package/client/src/components/ui/tooltip.tsx +27 -0
  29. package/client/src/layouts/dashboard.tsx +91 -221
  30. package/client/src/main.tsx +38 -42
  31. package/client/src/pages/dashboard-bots.tsx +91 -137
  32. package/client/src/pages/dashboard-home.tsx +133 -204
  33. package/client/src/pages/dashboard-logs.tsx +125 -196
  34. package/client/src/pages/dashboard-plugin-detail.tsx +261 -329
  35. package/client/src/pages/dashboard-plugins.tsx +108 -105
  36. package/client/src/style.css +156 -865
  37. package/client/src/theme/index.ts +60 -35
  38. package/client/tailwind.config.js +78 -69
  39. package/dist/client.js +1 -1
  40. package/dist/cva.js +47 -0
  41. package/dist/index.html +1 -1
  42. package/dist/index.js +6 -6
  43. package/dist/react-router.js +7121 -5585
  44. package/dist/react.js +192 -149
  45. package/dist/style.css +2 -2
  46. package/lib/bin.js +2 -2
  47. package/lib/build.js +2 -2
  48. package/lib/index.d.ts +0 -3
  49. package/lib/index.js +160 -205
  50. package/lib/transform.d.ts +26 -0
  51. package/lib/transform.js +78 -0
  52. package/lib/websocket.d.ts +0 -1
  53. package/package.json +9 -8
  54. package/dist/radix-ui-themes.js +0 -9305
  55. package/lib/dev.d.ts +0 -18
  56. package/lib/dev.js +0 -87
@@ -1,22 +1,20 @@
1
- import { Outlet, Link, useSelector, useDispatch, toggleSidebar, setActiveMenu, useWebSocket } from "@zhin.js/client"
2
- import React, { useMemo } from "react"
3
- import { Avatar, DropdownMenu } from 'radix-ui'
4
- import * as Themes from '@radix-ui/themes'
5
- import {Menu, Search,Bell,User,Users,BarChart3,HelpCircle,LogOut} from 'lucide-react'
1
+ import { Outlet, Link, useSelector, useDispatch, toggleSidebar, setActiveMenu } from "@zhin.js/client"
2
+ import { useMemo } from "react"
3
+ import { Menu, Search } from 'lucide-react'
6
4
  import { cn } from "@zhin.js/client"
7
5
  import { ThemeToggle } from "../components/ThemeToggle"
8
-
9
- const { Box, Flex, Text, Heading, IconButton, Badge, TextField, ScrollArea, Container } = Themes
6
+ import { Button } from "../components/ui/button"
7
+ import { Input } from "../components/ui/input"
8
+ import { ScrollArea } from "../components/ui/scroll-area"
9
+ import { Avatar, AvatarImage, AvatarFallback } from "../components/ui/avatar"
10
10
 
11
11
  export default function DashboardLayout() {
12
12
  const dispatch = useDispatch()
13
- const ws = useWebSocket()
14
13
  const sidebarOpen = useSelector(state => state.ui.sidebarOpen)
15
14
  const activeMenu = useSelector(state => state.ui.activeMenu)
16
15
  const routes = useSelector(state => state.route.routes)
17
16
 
18
17
  const menuItems = useMemo(() => {
19
- // 找到 dashboard 路由
20
18
  const dashboardRoute = routes.find(route => route.key === 'dashboard-layout')
21
19
  if (!dashboardRoute || !dashboardRoute.children) {
22
20
  return []
@@ -33,226 +31,98 @@ export default function DashboardLayout() {
33
31
  }, [routes])
34
32
 
35
33
  return (
36
- <Flex className="h-screen bg-gradient-to-br from-gray-50 to-gray-100 dark:from-gray-900 dark:to-gray-800">
37
- {/* 侧边栏 */}
38
- <Flex
39
- direction="column"
40
- className={cn(
41
- "glass transition-all duration-300 shadow-xl border-r border-gray-200/50 dark:border-gray-700/50",
42
- sidebarOpen ? "w-64" : "w-20"
43
- )}
44
- >
45
- {/* Logo 区域 */}
46
- <Box p="4" className="border-b border-gray-200/50 dark:border-gray-700/50">
47
- <Flex
48
- align="center"
49
- className={cn(
50
- "transition-all duration-300",
51
- sidebarOpen ? "gap-3" : "justify-center"
52
- )}
53
- >
54
- <Flex
55
- align="center"
56
- justify="center"
57
- className="w-11 min-w-11 h-11 rounded-xl shadow-md bg-gradient-to-br from-blue-500 to-purple-600"
58
- >
59
- <Text size="5" weight="bold" className="text-white">
60
- Z
61
- </Text>
62
- </Flex>
34
+ <div className="flex h-screen bg-background">
35
+ {/* Sidebar */}
36
+ <div className={cn(
37
+ "flex flex-col border-r bg-sidebar transition-all duration-300",
38
+ sidebarOpen ? "w-64" : "w-16"
39
+ )}>
40
+ {/* Logo */}
41
+ <div className="p-4 border-b">
42
+ <div className={cn(
43
+ "flex items-center transition-all duration-300",
44
+ sidebarOpen ? "gap-3" : "justify-center"
45
+ )}>
46
+ <div className="flex items-center justify-center w-9 h-9 min-w-9 rounded-lg bg-foreground text-background font-bold text-lg">
47
+ Z
48
+ </div>
63
49
  {sidebarOpen && (
64
- <Flex direction="column" gap="0">
65
- <Heading size="4" className="text-gray-900 dark:text-gray-100">
66
- Zhin.js
67
- </Heading>
68
- <Text size="1" color="gray">管理控制台</Text>
69
- </Flex>
50
+ <div className="flex flex-col">
51
+ <span className="text-base font-semibold">Zhin.js</span>
52
+ <span className="text-xs text-muted-foreground">管理控制台</span>
53
+ </div>
70
54
  )}
71
- </Flex>
72
- </Box>
55
+ </div>
56
+ </div>
73
57
 
74
- {/* 菜单列表 */}
75
- <ScrollArea className="flex-1" scrollbars="vertical">
76
- <Box p="3">
77
- <Flex direction="column" gap="2">
78
- {menuItems.map((item) => {
79
- const isActive = activeMenu === item.key
80
- return (
81
- <Link
82
- key={item.key}
83
- to={item.href}
84
- onClick={() => dispatch(setActiveMenu(item.key))}
85
- className={cn("menu-item", isActive && "active")}
86
- >
87
- <div className="icon">
88
- {item.icon}
89
- </div>
90
- {sidebarOpen && (
91
- <div className="text">
92
- <div className="title">{item.title}</div>
93
- </div>
94
- )}
95
- {isActive && sidebarOpen && <div className="indicator" />}
96
- </Link>
97
- )
98
- })}
99
- </Flex>
100
- </Box>
58
+ {/* Menu */}
59
+ <ScrollArea className="flex-1">
60
+ <div className="p-2 space-y-1">
61
+ {menuItems.map((item) => {
62
+ const isActive = activeMenu === item.key
63
+ return (
64
+ <Link
65
+ key={item.key}
66
+ to={item.href}
67
+ onClick={() => dispatch(setActiveMenu(item.key))}
68
+ className={cn(
69
+ "menu-item",
70
+ isActive && "active",
71
+ !sidebarOpen && "justify-center px-2"
72
+ )}
73
+ >
74
+ <span className="shrink-0">{item.icon}</span>
75
+ {sidebarOpen && <span className="truncate">{item.title}</span>}
76
+ </Link>
77
+ )
78
+ })}
79
+ </div>
101
80
  </ScrollArea>
81
+ </div>
102
82
 
103
- {/* 侧边栏底部用户信息 */}
104
- <Flex p="3" className="border-t border-gray-200/50 dark:border-gray-700/50 flex-shrink-0">
105
- <Flex
106
- align="center"
107
- gap="2"
108
- p="2"
109
- className={cn(
110
- "rounded-xl bg-gray-50/50 dark:bg-gray-800/50 transition-all duration-200 cursor-pointer hover:bg-gray-100 dark:hover:bg-gray-700",
111
- !sidebarOpen && "justify-center"
112
- )}
113
- >
114
- <Avatar.Root className="w-8 h-8 min-w-8 border-2 border-gray-200 dark:border-gray-700 flex-shrink-0">
115
- <Avatar.Image src="https://i.pravatar.cc/150?u=admin" alt="管理员" />
116
- <Avatar.Fallback>管</Avatar.Fallback>
117
- </Avatar.Root>
118
- {sidebarOpen && (
119
- <Flex direction="column" gap="0" className="flex-1 min-w-0">
120
- <Text size="2" weight="medium" className="truncate text-gray-900 dark:text-gray-100">
121
- 管理员
122
- </Text>
123
- <Text size="1" color="gray" className="truncate">
124
- admin@zhin.com
125
- </Text>
126
- </Flex>
127
- )}
128
- </Flex>
129
- </Flex>
130
- </Flex>
131
-
132
- {/* 主内容区域 */}
133
- <Flex direction="column" className="flex-1 overflow-hidden">
134
- {/* 顶部导航栏 */}
135
- <Box className="glass border-b border-gray-200/50 dark:border-gray-700/50 shadow-sm">
136
- <Flex justify="between" align="center" px="4" className="h-16">
137
- {/* 左侧 */}
138
- <Flex align="center" gap="3">
139
- <IconButton
140
- variant="ghost"
141
- size="2"
142
- onClick={() => dispatch(toggleSidebar())}
143
- className="hover-lift rounded-xl"
144
- >
145
- <Menu className="w-5 h-5" />
146
- </IconButton>
147
- <Flex direction="column" gap="0">
148
- <Heading size="3" className="text-gray-900 dark:text-gray-100">控制台</Heading>
149
- <Text size="1" color="gray">欢迎回来!</Text>
150
- </Flex>
151
- </Flex>
152
-
153
- {/* 中间搜索栏 */}
154
- <Flex className="hidden md:flex flex-1 max-w-xl mx-6">
155
- <Box className="relative">
156
- <Search className="absolute left-3 top-1/2 -translate-y-1/2 w-4 h-4 text-gray-400 dark:text-gray-500" />
157
- <TextField.Root
158
- placeholder="搜索功能、用户、设置..."
159
- size="2"
160
- className="w-full pl-10"
161
- />
162
- </Box>
163
- </Flex>
164
-
165
- {/* 右侧操作区 */}
166
- <Flex align="center" gap="2">
167
- {/* 主题切换 */}
168
- <ThemeToggle />
83
+ {/* Main content area */}
84
+ <div className="flex flex-col flex-1 overflow-hidden">
85
+ {/* Top bar */}
86
+ <header className="flex items-center justify-between h-14 px-4 border-b bg-background/95 backdrop-blur supports-[backdrop-filter]:bg-background/60">
87
+ {/* Left */}
88
+ <div className="flex items-center gap-3">
89
+ <Button
90
+ variant="ghost"
91
+ size="icon"
92
+ onClick={() => dispatch(toggleSidebar())}
93
+ >
94
+ <Menu className="h-5 w-5" />
95
+ </Button>
96
+ <div className="flex flex-col">
97
+ <h2 className="text-sm font-semibold">控制台</h2>
98
+ <span className="text-xs text-muted-foreground">欢迎回来</span>
99
+ </div>
100
+ </div>
169
101
 
170
- {/* 通知按钮 */}
171
- <Box className="relative">
172
- <IconButton
173
- variant="ghost"
174
- size="2"
175
- className="hover-lift rounded-xl"
176
- >
177
- <Bell className="w-5 h-5" />
178
- </IconButton>
179
- <Badge
180
- color="red"
181
- variant="solid"
182
- size="1"
183
- className="absolute -top-1 -right-1 min-w-5 h-5 flex items-center justify-center p-0"
184
- >
185
- 3
186
- </Badge>
187
- </Box>
102
+ {/* Center search */}
103
+ <div className="hidden md:flex flex-1 max-w-md mx-6">
104
+ <div className="relative w-full">
105
+ <Search className="absolute left-3 top-1/2 -translate-y-1/2 h-4 w-4 text-muted-foreground" />
106
+ <Input
107
+ placeholder="搜索功能、插件、设置..."
108
+ className="pl-9 bg-muted/50"
109
+ />
110
+ </div>
111
+ </div>
188
112
 
189
- {/* 用户菜单 */}
190
- <DropdownMenu.Root>
191
- <DropdownMenu.Trigger asChild>
192
- <Flex
193
- align="center"
194
- gap="2"
195
- px="2"
196
- py="1"
197
- className="cursor-pointer hover:bg-gray-100 dark:hover:bg-gray-800 transition-all duration-200 rounded-xl"
198
- >
199
- <Avatar.Root className="w-8 h-8 border-2 border-blue-500/20 dark:border-blue-400/30">
200
- <Avatar.Image src="https://i.pravatar.cc/150?u=admin" alt="管理员" />
201
- <Avatar.Fallback>管</Avatar.Fallback>
202
- </Avatar.Root>
203
- <Flex direction="column" gap="0" className="hidden lg:flex">
204
- <Text size="2" weight="medium" className="text-gray-900 dark:text-gray-100">
205
- 管理员
206
- </Text>
207
- <Text size="1" color="gray">
208
- admin@zhin.com
209
- </Text>
210
- </Flex>
211
- </Flex>
212
- </DropdownMenu.Trigger>
213
- <DropdownMenu.Content align="end" className="min-w-56">
214
- <Box p="3" className="border-b border-gray-200 dark:border-gray-700">
215
- <Text size="2" weight="bold" className="block">
216
- 登录为
217
- </Text>
218
- <Text size="1" color="gray" className="block">
219
- admin@zhin.com
220
- </Text>
221
- </Box>
222
- <DropdownMenu.Item>
223
- <User className="mr-2 h-4 w-4" />
224
- 我的设置
225
- </DropdownMenu.Item>
226
- <DropdownMenu.Item>
227
- <Users className="mr-2 h-4 w-4" />
228
- 团队设置
229
- </DropdownMenu.Item>
230
- <DropdownMenu.Item>
231
- <BarChart3 className="mr-2 h-4 w-4" />
232
- 数据分析
233
- </DropdownMenu.Item>
234
- <DropdownMenu.Item>
235
- <HelpCircle className="mr-2 h-4 w-4" />
236
- 帮助与反馈
237
- </DropdownMenu.Item>
238
- <DropdownMenu.Separator />
239
- <DropdownMenu.Item color="red">
240
- <LogOut className="mr-2 h-4 w-4" />
241
- 退出登录
242
- </DropdownMenu.Item>
243
- </DropdownMenu.Content>
244
- </DropdownMenu.Root>
245
- </Flex>
246
- </Flex>
247
- </Box>
113
+ {/* Right actions */}
114
+ <div className="flex items-center gap-1">
115
+ <ThemeToggle />
116
+ </div>
117
+ </header>
248
118
 
249
- {/* 主内容区域 */}
250
- <Flex className="flex-1 overflow-auto">
251
- <Container size="4" p="6">
119
+ {/* Page content */}
120
+ <main className="flex-1 overflow-auto">
121
+ <div className="max-w-7xl mx-auto p-6">
252
122
  <Outlet />
253
- </Container>
254
- </Flex>
255
- </Flex>
256
- </Flex>
123
+ </div>
124
+ </main>
125
+ </div>
126
+ </div>
257
127
  )
258
128
  }
@@ -1,37 +1,38 @@
1
1
  import { StrictMode, useEffect, useState } from 'react'
2
2
  import { createRoot } from 'react-dom/client'
3
3
  import { Provider as ReduxProvider } from 'react-redux'
4
- import {Home, Package, Bot, FileText} from 'lucide-react'
5
- import { store, DynamicRouter, persistor, addPage, useSelector } from '@zhin.js/client'
4
+ import { Home, Package, Bot, FileText } from 'lucide-react'
5
+ import { store, DynamicRouter, persistor, addPage, useSelector, useWebSocket } from '@zhin.js/client'
6
6
  import DashboardLayout from './layouts/dashboard'
7
7
  import DashboardHome from './pages/dashboard-home'
8
8
  import DashboardPlugins from './pages/dashboard-plugins'
9
9
  import DashboardPluginDetail from './pages/dashboard-plugin-detail'
10
10
  import DashboardBots from './pages/dashboard-bots'
11
11
  import DashboardLogs from './pages/dashboard-logs'
12
- import { Theme } from '@radix-ui/themes';
13
- import '@radix-ui/themes/styles.css'
14
12
  import './style.css'
15
13
  import { PersistGate } from 'redux-persist/integration/react'
16
14
  import { initializeTheme } from './theme'
15
+ import { TooltipProvider } from './components/ui/tooltip'
17
16
 
18
17
  // Initialize theme on app load
19
18
  initializeTheme()
20
19
 
21
- // 路由初始化组件
20
+ // Route initializer component
22
21
  function RouteInitializer() {
22
+ useWebSocket()
23
+
23
24
  const entries = useSelector(state => state.script.entries)
24
25
  const loadedScripts = useSelector(state => state.script.loadedScripts)
26
+ const synced = useSelector(state => state.script.synced)
25
27
  const [initialized, setInitialized] = useState(false)
26
28
 
27
29
  useEffect(() => {
28
- // 路由配置 - 使用 Component 属性而不是 element
29
30
  const routes = [
30
31
  {
31
32
  key: 'dashboard-layout',
32
33
  path: '/',
33
34
  title: 'Dashboard',
34
- element: <DashboardLayout/>,
35
+ element: <DashboardLayout />,
35
36
  redirect: '/dashboard',
36
37
  meta: { order: 0 },
37
38
  children: [
@@ -40,63 +41,58 @@ function RouteInitializer() {
40
41
  index: true,
41
42
  path: '',
42
43
  title: '系统概览',
43
- // Icon 仍然作为数据存储,不是在渲染上下文中使用
44
- icon: <Home className="w-5 h-5" />,
45
- element: <DashboardHome/>,
44
+ icon: <Home className="w-4 h-4" />,
45
+ element: <DashboardHome />,
46
+ },
47
+ {
48
+ key: 'dashboard-plugins',
49
+ path: '/plugins',
50
+ title: '插件管理',
51
+ icon: <Package className="w-4 h-4" />,
52
+ element: <DashboardPlugins />,
53
+ meta: { order: 2 }
54
+ },
55
+ {
56
+ key: 'dashboard-plugin-detail',
57
+ title: '插件详情',
58
+ path: '/plugins/:name',
59
+ element: <DashboardPluginDetail />,
60
+ meta: { hideInMenu: true }
46
61
  },
47
-
48
- {
49
- key: 'dashboard-plugins',
50
- path: '/plugins',
51
- title: '插件管理',
52
- icon: <Package className="w-5 h-5" />,
53
- element: <DashboardPlugins/>,
54
- meta: { order: 2 }
55
- },
56
- {
57
- key: 'dashboard-plugin-detail',
58
- title: '插件详情',
59
- path: '/plugins/:name',
60
- element: <DashboardPluginDetail/>,
61
- meta: { hideInMenu: true }
62
- },
63
62
  {
64
63
  key: 'dashboard-bots',
65
64
  path: '/bots',
66
65
  title: '机器人',
67
- icon: <Bot className="w-5 h-5" />,
68
- element: <DashboardBots/>,
66
+ icon: <Bot className="w-4 h-4" />,
67
+ element: <DashboardBots />,
69
68
  meta: { order: 3 }
70
69
  },
71
70
  {
72
71
  key: 'dashboard-logs',
73
72
  path: '/logs',
74
73
  title: '系统日志',
75
- icon: <FileText className="w-5 h-5" />,
76
- element: <DashboardLogs/>,
74
+ icon: <FileText className="w-4 h-4" />,
75
+ element: <DashboardLogs />,
77
76
  meta: { order: 4 }
78
77
  }
79
78
  ]
80
79
  }
81
80
  ]
82
-
83
- // 添加静态路由
81
+
84
82
  routes.forEach(route => {
85
83
  addPage(route)
86
84
  })
87
85
  setInitialized(true)
88
86
  }, [])
89
87
 
90
- // 检查是否所有脚本都已加载
91
- const allScriptsLoaded = entries.length === 0 || entries.length === loadedScripts.length
88
+ const allScriptsLoaded = synced && (entries.length === 0 || entries.length === loadedScripts.length)
92
89
 
93
- // 等待静态路由和动态脚本都加载完成
94
90
  if (!initialized || !allScriptsLoaded) {
95
91
  return (
96
- <div className="flex items-center justify-center h-screen">
92
+ <div className="flex items-center justify-center h-screen bg-background">
97
93
  <div className="text-center">
98
- <div className="inline-block animate-spin rounded-full h-8 w-8 border-b-2 border-gray-900"></div>
99
- <p className="mt-2 text-gray-600">
94
+ <div className="inline-block animate-spin rounded-full h-8 w-8 border-2 border-muted-foreground border-t-foreground"></div>
95
+ <p className="mt-3 text-sm text-muted-foreground">
100
96
  加载中... ({loadedScripts.length}/{entries.length})
101
97
  </p>
102
98
  </div>
@@ -108,15 +104,15 @@ function RouteInitializer() {
108
104
  }
109
105
 
110
106
  createRoot(
111
- document.getElementById('root'),
107
+ document.getElementById('root')!,
112
108
  ).render(
113
109
  <StrictMode>
114
- <Theme accentColor="blue" grayColor="slate" radius="large" scaling="100%">
110
+ <TooltipProvider>
115
111
  <PersistGate loading={null} persistor={persistor}>
116
112
  <ReduxProvider store={store}>
117
113
  <RouteInitializer />
118
114
  </ReduxProvider>
119
115
  </PersistGate>
120
- </Theme>
116
+ </TooltipProvider>
121
117
  </StrictMode>,
122
- )
118
+ )