@nextclaw/ui 0.2.3 → 0.2.5
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.
- package/CHANGELOG.md +12 -0
- package/dist/assets/index-BV3Gyu8h.js +225 -0
- package/dist/assets/index-iSLahgqA.css +1 -0
- package/dist/index.html +2 -2
- package/dist/logos/aihubmix.png +0 -0
- package/dist/logos/anthropic.svg +1 -0
- package/dist/logos/dashscope.png +0 -0
- package/dist/logos/deepseek.png +0 -0
- package/dist/logos/dingtalk.svg +1 -0
- package/dist/logos/discord.svg +1 -0
- package/dist/logos/email.svg +1 -0
- package/dist/logos/feishu.svg +12 -0
- package/dist/logos/gemini.svg +1 -0
- package/dist/logos/groq.svg +1 -0
- package/dist/logos/minimax.svg +1 -0
- package/dist/logos/mochat.svg +6 -0
- package/dist/logos/moonshot.png +0 -0
- package/dist/logos/openai.svg +1 -0
- package/dist/logos/openrouter.svg +1 -0
- package/dist/logos/qq.svg +1 -0
- package/dist/logos/slack.svg +1 -0
- package/dist/logos/telegram.svg +1 -0
- package/dist/logos/vllm.svg +1 -0
- package/dist/logos/whatsapp.svg +1 -0
- package/dist/logos/zhipu.svg +15 -0
- package/package.json +1 -1
- package/public/logos/aihubmix.png +0 -0
- package/public/logos/anthropic.svg +1 -0
- package/public/logos/dashscope.png +0 -0
- package/public/logos/deepseek.png +0 -0
- package/public/logos/dingtalk.svg +1 -0
- package/public/logos/discord.svg +1 -0
- package/public/logos/email.svg +1 -0
- package/public/logos/feishu.svg +12 -0
- package/public/logos/gemini.svg +1 -0
- package/public/logos/groq.svg +1 -0
- package/public/logos/minimax.svg +1 -0
- package/public/logos/mochat.svg +6 -0
- package/public/logos/moonshot.png +0 -0
- package/public/logos/openai.svg +1 -0
- package/public/logos/openrouter.svg +1 -0
- package/public/logos/qq.svg +1 -0
- package/public/logos/slack.svg +1 -0
- package/public/logos/telegram.svg +1 -0
- package/public/logos/vllm.svg +1 -0
- package/public/logos/whatsapp.svg +1 -0
- package/public/logos/zhipu.svg +15 -0
- package/src/App.tsx +0 -3
- package/src/api/client.ts +15 -1
- package/src/api/config.ts +5 -14
- package/src/api/types.ts +7 -8
- package/src/components/common/LogoBadge.tsx +35 -0
- package/src/components/common/StatusBadge.tsx +4 -4
- package/src/components/config/ChannelForm.tsx +48 -16
- package/src/components/config/ChannelsList.tsx +96 -34
- package/src/components/config/ModelConfig.tsx +30 -29
- package/src/components/config/ProviderForm.tsx +9 -11
- package/src/components/config/ProvidersList.tsx +90 -38
- package/src/components/layout/Header.tsx +7 -7
- package/src/components/layout/Sidebar.tsx +10 -23
- package/src/components/ui/HighlightCard.tsx +29 -29
- package/src/components/ui/button.tsx +13 -8
- package/src/components/ui/card.tsx +8 -7
- package/src/components/ui/dialog.tsx +8 -8
- package/src/components/ui/input.tsx +1 -1
- package/src/components/ui/label.tsx +1 -1
- package/src/components/ui/switch.tsx +3 -3
- package/src/components/ui/tabs-custom.tsx +6 -6
- package/src/components/ui/tabs.tsx +7 -6
- package/src/hooks/useConfig.ts +2 -29
- package/src/index.css +103 -56
- package/src/lib/i18n.ts +10 -6
- package/src/lib/logos.ts +42 -0
- package/src/stores/ui.store.ts +1 -1
- package/src/styles/design-system.css +248 -0
- package/tailwind.config.js +118 -10
- package/dist/assets/index-CDd3pWyf.js +0 -235
- package/dist/assets/index-CrA44GOI.css +0 -1
- package/src/components/config/UiConfig.tsx +0 -189
|
@@ -16,7 +16,7 @@ interface TabsProps {
|
|
|
16
16
|
|
|
17
17
|
export function Tabs({ tabs, activeTab, onChange, className }: TabsProps) {
|
|
18
18
|
return (
|
|
19
|
-
<div className={cn('flex items-center gap-8 border-b border-
|
|
19
|
+
<div className={cn('flex items-center gap-8 border-b border-gray-200 mb-8', className)}>
|
|
20
20
|
{tabs.map((tab) => {
|
|
21
21
|
const isActive = activeTab === tab.id;
|
|
22
22
|
return (
|
|
@@ -24,18 +24,18 @@ export function Tabs({ tabs, activeTab, onChange, className }: TabsProps) {
|
|
|
24
24
|
key={tab.id}
|
|
25
25
|
onClick={() => onChange(tab.id)}
|
|
26
26
|
className={cn(
|
|
27
|
-
'relative pb-4 text-[15px] font-semibold transition-all duration-
|
|
27
|
+
'relative pb-4 text-[15px] font-semibold transition-all duration-fast flex items-center gap-2',
|
|
28
28
|
isActive
|
|
29
|
-
? 'text-
|
|
30
|
-
: 'text-
|
|
29
|
+
? 'text-primary'
|
|
30
|
+
: 'text-gray-500 hover:text-gray-700'
|
|
31
31
|
)}
|
|
32
32
|
>
|
|
33
33
|
{tab.label}
|
|
34
34
|
{tab.count !== undefined && (
|
|
35
|
-
<span className="text-[11px] font-medium text-
|
|
35
|
+
<span className="text-[11px] font-medium text-gray-400">{tab.count.toLocaleString()}</span>
|
|
36
36
|
)}
|
|
37
37
|
{isActive && (
|
|
38
|
-
<div className="absolute bottom-0 left-0 right-0 h-0.5 bg-
|
|
38
|
+
<div className="absolute bottom-0 left-0 right-0 h-0.5 bg-primary animate-in fade-in slide-in-from-left-2 duration-300" />
|
|
39
39
|
)}
|
|
40
40
|
</button>
|
|
41
41
|
);
|
|
@@ -42,7 +42,7 @@ export function TabsList({ children, className }: TabsListProps) {
|
|
|
42
42
|
return (
|
|
43
43
|
<div
|
|
44
44
|
className={cn(
|
|
45
|
-
'inline-flex h-10 items-center justify-center rounded-
|
|
45
|
+
'inline-flex h-10 items-center justify-center rounded-lg bg-gray-100 p-1 text-gray-600',
|
|
46
46
|
className
|
|
47
47
|
)}
|
|
48
48
|
>
|
|
@@ -62,10 +62,10 @@ export function TabsTrigger({ value, children, className }: TabsTriggerProps) {
|
|
|
62
62
|
type="button"
|
|
63
63
|
onClick={() => context.onValueChange(value)}
|
|
64
64
|
className={cn(
|
|
65
|
-
'inline-flex items-center justify-center whitespace-nowrap rounded-
|
|
65
|
+
'inline-flex items-center justify-center whitespace-nowrap rounded-md px-3 py-1.5 text-sm font-medium ring-offset-white transition-all duration-fast focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50',
|
|
66
66
|
isActive
|
|
67
|
-
? 'bg-
|
|
68
|
-
: 'hover:bg-
|
|
67
|
+
? 'bg-white text-gray-900 shadow-sm'
|
|
68
|
+
: 'hover:bg-white/50 hover:text-gray-700',
|
|
69
69
|
className
|
|
70
70
|
)}
|
|
71
71
|
>
|
|
@@ -78,10 +78,11 @@ export function TabsContent({ value, children, className }: TabsContentProps) {
|
|
|
78
78
|
const context = React.useContext(TabsContext);
|
|
79
79
|
if (!context) throw new Error('TabsContent must be used within Tabs');
|
|
80
80
|
|
|
81
|
-
|
|
81
|
+
const isActive = context.value === value;
|
|
82
|
+
if (!isActive) return null;
|
|
82
83
|
|
|
83
84
|
return (
|
|
84
|
-
<div className={cn('mt-2
|
|
85
|
+
<div className={cn('mt-2 animate-fade-in', className)}>
|
|
85
86
|
{children}
|
|
86
87
|
</div>
|
|
87
88
|
);
|
package/src/hooks/useConfig.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';
|
|
2
|
-
import { fetchConfig, fetchConfigMeta, updateModel, updateProvider, updateChannel
|
|
2
|
+
import { fetchConfig, fetchConfigMeta, updateModel, updateProvider, updateChannel } from '@/api/config';
|
|
3
3
|
import { toast } from 'sonner';
|
|
4
4
|
import { t } from '@/lib/i18n';
|
|
5
5
|
|
|
@@ -59,37 +59,10 @@ export function useUpdateChannel() {
|
|
|
59
59
|
updateChannel(channel, data as Parameters<typeof updateChannel>[1]),
|
|
60
60
|
onSuccess: () => {
|
|
61
61
|
queryClient.invalidateQueries({ queryKey: ['config'] });
|
|
62
|
-
toast.success(t('
|
|
63
|
-
},
|
|
64
|
-
onError: (error: Error) => {
|
|
65
|
-
toast.error(t('configSaveFailed') + ': ' + error.message);
|
|
66
|
-
}
|
|
67
|
-
});
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
export function useUpdateUiConfig() {
|
|
71
|
-
const queryClient = useQueryClient();
|
|
72
|
-
|
|
73
|
-
return useMutation({
|
|
74
|
-
mutationFn: updateUiConfig,
|
|
75
|
-
onSuccess: () => {
|
|
76
|
-
queryClient.invalidateQueries({ queryKey: ['config'] });
|
|
77
|
-
toast.success(t('configSaved'));
|
|
62
|
+
toast.success(t('configSavedApplied'));
|
|
78
63
|
},
|
|
79
64
|
onError: (error: Error) => {
|
|
80
65
|
toast.error(t('configSaveFailed') + ': ' + error.message);
|
|
81
66
|
}
|
|
82
67
|
});
|
|
83
68
|
}
|
|
84
|
-
|
|
85
|
-
export function useReloadConfig() {
|
|
86
|
-
return useMutation({
|
|
87
|
-
mutationFn: reloadConfig,
|
|
88
|
-
onSuccess: () => {
|
|
89
|
-
toast.success(t('configReloaded'));
|
|
90
|
-
},
|
|
91
|
-
onError: (error: Error) => {
|
|
92
|
-
toast.error(t('configReloadFailed') + ': ' + error.message);
|
|
93
|
-
}
|
|
94
|
-
});
|
|
95
|
-
}
|
package/src/index.css
CHANGED
|
@@ -1,55 +1,67 @@
|
|
|
1
|
+
/* Import Design System - must be first */
|
|
2
|
+
@import './styles/design-system.css';
|
|
3
|
+
|
|
1
4
|
@tailwind base;
|
|
2
5
|
@tailwind components;
|
|
3
6
|
@tailwind utilities;
|
|
4
7
|
|
|
5
8
|
@layer base {
|
|
6
9
|
:root {
|
|
7
|
-
/*
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
--
|
|
11
|
-
|
|
10
|
+
/* ========================================
|
|
11
|
+
CORE VARIABLES (Mapped to Design System)
|
|
12
|
+
======================================== */
|
|
13
|
+
--background: 210 20% 98%;
|
|
14
|
+
--foreground: 221 39% 11%;
|
|
15
|
+
|
|
12
16
|
--card: 0 0% 100%;
|
|
13
|
-
|
|
14
|
-
|
|
17
|
+
--card-foreground: 221 39% 11%;
|
|
18
|
+
|
|
15
19
|
--popover: 0 0% 100%;
|
|
16
|
-
--popover-foreground:
|
|
17
|
-
|
|
18
|
-
/*
|
|
20
|
+
--popover-foreground: 221 39% 11%;
|
|
21
|
+
|
|
22
|
+
/* Primary: Brand Blue */
|
|
23
|
+
--primary: 217 80% 55%;
|
|
19
24
|
--primary-foreground: 0 0% 100%;
|
|
20
|
-
|
|
21
|
-
/*
|
|
22
|
-
--secondary
|
|
23
|
-
--
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
--
|
|
25
|
+
|
|
26
|
+
/* Secondary: Light Gray */
|
|
27
|
+
--secondary: 220 14% 96%;
|
|
28
|
+
--secondary-foreground: 215 28% 17%;
|
|
29
|
+
|
|
30
|
+
/* Muted */
|
|
31
|
+
--muted: 220 14% 96%;
|
|
32
|
+
--muted-foreground: 220 9% 46%;
|
|
33
|
+
|
|
34
|
+
/* Accent */
|
|
35
|
+
--accent: 217 100% 97%;
|
|
36
|
+
--accent-foreground: 217 70% 40%;
|
|
37
|
+
|
|
38
|
+
/* Destructive */
|
|
28
39
|
--destructive: 0 84% 60%;
|
|
29
40
|
--destructive-foreground: 0 0% 98%;
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
--
|
|
33
|
-
--
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
--milk-
|
|
39
|
-
--milk-
|
|
40
|
-
--milk-
|
|
41
|
-
--milk-
|
|
42
|
-
--milk-
|
|
43
|
-
--milk-
|
|
44
|
-
--milk-
|
|
45
|
-
--milk-
|
|
46
|
-
--milk-
|
|
41
|
+
|
|
42
|
+
/* UI Elements */
|
|
43
|
+
--border: 220 13% 91%;
|
|
44
|
+
--input: 220 13% 91%;
|
|
45
|
+
--ring: 217 80% 55%;
|
|
46
|
+
--radius: 0.75rem;
|
|
47
|
+
|
|
48
|
+
/* Legacy compatibility */
|
|
49
|
+
--milk-50: 210 20% 98%;
|
|
50
|
+
--milk-100: 220 14% 96%;
|
|
51
|
+
--milk-200: 220 13% 91%;
|
|
52
|
+
--milk-300: 216 12% 84%;
|
|
53
|
+
--milk-400: 218 11% 65%;
|
|
54
|
+
--milk-500: 220 9% 46%;
|
|
55
|
+
--milk-600: 215 14% 34%;
|
|
56
|
+
--milk-700: 217 19% 27%;
|
|
57
|
+
--milk-800: 215 28% 17%;
|
|
58
|
+
--milk-900: 221 39% 11%;
|
|
47
59
|
}
|
|
48
60
|
}
|
|
49
61
|
|
|
50
62
|
@layer base {
|
|
51
63
|
* {
|
|
52
|
-
@apply border-border
|
|
64
|
+
@apply border-border;
|
|
53
65
|
}
|
|
54
66
|
|
|
55
67
|
html {
|
|
@@ -60,20 +72,21 @@
|
|
|
60
72
|
|
|
61
73
|
body {
|
|
62
74
|
@apply bg-background text-foreground;
|
|
63
|
-
font-family: -
|
|
75
|
+
font-family: var(--font-sans);
|
|
64
76
|
overflow: hidden;
|
|
65
77
|
}
|
|
66
78
|
|
|
67
|
-
/* Smooth scrolling
|
|
79
|
+
/* Smooth scrolling */
|
|
68
80
|
* {
|
|
69
81
|
scrollbar-width: thin;
|
|
70
|
-
scrollbar-color: hsl(var(--
|
|
82
|
+
scrollbar-color: hsl(var(--gray-300)) transparent;
|
|
71
83
|
}
|
|
72
84
|
}
|
|
73
85
|
|
|
74
86
|
@layer utilities {
|
|
75
|
-
|
|
76
|
-
|
|
87
|
+
/* ========================================
|
|
88
|
+
SCROLLBAR
|
|
89
|
+
======================================== */
|
|
77
90
|
.custom-scrollbar::-webkit-scrollbar {
|
|
78
91
|
width: 6px;
|
|
79
92
|
height: 6px;
|
|
@@ -84,24 +97,42 @@
|
|
|
84
97
|
}
|
|
85
98
|
|
|
86
99
|
.custom-scrollbar::-webkit-scrollbar-thumb {
|
|
87
|
-
background: hsl(var(--
|
|
100
|
+
background: hsl(var(--gray-300));
|
|
88
101
|
border-radius: 6px;
|
|
89
102
|
}
|
|
90
103
|
|
|
91
104
|
.custom-scrollbar::-webkit-scrollbar-thumb:hover {
|
|
92
|
-
background: hsl(var(--
|
|
105
|
+
background: hsl(var(--gray-400));
|
|
93
106
|
}
|
|
94
107
|
|
|
95
|
-
/*
|
|
108
|
+
/* ========================================
|
|
109
|
+
GLASSMORPHISM
|
|
110
|
+
======================================== */
|
|
96
111
|
.glass {
|
|
97
|
-
|
|
112
|
+
background: rgba(255, 255, 255, 0.8);
|
|
113
|
+
backdrop-filter: blur(12px);
|
|
114
|
+
-webkit-backdrop-filter: blur(12px);
|
|
115
|
+
border: 1px solid rgba(255, 255, 255, 0.2);
|
|
98
116
|
}
|
|
99
117
|
|
|
100
118
|
.glass-dark {
|
|
101
|
-
|
|
119
|
+
background: rgba(0, 0, 0, 0.1);
|
|
120
|
+
backdrop-filter: blur(12px);
|
|
121
|
+
-webkit-backdrop-filter: blur(12px);
|
|
122
|
+
border: 1px solid rgba(255, 255, 255, 0.1);
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/* ========================================
|
|
126
|
+
SHADOWS
|
|
127
|
+
======================================== */
|
|
128
|
+
.shadow-card {
|
|
129
|
+
box-shadow: 0 1px 3px 0 rgb(0 0 0 / 0.05), 0 1px 2px -1px rgb(0 0 0 / 0.05);
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
.shadow-card-hover {
|
|
133
|
+
box-shadow: 0 10px 15px -3px rgb(0 0 0 / 0.08), 0 4px 6px -4px rgb(0 0 0 / 0.05);
|
|
102
134
|
}
|
|
103
135
|
|
|
104
|
-
/* Premium Shadows */
|
|
105
136
|
.shadow-premium {
|
|
106
137
|
box-shadow: 0 10px 30px -10px rgba(0, 0, 0, 0.08), 0 4px 10px -4px rgba(0, 0, 0, 0.04);
|
|
107
138
|
}
|
|
@@ -109,15 +140,36 @@
|
|
|
109
140
|
.shadow-premium-hover {
|
|
110
141
|
box-shadow: 0 20px 40px -15px rgba(0, 0, 0, 0.12), 0 8px 15px -6px rgba(0, 0, 0, 0.06);
|
|
111
142
|
}
|
|
143
|
+
|
|
144
|
+
/* ========================================
|
|
145
|
+
GRADIENTS
|
|
146
|
+
======================================== */
|
|
147
|
+
.bg-gradient-hero {
|
|
148
|
+
background: linear-gradient(180deg, hsl(var(--background)) 0%, hsl(var(--gray-50)) 100%);
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
.bg-gradient-subtle {
|
|
152
|
+
background: linear-gradient(180deg, hsl(var(--gray-50)) 0%, hsl(var(--background)) 100%);
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
/* ========================================
|
|
156
|
+
FOCUS STATES
|
|
157
|
+
======================================== */
|
|
158
|
+
.focus-ring {
|
|
159
|
+
@apply focus:outline-none focus-visible:ring-2 focus-visible:ring-offset-2;
|
|
160
|
+
--tw-ring-color: hsl(var(--ring));
|
|
161
|
+
--tw-ring-offset-color: hsl(var(--ring-offset));
|
|
162
|
+
}
|
|
112
163
|
}
|
|
113
164
|
|
|
114
|
-
/*
|
|
165
|
+
/* ========================================
|
|
166
|
+
ANIMATIONS
|
|
167
|
+
======================================== */
|
|
115
168
|
@keyframes fadeIn {
|
|
116
169
|
from {
|
|
117
170
|
opacity: 0;
|
|
118
171
|
transform: translateY(12px);
|
|
119
172
|
}
|
|
120
|
-
|
|
121
173
|
to {
|
|
122
174
|
opacity: 1;
|
|
123
175
|
transform: translateY(0);
|
|
@@ -129,7 +181,6 @@
|
|
|
129
181
|
opacity: 0;
|
|
130
182
|
transform: translateX(-12px);
|
|
131
183
|
}
|
|
132
|
-
|
|
133
184
|
to {
|
|
134
185
|
opacity: 1;
|
|
135
186
|
transform: translateX(0);
|
|
@@ -141,7 +192,6 @@
|
|
|
141
192
|
opacity: 0;
|
|
142
193
|
transform: scale(0.97);
|
|
143
194
|
}
|
|
144
|
-
|
|
145
195
|
to {
|
|
146
196
|
opacity: 1;
|
|
147
197
|
transform: scale(1);
|
|
@@ -149,12 +199,9 @@
|
|
|
149
199
|
}
|
|
150
200
|
|
|
151
201
|
@keyframes pulse-soft {
|
|
152
|
-
|
|
153
|
-
0%,
|
|
154
|
-
100% {
|
|
202
|
+
0%, 100% {
|
|
155
203
|
opacity: 1;
|
|
156
204
|
}
|
|
157
|
-
|
|
158
205
|
50% {
|
|
159
206
|
opacity: 0.8;
|
|
160
207
|
}
|
|
@@ -174,4 +221,4 @@
|
|
|
174
221
|
|
|
175
222
|
.animate-pulse-soft {
|
|
176
223
|
animation: pulse-soft 3s ease-in-out infinite;
|
|
177
|
-
}
|
|
224
|
+
}
|
package/src/lib/i18n.ts
CHANGED
|
@@ -4,7 +4,6 @@ export const LABELS: Record<string, { zh: string; en: string }> = {
|
|
|
4
4
|
model: { zh: '模型', en: 'Model' },
|
|
5
5
|
providers: { zh: '提供商', en: 'Providers' },
|
|
6
6
|
channels: { zh: '渠道', en: 'Channels' },
|
|
7
|
-
uiConfig: { zh: '界面', en: 'UI' },
|
|
8
7
|
|
|
9
8
|
// Common
|
|
10
9
|
enabled: { zh: '启用', en: 'Enabled' },
|
|
@@ -41,6 +40,7 @@ export const LABELS: Record<string, { zh: string; en: string }> = {
|
|
|
41
40
|
appToken: { zh: 'App Token', en: 'App Token' },
|
|
42
41
|
appId: { zh: 'App ID', en: 'App ID' },
|
|
43
42
|
appSecret: { zh: 'App Secret', en: 'App Secret' },
|
|
43
|
+
markdownSupport: { zh: 'Markdown 支持', en: 'Markdown Support' },
|
|
44
44
|
clientId: { zh: 'Client ID', en: 'Client ID' },
|
|
45
45
|
clientSecret: { zh: 'Client Secret', en: 'Client Secret' },
|
|
46
46
|
encryptKey: { zh: '加密密钥', en: 'Encrypt Key' },
|
|
@@ -93,22 +93,26 @@ export const LABELS: Record<string, { zh: string; en: string }> = {
|
|
|
93
93
|
replyDelayMs: { zh: '回复延迟(ms)', en: 'Reply Delay (ms)' },
|
|
94
94
|
secret: { zh: '密钥', en: 'Secret' },
|
|
95
95
|
|
|
96
|
-
// UI
|
|
97
|
-
|
|
98
|
-
port: { zh: '端口', en: 'Port' },
|
|
99
|
-
open: { zh: '自动打开', en: 'Open Automatically' },
|
|
100
|
-
reloadConfig: { zh: '重载配置', en: 'Reload Config' },
|
|
96
|
+
// UI
|
|
97
|
+
saveVerifyConnect: { zh: '保存并验证 / 连接', en: 'Save & Verify / Connect' },
|
|
101
98
|
|
|
102
99
|
// Status
|
|
103
100
|
connected: { zh: '已连接', en: 'Connected' },
|
|
104
101
|
disconnected: { zh: '未连接', en: 'Disconnected' },
|
|
105
102
|
connecting: { zh: '连接中...', en: 'Connecting...' },
|
|
103
|
+
feishuConnecting: { zh: '验证 / 连接中...', en: 'Verifying / connecting...' },
|
|
106
104
|
|
|
107
105
|
// Messages
|
|
108
106
|
configSaved: { zh: '配置已保存', en: 'Configuration saved' },
|
|
107
|
+
configSavedApplied: { zh: '配置已保存并已应用', en: 'Configuration saved and applied' },
|
|
109
108
|
configSaveFailed: { zh: '保存配置失败', en: 'Failed to save configuration' },
|
|
110
109
|
configReloaded: { zh: '配置已重载', en: 'Configuration reloaded' },
|
|
111
110
|
configReloadFailed: { zh: '重载配置失败', en: 'Failed to reload configuration' },
|
|
111
|
+
feishuVerifySuccess: {
|
|
112
|
+
zh: '验证成功,请到飞书开放平台完成事件订阅与发布后再开始使用。',
|
|
113
|
+
en: 'Verified. Please finish Feishu event subscription and app publishing before using.'
|
|
114
|
+
},
|
|
115
|
+
feishuVerifyFailed: { zh: '验证失败', en: 'Verification failed' },
|
|
112
116
|
enterTag: { zh: '输入后按回车...', en: 'Type and press Enter...' },
|
|
113
117
|
headerName: { zh: 'Header 名称', en: 'Header Name' },
|
|
114
118
|
headerValue: { zh: 'Header 值', en: 'Header Value' }
|
package/src/lib/logos.ts
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
type LogoMap = Record<string, string>;
|
|
2
|
+
|
|
3
|
+
const PROVIDER_LOGOS: LogoMap = {
|
|
4
|
+
openrouter: "openrouter.svg",
|
|
5
|
+
aihubmix: "aihubmix.png",
|
|
6
|
+
anthropic: "anthropic.svg",
|
|
7
|
+
openai: "openai.svg",
|
|
8
|
+
gemini: "gemini.svg",
|
|
9
|
+
deepseek: "deepseek.png",
|
|
10
|
+
zhipu: "zhipu.svg",
|
|
11
|
+
dashscope: "dashscope.png",
|
|
12
|
+
moonshot: "moonshot.png",
|
|
13
|
+
minimax: "minimax.svg",
|
|
14
|
+
vllm: "vllm.svg",
|
|
15
|
+
groq: "groq.svg"
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
const CHANNEL_LOGOS: LogoMap = {
|
|
19
|
+
telegram: "telegram.svg",
|
|
20
|
+
slack: "slack.svg",
|
|
21
|
+
discord: "discord.svg",
|
|
22
|
+
whatsapp: "whatsapp.svg",
|
|
23
|
+
qq: "qq.svg",
|
|
24
|
+
feishu: "feishu.svg",
|
|
25
|
+
dingtalk: "dingtalk.svg",
|
|
26
|
+
mochat: "mochat.svg",
|
|
27
|
+
email: "email.svg"
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
function resolveLogo(map: LogoMap, name: string): string | null {
|
|
31
|
+
const key = name.toLowerCase();
|
|
32
|
+
const file = map[key];
|
|
33
|
+
return file ? `/logos/${file}` : null;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export function getProviderLogo(name: string): string | null {
|
|
37
|
+
return resolveLogo(PROVIDER_LOGOS, name);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export function getChannelLogo(name: string): string | null {
|
|
41
|
+
return resolveLogo(CHANNEL_LOGOS, name);
|
|
42
|
+
}
|
package/src/stores/ui.store.ts
CHANGED
|
@@ -4,7 +4,7 @@ type ConnectionStatus = 'connected' | 'disconnected' | 'connecting';
|
|
|
4
4
|
|
|
5
5
|
interface UiState {
|
|
6
6
|
// Active configuration tab
|
|
7
|
-
activeTab: 'model' | 'providers' | 'channels'
|
|
7
|
+
activeTab: 'model' | 'providers' | 'channels';
|
|
8
8
|
setActiveTab: (tab: UiState['activeTab']) => void;
|
|
9
9
|
|
|
10
10
|
// Connection status
|