moelove commited on
Commit
fa4edd5
·
1 Parent(s): ee61dec

Add multiple profiles

Browse files

Signed-off-by: Jintao Zhang <zhangjintao9020@gmail.com>

Files changed (4) hide show
  1. src/App.jsx +67 -19
  2. src/components/ChatWindow.jsx +63 -30
  3. src/components/Settings.jsx +155 -53
  4. src/index.css +511 -617
src/App.jsx CHANGED
@@ -5,11 +5,17 @@ import Settings from './components/Settings';
5
  import useLocalStorage from './hooks/useLocalStorage';
6
 
7
  function App() {
8
- const [settings, setSettings] = useLocalStorage('settings', {
9
- apiEndpoint: '',
10
- apiKey: '',
11
- model: 'DeepSeek-R1'
12
- });
 
 
 
 
 
 
13
 
14
  const [chats, setChats] = useLocalStorage('chats', []);
15
  const [currentChatId, setCurrentChatId] = useState(null);
@@ -17,6 +23,9 @@ function App() {
17
  const [loading, setLoading] = useState(false);
18
  const [showSettings, setShowSettings] = useState(false);
19
  const [sidebarCollapsed, setSidebarCollapsed] = useState(false);
 
 
 
20
 
21
  const createNewChat = () => {
22
  const newChat = {
@@ -43,16 +52,50 @@ function App() {
43
  setSidebarCollapsed(!sidebarCollapsed);
44
  };
45
 
 
 
 
 
 
 
 
 
 
46
  return (
47
  <div className="app-container">
48
  <div className="app-header">
49
- <h1 className="app-title">Thinking Model Client</h1>
50
- <button
51
- className="settings-button"
52
- onClick={toggleSettings}
53
- >
54
- Settings
55
- </button>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
56
  </div>
57
 
58
  <div className="app-content">
@@ -80,7 +123,7 @@ function App() {
80
  {currentChatId ? (
81
  <ChatWindow
82
  chat={chats.find(c => c.id === currentChatId)}
83
- settings={settings}
84
  onUpdateChat={(updatedChat) => {
85
  setChats(chats.map(c =>
86
  c.id === updatedChat.id ? updatedChat : c
@@ -109,12 +152,17 @@ function App() {
109
  ×
110
  </button>
111
  <Settings
112
- settings={settings}
113
- onSave={(newSettings) => {
114
- setSettings(newSettings);
115
- setShowSettings(false);
116
- }}
117
- setSettings={setSettings}
 
 
 
 
 
118
  />
119
  </div>
120
  </div>
 
5
  import useLocalStorage from './hooks/useLocalStorage';
6
 
7
  function App() {
8
+ const [profiles, setProfiles] = useLocalStorage('profiles', [
9
+ {
10
+ id: 'default',
11
+ name: 'Default Profile',
12
+ apiEndpoint: '',
13
+ apiKey: '',
14
+ model: 'DeepSeek-R1'
15
+ }
16
+ ]);
17
+
18
+ const [activeProfileId, setActiveProfileId] = useLocalStorage('activeProfileId', 'default');
19
 
20
  const [chats, setChats] = useLocalStorage('chats', []);
21
  const [currentChatId, setCurrentChatId] = useState(null);
 
23
  const [loading, setLoading] = useState(false);
24
  const [showSettings, setShowSettings] = useState(false);
25
  const [sidebarCollapsed, setSidebarCollapsed] = useState(false);
26
+ const [showProfileDropdown, setShowProfileDropdown] = useState(false);
27
+
28
+ const activeProfile = profiles.find(p => p.id === activeProfileId) || profiles[0];
29
 
30
  const createNewChat = () => {
31
  const newChat = {
 
52
  setSidebarCollapsed(!sidebarCollapsed);
53
  };
54
 
55
+ const toggleProfileDropdown = () => {
56
+ setShowProfileDropdown(!showProfileDropdown);
57
+ };
58
+
59
+ const handleProfileSelect = (profileId) => {
60
+ setActiveProfileId(profileId);
61
+ setShowProfileDropdown(false);
62
+ };
63
+
64
  return (
65
  <div className="app-container">
66
  <div className="app-header">
67
+ <div className="left-section">
68
+ <h1 className="app-title">Thinking Model Client</h1>
69
+ </div>
70
+ <div className="right-section">
71
+ <div className="profile-dropdown-container">
72
+ <button
73
+ className="profile-dropdown-button"
74
+ onClick={toggleProfileDropdown}
75
+ >
76
+ {activeProfile.name} ▼
77
+ </button>
78
+ {showProfileDropdown && (
79
+ <div className="profile-dropdown-menu">
80
+ {profiles.map(profile => (
81
+ <div
82
+ key={profile.id}
83
+ className={`profile-option ${profile.id === activeProfileId ? 'active' : ''}`}
84
+ onClick={() => handleProfileSelect(profile.id)}
85
+ >
86
+ {profile.name}
87
+ </div>
88
+ ))}
89
+ </div>
90
+ )}
91
+ </div>
92
+ <button
93
+ className="settings-button"
94
+ onClick={toggleSettings}
95
+ >
96
+ Settings
97
+ </button>
98
+ </div>
99
  </div>
100
 
101
  <div className="app-content">
 
123
  {currentChatId ? (
124
  <ChatWindow
125
  chat={chats.find(c => c.id === currentChatId)}
126
+ profile={activeProfile}
127
  onUpdateChat={(updatedChat) => {
128
  setChats(chats.map(c =>
129
  c.id === updatedChat.id ? updatedChat : c
 
152
  ×
153
  </button>
154
  <Settings
155
+ profiles={profiles}
156
+ activeProfileId={activeProfileId}
157
+ onSaveProfiles={(newProfiles) => {
158
+ setProfiles(newProfiles);
159
+ // Ensure the active profile still exists, otherwise select the first one
160
+ if (!newProfiles.some(p => p.id === activeProfileId)) {
161
+ setActiveProfileId(newProfiles[0]?.id || null);
162
+ }
163
+ }}
164
+ onChangeActiveProfile={setActiveProfileId}
165
+ onCloseSettings={() => setShowSettings(false)}
166
  />
167
  </div>
168
  </div>
src/components/ChatWindow.jsx CHANGED
@@ -1,13 +1,23 @@
1
- import React, { useState } from 'react';
2
 
3
- function ChatWindow({ chat, settings, onUpdateChat }) {
4
  const [input, setInput] = useState('');
5
  const [isLoading, setIsLoading] = useState(false);
6
  const [collapsedThinks, setCollapsedThinks] = useState(new Set());
7
  const [partialResponse, setPartialResponse] = useState('');
8
  const [streamController, setStreamController] = useState(null);
 
9
 
10
- // 添加解析消息的函数
 
 
 
 
 
 
 
 
 
11
  const parseMessage = (content) => {
12
  const thinkMatch = content.match(/<think>([\s\S]*?)<\/think>/);
13
  const mainContent = content.replace(/<think>[\s\S]*?<\/think>/, '').trim();
@@ -17,7 +27,7 @@ function ChatWindow({ chat, settings, onUpdateChat }) {
17
  };
18
  };
19
 
20
- // 添加切换折叠状态的函数
21
  const toggleThink = (timestamp) => {
22
  const newCollapsed = new Set(collapsedThinks);
23
  if (newCollapsed.has(timestamp)) {
@@ -66,9 +76,9 @@ function ChatWindow({ chat, settings, onUpdateChat }) {
66
  role: msg.role,
67
  content: msg.content
68
  }))],
69
- apiKey: settings.apiKey,
70
- model: settings.model,
71
- apiEndpoint: settings.apiEndpoint,
72
  }),
73
  signal: controller.signal
74
  });
@@ -125,6 +135,12 @@ function ChatWindow({ chat, settings, onUpdateChat }) {
125
  }
126
  };
127
 
 
 
 
 
 
 
128
  return (
129
  <div className="chat-window">
130
  <div className="chat-messages">
@@ -135,51 +151,62 @@ function ChatWindow({ chat, settings, onUpdateChat }) {
135
 
136
  return (
137
  <div key={index} className={`message ${message.role}`}>
138
- <div className="message-content">
139
- {message.role === 'assistant' && parsedMessage.think && (
140
- <div className="think-block">
141
- <div
142
- className="think-header"
143
- onClick={() => toggleThink(message.timestamp)}
144
- >
145
- Thinking Process {collapsedThinks.has(message.timestamp) ? '▼' : '▲'}
146
- </div>
147
- {!collapsedThinks.has(message.timestamp) && (
148
- <div className="think-content">
149
- {parsedMessage.think}
150
  </div>
151
- )}
 
 
 
 
 
 
 
 
152
  </div>
153
- )}
154
- <div className="content-text">
 
 
 
155
  {parsedMessage.content}
156
  </div>
157
- </div>
 
158
  {message.timestamp && (
159
  <div className="message-time">
160
- {new Date(message.timestamp).toLocaleTimeString()}
161
  </div>
162
  )}
163
  </div>
164
  );
165
  })}
 
166
  {isLoading && (
167
  <div className="message assistant">
168
  <div className="message-content">
169
- <div className="content-text">
170
- {partialResponse}
171
- <span className="loading-cursor">|</span>
172
- </div>
173
  </div>
174
  </div>
175
  )}
 
176
  </div>
 
177
  <div className="chat-input">
178
  <textarea
179
  className="message-input"
180
  value={input}
181
  onChange={(e) => setInput(e.target.value)}
182
- placeholder="Type your message..."
183
  onKeyDown={(e) => {
184
  if (e.key === 'Enter' && !e.shiftKey) {
185
  e.preventDefault();
@@ -187,7 +214,13 @@ function ChatWindow({ chat, settings, onUpdateChat }) {
187
  }
188
  }}
189
  />
190
- <button onClick={handleSendMessage}>Send</button>
 
 
 
 
 
 
191
  </div>
192
  </div>
193
  );
 
1
+ import React, { useState, useRef, useEffect } from 'react';
2
 
3
+ function ChatWindow({ chat, profile, onUpdateChat }) {
4
  const [input, setInput] = useState('');
5
  const [isLoading, setIsLoading] = useState(false);
6
  const [collapsedThinks, setCollapsedThinks] = useState(new Set());
7
  const [partialResponse, setPartialResponse] = useState('');
8
  const [streamController, setStreamController] = useState(null);
9
+ const messagesEndRef = useRef(null);
10
 
11
+ // Scroll to bottom when messages change
12
+ useEffect(() => {
13
+ scrollToBottom();
14
+ }, [chat.messages, partialResponse]);
15
+
16
+ const scrollToBottom = () => {
17
+ messagesEndRef.current?.scrollIntoView({ behavior: 'smooth' });
18
+ };
19
+
20
+ // Parse message to separate thinking process and content
21
  const parseMessage = (content) => {
22
  const thinkMatch = content.match(/<think>([\s\S]*?)<\/think>/);
23
  const mainContent = content.replace(/<think>[\s\S]*?<\/think>/, '').trim();
 
27
  };
28
  };
29
 
30
+ // Toggle thinking process visibility
31
  const toggleThink = (timestamp) => {
32
  const newCollapsed = new Set(collapsedThinks);
33
  if (newCollapsed.has(timestamp)) {
 
76
  role: msg.role,
77
  content: msg.content
78
  }))],
79
+ apiKey: profile.apiKey,
80
+ model: profile.model,
81
+ apiEndpoint: profile.apiEndpoint,
82
  }),
83
  signal: controller.signal
84
  });
 
135
  }
136
  };
137
 
138
+ // Format the timestamp
139
+ const formatTime = (timestamp) => {
140
+ const date = new Date(timestamp);
141
+ return date.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit', hour12: false });
142
+ };
143
+
144
  return (
145
  <div className="chat-window">
146
  <div className="chat-messages">
 
151
 
152
  return (
153
  <div key={index} className={`message ${message.role}`}>
154
+ {message.role === 'assistant' && (
155
+ <>
156
+ {parsedMessage.think && (
157
+ <div className="reasoning-container">
158
+ <div
159
+ className="reasoning-header"
160
+ onClick={() => toggleThink(message.timestamp)}
161
+ >
162
+ <span>Reasoned for a few seconds</span>
163
+ <span className="toggle-icon">{collapsedThinks.has(message.timestamp) ? '▼' : '▲'}</span>
 
 
164
  </div>
165
+ {!collapsedThinks.has(message.timestamp) && (
166
+ <div className="reasoning-content">
167
+ {parsedMessage.think}
168
+ </div>
169
+ )}
170
+ </div>
171
+ )}
172
+ <div className="message-content">
173
+ {parsedMessage.content}
174
  </div>
175
+ </>
176
+ )}
177
+
178
+ {message.role === 'user' && (
179
+ <div className="message-content">
180
  {parsedMessage.content}
181
  </div>
182
+ )}
183
+
184
  {message.timestamp && (
185
  <div className="message-time">
186
+ {formatTime(message.timestamp)}
187
  </div>
188
  )}
189
  </div>
190
  );
191
  })}
192
+
193
  {isLoading && (
194
  <div className="message assistant">
195
  <div className="message-content">
196
+ {partialResponse}
197
+ <span className="loading-cursor">|</span>
 
 
198
  </div>
199
  </div>
200
  )}
201
+ <div ref={messagesEndRef} />
202
  </div>
203
+
204
  <div className="chat-input">
205
  <textarea
206
  className="message-input"
207
  value={input}
208
  onChange={(e) => setInput(e.target.value)}
209
+ placeholder="Send a message..."
210
  onKeyDown={(e) => {
211
  if (e.key === 'Enter' && !e.shiftKey) {
212
  e.preventDefault();
 
214
  }
215
  }}
216
  />
217
+ <button
218
+ onClick={handleSendMessage}
219
+ disabled={!input.trim() || isLoading}
220
+ className="send-button"
221
+ >
222
+ Send
223
+ </button>
224
  </div>
225
  </div>
226
  );
src/components/Settings.jsx CHANGED
@@ -1,72 +1,174 @@
1
  import { useState } from 'react';
2
 
3
- function Settings({ settings, onSave, setSettings }) {
4
- const [formData, setFormData] = useState(settings);
 
5
  const [isHintExpanded, setIsHintExpanded] = useState(false);
6
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
7
  const handleSubmit = (e) => {
8
  e.preventDefault();
9
- onSave(formData);
 
 
10
  };
11
 
12
  return (
13
  <div className="settings-panel">
14
  <h2>Settings</h2>
15
- <form onSubmit={handleSubmit}>
16
- <div className="setting-item">
17
- <label>API Endpoint:</label>
18
- <input
19
- type="text"
20
- value={formData.apiEndpoint}
21
- onChange={(e) => setFormData({
22
- ...formData,
23
- apiEndpoint: e.target.value
24
- })}
25
- placeholder="Enter API endpoint"
26
- />
27
- <div className="setting-hint">
28
- <button
29
- type="button"
30
- className="hint-toggle"
31
- onClick={() => setIsHintExpanded(!isHintExpanded)}
 
 
32
  >
33
- {isHintExpanded ? 'Hide' : 'Show'} API Endpoint Format Examples
34
- </button>
35
- <div className={`hint-content ${isHintExpanded ? 'expanded' : ''}`}>
36
- <p>API Endpoint format examples:</p>
37
- <ul>
38
- <li>Ends with / → /chat/completions will be appended</li>
39
- <li>Ends with # → # will be removed</li>
40
- <li>Other cases → /v1/chat/completions will be appended</li>
41
- </ul>
 
 
 
42
  </div>
43
- </div>
44
  </div>
45
- <div className="setting-item">
46
- <label>API Key:</label>
47
- <input
48
- type="password"
49
- value={formData.apiKey}
50
- onChange={(e) => setFormData({
51
- ...formData,
52
- apiKey: e.target.value
53
- })}
54
- placeholder="Enter your API key"
55
- />
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
56
  </div>
57
- <div className="setting-item">
58
- <label>Model:</label>
59
- <input
60
- type="text"
61
- value={formData.model}
62
- onChange={(e) => setFormData({
63
- ...formData,
64
- model: e.target.value
65
- })}
66
- placeholder="Enter model name (e.g., gpt-3.5-turbo)"
67
- />
68
  </div>
69
- <button type="submit" className="save-button">Save Settings</button>
70
  </form>
71
  </div>
72
  );
 
1
  import { useState } from 'react';
2
 
3
+ function Settings({ profiles, activeProfileId, onSaveProfiles, onChangeActiveProfile, onCloseSettings }) {
4
+ const [localProfiles, setLocalProfiles] = useState(profiles);
5
+ const [editingProfileId, setEditingProfileId] = useState(activeProfileId);
6
  const [isHintExpanded, setIsHintExpanded] = useState(false);
7
 
8
+ const editingProfile = localProfiles.find(p => p.id === editingProfileId) || localProfiles[0];
9
+
10
+ const handleProfileChange = (updatedProfile) => {
11
+ setLocalProfiles(localProfiles.map(p =>
12
+ p.id === updatedProfile.id ? updatedProfile : p
13
+ ));
14
+ };
15
+
16
+ const handleAddProfile = () => {
17
+ const newId = `profile-${Date.now()}`;
18
+ const newProfile = {
19
+ id: newId,
20
+ name: `New Profile ${localProfiles.length + 1}`,
21
+ apiEndpoint: '',
22
+ apiKey: '',
23
+ model: 'DeepSeek-R1'
24
+ };
25
+
26
+ const updatedProfiles = [...localProfiles, newProfile];
27
+ setLocalProfiles(updatedProfiles);
28
+ setEditingProfileId(newId);
29
+ };
30
+
31
+ const handleDeleteProfile = (profileId) => {
32
+ // Prevent deleting the last profile
33
+ if (localProfiles.length <= 1) {
34
+ alert("Cannot delete the last profile");
35
+ return;
36
+ }
37
+
38
+ const updatedProfiles = localProfiles.filter(p => p.id !== profileId);
39
+ setLocalProfiles(updatedProfiles);
40
+
41
+ // If we're deleting the currently edited profile, switch to another one
42
+ if (editingProfileId === profileId) {
43
+ setEditingProfileId(updatedProfiles[0].id);
44
+ }
45
+ };
46
+
47
  const handleSubmit = (e) => {
48
  e.preventDefault();
49
+ onSaveProfiles(localProfiles);
50
+ onChangeActiveProfile(editingProfileId);
51
+ onCloseSettings();
52
  };
53
 
54
  return (
55
  <div className="settings-panel">
56
  <h2>Settings</h2>
57
+
58
+ <div className="profiles-section">
59
+ <div className="profiles-header">
60
+ <h3>Profiles</h3>
61
+ <button
62
+ type="button"
63
+ className="add-profile-button"
64
+ onClick={handleAddProfile}
65
+ >
66
+ + Add Profile
67
+ </button>
68
+ </div>
69
+
70
+ <div className="profiles-list">
71
+ {localProfiles.map(profile => (
72
+ <div
73
+ key={profile.id}
74
+ className={`profile-item ${profile.id === editingProfileId ? 'active' : ''}`}
75
+ onClick={() => setEditingProfileId(profile.id)}
76
  >
77
+ <span>{profile.name}</span>
78
+ {localProfiles.length > 1 && (
79
+ <button
80
+ className="delete-profile-button"
81
+ onClick={(e) => {
82
+ e.stopPropagation();
83
+ handleDeleteProfile(profile.id);
84
+ }}
85
+ >
86
+ ×
87
+ </button>
88
+ )}
89
  </div>
90
+ ))}
91
  </div>
92
+ </div>
93
+
94
+ <form onSubmit={handleSubmit}>
95
+ <div className="current-profile-section">
96
+ <h3>Edit Profile: {editingProfile.name}</h3>
97
+
98
+ <div className="setting-item">
99
+ <label>Profile Name:</label>
100
+ <input
101
+ type="text"
102
+ value={editingProfile.name}
103
+ onChange={(e) => handleProfileChange({
104
+ ...editingProfile,
105
+ name: e.target.value
106
+ })}
107
+ placeholder="Enter profile name"
108
+ />
109
+ </div>
110
+
111
+ <div className="setting-item">
112
+ <label>API Endpoint:</label>
113
+ <input
114
+ type="text"
115
+ value={editingProfile.apiEndpoint}
116
+ onChange={(e) => handleProfileChange({
117
+ ...editingProfile,
118
+ apiEndpoint: e.target.value
119
+ })}
120
+ placeholder="Enter API endpoint"
121
+ />
122
+ <div className="setting-hint">
123
+ <button
124
+ type="button"
125
+ className="hint-toggle"
126
+ onClick={() => setIsHintExpanded(!isHintExpanded)}
127
+ >
128
+ {isHintExpanded ? 'Hide' : 'Show'} API Endpoint Format Examples
129
+ </button>
130
+ <div className={`hint-content ${isHintExpanded ? 'expanded' : ''}`}>
131
+ <p>API Endpoint format examples:</p>
132
+ <ul>
133
+ <li>Ends with / → /chat/completions will be appended</li>
134
+ <li>Ends with # → # will be removed</li>
135
+ <li>Other cases → /v1/chat/completions will be appended</li>
136
+ </ul>
137
+ </div>
138
+ </div>
139
+ </div>
140
+
141
+ <div className="setting-item">
142
+ <label>API Key:</label>
143
+ <input
144
+ type="password"
145
+ value={editingProfile.apiKey}
146
+ onChange={(e) => handleProfileChange({
147
+ ...editingProfile,
148
+ apiKey: e.target.value
149
+ })}
150
+ placeholder="Enter your API key"
151
+ />
152
+ </div>
153
+
154
+ <div className="setting-item">
155
+ <label>Model:</label>
156
+ <input
157
+ type="text"
158
+ value={editingProfile.model}
159
+ onChange={(e) => handleProfileChange({
160
+ ...editingProfile,
161
+ model: e.target.value
162
+ })}
163
+ placeholder="Enter model name (e.g., DeepSeek-R1)"
164
+ />
165
+ </div>
166
  </div>
167
+
168
+ <div className="settings-actions">
169
+ <button type="submit" className="save-button">Save Settings</button>
170
+ <button type="button" className="cancel-button" onClick={onCloseSettings}>Cancel</button>
 
 
 
 
 
 
 
171
  </div>
 
172
  </form>
173
  </div>
174
  );
src/index.css CHANGED
@@ -1,101 +1,257 @@
1
- .app {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2
  display: flex;
3
  flex-direction: column;
4
  height: 100vh;
5
- position: relative;
 
6
  }
7
 
8
- .settings {
9
- margin-bottom: 20px;
10
- padding: 15px;
11
- background: #f5f5f5;
12
- border-radius: 8px;
 
 
 
 
13
  }
14
 
15
- .settings form {
16
  display: flex;
17
- gap: 15px;
18
  align-items: center;
19
  }
20
 
21
- .main-content {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
22
  display: flex;
23
- gap: 20px;
24
  flex: 1;
25
  overflow: hidden;
26
- padding: 20px 20px 80px 20px;
27
- white-space: pre-wrap;
28
  }
29
 
30
- .chat-list {
31
- flex: 1;
32
- padding: 15px;
 
33
  display: flex;
34
  flex-direction: column;
35
- gap: 10px;
36
- overflow-y: auto;
37
- padding-bottom: 200px;
38
- position: relative;
39
  }
40
 
41
- .chat-list h2 {
42
- font-size: 16px;
43
- font-weight: 500;
44
- color: #333;
45
- margin-bottom: 12px;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
46
  }
47
 
48
  .new-chat {
49
- background: linear-gradient(135deg, var(--primary-color) 0%, var(--primary-hover) 100%);
 
 
50
  color: white;
51
- padding: 8px 16px;
52
  border: none;
53
- border-radius: 6px;
54
  cursor: pointer;
55
  font-size: 0.875rem;
56
- font-weight: 500;
57
- width: 100%;
58
  margin-bottom: 15px;
59
- transition: all 0.2s ease;
60
- z-index: 1;
 
 
61
  }
62
 
63
  .new-chat:hover {
64
- background: linear-gradient(135deg, var(--primary-hover) 0%, #1d4ed8 100%);
65
- transform: translateY(-1px);
66
- box-shadow: 0 4px 12px rgba(37, 99, 235, 0.2);
67
  }
68
 
69
  .chat-item {
70
  display: flex;
71
  justify-content: space-between;
72
  align-items: center;
73
- padding: 8px 12px;
74
- margin-bottom: 6px;
75
- border-radius: 6px;
76
  cursor: pointer;
77
- font-size: 12px;
78
- transition: all 0.2s ease;
 
 
79
  }
80
 
81
  .chat-item:hover {
82
- background: #f5f5f5;
83
  }
84
 
85
  .chat-item.active {
86
- background: #e3f2fd;
 
87
  }
88
 
89
  .delete-btn {
90
- padding: 4px 8px;
91
- background: #fee2e2;
92
- color: #dc2626;
93
  border: none;
94
- border-radius: 4px;
95
- font-size: 11px;
96
  cursor: pointer;
97
- opacity: 0;
98
- transition: opacity 0.2s ease;
 
99
  }
100
 
101
  .chat-item:hover .delete-btn {
@@ -103,656 +259,480 @@
103
  }
104
 
105
  .delete-btn:hover {
106
- background: #fecaca;
107
  }
108
 
109
  .empty-state {
 
110
  text-align: center;
111
- color: #666;
112
- font-size: 12px;
113
- margin-top: 20px;
114
  }
115
 
116
- /* 聊天窗口的基础样式 */
117
- .chat-window {
118
  flex: 1;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
119
  display: flex;
120
  flex-direction: column;
121
  height: 100%;
 
122
  }
123
 
124
- /* 消息列表区域 */
125
  .chat-messages {
126
  flex: 1;
127
  overflow-y: auto;
128
  padding: 20px;
129
  display: flex;
130
  flex-direction: column;
 
131
  }
132
 
133
- /* 消息样式 */
134
  .message {
135
- margin-bottom: 16px;
136
- max-width: 85%;
137
  display: flex;
138
  flex-direction: column;
 
 
 
139
  }
140
 
141
  .message.user {
142
- margin-left: auto;
143
  align-items: flex-end;
144
  }
145
 
146
  .message.assistant {
147
- margin-right: auto;
148
  align-items: flex-start;
149
  }
150
 
151
- .message-content {
152
- padding: 12px 16px;
153
- max-width: 70%;
154
- word-wrap: break-word;
155
- }
156
-
157
- .content-text {
158
- white-space: pre-wrap;
159
- word-break: break-word;
160
- }
161
-
162
- .message.user .message-content {
163
- background: var(--primary-color);
164
- color: white;
165
- }
166
-
167
- .message.assistant .message-content {
168
- align-self: flex-start;
169
- background-color: #f1f3f5;
170
- color: #212529;
171
- border-radius: 18px 18px 18px 0;
172
- position: relative;
173
- }
174
-
175
- .message-time {
176
- font-size: 12px;
177
- color: #666;
178
- margin-top: 4px;
179
- opacity: 0.8;
180
- }
181
-
182
- /* 思考过程块样式 */
183
- .think-block {
184
- margin-bottom: 10px;
185
- border: 1px solid #dee2e6;
186
- border-radius: 4px;
187
  overflow: hidden;
188
- width: 90%;
189
- background-color: #f8f9fa;
190
- margin-left: auto;
191
- margin-right: auto;
192
  }
193
 
194
- .think-header {
195
- background-color: #e9ecef;
196
- padding: 8px 12px;
197
- font-size: 0.875rem;
198
- font-weight: 600;
199
- cursor: pointer;
200
  display: flex;
201
  justify-content: space-between;
202
  align-items: center;
 
 
 
 
 
 
 
 
 
 
203
  }
204
 
205
- .think-content {
206
  padding: 12px;
207
- background-color: #f8f9fa;
208
- font-size: 0.875rem;
209
  white-space: pre-wrap;
 
 
210
  max-height: 300px;
211
  overflow-y: auto;
212
  }
213
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
214
  .message-time {
215
- font-size: 12px;
216
- color: #666;
217
  margin-top: 4px;
218
- opacity: 0.8;
219
  }
220
 
221
- /* 输入区域样式 */
222
  .chat-input {
 
223
  padding: 16px;
224
- background: #ffffff;
225
  border-top: 1px solid var(--border-color);
226
- display: flex;
227
- gap: 12px;
228
- align-items: flex-start;
229
  }
230
 
231
  .message-input {
232
  flex: 1;
233
- padding: 10px;
234
  border: 1px solid var(--border-color);
235
- border-radius: 8px;
236
- font-size: 14px;
237
- min-height: 40px;
238
- max-height: 120px;
239
  resize: none;
240
- line-height: 1.5;
 
 
 
 
241
  }
242
 
243
  .message-input:focus {
244
- outline: none;
245
  border-color: var(--primary-color);
246
- box-shadow: 0 0 0 2px rgba(59, 130, 246, 0.1);
247
  }
248
 
249
- .chat-input button {
250
- background: linear-gradient(135deg, #3b82f6 0%, #2563eb 100%);
 
251
  color: white;
252
- padding: 10px 20px;
253
  border: none;
254
- border-radius: 8px;
255
- font-size: 13px;
256
- font-weight: 500;
257
  cursor: pointer;
258
- transition: all 0.2s ease;
259
- height: fit-content;
260
- }
261
-
262
- .chat-input button:hover {
263
- background: linear-gradient(135deg, #2563eb 0%, #1d4ed8 100%);
264
- transform: translateY(-1px);
265
- box-shadow: 0 4px 12px rgba(37, 99, 235, 0.2);
266
- }
267
-
268
- .chat-input button:disabled {
269
- background: #e2e8f0;
270
- cursor: not-allowed;
271
- transform: none;
272
- box-shadow: none;
273
- }
274
-
275
- /* Loading状态 */
276
- .loading {
277
- padding: 12px 16px;
278
- background: #f8f9fa;
279
- border-radius: 8px;
280
- color: #666;
281
- font-size: 13px;
282
  display: flex;
283
  align-items: center;
284
- gap: 8px;
285
- margin-bottom: 16px;
286
- width: fit-content;
287
  }
288
 
289
- /* 添加滚动条样式 */
290
- .chat-messages::-webkit-scrollbar {
291
- width: 6px;
292
  }
293
 
294
- .chat-messages::-webkit-scrollbar-track {
295
- background: #f1f1f1;
 
 
296
  }
297
 
298
- .chat-messages::-webkit-scrollbar-thumb {
299
- background: #ccc;
300
- border-radius: 3px;
 
 
 
 
 
 
301
  }
302
 
303
- .chat-messages::-webkit-scrollbar-thumb:hover {
304
- background: #999;
 
305
  }
306
 
307
- .settings-wrapper {
 
308
  position: fixed;
309
- left: 20px;
310
- bottom: 20px;
311
- width: 250px;
312
- z-index: 100;
 
 
 
 
 
313
  }
314
 
315
- .settings-container {
316
- background: #ffffff;
317
  border-radius: 8px;
318
- box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
319
- padding: 15px;
320
- margin: 0;
321
- }
322
-
323
- .setting-item {
324
- margin-bottom: 12px;
325
- }
326
-
327
- .setting-item:last-child {
328
- margin-bottom: 0;
329
- }
330
-
331
- .setting-item label {
332
- display: block;
333
- margin-bottom: 4px;
334
- font-weight: 500;
335
- color: #333;
336
- font-size: 12px;
337
- }
338
-
339
- .setting-item input {
340
- width: 100%;
341
- padding: 6px 8px;
342
- border: 1px solid #ddd;
343
- border-radius: 4px;
344
- font-size: 12px;
345
  }
346
 
347
- .setting-item input:focus {
348
- outline: none;
349
- border-color: #007bff;
350
- box-shadow: 0 0 0 2px rgba(0, 123, 255, 0.25);
 
351
  }
352
 
353
- button[type="submit"] {
354
- background: linear-gradient(135deg, #3b82f6 0%, #2563eb 100%);
355
- color: white;
356
- padding: 8px 16px;
 
357
  border: none;
358
- border-radius: 6px;
359
  cursor: pointer;
360
- font-size: 0.875rem;
361
- font-weight: 500;
362
- width: 100%;
363
- margin-top: 8px;
364
- transition: all 0.2s ease;
365
- }
366
-
367
- button[type="submit"]:hover {
368
- background: linear-gradient(135deg, #2563eb 0%, #1d4ed8 100%);
369
- transform: translateY(-1px);
370
- box-shadow: 0 4px 12px rgba(37, 99, 235, 0.2);
371
- }
372
-
373
- button[type="submit"]:disabled {
374
- background: #e2e8f0;
375
- cursor: not-allowed;
376
- transform: none;
377
- box-shadow: none;
378
  }
379
 
380
- .assistant .message-content .think-block {
381
- margin: 0 0 8px 0;
 
382
  }
383
 
384
- /* 添加一些基础变量 */
385
- :root {
386
- --sidebar-width: 260px;
387
- --primary-color: #3b82f6;
388
- --primary-hover: #2563eb;
389
- --border-color: #e5e5e5;
390
  }
391
 
392
- /* 重置一些基础样式 */
393
- .app-container {
394
- display: flex;
395
- flex-direction: column;
396
- height: 100vh;
397
- width: 100%;
398
- overflow: hidden;
399
- }
400
-
401
- .app-header {
402
  display: flex;
403
  justify-content: space-between;
404
  align-items: center;
405
- padding: 10px 20px;
406
- background-color: #f8f9fa;
407
- border-bottom: 1px solid #e9ecef;
408
- height: 60px;
409
  }
410
 
411
- .app-title {
412
- font-size: 1.5rem;
413
  font-weight: 600;
414
- color: #333;
415
- margin: 0;
416
  }
417
 
418
- .settings-button {
419
- background-color: #f8f9fa;
420
- border: 1px solid #dee2e6;
 
421
  border-radius: 4px;
422
- padding: 8px 16px;
423
  font-size: 0.875rem;
 
424
  cursor: pointer;
425
- transition: all 0.2s ease;
426
  }
427
 
428
- .settings-button:hover {
429
- background-color: #e9ecef;
430
  }
431
 
432
- .app-content {
433
  display: flex;
434
- flex: 1;
435
- overflow: hidden;
436
- }
437
-
438
- /* Sidebar */
439
- .sidebar {
440
- width: 280px;
441
- border-right: 1px solid #e9ecef;
442
- display: flex;
443
- flex-direction: column;
444
- transition: width 0.3s ease;
445
- }
446
-
447
- .sidebar.collapsed {
448
- width: 50px;
449
- overflow: hidden;
450
- }
451
-
452
- .sidebar.collapsed .chat-list,
453
- .sidebar.collapsed .sidebar-header h2 {
454
- display: none;
455
  }
456
 
457
- .sidebar-header {
 
 
 
 
 
 
 
458
  display: flex;
459
- justify-content: space-between;
460
  align-items: center;
461
- padding: 15px;
462
- border-bottom: 1px solid #e9ecef;
463
  }
464
 
465
- .sidebar.collapsed .sidebar-header {
466
- padding: 15px 5px;
467
- justify-content: center;
468
  }
469
 
470
- .sidebar-header h2 {
471
- font-size: 1rem;
472
- font-weight: 600;
473
- margin: 0;
474
  }
475
 
476
- .toggle-sidebar-button {
477
  background: none;
478
  border: none;
479
- font-size: 1.2rem;
480
  cursor: pointer;
481
- padding: 5px;
 
482
  display: flex;
483
  align-items: center;
484
  justify-content: center;
485
- z-index: 10;
486
- }
487
-
488
- .toggle-sidebar-button:hover {
489
- background-color: #f1f3f5;
490
- border-radius: 4px;
491
- }
492
-
493
- /* Chat List */
494
- .chat-list {
495
- flex: 1;
496
- overflow-y: auto;
497
- padding: 15px;
498
- }
499
-
500
- .new-chat {
501
- width: 100%;
502
- padding: 10px;
503
- background-color: #4263eb;
504
- color: white;
505
- border: none;
506
- border-radius: 4px;
507
- cursor: pointer;
508
- font-size: 0.875rem;
509
- margin-bottom: 15px;
510
- transition: background-color 0.2s ease;
511
- }
512
-
513
- .new-chat:hover {
514
- background-color: #3b5bdb;
515
- }
516
-
517
- .chat-item {
518
- display: flex;
519
- justify-content: space-between;
520
- align-items: center;
521
- padding: 10px;
522
- border-radius: 4px;
523
- cursor: pointer;
524
- margin-bottom: 5px;
525
- transition: background-color 0.2s ease;
526
  }
527
 
528
- .chat-item:hover {
529
- background-color: #f1f3f5;
530
  }
531
 
532
- .chat-item.active {
533
- background-color: #e7f5ff;
534
- }
535
-
536
- .delete-btn {
537
- opacity: 0;
538
- background-color: #ffe3e3;
539
- color: #e03131;
540
- border: none;
541
- border-radius: 4px;
542
- padding: 4px 8px;
543
- font-size: 0.75rem;
544
- cursor: pointer;
545
- transition: opacity 0.2s ease, background-color 0.2s ease;
546
- }
547
-
548
- .chat-item:hover .delete-btn {
549
- opacity: 1;
550
  }
551
 
552
- .delete-btn:hover {
553
- background-color: #ffc9c9;
554
  }
555
 
556
- .empty-state {
557
- color: #868e96;
558
- text-align: center;
559
- padding: 20px;
560
  font-size: 0.875rem;
 
561
  }
562
 
563
- /* Main Content */
564
- .main-content {
565
- flex: 1;
566
- display: flex;
567
- flex-direction: column;
568
- overflow: hidden;
569
- }
570
-
571
- /* Welcome Screen */
572
- .welcome-screen {
573
- display: flex;
574
- flex-direction: column;
575
- align-items: center;
576
- justify-content: center;
577
- height: 100%;
578
- padding: 20px;
579
- text-align: center;
580
- }
581
-
582
- .welcome-screen h2 {
583
- font-size: 1.5rem;
584
- font-weight: 600;
585
- margin-bottom: 10px;
586
- }
587
-
588
- .welcome-screen p {
589
- color: #495057;
590
- margin-bottom: 20px;
591
- }
592
-
593
- .new-chat-button {
594
- background-color: #4263eb;
595
- color: white;
596
- border: none;
597
  border-radius: 4px;
598
- padding: 10px 20px;
599
- font-size: 1rem;
600
- cursor: pointer;
601
- transition: background-color 0.2s ease;
602
- }
603
-
604
- .new-chat-button:hover {
605
- background-color: #3b5bdb;
606
  }
607
 
608
- /* Settings Modal */
609
- .settings-overlay {
610
- position: fixed;
611
- top: 0;
612
- left: 0;
613
- right: 0;
614
- bottom: 0;
615
- background-color: rgba(0, 0, 0, 0.5);
616
- display: flex;
617
- align-items: center;
618
- justify-content: center;
619
- z-index: 1000;
620
  }
621
 
622
- .settings-modal {
623
- background-color: white;
624
- border-radius: 8px;
625
- width: 500px;
626
- max-width: 90%;
627
- max-height: 90vh;
628
- overflow-y: auto;
629
- position: relative;
630
- padding: 20px;
631
- box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
632
  }
633
 
634
- .close-settings-button {
635
- position: absolute;
636
- top: 10px;
637
- right: 10px;
638
  background: none;
639
  border: none;
640
- font-size: 1.5rem;
641
  cursor: pointer;
642
- color: #868e96;
643
- }
644
-
645
- .close-settings-button:hover {
646
- color: #495057;
647
- }
648
-
649
- /* Chat Window */
650
- .chat-window {
651
- display: flex;
652
- flex-direction: column;
653
- height: 100%;
654
- overflow: hidden;
655
- }
656
-
657
- .chat-messages {
658
- flex: 1;
659
- overflow-y: auto;
660
- padding: 20px;
661
- }
662
-
663
- .message {
664
- margin-bottom: 20px;
665
- display: flex;
666
- flex-direction: column;
667
- }
668
-
669
- .message.user .message-content {
670
- align-self: flex-end;
671
- background-color: #4263eb;
672
- color: white;
673
- border-radius: 18px 18px 0 18px;
674
- max-width: 85%;
675
  }
676
 
677
- .message.assistant .message-content {
678
- align-self: flex-start;
679
- background-color: #f1f3f5;
680
- color: #212529;
681
- border-radius: 18px 18px 18px 0;
 
 
 
682
  }
683
 
684
- .message-content {
685
- padding: 12px 16px;
686
- max-width: 70%;
687
- word-wrap: break-word;
688
  }
689
 
690
- .message-time {
691
- font-size: 0.75rem;
692
- color: #868e96;
693
- margin-top: 5px;
694
- align-self: flex-end;
695
  }
696
 
697
- .message.user .message-time {
698
- align-self: flex-end;
699
  }
700
 
701
- .message.assistant .message-time {
702
- align-self: flex-start;
703
  }
704
 
705
- .chat-input {
706
  display: flex;
707
- padding: 15px;
708
- border-top: 1px solid #e9ecef;
 
709
  }
710
 
711
- .message-input {
712
- flex: 1;
713
- padding: 12px;
714
- border: 1px solid #dee2e6;
 
715
  border-radius: 4px;
716
- font-size: 1rem;
717
- resize: none;
718
- min-height: 50px;
719
- max-height: 150px;
720
- margin-right: 10px;
721
  }
722
 
723
- .message-input:focus {
724
- outline: none;
725
- border-color: #4263eb;
726
  }
727
 
728
- .chat-input button {
729
- background-color: #4263eb;
730
- color: white;
731
- border: none;
732
  border-radius: 4px;
733
- padding: 0 20px;
734
- font-size: 1rem;
735
  cursor: pointer;
736
- transition: background-color 0.2s ease;
737
- }
738
-
739
- .chat-input button:hover {
740
- background-color: #3b5bdb;
741
  }
742
 
743
- .chat-input button:disabled {
744
- background-color: #adb5bd;
745
- cursor: not-allowed;
746
  }
747
 
748
- /* Error and Loading */
749
  .error-message {
750
  position: fixed;
751
  bottom: 20px;
752
  left: 50%;
753
  transform: translateX(-50%);
754
- background-color: #ffe3e3;
755
- color: #e03131;
756
  padding: 10px 20px;
757
  border-radius: 4px;
758
  font-size: 0.875rem;
@@ -765,8 +745,8 @@ button[type="submit"]:disabled {
765
  bottom: 20px;
766
  left: 50%;
767
  transform: translateX(-50%);
768
- background-color: #e7f5ff;
769
- color: #1971c2;
770
  padding: 10px 20px;
771
  border-radius: 4px;
772
  font-size: 0.875rem;
@@ -774,107 +754,21 @@ button[type="submit"]:disabled {
774
  z-index: 1000;
775
  }
776
 
777
- .loading-cursor {
778
- display: inline-block;
779
- width: 10px;
780
- height: 20px;
781
- background-color: #212529;
782
- animation: blink 1s infinite;
783
- margin-left: 5px;
784
- }
785
-
786
- @keyframes blink {
787
- 0%, 100% { opacity: 1; }
788
- 50% { opacity: 0; }
789
- }
790
-
791
- /* 状态提示样式 */
792
- .error-message,
793
- .loading {
794
- position: fixed;
795
- bottom: 20px;
796
- right: 20px;
797
- padding: 12px 20px;
798
- border-radius: 8px;
799
- background: #ffffff;
800
- box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
801
- z-index: 1000;
802
- }
803
-
804
- .error-message {
805
- background: #fee2e2;
806
- color: #dc2626;
807
- }
808
-
809
- /* Loading indicator */
810
- .loading-indicator {
811
- display: flex;
812
- align-items: center;
813
- gap: 8px;
814
- color: #666;
815
- font-size: 14px;
816
- }
817
-
818
- /* Setting hint styles */
819
- .setting-hint {
820
- margin-top: 8px;
821
- padding: 12px;
822
- background: #f8f9fa;
823
- border-radius: 6px;
824
- border: 1px solid #e9ecef;
825
- font-size: 0.9em;
826
- }
827
-
828
- .setting-hint p {
829
- margin: 0 0 8px 0;
830
- color: #495057;
831
- font-weight: 500;
832
- }
833
-
834
- .setting-hint ul {
835
- margin: 0;
836
- padding-left: 20px;
837
- list-style-type: none;
838
- }
839
-
840
- .setting-hint li {
841
- position: relative;
842
- padding-left: 1.2em;
843
- margin-bottom: 4px;
844
- color: #6c757d;
845
- }
846
-
847
- .setting-hint li::before {
848
- content: "•";
849
- position: absolute;
850
- left: 0;
851
- color: #adb5bd;
852
- }
853
-
854
- .hint-toggle {
855
- width: 100%;
856
- padding: 8px;
857
- background: #ffffff;
858
- border: 1px solid #e9ecef;
859
- border-radius: 4px;
860
- cursor: pointer;
861
- font-size: 0.8em;
862
- color: #495057;
863
- text-align: left;
864
- margin-bottom: 8px;
865
- transition: all 0.2s ease;
866
  }
867
 
868
- .hint-toggle:hover {
869
- background: #f8f9fa;
870
  }
871
 
872
- .hint-content {
873
- max-height: 0;
874
- overflow: hidden;
875
- transition: max-height 0.3s ease-out;
876
  }
877
 
878
- .hint-content.expanded {
879
- max-height: 500px; /* Increased to accommodate more content */
880
  }
 
1
+ /* Base styles */
2
+ :root {
3
+ --primary-color: #3e6ae1;
4
+ --primary-hover: #2952c8;
5
+ --text-color: #1a1a1a;
6
+ --light-text: #4d4d4d;
7
+ --lightest-text: #737373;
8
+ --border-color: #e5e7eb;
9
+ --background-color: #ffffff;
10
+ --sidebar-width: 280px;
11
+ --header-height: 60px;
12
+ --reasoning-background: #f9fafb;
13
+ --user-message-bg: #3e6ae1;
14
+ --user-message-color: #ffffff;
15
+ --assistant-message-bg: #f9f9f9;
16
+ --assistant-message-color: #1a1a1a;
17
+ --hover-color: #f5f5f5;
18
+ --active-color: #e6f2ff;
19
+ }
20
+
21
+ * {
22
+ box-sizing: border-box;
23
+ margin: 0;
24
+ padding: 0;
25
+ }
26
+
27
+ body {
28
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
29
+ color: var(--text-color);
30
+ line-height: 1.5;
31
+ background-color: var(--background-color);
32
+ }
33
+
34
+ /* App container */
35
+ .app-container {
36
  display: flex;
37
  flex-direction: column;
38
  height: 100vh;
39
+ width: 100%;
40
+ overflow: hidden;
41
  }
42
 
43
+ /* Header styles */
44
+ .app-header {
45
+ display: flex;
46
+ justify-content: space-between;
47
+ align-items: center;
48
+ padding: 0 20px;
49
+ height: var(--header-height);
50
+ border-bottom: 1px solid var(--border-color);
51
+ background-color: var(--background-color);
52
  }
53
 
54
+ .left-section {
55
  display: flex;
 
56
  align-items: center;
57
  }
58
 
59
+ .right-section {
60
+ display: flex;
61
+ align-items: center;
62
+ gap: 10px;
63
+ }
64
+
65
+ .app-title {
66
+ font-size: 1.25rem;
67
+ font-weight: 600;
68
+ color: var(--text-color);
69
+ }
70
+
71
+ /* Profile dropdown */
72
+ .profile-dropdown-container {
73
+ position: relative;
74
+ }
75
+
76
+ .profile-dropdown-button {
77
+ padding: 6px 12px;
78
+ background-color: var(--background-color);
79
+ border: 1px solid var(--border-color);
80
+ border-radius: 4px;
81
+ font-size: 0.875rem;
82
+ color: var(--text-color);
83
+ cursor: pointer;
84
+ display: flex;
85
+ align-items: center;
86
+ gap: 5px;
87
+ }
88
+
89
+ .profile-dropdown-menu {
90
+ position: absolute;
91
+ top: 100%;
92
+ right: 0;
93
+ width: 200px;
94
+ background-color: var(--background-color);
95
+ border: 1px solid var(--border-color);
96
+ border-radius: 4px;
97
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
98
+ z-index: 100;
99
+ margin-top: 5px;
100
+ }
101
+
102
+ .profile-option {
103
+ padding: 8px 12px;
104
+ cursor: pointer;
105
+ transition: background-color 0.2s;
106
+ font-size: 0.875rem;
107
+ }
108
+
109
+ .profile-option:hover {
110
+ background-color: var(--hover-color);
111
+ }
112
+
113
+ .profile-option.active {
114
+ background-color: var(--active-color);
115
+ font-weight: 500;
116
+ }
117
+
118
+ .settings-button {
119
+ padding: 6px 12px;
120
+ background-color: var(--background-color);
121
+ border: 1px solid var(--border-color);
122
+ border-radius: 4px;
123
+ font-size: 0.875rem;
124
+ color: var(--text-color);
125
+ cursor: pointer;
126
+ }
127
+
128
+ .settings-button:hover {
129
+ background-color: var(--hover-color);
130
+ }
131
+
132
+ /* Main content area */
133
+ .app-content {
134
  display: flex;
 
135
  flex: 1;
136
  overflow: hidden;
 
 
137
  }
138
 
139
+ /* Sidebar */
140
+ .sidebar {
141
+ width: var(--sidebar-width);
142
+ border-right: 1px solid var(--border-color);
143
  display: flex;
144
  flex-direction: column;
145
+ transition: width 0.3s ease;
146
+ background-color: var(--background-color);
 
 
147
  }
148
 
149
+ .sidebar.collapsed {
150
+ width: 50px;
151
+ overflow: hidden;
152
+ }
153
+
154
+ .sidebar.collapsed .chat-list,
155
+ .sidebar.collapsed .sidebar-header h2 {
156
+ display: none;
157
+ }
158
+
159
+ .sidebar-header {
160
+ display: flex;
161
+ justify-content: space-between;
162
+ align-items: center;
163
+ padding: 12px 15px;
164
+ border-bottom: 1px solid var(--border-color);
165
+ }
166
+
167
+ .sidebar.collapsed .sidebar-header {
168
+ padding: 12px 5px;
169
+ justify-content: center;
170
+ }
171
+
172
+ .sidebar-header h2 {
173
+ font-size: 0.875rem;
174
+ font-weight: 600;
175
+ color: var(--light-text);
176
+ margin: 0;
177
+ }
178
+
179
+ .toggle-sidebar-button {
180
+ background: none;
181
+ border: none;
182
+ font-size: 1rem;
183
+ color: var(--lightest-text);
184
+ cursor: pointer;
185
+ display: flex;
186
+ align-items: center;
187
+ justify-content: center;
188
+ z-index: 10;
189
+ width: 24px;
190
+ height: 24px;
191
+ }
192
+
193
+ .toggle-sidebar-button:hover {
194
+ color: var(--text-color);
195
+ }
196
+
197
+ /* Chat list */
198
+ .chat-list {
199
+ flex: 1;
200
+ overflow-y: auto;
201
+ padding: 10px;
202
  }
203
 
204
  .new-chat {
205
+ width: 100%;
206
+ padding: 8px 12px;
207
+ background-color: var(--primary-color);
208
  color: white;
 
209
  border: none;
210
+ border-radius: 4px;
211
  cursor: pointer;
212
  font-size: 0.875rem;
 
 
213
  margin-bottom: 15px;
214
+ transition: background-color 0.2s;
215
+ display: flex;
216
+ align-items: center;
217
+ justify-content: center;
218
  }
219
 
220
  .new-chat:hover {
221
+ background-color: var(--primary-hover);
 
 
222
  }
223
 
224
  .chat-item {
225
  display: flex;
226
  justify-content: space-between;
227
  align-items: center;
228
+ padding: 8px 10px;
229
+ border-radius: 4px;
 
230
  cursor: pointer;
231
+ margin-bottom: 2px;
232
+ transition: background-color 0.2s;
233
+ font-size: 0.875rem;
234
+ color: var(--light-text);
235
  }
236
 
237
  .chat-item:hover {
238
+ background-color: var(--hover-color);
239
  }
240
 
241
  .chat-item.active {
242
+ background-color: var(--active-color);
243
+ color: var(--primary-color);
244
  }
245
 
246
  .delete-btn {
247
+ opacity: 0;
248
+ background: none;
 
249
  border: none;
250
+ color: var(--lightest-text);
 
251
  cursor: pointer;
252
+ font-size: 0.75rem;
253
+ transition: color 0.2s, opacity 0.2s;
254
+ padding: 2px 5px;
255
  }
256
 
257
  .chat-item:hover .delete-btn {
 
259
  }
260
 
261
  .delete-btn:hover {
262
+ color: #e53e3e;
263
  }
264
 
265
  .empty-state {
266
+ color: var(--lightest-text);
267
  text-align: center;
268
+ padding: 20px;
269
+ font-size: 0.875rem;
 
270
  }
271
 
272
+ /* Main chat area */
273
+ .main-content {
274
  flex: 1;
275
+ display: flex;
276
+ flex-direction: column;
277
+ overflow: hidden;
278
+ background-color: var(--background-color);
279
+ }
280
+
281
+ /* Welcome screen */
282
+ .welcome-screen {
283
+ display: flex;
284
+ flex-direction: column;
285
+ align-items: center;
286
+ justify-content: center;
287
+ height: 100%;
288
+ padding: 20px;
289
+ text-align: center;
290
+ }
291
+
292
+ .welcome-screen h2 {
293
+ font-size: 1.5rem;
294
+ font-weight: 600;
295
+ margin-bottom: 10px;
296
+ color: var(--text-color);
297
+ }
298
+
299
+ .welcome-screen p {
300
+ color: var(--light-text);
301
+ margin-bottom: 20px;
302
+ font-size: 0.875rem;
303
+ }
304
+
305
+ .new-chat-button {
306
+ padding: 10px 20px;
307
+ background-color: var(--primary-color);
308
+ color: white;
309
+ border: none;
310
+ border-radius: 4px;
311
+ cursor: pointer;
312
+ font-size: 0.875rem;
313
+ transition: background-color 0.2s;
314
+ }
315
+
316
+ .new-chat-button:hover {
317
+ background-color: var(--primary-hover);
318
+ }
319
+
320
+ /* Chat window */
321
+ .chat-window {
322
  display: flex;
323
  flex-direction: column;
324
  height: 100%;
325
+ overflow: hidden;
326
  }
327
 
 
328
  .chat-messages {
329
  flex: 1;
330
  overflow-y: auto;
331
  padding: 20px;
332
  display: flex;
333
  flex-direction: column;
334
+ gap: 24px;
335
  }
336
 
337
+ /* Message styles */
338
  .message {
 
 
339
  display: flex;
340
  flex-direction: column;
341
+ max-width: 800px;
342
+ margin: 0 auto;
343
+ width: 100%;
344
  }
345
 
346
  .message.user {
 
347
  align-items: flex-end;
348
  }
349
 
350
  .message.assistant {
 
351
  align-items: flex-start;
352
  }
353
 
354
+ .reasoning-container {
355
+ width: 100%;
356
+ border: 1px solid var(--border-color);
357
+ border-radius: 8px;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
358
  overflow: hidden;
359
+ margin-bottom: 10px;
360
+ background-color: var(--reasoning-background);
 
 
361
  }
362
 
363
+ .reasoning-header {
 
 
 
 
 
364
  display: flex;
365
  justify-content: space-between;
366
  align-items: center;
367
+ padding: 8px 12px;
368
+ cursor: pointer;
369
+ font-size: 0.8125rem;
370
+ color: var(--light-text);
371
+ background-color: #f5f5f5;
372
+ border-bottom: 1px solid var(--border-color);
373
+ }
374
+
375
+ .toggle-icon {
376
+ font-size: 0.625rem;
377
  }
378
 
379
+ .reasoning-content {
380
  padding: 12px;
381
+ font-size: 0.8125rem;
 
382
  white-space: pre-wrap;
383
+ overflow-x: auto;
384
+ color: var(--light-text);
385
  max-height: 300px;
386
  overflow-y: auto;
387
  }
388
 
389
+ .message-content {
390
+ padding: 12px 16px;
391
+ border-radius: 6px;
392
+ font-size: 0.9375rem;
393
+ line-height: 1.5;
394
+ word-wrap: break-word;
395
+ max-width: 90%;
396
+ }
397
+
398
+ .message.user .message-content {
399
+ background-color: var(--user-message-bg);
400
+ color: var(--user-message-color);
401
+ border-radius: 6px 6px 0 6px;
402
+ }
403
+
404
+ .message.assistant .message-content {
405
+ background-color: var(--assistant-message-bg);
406
+ color: var(--assistant-message-color);
407
+ border-radius: 6px 6px 6px 0;
408
+ }
409
+
410
  .message-time {
411
+ font-size: 0.75rem;
412
+ color: var(--lightest-text);
413
  margin-top: 4px;
 
414
  }
415
 
416
+ /* Input area */
417
  .chat-input {
418
+ display: flex;
419
  padding: 16px;
 
420
  border-top: 1px solid var(--border-color);
421
+ gap: 10px;
422
+ background-color: var(--background-color);
 
423
  }
424
 
425
  .message-input {
426
  flex: 1;
427
+ padding: 12px 16px;
428
  border: 1px solid var(--border-color);
429
+ border-radius: 6px;
430
+ font-size: 0.9375rem;
 
 
431
  resize: none;
432
+ min-height: 24px;
433
+ max-height: 200px;
434
+ color: var(--text-color);
435
+ outline: none;
436
+ transition: border-color 0.2s;
437
  }
438
 
439
  .message-input:focus {
 
440
  border-color: var(--primary-color);
 
441
  }
442
 
443
+ .send-button {
444
+ padding: 0 20px;
445
+ background-color: var(--primary-color);
446
  color: white;
 
447
  border: none;
448
+ border-radius: 6px;
 
 
449
  cursor: pointer;
450
+ font-size: 0.9375rem;
451
+ transition: background-color 0.2s;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
452
  display: flex;
453
  align-items: center;
454
+ justify-content: center;
 
 
455
  }
456
 
457
+ .send-button:hover:not(:disabled) {
458
+ background-color: var(--primary-hover);
 
459
  }
460
 
461
+ .send-button:disabled {
462
+ background-color: #a0aec0;
463
+ cursor: not-allowed;
464
+ opacity: 0.7;
465
  }
466
 
467
+ /* Loading cursor */
468
+ .loading-cursor {
469
+ display: inline-block;
470
+ width: 2px;
471
+ height: 1.2em;
472
+ background-color: currentColor;
473
+ margin-left: 2px;
474
+ animation: blink 1s infinite;
475
+ vertical-align: text-bottom;
476
  }
477
 
478
+ @keyframes blink {
479
+ 0%, 100% { opacity: 1; }
480
+ 50% { opacity: 0; }
481
  }
482
 
483
+ /* Settings modal */
484
+ .settings-overlay {
485
  position: fixed;
486
+ top: 0;
487
+ left: 0;
488
+ right: 0;
489
+ bottom: 0;
490
+ background-color: rgba(0, 0, 0, 0.5);
491
+ display: flex;
492
+ align-items: center;
493
+ justify-content: center;
494
+ z-index: 1000;
495
  }
496
 
497
+ .settings-modal {
498
+ background-color: var(--background-color);
499
  border-radius: 8px;
500
+ width: 600px;
501
+ max-width: 90%;
502
+ max-height: 90vh;
503
+ overflow-y: auto;
504
+ position: relative;
505
+ padding: 24px;
506
+ box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
507
  }
508
 
509
+ .settings-panel h2 {
510
+ font-size: 1.25rem;
511
+ font-weight: 600;
512
+ margin-bottom: 20px;
513
+ color: var(--text-color);
514
  }
515
 
516
+ .close-settings-button {
517
+ position: absolute;
518
+ top: 16px;
519
+ right: 16px;
520
+ background: none;
521
  border: none;
522
+ font-size: 1.25rem;
523
  cursor: pointer;
524
+ color: var(--lightest-text);
525
+ width: 24px;
526
+ height: 24px;
527
+ display: flex;
528
+ align-items: center;
529
+ justify-content: center;
530
+ border-radius: 4px;
 
 
 
 
 
 
 
 
 
 
 
531
  }
532
 
533
+ .close-settings-button:hover {
534
+ background-color: var(--hover-color);
535
+ color: var(--text-color);
536
  }
537
 
538
+ /* Profile management in settings */
539
+ .profiles-section {
540
+ margin-bottom: 24px;
541
+ border-bottom: 1px solid var(--border-color);
542
+ padding-bottom: 20px;
 
543
  }
544
 
545
+ .profiles-header {
 
 
 
 
 
 
 
 
 
546
  display: flex;
547
  justify-content: space-between;
548
  align-items: center;
549
+ margin-bottom: 12px;
 
 
 
550
  }
551
 
552
+ .profiles-header h3 {
553
+ font-size: 1rem;
554
  font-weight: 600;
555
+ color: var(--text-color);
 
556
  }
557
 
558
+ .add-profile-button {
559
+ padding: 4px 8px;
560
+ background-color: transparent;
561
+ border: 1px solid var(--border-color);
562
  border-radius: 4px;
 
563
  font-size: 0.875rem;
564
+ color: var(--text-color);
565
  cursor: pointer;
566
+ transition: all 0.2s;
567
  }
568
 
569
+ .add-profile-button:hover {
570
+ background-color: var(--hover-color);
571
  }
572
 
573
+ .profiles-list {
574
  display: flex;
575
+ flex-wrap: wrap;
576
+ gap: 8px;
577
+ margin-bottom: 16px;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
578
  }
579
 
580
+ .profile-item {
581
+ padding: 8px 12px;
582
+ background-color: var(--background-color);
583
+ border: 1px solid var(--border-color);
584
+ border-radius: 4px;
585
+ font-size: 0.875rem;
586
+ cursor: pointer;
587
+ transition: all 0.2s;
588
  display: flex;
 
589
  align-items: center;
590
+ gap: 8px;
 
591
  }
592
 
593
+ .profile-item:hover {
594
+ background-color: var(--hover-color);
 
595
  }
596
 
597
+ .profile-item.active {
598
+ background-color: var(--active-color);
599
+ border-color: var(--primary-color);
 
600
  }
601
 
602
+ .delete-profile-button {
603
  background: none;
604
  border: none;
605
+ color: var(--lightest-text);
606
  cursor: pointer;
607
+ font-size: 1rem;
608
+ transition: color 0.2s;
609
  display: flex;
610
  align-items: center;
611
  justify-content: center;
612
+ width: 20px;
613
+ height: 20px;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
614
  }
615
 
616
+ .delete-profile-button:hover {
617
+ color: #e53e3e;
618
  }
619
 
620
+ .current-profile-section h3 {
621
+ font-size: 1rem;
622
+ font-weight: 600;
623
+ color: var(--text-color);
624
+ margin-bottom: 16px;
 
 
 
 
 
 
 
 
 
 
 
 
 
625
  }
626
 
627
+ .setting-item {
628
+ margin-bottom: 16px;
629
  }
630
 
631
+ .setting-item label {
632
+ display: block;
633
+ margin-bottom: 6px;
 
634
  font-size: 0.875rem;
635
+ color: var(--light-text);
636
  }
637
 
638
+ .setting-item input {
639
+ width: 100%;
640
+ padding: 8px 12px;
641
+ border: 1px solid var(--border-color);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
642
  border-radius: 4px;
643
+ font-size: 0.875rem;
644
+ transition: border-color 0.2s;
 
 
 
 
 
 
645
  }
646
 
647
+ .setting-item input:focus {
648
+ outline: none;
649
+ border-color: var(--primary-color);
 
 
 
 
 
 
 
 
 
650
  }
651
 
652
+ .setting-hint {
653
+ margin-top: 4px;
 
 
 
 
 
 
 
 
654
  }
655
 
656
+ .hint-toggle {
 
 
 
657
  background: none;
658
  border: none;
659
+ color: var(--primary-color);
660
  cursor: pointer;
661
+ font-size: 0.75rem;
662
+ padding: 0;
663
+ text-align: left;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
664
  }
665
 
666
+ .hint-content {
667
+ display: none;
668
+ margin-top: 8px;
669
+ font-size: 0.75rem;
670
+ color: var(--light-text);
671
+ background-color: var(--hover-color);
672
+ padding: 8px;
673
+ border-radius: 4px;
674
  }
675
 
676
+ .hint-content.expanded {
677
+ display: block;
 
 
678
  }
679
 
680
+ .hint-content p {
681
+ margin-bottom: 6px;
 
 
 
682
  }
683
 
684
+ .hint-content ul {
685
+ margin-left: 20px;
686
  }
687
 
688
+ .hint-content li {
689
+ margin-bottom: 4px;
690
  }
691
 
692
+ .settings-actions {
693
  display: flex;
694
+ justify-content: flex-end;
695
+ gap: 10px;
696
+ margin-top: 24px;
697
  }
698
 
699
+ .save-button {
700
+ padding: 8px 16px;
701
+ background-color: var(--primary-color);
702
+ color: white;
703
+ border: none;
704
  border-radius: 4px;
705
+ cursor: pointer;
706
+ font-size: 0.875rem;
707
+ transition: background-color 0.2s;
 
 
708
  }
709
 
710
+ .save-button:hover {
711
+ background-color: var(--primary-hover);
 
712
  }
713
 
714
+ .cancel-button {
715
+ padding: 8px 16px;
716
+ background-color: transparent;
717
+ border: 1px solid var(--border-color);
718
  border-radius: 4px;
 
 
719
  cursor: pointer;
720
+ font-size: 0.875rem;
721
+ transition: all 0.2s;
 
 
 
722
  }
723
 
724
+ .cancel-button:hover {
725
+ background-color: var(--hover-color);
 
726
  }
727
 
728
+ /* Error and notification styles */
729
  .error-message {
730
  position: fixed;
731
  bottom: 20px;
732
  left: 50%;
733
  transform: translateX(-50%);
734
+ background-color: #fee2e2;
735
+ color: #dc2626;
736
  padding: 10px 20px;
737
  border-radius: 4px;
738
  font-size: 0.875rem;
 
745
  bottom: 20px;
746
  left: 50%;
747
  transform: translateX(-50%);
748
+ background-color: #e6f2ff;
749
+ color: #0369a1;
750
  padding: 10px 20px;
751
  border-radius: 4px;
752
  font-size: 0.875rem;
 
754
  z-index: 1000;
755
  }
756
 
757
+ /* Custom scrollbar */
758
+ ::-webkit-scrollbar {
759
+ width: 6px;
760
+ height: 6px;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
761
  }
762
 
763
+ ::-webkit-scrollbar-track {
764
+ background-color: transparent;
765
  }
766
 
767
+ ::-webkit-scrollbar-thumb {
768
+ background-color: #d1d5db;
769
+ border-radius: 3px;
 
770
  }
771
 
772
+ ::-webkit-scrollbar-thumb:hover {
773
+ background-color: #9ca3af;
774
  }