@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
package/CHANGELOG.md CHANGED
@@ -1,5 +1,16 @@
1
1
  # @zhin.js/console
2
2
 
3
+ ## 1.0.22
4
+
5
+ ### Patch Changes
6
+
7
+ - 106d357: fix: ai
8
+ - Updated dependencies [106d357]
9
+ - @zhin.js/http@1.0.17
10
+ - @zhin.js/client@1.0.9
11
+ - @zhin.js/core@1.0.26
12
+ - zhin.js@1.0.26
13
+
3
14
  ## 1.0.21
4
15
 
5
16
  ### Patch Changes
package/README.md CHANGED
@@ -221,7 +221,7 @@ Console 插件采用智能的构建优化策略,显著减少重复打包:
221
221
  - 浏览器缓存复用,提升加载速度
222
222
  - 开发和生产环境统一体验
223
223
 
224
- 详见 [BUILD_OPTIMIZATION.md](./BUILD_OPTIMIZATION.md)
224
+ <!-- 构建优化详见源码中的 build 逻辑 -->
225
225
 
226
226
  ### 实时数据同步
227
227
 
@@ -295,11 +295,11 @@ node ../../plugins/console/lib/bin.js build
295
295
 
296
296
  ```
297
297
  console/
298
- ├── app/ # 构建工具
298
+ ├── src/ # 服务端源码
299
299
  │ ├── index.ts # Console 插件主入口
300
- │ ├── build.ts # 构建逻辑 (buildConsoleClient, buildPluginClient)
300
+ │ ├── build.ts # 构建逻辑
301
301
  │ ├── dev.ts # Vite 开发服务器
302
- │ ├── websocket.ts # WebSocket 管理
302
+ │ ├── transform.ts # TS/TSX/JSX 按需转译
303
303
  │ └── bin.ts # CLI 工具
304
304
  ├── client/ # 前端应用
305
305
  │ ├── src/ # React 应用源码
@@ -0,0 +1,17 @@
1
+ {
2
+ "$schema": "https://ui.shadcn.com/schema.json",
3
+ "style": "default",
4
+ "rsc": false,
5
+ "tsx": true,
6
+ "tailwind": {
7
+ "config": "tailwind.config.js",
8
+ "css": "src/style.css",
9
+ "baseColor": "zinc",
10
+ "cssVariables": true
11
+ },
12
+ "aliases": {
13
+ "components": "@/components",
14
+ "utils": "@zhin.js/client",
15
+ "ui": "@/components/ui"
16
+ }
17
+ }
package/client/index.html CHANGED
@@ -16,7 +16,7 @@
16
16
  "react/jsx-dev-runtime": "./react-jsx-dev-runtime.js",
17
17
  "lucide-react": "./lucide-react.js",
18
18
  "radix-ui": "./radix-ui.js",
19
- "@radix-ui/themes": "./radix-ui-themes.js",
19
+ "class-variance-authority": "./cva.js",
20
20
  "@zhin.js/client": "./client.js"
21
21
  }
22
22
  }
@@ -1,252 +1,161 @@
1
1
  /**
2
- * 基础类型字段渲染器
3
- * 处理: string, number, boolean, percent, date, regexp, const
2
+ * Basic type field renderers
4
3
  */
5
4
 
6
- import { Flex, Box, Text, TextField, TextArea, Switch, Select, Badge, Callout } from '@radix-ui/themes'
7
5
  import type { FieldRendererProps } from './types.js'
6
+ import { Input } from '../ui/input'
7
+ import { Textarea } from '../ui/textarea'
8
+ import { Switch } from '../ui/switch'
9
+ import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '../ui/select'
10
+ import { Badge } from '../ui/badge'
8
11
  import { Calendar, Info, Lock, Code } from 'lucide-react'
12
+
9
13
  export function StringFieldRenderer({ field, value, onChange }: FieldRendererProps) {
10
- // 枚举类型 - 下拉选择 - 优化样式
11
14
  if (field.enum) {
12
15
  return (
13
- <Select.Root
14
- size="2"
15
- value={value?.toString() || ''}
16
- onValueChange={onChange}
17
- >
18
- <Select.Trigger className="w-full hover:border-blue-500 dark:hover:border-blue-400 transition-colors" />
19
- <Select.Content className="shadow-lg">
16
+ <Select value={value?.toString() || ''} onValueChange={onChange}>
17
+ <SelectTrigger><SelectValue placeholder="请选择" /></SelectTrigger>
18
+ <SelectContent>
20
19
  {field.enum.map((option) => (
21
- <Select.Item
22
- key={option}
23
- value={option.toString()}
24
- className="hover:bg-blue-50 dark:hover:bg-blue-900/20 transition-colors"
25
- >
26
- {option.toString()}
27
- </Select.Item>
20
+ <SelectItem key={option} value={option.toString()}>{option.toString()}</SelectItem>
28
21
  ))}
29
- </Select.Content>
30
- </Select.Root>
22
+ </SelectContent>
23
+ </Select>
31
24
  )
32
25
  }
33
-
34
- // 多行文本 - 优化样式
26
+
35
27
  if (field.description?.includes('多行') || field.key?.includes('description')) {
36
28
  return (
37
- <TextArea
38
- size="2"
39
- value={value || ''}
40
- onChange={(e) => onChange(e.target.value)}
41
- placeholder={field.description || `请输入`}
42
- rows={3}
43
- className="font-sans resize-y hover:border-blue-500 dark:hover:border-blue-400 transition-colors focus:ring-2 focus:ring-blue-500/20"
29
+ <Textarea
30
+ value={value || ''} onChange={(e) => onChange(e.target.value)}
31
+ placeholder={field.description || '请输入'} rows={3}
44
32
  />
45
33
  )
46
34
  }
47
-
48
- // 单行文本 - 优化样式
35
+
49
36
  return (
50
- <TextField.Root
51
- size="2"
52
- value={value || ''}
53
- onChange={(e) => onChange(e.target.value)}
54
- placeholder={field.description || `请输入`}
55
- className="hover:border-blue-500 dark:hover:border-blue-400 transition-colors focus-within:ring-2 focus-within:ring-blue-500/20"
37
+ <Input
38
+ value={value || ''} onChange={(e) => onChange(e.target.value)}
39
+ placeholder={field.description || '请输入'}
56
40
  />
57
41
  )
58
42
  }
59
43
 
60
44
  export function NumberFieldRenderer({ field, value, onChange }: FieldRendererProps) {
61
45
  return (
62
- <TextField.Root
63
- size="2"
64
- type="number"
65
- value={value?.toString() || ''}
46
+ <Input
47
+ type="number" value={value?.toString() || ''}
66
48
  onChange={(e) => onChange(parseFloat(e.target.value) || 0)}
67
- placeholder={field.description || `请输入数字`}
68
- min={field.min}
69
- max={field.max}
70
- className="hover:border-blue-500 dark:hover:border-blue-400 transition-colors focus-within:ring-2 focus-within:ring-blue-500/20"
49
+ placeholder={field.description || '请输入数字'}
50
+ min={field.min} max={field.max}
71
51
  />
72
52
  )
73
53
  }
74
54
 
75
55
  export function BooleanFieldRenderer({ value, onChange }: FieldRendererProps) {
76
56
  return (
77
- <Flex align="center" gap="3" className="p-3 rounded-lg bg-gray-50 dark:bg-gray-900 border border-gray-200 dark:border-gray-800">
78
- <Switch
79
- size="2"
80
- checked={value === true}
81
- onCheckedChange={onChange}
82
- className="cursor-pointer"
83
- />
84
- <Flex direction="column" gap="1">
85
- <Text size="2" weight="bold" color={value ? 'green' : 'gray'}>
57
+ <div className="flex items-center gap-3 p-3 rounded-lg bg-muted/50 border">
58
+ <Switch checked={value === true} onCheckedChange={onChange} />
59
+ <div className="flex flex-col gap-0.5">
60
+ <span className={`text-sm font-medium ${value ? 'text-emerald-600 dark:text-emerald-400' : 'text-muted-foreground'}`}>
86
61
  {value ? '已启用' : '已禁用'}
87
- </Text>
88
- <Text size="1" color="gray">
62
+ </span>
63
+ <span className="text-xs text-muted-foreground">
89
64
  {value ? '功能当前处于开启状态' : '功能当前处于关闭状态'}
90
- </Text>
91
- </Flex>
92
- </Flex>
65
+ </span>
66
+ </div>
67
+ </div>
93
68
  )
94
69
  }
95
70
 
96
71
  export function PercentFieldRenderer({ field, value, onChange }: FieldRendererProps) {
97
72
  const percentValue = typeof value === 'number' ? value : (field.default || 0)
98
-
99
73
  return (
100
- <div className="p-4 rounded-lg bg-gradient-to-br from-blue-50 to-cyan-50 dark:from-blue-900/20 dark:to-cyan-900/20 border border-blue-200 dark:border-blue-800">
101
- <Flex direction="column" gap="3">
102
- <Flex align="center" gap="3">
103
- <input
104
- type="range"
105
- min={field.min || 0}
106
- max={field.max || 1}
107
- step={field.step || 0.01}
108
- value={percentValue}
109
- onChange={(e) => onChange(parseFloat(e.target.value))}
110
- className="flex-1 h-2 rounded-lg appearance-none cursor-pointer bg-blue-200 dark:bg-blue-800 [&::-webkit-slider-thumb]:appearance-none [&::-webkit-slider-thumb]:w-4 [&::-webkit-slider-thumb]:h-4 [&::-webkit-slider-thumb]:rounded-full [&::-webkit-slider-thumb]:bg-blue-600 dark:[&::-webkit-slider-thumb]:bg-blue-400 [&::-webkit-slider-thumb]:cursor-pointer hover:[&::-webkit-slider-thumb]:bg-blue-700 dark:hover:[&::-webkit-slider-thumb]:bg-blue-300 transition-all"
111
- />
112
- <TextField.Root
113
- size="2"
114
- type="number"
74
+ <div className="p-3 rounded-lg bg-muted/50 border space-y-3">
75
+ <div className="flex items-center gap-3">
76
+ <input
77
+ type="range" min={field.min || 0} max={field.max || 1}
78
+ step={field.step || 0.01} value={percentValue}
79
+ onChange={(e) => onChange(parseFloat(e.target.value))}
80
+ className="flex-1 h-2 rounded-lg appearance-none cursor-pointer bg-secondary"
81
+ />
82
+ <div className="flex items-center gap-1">
83
+ <Input
84
+ type="number" className="w-20 h-8 text-xs"
115
85
  value={(percentValue * 100).toFixed(0)}
116
86
  onChange={(e) => onChange(parseFloat(e.target.value) / 100)}
117
- min={(field.min || 0) * 100}
118
- max={(field.max || 1) * 100}
119
- className="w-24"
120
- >
121
- <TextField.Slot side="right">
122
- <Text size="1" weight="bold" className="text-blue-600 dark:text-blue-400">%</Text>
123
- </TextField.Slot>
124
- </TextField.Root>
125
- </Flex>
126
- <Flex align="center" justify="between">
127
- <Text size="1" color="gray">
128
- 当前值
129
- </Text>
130
- <Badge color="blue" size="2" variant="soft" className="font-mono">
131
- {(percentValue * 100).toFixed(1)}%
132
- </Badge>
133
- </Flex>
134
- </Flex>
87
+ min={(field.min || 0) * 100} max={(field.max || 1) * 100}
88
+ />
89
+ <span className="text-xs font-bold text-muted-foreground">%</span>
90
+ </div>
91
+ </div>
92
+ <div className="flex items-center justify-between">
93
+ <span className="text-xs text-muted-foreground">当前值</span>
94
+ <Badge variant="secondary" className="font-mono">{(percentValue * 100).toFixed(1)}%</Badge>
95
+ </div>
135
96
  </div>
136
97
  )
137
98
  }
138
99
 
139
100
  export function DateFieldRenderer({ field, value, onChange }: FieldRendererProps) {
140
- const dateValue = value instanceof Date
141
- ? value.toISOString().split('T')[0]
142
- : value || ''
143
-
101
+ const dateValue = value instanceof Date ? value.toISOString().split('T')[0] : value || ''
144
102
  return (
145
103
  <div className="relative">
146
- <TextField.Root
147
- size="2"
148
- type="date"
149
- value={dateValue}
104
+ <Calendar className="absolute left-3 top-1/2 -translate-y-1/2 w-4 h-4 text-muted-foreground pointer-events-none" />
105
+ <Input
106
+ type="date" value={dateValue}
150
107
  onChange={(e) => onChange(new Date(e.target.value))}
151
- placeholder={field.description || '选择日期'}
152
- className="hover:border-blue-500 dark:hover:border-blue-400 transition-colors focus-within:ring-2 focus-within:ring-blue-500/20"
153
- >
154
- <TextField.Slot side="left">
155
- <Calendar className="w-4 h-4 text-gray-500" />
156
- </TextField.Slot>
157
- </TextField.Root>
108
+ placeholder={field.description || '选择日期'} className="pl-9"
109
+ />
158
110
  </div>
159
111
  )
160
112
  }
161
113
 
162
114
  export function RegexpFieldRenderer({ field, value, onChange }: FieldRendererProps) {
163
- const regexpValue = value instanceof RegExp
164
- ? value.source
165
- : (typeof value === 'string' ? value : '')
166
-
115
+ const regexpValue = value instanceof RegExp ? value.source : (typeof value === 'string' ? value : '')
167
116
  return (
168
- <div className="p-3 rounded-lg bg-amber-50 dark:bg-amber-900/20 border border-amber-200 dark:border-amber-800">
169
- <Flex direction="column" gap="2">
170
- <TextField.Root
171
- size="2"
172
- value={regexpValue}
173
- onChange={(e) => {
174
- try {
175
- onChange(new RegExp(e.target.value))
176
- } catch {
177
- onChange(e.target.value)
178
- }
179
- }}
117
+ <div className="p-3 rounded-lg bg-amber-50 dark:bg-amber-950/20 border border-amber-200 dark:border-amber-800 space-y-2">
118
+ <div className="flex items-center gap-1">
119
+ <span className="text-sm font-bold text-amber-600 dark:text-amber-400">/</span>
120
+ <Input
121
+ value={regexpValue} className="font-mono text-sm"
122
+ onChange={(e) => { try { onChange(new RegExp(e.target.value)) } catch { onChange(e.target.value) } }}
180
123
  placeholder={field.description || '请输入正则表达式'}
181
- className="font-mono text-sm hover:border-amber-500 dark:hover:border-amber-400 transition-colors"
182
- >
183
- <TextField.Slot side="left">
184
- <Text size="1" className="text-amber-600 dark:text-amber-400 font-bold">/</Text>
185
- </TextField.Slot>
186
- <TextField.Slot side="right">
187
- <Text size="1" className="text-amber-600 dark:text-amber-400 font-bold">/</Text>
188
- </TextField.Slot>
189
- </TextField.Root>
190
- <Flex align="center" gap="2">
191
- <Info className="w-3 h-3 text-amber-600 dark:text-amber-400" />
192
- <Text size="1" className="text-amber-700 dark:text-amber-300">
193
- 输入正则表达式模式 (省略斜杠)
194
- </Text>
195
- </Flex>
196
- </Flex>
124
+ />
125
+ <span className="text-sm font-bold text-amber-600 dark:text-amber-400">/</span>
126
+ </div>
127
+ <p className="flex items-center gap-1 text-xs text-amber-700 dark:text-amber-300">
128
+ <Info className="w-3 h-3" /> 输入正则表达式模式 (省略斜杠)
129
+ </p>
197
130
  </div>
198
131
  )
199
132
  }
200
133
 
201
134
  export function ConstFieldRenderer({ field, value }: FieldRendererProps) {
202
135
  const constValue = field.default || value
203
-
204
136
  return (
205
- <div className="p-3 rounded-lg bg-gray-100 dark:bg-gray-900 border border-gray-300 dark:border-gray-700">
206
- <Flex align="center" gap="3">
207
- <Lock className="w-4 h-4 text-gray-500 dark:text-gray-400" />
208
- <Badge variant="soft" size="2" className="font-mono">
209
- {String(constValue)}
210
- </Badge>
211
- <Text size="1" color="gray" className="ml-auto">
212
- (常量,不可修改)
213
- </Text>
214
- </Flex>
137
+ <div className="flex items-center gap-3 p-3 rounded-lg bg-muted border">
138
+ <Lock className="w-4 h-4 text-muted-foreground" />
139
+ <Badge variant="secondary" className="font-mono">{String(constValue)}</Badge>
140
+ <span className="text-xs text-muted-foreground ml-auto">(常量,不可修改)</span>
215
141
  </div>
216
142
  )
217
143
  }
218
144
 
219
145
  export function AnyFieldRenderer({ field, value, onChange }: FieldRendererProps) {
220
146
  return (
221
- <div className="p-3 rounded-lg bg-purple-50 dark:bg-purple-900/20 border border-purple-200 dark:border-purple-800">
222
- <Flex direction="column" gap="3">
223
- <Flex align="center" gap="2">
224
- <Code className="w-4 h-4 text-purple-600 dark:text-purple-400" />
225
- <Text size="1" weight="bold" className="text-purple-700 dark:text-purple-300">
226
- JSON 格式输入
227
- </Text>
228
- </Flex>
229
- <TextArea
230
- size="2"
231
- value={typeof value === 'object' ? JSON.stringify(value, null, 2) : String(value || '')}
232
- onChange={(e) => {
233
- try {
234
- onChange(JSON.parse(e.target.value))
235
- } catch {
236
- onChange(e.target.value)
237
- }
238
- }}
239
- placeholder={field.description || '支持任意类型 (JSON 格式)'}
240
- rows={4}
241
- className="font-mono text-sm bg-white dark:bg-gray-950 hover:border-purple-500 dark:hover:border-purple-400 transition-colors"
242
- />
243
- <Flex align="center" gap="2">
244
- <Info className="w-3 h-3 text-purple-600 dark:text-purple-400" />
245
- <Text size="1" className="text-purple-700 dark:text-purple-300">
246
- 支持: 字符串、数字、布尔值、对象、数组
247
- </Text>
248
- </Flex>
249
- </Flex>
147
+ <div className="p-3 rounded-lg bg-muted/50 border space-y-2">
148
+ <p className="flex items-center gap-1 text-xs font-semibold text-muted-foreground">
149
+ <Code className="w-3 h-3" /> JSON 格式输入
150
+ </p>
151
+ <Textarea
152
+ value={typeof value === 'object' ? JSON.stringify(value, null, 2) : String(value || '')}
153
+ onChange={(e) => { try { onChange(JSON.parse(e.target.value)) } catch { onChange(e.target.value) } }}
154
+ placeholder={field.description || '支持任意类型 (JSON 格式)'} rows={4} className="font-mono text-sm"
155
+ />
156
+ <p className="flex items-center gap-1 text-xs text-muted-foreground">
157
+ <Info className="w-3 h-3" /> 支持: 字符串、数字、布尔值、对象、数组
158
+ </p>
250
159
  </div>
251
160
  )
252
161
  }