@zhin.js/console 1.0.21 → 1.0.22

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 +11 -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 +8 -7
  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,18 +1,45 @@
1
1
  import { useEffect, useState } from 'react'
2
2
  import { useNavigate } from 'react-router'
3
- import * as Themes from '@radix-ui/themes'
4
- import { AlertCircle, Package, Terminal, Box as IconBox, Layers, Database, Clock, FileText } from 'lucide-react'
5
-
6
- const { Flex, Box, Spinner, Text, Callout, Heading, Badge, Grid, Card, Separator } = Themes
3
+ import { AlertCircle, Package, Terminal, Box as IconBox, Layers, Clock, Brain, Wrench, Database, Shield, Settings, Plug, Server, type LucideIcon } from 'lucide-react'
4
+ import { Card, CardContent } from '../components/ui/card'
5
+ import { Badge } from '../components/ui/badge'
6
+ import { Alert, AlertDescription } from '../components/ui/alert'
7
+ import { Skeleton } from '../components/ui/skeleton'
8
+ import { Separator } from '../components/ui/separator'
9
+
10
+ /** Feature 序列化格式(与后端 FeatureJSON 一致) */
11
+ interface FeatureJSON {
12
+ name: string
13
+ icon: string
14
+ desc: string
15
+ count: number
16
+ items: any[]
17
+ }
7
18
 
8
19
  interface Plugin {
9
20
  name: string
10
21
  status: 'active' | 'inactive'
11
- commandCount: number
12
- componentCount: number
13
- middlewareCount: number
14
- contextCount: number
15
22
  description: string
23
+ features: FeatureJSON[]
24
+ }
25
+
26
+ /** 根据后端返回的 icon 名称映射到 lucide-react 图标组件 */
27
+ const iconMap: Record<string, LucideIcon> = {
28
+ Terminal,
29
+ Box: IconBox,
30
+ Layers,
31
+ Clock,
32
+ Brain,
33
+ Wrench,
34
+ Database,
35
+ Shield,
36
+ Settings,
37
+ Plug,
38
+ Server,
39
+ }
40
+
41
+ function getIcon(iconName: string): LucideIcon {
42
+ return iconMap[iconName] || Package
16
43
  }
17
44
 
