@nextclaw/ui 0.5.7 → 0.5.8
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 +6 -0
- package/dist/assets/index-BtwwwWcv.css +1 -0
- package/dist/assets/index-STUSj6p9.js +337 -0
- package/dist/index.html +2 -2
- package/package.json +1 -1
- package/src/components/common/StatusBadge.tsx +9 -13
- package/src/components/config/ChannelsList.tsx +33 -59
- package/src/components/config/CronConfig.tsx +28 -30
- package/src/components/config/ModelConfig.tsx +1 -1
- package/src/components/config/ProvidersList.tsx +32 -58
- package/src/components/config/RuntimeConfig.tsx +1 -1
- package/src/components/config/SessionsConfig.tsx +1 -1
- package/src/components/layout/AppLayout.tsx +3 -3
- package/src/components/layout/Header.tsx +4 -19
- package/src/components/layout/Sidebar.tsx +13 -21
- package/src/components/marketplace/MarketplacePage.tsx +17 -17
- package/src/components/ui/action-link.tsx +27 -0
- package/src/components/ui/button.tsx +12 -13
- package/src/components/ui/card.tsx +5 -5
- package/src/components/ui/config-card.tsx +71 -0
- package/src/components/ui/dialog.tsx +1 -1
- package/src/components/ui/input.tsx +2 -2
- package/src/components/ui/select.tsx +1 -1
- package/src/components/ui/status-dot.tsx +51 -0
- package/src/components/ui/switch.tsx +2 -2
- package/src/components/ui/tabs-custom.tsx +9 -6
- package/src/components/ui/tabs.tsx +3 -3
- package/src/index.css +25 -37
- package/src/styles/design-system.css +53 -49
- package/dist/assets/index-B3foa-xK.css +0 -1
- package/dist/assets/index-BI7rqOrL.js +0 -347
package/dist/index.html
CHANGED
|
@@ -6,8 +6,8 @@
|
|
|
6
6
|
<link rel="icon" type="image/svg+xml" href="/logo.svg" />
|
|
7
7
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
8
8
|
<title>NextClaw - 系统配置</title>
|
|
9
|
-
<script type="module" crossorigin src="/assets/index-
|
|
10
|
-
<link rel="stylesheet" crossorigin href="/assets/index-
|
|
9
|
+
<script type="module" crossorigin src="/assets/index-STUSj6p9.js"></script>
|
|
10
|
+
<link rel="stylesheet" crossorigin href="/assets/index-BtwwwWcv.css">
|
|
11
11
|
</head>
|
|
12
12
|
|
|
13
13
|
<body>
|
package/package.json
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { cn } from '@/lib/utils';
|
|
2
|
-
import {
|
|
2
|
+
import { Loader2 } from 'lucide-react';
|
|
3
3
|
import { t } from '@/lib/i18n';
|
|
4
4
|
|
|
5
5
|
type Status = 'connected' | 'disconnected' | 'connecting';
|
|
@@ -11,45 +11,41 @@ interface StatusBadgeProps {
|
|
|
11
11
|
|
|
12
12
|
const statusConfig: Record<
|
|
13
13
|
Status,
|
|
14
|
-
{ label: string; dotClass: string; textClass: string; bgClass: string
|
|
14
|
+
{ label: string; dotClass: string; textClass: string; bgClass: string }
|
|
15
15
|
> = {
|
|
16
16
|
connected: {
|
|
17
17
|
label: t('connected'),
|
|
18
18
|
dotClass: 'bg-emerald-500',
|
|
19
19
|
textClass: 'text-emerald-600',
|
|
20
20
|
bgClass: 'bg-emerald-50',
|
|
21
|
-
icon: Check
|
|
22
21
|
},
|
|
23
22
|
disconnected: {
|
|
24
23
|
label: t('disconnected'),
|
|
25
|
-
dotClass: 'bg-gray-
|
|
26
|
-
textClass: 'text-gray-
|
|
27
|
-
bgClass: 'bg-gray-100',
|
|
28
|
-
icon: X
|
|
24
|
+
dotClass: 'bg-gray-300',
|
|
25
|
+
textClass: 'text-gray-400',
|
|
26
|
+
bgClass: 'bg-gray-100/80',
|
|
29
27
|
},
|
|
30
28
|
connecting: {
|
|
31
29
|
label: t('connecting'),
|
|
32
30
|
dotClass: 'bg-amber-400',
|
|
33
31
|
textClass: 'text-amber-600',
|
|
34
32
|
bgClass: 'bg-amber-50',
|
|
35
|
-
icon: Loader2
|
|
36
33
|
}
|
|
37
34
|
};
|
|
38
35
|
|
|
39
36
|
export function StatusBadge({ status, className }: StatusBadgeProps) {
|
|
40
37
|
const config = statusConfig[status];
|
|
41
|
-
const Icon = config.icon;
|
|
42
38
|
|
|
43
39
|
return (
|
|
44
40
|
<div className={cn(
|
|
45
|
-
'flex items-center gap-
|
|
41
|
+
'flex items-center gap-1.5 px-2 py-0.5 rounded-full',
|
|
46
42
|
config.bgClass,
|
|
47
43
|
className
|
|
48
44
|
)}>
|
|
49
|
-
<
|
|
50
|
-
<span className={cn('text-
|
|
45
|
+
<span className={cn('h-1.5 w-1.5 rounded-full', config.dotClass)} />
|
|
46
|
+
<span className={cn('text-[11px] font-medium flex items-center gap-1', config.textClass)}>
|
|
51
47
|
{config.label}
|
|
52
|
-
{status === 'connecting' && <
|
|
48
|
+
{status === 'connecting' && <Loader2 className="h-2.5 w-2.5 animate-spin" />}
|
|
53
49
|
</span>
|
|
54
50
|
</div>
|
|
55
51
|
);
|
|
@@ -1,13 +1,16 @@
|
|
|
1
1
|
import { useConfig, useConfigMeta, useConfigSchema } from '@/hooks/useConfig';
|
|
2
|
-
import { MessageCircle, Mail, MessageSquare, Slack, ExternalLink, Bell,
|
|
2
|
+
import { MessageCircle, Mail, MessageSquare, Slack, ExternalLink, Bell, ArrowRight } from 'lucide-react';
|
|
3
3
|
import { useState } from 'react';
|
|
4
4
|
import { ChannelForm } from './ChannelForm';
|
|
5
5
|
import { useUiStore } from '@/stores/ui.store';
|
|
6
|
-
import { cn } from '@/lib/utils';
|
|
7
6
|
import { Tabs } from '@/components/ui/tabs-custom';
|
|
8
7
|
import { LogoBadge } from '@/components/common/LogoBadge';
|
|
9
8
|
import { getChannelLogo } from '@/lib/logos';
|
|
10
9
|
import { hintForPath } from '@/lib/config-hints';
|
|
10
|
+
import { ConfigCard, ConfigCardHeader, ConfigCardBody, ConfigCardFooter } from '@/components/ui/config-card';
|
|
11
|
+
import { StatusDot } from '@/components/ui/status-dot';
|
|
12
|
+
import { ActionLink } from '@/components/ui/action-link';
|
|
13
|
+
import { cn } from '@/lib/utils';
|
|
11
14
|
|
|
12
15
|
const channelIcons: Record<string, typeof MessageCircle> = {
|
|
13
16
|
telegram: MessageCircle,
|
|
@@ -50,8 +53,8 @@ export function ChannelsList() {
|
|
|
50
53
|
|
|
51
54
|
return (
|
|
52
55
|
<div className="animate-fade-in pb-20">
|
|
53
|
-
<div className="flex items-center justify-between mb-
|
|
54
|
-
<h2 className="text-
|
|
56
|
+
<div className="flex items-center justify-between mb-6">
|
|
57
|
+
<h2 className="text-xl font-semibold text-gray-900">Message Channels</h2>
|
|
55
58
|
</div>
|
|
56
59
|
|
|
57
60
|
<Tabs tabs={tabs} activeTab={activeTab} onChange={setActiveTab} />
|
|
@@ -69,76 +72,47 @@ export function ChannelsList() {
|
|
|
69
72
|
'Configure this communication channel';
|
|
70
73
|
|
|
71
74
|
return (
|
|
72
|
-
<
|
|
73
|
-
|
|
74
|
-
className={cn(
|
|
75
|
-
'group relative flex flex-col p-5 rounded-2xl border transition-all duration-base cursor-pointer',
|
|
76
|
-
'hover:shadow-card-hover hover:-translate-y-0.5',
|
|
77
|
-
enabled
|
|
78
|
-
? 'bg-white border-gray-200 shadow-sm'
|
|
79
|
-
: 'bg-white border-transparent shadow-sm hover:border-gray-200'
|
|
80
|
-
)}
|
|
81
|
-
onClick={() => openChannelModal(channel.name)}
|
|
82
|
-
>
|
|
83
|
-
{/* Header with Icon and Status */}
|
|
84
|
-
<div className="flex items-start justify-between mb-4">
|
|
75
|
+
<ConfigCard key={channel.name} onClick={() => openChannelModal(channel.name)}>
|
|
76
|
+
<ConfigCardHeader>
|
|
85
77
|
<LogoBadge
|
|
86
78
|
name={channel.name}
|
|
87
79
|
src={getChannelLogo(channel.name)}
|
|
88
80
|
className={cn(
|
|
89
|
-
'h-
|
|
81
|
+
'h-11 w-11 rounded-xl border transition-all',
|
|
90
82
|
enabled
|
|
91
|
-
? 'bg-white border-primary'
|
|
92
|
-
: 'bg-white border-gray-200 group-hover:border-gray-300'
|
|
83
|
+
? 'bg-white border-primary/30'
|
|
84
|
+
: 'bg-white border-gray-200/60 group-hover:border-gray-300'
|
|
93
85
|
)}
|
|
94
|
-
imgClassName="h-
|
|
95
|
-
fallback={<Icon className="h-
|
|
86
|
+
imgClassName="h-5 w-5"
|
|
87
|
+
fallback={<Icon className="h-5 w-5" />}
|
|
96
88
|
/>
|
|
89
|
+
<StatusDot
|
|
90
|
+
status={enabled ? 'active' : 'inactive'}
|
|
91
|
+
label={enabled ? 'Active' : 'Inactive'}
|
|
92
|
+
/>
|
|
93
|
+
</ConfigCardHeader>
|
|
97
94
|
|
|
98
|
-
|
|
99
|
-
{
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
<span className="text-[11px] font-bold">Active</span>
|
|
103
|
-
</div>
|
|
104
|
-
) : (
|
|
105
|
-
<div className="flex items-center gap-1.5 px-2.5 py-1 rounded-full bg-gray-100 text-gray-500">
|
|
106
|
-
<Radio className="h-3.5 w-3.5" />
|
|
107
|
-
<span className="text-[11px] font-bold">Inactive</span>
|
|
108
|
-
</div>
|
|
109
|
-
)}
|
|
110
|
-
</div>
|
|
111
|
-
|
|
112
|
-
{/* Channel Info */}
|
|
113
|
-
<div className="flex-1">
|
|
114
|
-
<h3 className="text-[15px] font-bold text-gray-900 mb-1">
|
|
115
|
-
{channel.displayName || channel.name}
|
|
116
|
-
</h3>
|
|
117
|
-
<p className="text-[12px] text-gray-500 leading-relaxed line-clamp-2">
|
|
118
|
-
{description}
|
|
119
|
-
</p>
|
|
120
|
-
</div>
|
|
95
|
+
<ConfigCardBody
|
|
96
|
+
title={channel.displayName || channel.name}
|
|
97
|
+
description={description}
|
|
98
|
+
/>
|
|
121
99
|
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
<span className="inline-flex items-center gap-1 text-[13px] font-semibold text-gray-600 group-hover:text-primary transition-colors cursor-pointer">
|
|
125
|
-
{enabled ? 'Configure' : 'Enable'}
|
|
126
|
-
<ArrowRight className="h-3.5 w-3.5 transition-transform group-hover:translate-x-0.5" />
|
|
127
|
-
</span>
|
|
100
|
+
<ConfigCardFooter>
|
|
101
|
+
<ActionLink label={enabled ? 'Configure' : 'Enable'} />
|
|
128
102
|
{channel.tutorialUrl && (
|
|
129
103
|
<a
|
|
130
104
|
href={channel.tutorialUrl}
|
|
131
105
|
target="_blank"
|
|
132
106
|
rel="noreferrer"
|
|
133
107
|
onClick={(e) => e.stopPropagation()}
|
|
134
|
-
className="flex items-center justify-center h-
|
|
108
|
+
className="flex items-center justify-center h-6 w-6 rounded-md text-gray-300 hover:text-gray-500 hover:bg-gray-100/60 transition-colors"
|
|
135
109
|
title="View Guide"
|
|
136
110
|
>
|
|
137
111
|
<ExternalLink className="h-3.5 w-3.5" />
|
|
138
112
|
</a>
|
|
139
113
|
)}
|
|
140
|
-
</
|
|
141
|
-
</
|
|
114
|
+
</ConfigCardFooter>
|
|
115
|
+
</ConfigCard>
|
|
142
116
|
);
|
|
143
117
|
})}
|
|
144
118
|
</div>
|
|
@@ -146,14 +120,14 @@ export function ChannelsList() {
|
|
|
146
120
|
{/* Empty State */}
|
|
147
121
|
{filteredChannels.length === 0 && (
|
|
148
122
|
<div className="flex flex-col items-center justify-center py-16 text-center">
|
|
149
|
-
<div className="h-
|
|
150
|
-
<MessageSquare className="h-
|
|
123
|
+
<div className="h-14 w-14 flex items-center justify-center rounded-xl bg-gray-100/80 mb-4">
|
|
124
|
+
<MessageSquare className="h-6 w-6 text-gray-300" />
|
|
151
125
|
</div>
|
|
152
|
-
<h3 className="text-[
|
|
126
|
+
<h3 className="text-[14px] font-semibold text-gray-900 mb-1.5">
|
|
153
127
|
No channels enabled
|
|
154
128
|
</h3>
|
|
155
|
-
<p className="text-[13px] text-gray-
|
|
156
|
-
Enable a messaging channel to start receiving messages.
|
|
129
|
+
<p className="text-[13px] text-gray-400 max-w-sm">
|
|
130
|
+
Enable a messaging channel to start receiving messages.
|
|
157
131
|
</p>
|
|
158
132
|
</div>
|
|
159
133
|
)}
|
|
@@ -144,7 +144,7 @@ export function CronConfig() {
|
|
|
144
144
|
<div className="h-[calc(100vh-80px)] w-full max-w-[1200px] mx-auto animate-fade-in flex flex-col pt-6 pb-2">
|
|
145
145
|
<div className="flex items-center justify-between mb-6 shrink-0">
|
|
146
146
|
<div>
|
|
147
|
-
<h2 className="text-
|
|
147
|
+
<h2 className="text-xl font-semibold text-gray-900 tracking-tight">{t('cronPageTitle')}</h2>
|
|
148
148
|
<p className="text-sm text-gray-500 mt-1">{t('cronPageDescription')}</p>
|
|
149
149
|
</div>
|
|
150
150
|
<Button
|
|
@@ -157,36 +157,34 @@ export function CronConfig() {
|
|
|
157
157
|
</Button>
|
|
158
158
|
</div>
|
|
159
159
|
|
|
160
|
-
<
|
|
161
|
-
<
|
|
162
|
-
<div className="
|
|
163
|
-
<
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
<AlarmClock className="absolute left-3 top-1/2 -translate-y-1/2 h-4 w-4 text-gray-400" />
|
|
171
|
-
</div>
|
|
172
|
-
<div className="min-w-[180px]">
|
|
173
|
-
<Select value={status} onValueChange={(value) => setStatus(value as StatusFilter)}>
|
|
174
|
-
<SelectTrigger className="w-full">
|
|
175
|
-
<SelectValue placeholder={t('cronStatusLabel')} />
|
|
176
|
-
</SelectTrigger>
|
|
177
|
-
<SelectContent>
|
|
178
|
-
<SelectItem value="all">{t('cronStatusAll')}</SelectItem>
|
|
179
|
-
<SelectItem value="enabled">{t('cronStatusEnabled')}</SelectItem>
|
|
180
|
-
<SelectItem value="disabled">{t('cronStatusDisabled')}</SelectItem>
|
|
181
|
-
</SelectContent>
|
|
182
|
-
</Select>
|
|
183
|
-
</div>
|
|
184
|
-
<div className="text-xs text-gray-500 ml-auto">
|
|
185
|
-
{t('cronTotalLabel')}: {cronQuery.data?.total ?? 0} / {jobs.length}
|
|
186
|
-
</div>
|
|
160
|
+
<div className="mb-6">
|
|
161
|
+
<div className="flex flex-wrap gap-3 items-center">
|
|
162
|
+
<div className="relative flex-1 min-w-[240px]">
|
|
163
|
+
<Input
|
|
164
|
+
value={query}
|
|
165
|
+
onChange={(e) => setQuery(e.target.value)}
|
|
166
|
+
placeholder={t('cronSearchPlaceholder')}
|
|
167
|
+
className="pl-9"
|
|
168
|
+
/>
|
|
169
|
+
<AlarmClock className="absolute left-3 top-1/2 -translate-y-1/2 h-4 w-4 text-gray-400" />
|
|
187
170
|
</div>
|
|
188
|
-
|
|
189
|
-
|
|
171
|
+
<div className="min-w-[180px]">
|
|
172
|
+
<Select value={status} onValueChange={(value) => setStatus(value as StatusFilter)}>
|
|
173
|
+
<SelectTrigger className="w-full">
|
|
174
|
+
<SelectValue placeholder={t('cronStatusLabel')} />
|
|
175
|
+
</SelectTrigger>
|
|
176
|
+
<SelectContent>
|
|
177
|
+
<SelectItem value="all">{t('cronStatusAll')}</SelectItem>
|
|
178
|
+
<SelectItem value="enabled">{t('cronStatusEnabled')}</SelectItem>
|
|
179
|
+
<SelectItem value="disabled">{t('cronStatusDisabled')}</SelectItem>
|
|
180
|
+
</SelectContent>
|
|
181
|
+
</Select>
|
|
182
|
+
</div>
|
|
183
|
+
<div className="text-xs text-gray-500 ml-auto">
|
|
184
|
+
{t('cronTotalLabel')}: {cronQuery.data?.total ?? 0} / {jobs.length}
|
|
185
|
+
</div>
|
|
186
|
+
</div>
|
|
187
|
+
</div>
|
|
190
188
|
|
|
191
189
|
<div className="flex-1 overflow-auto custom-scrollbar">
|
|
192
190
|
{cronQuery.isLoading ? (
|
|
@@ -69,7 +69,7 @@ export function ModelConfig() {
|
|
|
69
69
|
return (
|
|
70
70
|
<div className="max-w-4xl animate-fade-in pb-20">
|
|
71
71
|
<div className="mb-10">
|
|
72
|
-
<h2 className="text-
|
|
72
|
+
<h2 className="text-xl font-semibold text-gray-900">Model Configuration</h2>
|
|
73
73
|
<p className="text-sm text-gray-500 mt-1">Configure default AI model and runtime limits</p>
|
|
74
74
|
</div>
|
|
75
75
|
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { useConfig, useConfigMeta, useConfigSchema } from '@/hooks/useConfig';
|
|
2
|
-
import { KeyRound
|
|
2
|
+
import { KeyRound } from 'lucide-react';
|
|
3
3
|
import { useState } from 'react';
|
|
4
4
|
import { ProviderForm } from './ProviderForm';
|
|
5
5
|
import { useUiStore } from '@/stores/ui.store';
|
|
@@ -8,6 +8,9 @@ import { Tabs } from '@/components/ui/tabs-custom';
|
|
|
8
8
|
import { LogoBadge } from '@/components/common/LogoBadge';
|
|
9
9
|
import { getProviderLogo } from '@/lib/logos';
|
|
10
10
|
import { hintForPath } from '@/lib/config-hints';
|
|
11
|
+
import { ConfigCard, ConfigCardHeader, ConfigCardBody, ConfigCardFooter } from '@/components/ui/config-card';
|
|
12
|
+
import { StatusDot } from '@/components/ui/status-dot';
|
|
13
|
+
import { ActionLink } from '@/components/ui/action-link';
|
|
11
14
|
|
|
12
15
|
export function ProvidersList() {
|
|
13
16
|
const { data: config } = useConfig();
|
|
@@ -32,8 +35,8 @@ export function ProvidersList() {
|
|
|
32
35
|
|
|
33
36
|
return (
|
|
34
37
|
<div className="animate-fade-in pb-20">
|
|
35
|
-
<div className="flex items-center justify-between mb-
|
|
36
|
-
<h2 className="text-
|
|
38
|
+
<div className="flex items-center justify-between mb-6">
|
|
39
|
+
<h2 className="text-xl font-semibold text-gray-900">AI Providers</h2>
|
|
37
40
|
</div>
|
|
38
41
|
|
|
39
42
|
<Tabs tabs={tabs} activeTab={activeTab} onChange={setActiveTab} />
|
|
@@ -47,71 +50,42 @@ export function ProvidersList() {
|
|
|
47
50
|
const description = providerHint?.help || 'Configure AI services for your agents';
|
|
48
51
|
|
|
49
52
|
return (
|
|
50
|
-
<
|
|
51
|
-
|
|
52
|
-
className={cn(
|
|
53
|
-
'group relative flex-col p-5 rounded-2xl border transition-all duration-base cursor-pointer',
|
|
54
|
-
'hover:shadow-card-hover hover:-translate-y-0.5',
|
|
55
|
-
hasConfig
|
|
56
|
-
? 'bg-white border-gray-200 shadow-sm'
|
|
57
|
-
: 'bg-white border-transparent shadow-sm hover:border-gray-200'
|
|
58
|
-
)}
|
|
59
|
-
onClick={() => openProviderModal(provider.name)}
|
|
60
|
-
>
|
|
61
|
-
{/* Header with Logo and Status */}
|
|
62
|
-
<div className="flex items-start justify-between mb-4">
|
|
53
|
+
<ConfigCard key={provider.name} onClick={() => openProviderModal(provider.name)}>
|
|
54
|
+
<ConfigCardHeader>
|
|
63
55
|
<LogoBadge
|
|
64
56
|
name={provider.name}
|
|
65
57
|
src={getProviderLogo(provider.name)}
|
|
66
58
|
className={cn(
|
|
67
|
-
'h-
|
|
59
|
+
'h-11 w-11 rounded-xl border transition-all',
|
|
68
60
|
hasConfig
|
|
69
|
-
? 'bg-white border-primary'
|
|
70
|
-
: 'bg-white border-gray-200 group-hover:border-gray-300'
|
|
61
|
+
? 'bg-white border-primary/30'
|
|
62
|
+
: 'bg-white border-gray-200/60 group-hover:border-gray-300'
|
|
71
63
|
)}
|
|
72
|
-
imgClassName="h-
|
|
64
|
+
imgClassName="h-6 w-6"
|
|
73
65
|
fallback={(
|
|
74
66
|
<span className={cn(
|
|
75
|
-
'text-
|
|
76
|
-
hasConfig ? 'text-gray-
|
|
67
|
+
'text-base font-semibold uppercase',
|
|
68
|
+
hasConfig ? 'text-gray-800' : 'text-gray-400'
|
|
77
69
|
)}>
|
|
78
70
|
{provider.name[0]}
|
|
79
71
|
</span>
|
|
80
72
|
)}
|
|
81
73
|
/>
|
|
74
|
+
<StatusDot
|
|
75
|
+
status={hasConfig ? 'ready' : 'setup'}
|
|
76
|
+
label={hasConfig ? 'Ready' : 'Setup'}
|
|
77
|
+
/>
|
|
78
|
+
</ConfigCardHeader>
|
|
82
79
|
|
|
83
|
-
|
|
84
|
-
{
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
<span className="text-[11px] font-bold">Ready</span>
|
|
88
|
-
</div>
|
|
89
|
-
) : (
|
|
90
|
-
<div className="flex items-center gap-1.5 px-2.5 py-1 rounded-full bg-gray-100 text-gray-500">
|
|
91
|
-
<Settings className="h-3.5 w-3.5" />
|
|
92
|
-
<span className="text-[11px] font-bold">Setup</span>
|
|
93
|
-
</div>
|
|
94
|
-
)}
|
|
95
|
-
</div>
|
|
96
|
-
|
|
97
|
-
{/* Provider Info */}
|
|
98
|
-
<div className="flex-1">
|
|
99
|
-
<h3 className="text-[15px] font-bold text-gray-900 mb-1">
|
|
100
|
-
{provider.displayName || provider.name}
|
|
101
|
-
</h3>
|
|
102
|
-
<p className="text-[12px] text-gray-500 leading-relaxed line-clamp-2">
|
|
103
|
-
{description}
|
|
104
|
-
</p>
|
|
105
|
-
</div>
|
|
80
|
+
<ConfigCardBody
|
|
81
|
+
title={provider.displayName || provider.name}
|
|
82
|
+
description={description}
|
|
83
|
+
/>
|
|
106
84
|
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
<ArrowRight className="h-3.5 w-3.5 transition-transform group-hover:translate-x-0.5" />
|
|
112
|
-
</span>
|
|
113
|
-
</div>
|
|
114
|
-
</div>
|
|
85
|
+
<ConfigCardFooter>
|
|
86
|
+
<ActionLink label={hasConfig ? 'Configure' : 'Add Provider'} />
|
|
87
|
+
</ConfigCardFooter>
|
|
88
|
+
</ConfigCard>
|
|
115
89
|
);
|
|
116
90
|
})}
|
|
117
91
|
</div>
|
|
@@ -119,14 +93,14 @@ export function ProvidersList() {
|
|
|
119
93
|
{/* Empty State */}
|
|
120
94
|
{filteredProviders.length === 0 && (
|
|
121
95
|
<div className="flex flex-col items-center justify-center py-16 text-center">
|
|
122
|
-
<div className="h-
|
|
123
|
-
<KeyRound className="h-
|
|
96
|
+
<div className="h-14 w-14 flex items-center justify-center rounded-xl bg-gray-100/80 mb-4">
|
|
97
|
+
<KeyRound className="h-6 w-6 text-gray-300" />
|
|
124
98
|
</div>
|
|
125
|
-
<h3 className="text-[
|
|
99
|
+
<h3 className="text-[14px] font-semibold text-gray-900 mb-1.5">
|
|
126
100
|
No providers configured
|
|
127
101
|
</h3>
|
|
128
|
-
<p className="text-[13px] text-gray-
|
|
129
|
-
Add an AI provider to start using the platform.
|
|
102
|
+
<p className="text-[13px] text-gray-400 max-w-sm">
|
|
103
|
+
Add an AI provider to start using the platform.
|
|
130
104
|
</p>
|
|
131
105
|
</div>
|
|
132
106
|
)}
|
|
@@ -232,7 +232,7 @@ export function RuntimeConfig() {
|
|
|
232
232
|
return (
|
|
233
233
|
<div className="space-y-6 pb-20 animate-fade-in">
|
|
234
234
|
<div>
|
|
235
|
-
<h2 className="text-
|
|
235
|
+
<h2 className="text-xl font-semibold text-gray-900">Routing & Runtime</h2>
|
|
236
236
|
<p className="text-sm text-gray-500 mt-1">
|
|
237
237
|
Align multi-agent routing with OpenClaw: bindings, agent pool, and DM scope.
|
|
238
238
|
</p>
|
|
@@ -224,7 +224,7 @@ export function SessionsConfig() {
|
|
|
224
224
|
|
|
225
225
|
<div className="flex items-center justify-between mb-6 shrink-0">
|
|
226
226
|
<div>
|
|
227
|
-
<h2 className="text-
|
|
227
|
+
<h2 className="text-xl font-semibold text-gray-900 tracking-tight">{t('sessionsPageTitle')}</h2>
|
|
228
228
|
<p className="text-sm text-gray-500 mt-1">{t('sessionsPageDescription')}</p>
|
|
229
229
|
</div>
|
|
230
230
|
</div>
|
|
@@ -10,11 +10,11 @@ function AppLayoutInner({ children }: AppLayoutProps) {
|
|
|
10
10
|
useDocLinkInterceptor();
|
|
11
11
|
|
|
12
12
|
return (
|
|
13
|
-
<div className="h-screen flex bg-
|
|
13
|
+
<div className="h-screen flex bg-background font-sans text-foreground">
|
|
14
14
|
<Sidebar />
|
|
15
15
|
<div className="flex-1 flex min-w-0 overflow-hidden relative">
|
|
16
|
-
<div className="flex-1 flex flex-col min-w-0
|
|
17
|
-
<main className="flex-1 overflow-auto custom-scrollbar p-
|
|
16
|
+
<div className="flex-1 flex flex-col min-w-0 overflow-hidden">
|
|
17
|
+
<main className="flex-1 overflow-auto custom-scrollbar p-8">
|
|
18
18
|
<div className="max-w-6xl mx-auto animate-fade-in h-full">
|
|
19
19
|
{children}
|
|
20
20
|
</div>
|
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
import { Bell, Search } from 'lucide-react';
|
|
2
|
-
|
|
3
1
|
interface HeaderProps {
|
|
4
2
|
title?: string;
|
|
5
3
|
description?: string;
|
|
@@ -7,30 +5,17 @@ interface HeaderProps {
|
|
|
7
5
|
|
|
8
6
|
export function Header({ title, description }: HeaderProps) {
|
|
9
7
|
return (
|
|
10
|
-
<header className="h-
|
|
11
|
-
<div className="flex items-center gap-
|
|
8
|
+
<header className="h-14 bg-white/90 backdrop-blur-sm sticky top-0 z-10 flex items-center px-6 transition-all duration-base">
|
|
9
|
+
<div className="flex items-center gap-3">
|
|
12
10
|
{title && (
|
|
13
11
|
<div>
|
|
14
|
-
<h2 className="text-
|
|
12
|
+
<h2 className="text-[15px] font-semibold text-gray-900">{title}</h2>
|
|
15
13
|
{description && (
|
|
16
|
-
<p className="text-xs text-gray-
|
|
14
|
+
<p className="text-xs text-gray-400 mt-0.5">{description}</p>
|
|
17
15
|
)}
|
|
18
16
|
</div>
|
|
19
17
|
)}
|
|
20
18
|
</div>
|
|
21
|
-
|
|
22
|
-
<div className="flex items-center gap-3">
|
|
23
|
-
<button className="h-9 w-9 rounded-lg bg-gray-100 flex items-center justify-center text-gray-500 hover:bg-gray-200 hover:text-gray-700 transition-colors duration-fast">
|
|
24
|
-
<Search className="h-4 w-4" />
|
|
25
|
-
</button>
|
|
26
|
-
<button className="h-9 w-9 rounded-lg bg-gray-100 flex items-center justify-center text-gray-500 hover:bg-gray-200 hover:text-gray-700 transition-colors duration-fast relative">
|
|
27
|
-
<Bell className="h-4 w-4" />
|
|
28
|
-
<span className="absolute top-1.5 right-1.5 h-2 w-2 rounded-full bg-primary" />
|
|
29
|
-
</button>
|
|
30
|
-
<div className="h-9 w-9 rounded-lg bg-gradient-to-br from-primary to-primary-600 flex items-center justify-center">
|
|
31
|
-
<span className="text-xs font-semibold text-white">N</span>
|
|
32
|
-
</div>
|
|
33
|
-
</div>
|
|
34
19
|
</header>
|
|
35
20
|
);
|
|
36
21
|
}
|
|
@@ -46,14 +46,14 @@ export function Sidebar() {
|
|
|
46
46
|
const docBrowser = useDocBrowser();
|
|
47
47
|
|
|
48
48
|
return (
|
|
49
|
-
<aside className="w-[240px] bg-
|
|
49
|
+
<aside className="w-[240px] bg-[#f0f2f7] flex flex-col h-full py-6 px-4">
|
|
50
50
|
{/* Logo Area */}
|
|
51
|
-
<div className="px-
|
|
52
|
-
<div className="flex items-center gap-2.5
|
|
53
|
-
<div className="h-7 w-7 rounded-lg overflow-hidden flex items-center justify-center
|
|
51
|
+
<div className="px-2 mb-8">
|
|
52
|
+
<div className="flex items-center gap-2.5 cursor-pointer">
|
|
53
|
+
<div className="h-7 w-7 rounded-lg overflow-hidden flex items-center justify-center">
|
|
54
54
|
<img src="/logo.svg" alt="NextClaw" className="h-full w-full object-contain" />
|
|
55
55
|
</div>
|
|
56
|
-
<span className="text-[15px] font-
|
|
56
|
+
<span className="text-[15px] font-semibold text-gray-800 tracking-[-0.01em]">NextClaw</span>
|
|
57
57
|
</div>
|
|
58
58
|
</div>
|
|
59
59
|
|
|
@@ -68,17 +68,17 @@ export function Sidebar() {
|
|
|
68
68
|
<NavLink
|
|
69
69
|
to={item.target}
|
|
70
70
|
className={({ isActive }) => cn(
|
|
71
|
-
'group w-full flex items-center gap-3 px-
|
|
71
|
+
'group w-full flex items-center gap-3 px-3 py-2.5 rounded-xl text-[14px] font-medium transition-all duration-base',
|
|
72
72
|
isActive
|
|
73
|
-
? 'bg-
|
|
74
|
-
: 'text-gray-600 hover:bg-
|
|
73
|
+
? 'bg-primary/10 text-primary font-semibold'
|
|
74
|
+
: 'text-gray-600 hover:bg-[#e4e7ef] hover:text-gray-800'
|
|
75
75
|
)}
|
|
76
76
|
>
|
|
77
77
|
{({ isActive }) => (
|
|
78
78
|
<>
|
|
79
79
|
<Icon className={cn(
|
|
80
|
-
'h-
|
|
81
|
-
isActive ? 'text-primary' : 'text-gray-
|
|
80
|
+
'h-[17px] w-[17px] transition-colors',
|
|
81
|
+
isActive ? 'text-primary' : 'text-gray-400'
|
|
82
82
|
)} />
|
|
83
83
|
<span className="flex-1 text-left">{item.label}</span>
|
|
84
84
|
</>
|
|
@@ -91,20 +91,12 @@ export function Sidebar() {
|
|
|
91
91
|
</nav>
|
|
92
92
|
|
|
93
93
|
{/* Help Button */}
|
|
94
|
-
<div className="pt-
|
|
94
|
+
<div className="pt-3 border-t border-[#dde0ea] mt-3">
|
|
95
95
|
<button
|
|
96
96
|
onClick={() => docBrowser.open()}
|
|
97
|
-
className=
|
|
98
|
-
'w-full flex items-center gap-3 px-4 py-2.5 rounded-lg text-sm font-medium transition-all duration-base',
|
|
99
|
-
docBrowser.isOpen
|
|
100
|
-
? 'bg-brand-50 text-brand-700'
|
|
101
|
-
: 'text-gray-600 hover:bg-gray-100/80 hover:text-gray-900'
|
|
102
|
-
)}
|
|
97
|
+
className="w-full flex items-center gap-3 px-3 py-2.5 rounded-xl text-[14px] font-medium transition-all duration-base text-gray-600 hover:bg-[#e4e7ef] hover:text-gray-800"
|
|
103
98
|
>
|
|
104
|
-
<BookOpen className=
|
|
105
|
-
'h-4 w-4',
|
|
106
|
-
docBrowser.isOpen ? 'text-primary' : 'text-gray-500'
|
|
107
|
-
)} />
|
|
99
|
+
<BookOpen className="h-[17px] w-[17px] text-gray-400" />
|
|
108
100
|
<span className="flex-1 text-left">{t('docBrowserHelp')}</span>
|
|
109
101
|
</button>
|
|
110
102
|
</div>
|