18
45
  export default function DashboardPlugins() {
@@ -31,14 +58,9 @@ export default function DashboardPlugins() {
31
58
  try {
32
59
  const res = await fetch('/api/plugins', { credentials: 'include' })
33
60
  if (!res.ok) throw new Error('API 请求失败')
34
-
35
61
  const data = await res.json()
36
- if (data.success) {
37
- setPlugins(data.data)
38
- setError(null)
39
- } else {
40
- throw new Error('数据格式错误')
41
- }
62
+ if (data.success) { setPlugins(data.data); setError(null) }
63
+ else throw new Error('数据格式错误')
42
64
  } catch (err) {
43
65
  setError((err as Error).message)
44
66
  } finally {
@@ -48,119 +70,100 @@ export default function DashboardPlugins() {
48
70
 
49
71
  if (loading) {
50
72
  return (
51
- <Flex align="center" justify="center" className="h-full">
52
- <Flex direction="column" align="center" gap="3">
53
- <Spinner size="3" />
54
- <Text size="2" color="gray">加载中...</Text>
55
- </Flex>
56
- </Flex>
73
+ <div className="space-y-4">
74
+ <Skeleton className="h-8 w-48" />
75
+ <div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-3">
76
+ {[...Array(6)].map((_, i) => <Skeleton key={i} className="h-40" />)}
77
+ </div>
78
+ </div>
57
79
  )
58
80
  }
59
81
 
60
82
  if (error) {
61
83
  return (
62
- <Flex align="center" justify="center" className="h-full">
63
- <Callout.Root color="red">
64
- <Callout.Icon>
65
- <AlertCircle />
66
- </Callout.Icon>
67
- <Callout.Text>
68
- 加载失败: {error}
69
- </Callout.Text>
70
- </Callout.Root>
71
- </Flex>
84
+ <div className="flex items-center justify-center h-full">
85
+ <Alert variant="destructive" className="max-w-md">
86
+ <AlertCircle className="h-4 w-4" />
87
+ <AlertDescription>加载失败: {error}</AlertDescription>
88
+ </Alert>
89
+ </div>
72
90
  )
73
91
  }
74
92
 
75
93
  return (
76
- <Box>
77
- {/* 页面标题 */}
78
- <Flex direction="column" gap="2" mb="4">
79
- <Heading size="6">插件管理</Heading>
80
- <Flex align="center" gap="2">
81
- <Text size="2" color="gray">共 {plugins.length} 个插件</Text>
82
- <Badge color="green" size="1">{plugins.filter(p => p.status === 'active').length}</Badge>
83
- <Text size="2" color="gray">个运行中</Text>
84
- </Flex>
85
- </Flex>
86
-
87
- {/* 插件列表 - 紧凑网格 */}
88
- <Grid columns={{ initial: '1', sm: '2', lg: '3' }} gap="3">
94
+ <div className="space-y-4">
95
+ {/* Header */}
96
+ <div>
97
+ <h1 className="text-2xl font-bold tracking-tight">插件管理</h1>
98
+ <div className="flex items-center gap-2 mt-1">
99
+ <span className="text-sm text-muted-foreground">共 {plugins.length} 个插件</span>
100
+ <Badge variant="success">{plugins.filter(p => p.status === 'active').length}</Badge>
101
+ <span className="text-sm text-muted-foreground">个运行中</span>
102
+ </div>
103
+ </div>
104
+
105
+ {/* Plugin grid */}
106
+ <div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-3">
89
107
  {plugins.map((plugin, index) => (
90
- <Card
108
+ <Card
91
109
  key={`${plugin.name}-${index}`}
92
- className="cursor-pointer hover-lift transition-smooth"
110
+ className="cursor-pointer transition-all hover:shadow-md hover:-translate-y-0.5"
93
111
  onClick={() => navigate(`/plugins/${encodeURIComponent(plugin.name)}`)}
94
112
  >
95
- <Flex direction="column" gap="2" p="2">
96
- {/* 头部 - 紧凑布局 */}
97
- <Flex justify="between" align="center">
98
- <Flex align="center" gap="2">
99
- <Flex align="center" justify="center" className="w-8 h-8 rounded-lg bg-blue-500/10 dark:bg-blue-400/10">
100
- <Package className="w-4 h-4 text-blue-600 dark:text-blue-400" />
101
- </Flex>
102
- <Heading size="3">{plugin.name}</Heading>
103
- </Flex>
104
- <Badge
105
- color={plugin.status === 'active' ? 'green' : 'gray'}
106
- size="1"
107
- >
113
+ <CardContent className="p-4 space-y-3">
114
+ {/* Header */}
115
+ <div className="flex justify-between items-center">
116
+ <div className="flex items-center gap-2">
117
+ <div className="flex items-center justify-center w-8 h-8 rounded-md bg-secondary">
118
+ <Package className="w-4 h-4" />
119
+ </div>
120
+ <span className="font-semibold text-sm">{plugin.name}</span>
121
+ </div>
122
+ <Badge variant={plugin.status === 'active' ? 'success' : 'secondary'}>
108
123
  {plugin.status === 'active' ? '运行中' : '已停止'}
109
124
  </Badge>
110
- </Flex>
125
+ </div>
111
126
 
112
- {/* 描述 - 限制两行 */}
113
- <Text size="1" color="gray" className="line-clamp-2">
127
+ <p className="text-xs text-muted-foreground line-clamp-2">
114
128
  {plugin.description || '暂无描述'}
115
- </Text>
116
-
117
- <Separator size="4" my="1" />
118
-
119
- {/* 统计信息 - 紧凑网格 */}
120
- <Grid columns={{ initial: '1', sm: '2', lg: '4' }} gap="1">
121
- <Flex gap="2" justify="center" align="center" className="rounded-md bg-blue-500/5 dark:bg-blue-400/5 p-1.5">
122
- <Terminal className="w-3 h-3 text-blue-600 dark:text-blue-400" />
123
- <Text size="2" weight="bold" className="mt-0.5">{plugin.commandCount}</Text>
124
- <Text size="1" color="gray">命令</Text>
125
- </Flex>
126
-
127
- <Flex gap="2" justify="center" align="center" className="rounded-md bg-green-500/5 dark:bg-green-400/5 p-1.5">
128
- <IconBox className="w-3 h-3 text-green-600 dark:text-green-400" />
129
- <Text size="2" weight="bold" className="mt-0.5">{plugin.componentCount}</Text>
130
- <Text size="1" color="gray">组件</Text>
131
- </Flex>
132
-
133
- <Flex gap="2" justify="center" align="center" className="rounded-md bg-purple-500/5 dark:bg-purple-400/5 p-1.5">
134
- <Layers className="w-3 h-3 text-purple-600 dark:text-purple-400" />
135
- <Text size="2" weight="bold" className="mt-0.5">{plugin.middlewareCount}</Text>
136
- <Text size="1" color="gray">中间件</Text>
137
- </Flex>
138
-
139
- <Flex gap="2" justify="center" align="center" className="rounded-md bg-orange-500/5 dark:bg-orange-400/5 p-1.5">
140
- <Database className="w-3 h-3 text-orange-600 dark:text-orange-400" />
141
- <Text size="2" weight="bold" className="mt-0.5">{plugin.contextCount}</Text>
142
- <Text size="1" color="gray">上下文</Text>
143
- </Flex>
144
- </Grid>
145
- </Flex>
129
+ </p>
130
+
131
+ <Separator />
132
+
133
+ {/* Features - 动态渲染,每个 Feature 自带 icon/name/count */}
134
+ {plugin.features.length > 0 ? (
135
+ <div className={`grid gap-1`} style={{ gridTemplateColumns: `repeat(${Math.min(plugin.features.length, 4)}, 1fr)` }}>
136
+ {plugin.features.map((feature) => {
137
+ const Icon = getIcon(feature.icon)
138
+ return (
139
+ <div key={feature.name} className="flex flex-col items-center gap-0.5 rounded-md bg-secondary/50 p-1.5">
140
+ <Icon className="w-3 h-3 text-muted-foreground" />
141
+ <span className="text-sm font-bold">{feature.count}</span>
142
+ <span className="text-[10px] text-muted-foreground">{feature.desc}</span>
143
+ </div>
144
+ )
145
+ })}
146
+ </div>
147
+ ) : (
148
+ <p className="text-xs text-muted-foreground text-center py-1">无注册功能</p>
149
+ )}
150
+ </CardContent>
146
151
  </Card>
147
152
  ))}
148
- </Grid>
153
+ </div>
149
154
 
150
- {/* 空状态 */}
155
+ {/* Empty state */}
151
156
  {plugins.length === 0 && (
152
157
  <Card className="mt-6">
153
- <Flex direction="column" align="center" gap="3" py="8">
154
- <Flex align="center" justify="center" className="w-16 h-16 rounded-full bg-gray-100 dark:bg-gray-800">
155
- <Package className="w-8 h-8 text-gray-400" />
156
- </Flex>
157
- <Flex direction="column" align="center" gap="1">
158
- <Heading size="4">暂无插件</Heading>
159
- <Text size="2" color="gray">请先安装并启用插件</Text>
160
- </Flex>
161
- </Flex>
158
+ <CardContent className="flex flex-col items-center gap-3 py-12">
159
+ <div className="flex items-center justify-center w-16 h-16 rounded-full bg-muted">
160
+ <Package className="w-8 h-8 text-muted-foreground" />
161
+ </div>
162
+ <h3 className="text-lg font-semibold">暂无插件</h3>
163
+ <p className="text-sm text-muted-foreground">请先安装并启用插件</p>
164
+ </CardContent>
162
165
  </Card>
163
166
  )}
164
- </Box>
167
+ </div>
165
168
  )
166
169
  }