@huyooo/ai-chat-frontend-vue 0.1.6 → 0.1.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (159) hide show
  1. package/README.md +367 -0
  2. package/dist/adapter.d.ts +7 -7
  3. package/dist/adapter.d.ts.map +1 -1
  4. package/dist/components/ChatPanel.vue.d.ts +120 -9
  5. package/dist/components/ChatPanel.vue.d.ts.map +1 -1
  6. package/dist/components/common/ConfirmDialog.vue.d.ts +30 -0
  7. package/dist/components/common/ConfirmDialog.vue.d.ts.map +1 -0
  8. package/dist/components/common/CopyButton.vue.d.ts +18 -0
  9. package/dist/components/common/CopyButton.vue.d.ts.map +1 -0
  10. package/dist/components/common/IndexingSettings.vue.d.ts +3 -0
  11. package/dist/components/common/IndexingSettings.vue.d.ts.map +1 -0
  12. package/dist/components/common/SettingsPanel.vue.d.ts +16 -0
  13. package/dist/components/common/SettingsPanel.vue.d.ts.map +1 -0
  14. package/dist/components/common/Toast.vue.d.ts +18 -0
  15. package/dist/components/common/Toast.vue.d.ts.map +1 -0
  16. package/dist/components/common/ToggleSwitch.vue.d.ts +10 -0
  17. package/dist/components/common/ToggleSwitch.vue.d.ts.map +1 -0
  18. package/dist/components/{chat/ui → header}/ChatHeader.vue.d.ts +5 -3
  19. package/dist/components/header/ChatHeader.vue.d.ts.map +1 -0
  20. package/dist/components/input/AtFilePicker.vue.d.ts +21 -0
  21. package/dist/components/input/AtFilePicker.vue.d.ts.map +1 -0
  22. package/dist/components/{ChatInput.vue.d.ts → input/ChatInput.vue.d.ts} +16 -14
  23. package/dist/components/input/ChatInput.vue.d.ts.map +1 -0
  24. package/dist/components/input/DropdownSelector.vue.d.ts +42 -0
  25. package/dist/components/input/DropdownSelector.vue.d.ts.map +1 -0
  26. package/dist/components/input/ImagePreviewModal.vue.d.ts +17 -0
  27. package/dist/components/input/ImagePreviewModal.vue.d.ts.map +1 -0
  28. package/dist/components/input/at-views/AtBranchView.vue.d.ts +18 -0
  29. package/dist/components/input/at-views/AtBranchView.vue.d.ts.map +1 -0
  30. package/dist/components/input/at-views/AtBrowserView.vue.d.ts +18 -0
  31. package/dist/components/input/at-views/AtBrowserView.vue.d.ts.map +1 -0
  32. package/dist/components/input/at-views/AtChatsView.vue.d.ts +18 -0
  33. package/dist/components/input/at-views/AtChatsView.vue.d.ts.map +1 -0
  34. package/dist/components/input/at-views/AtDocsView.vue.d.ts +18 -0
  35. package/dist/components/input/at-views/AtDocsView.vue.d.ts.map +1 -0
  36. package/dist/components/input/at-views/AtFilesView.vue.d.ts +23 -0
  37. package/dist/components/input/at-views/AtFilesView.vue.d.ts.map +1 -0
  38. package/dist/components/input/at-views/AtTerminalsView.vue.d.ts +18 -0
  39. package/dist/components/input/at-views/AtTerminalsView.vue.d.ts.map +1 -0
  40. package/dist/components/message/MessageBubble.vue.d.ts +45 -0
  41. package/dist/components/message/MessageBubble.vue.d.ts.map +1 -0
  42. package/dist/components/message/PartsRenderer.vue.d.ts +15 -0
  43. package/dist/components/message/PartsRenderer.vue.d.ts.map +1 -0
  44. package/dist/components/message/WelcomeMessage.vue.d.ts +14 -0
  45. package/dist/components/message/WelcomeMessage.vue.d.ts.map +1 -0
  46. package/dist/components/message/blocks/CodeBlock.vue.d.ts +11 -0
  47. package/dist/components/message/blocks/CodeBlock.vue.d.ts.map +1 -0
  48. package/dist/components/{chat/SearchResultBlock.vue.d.ts → message/blocks/TextBlock.vue.d.ts} +3 -4
  49. package/dist/components/message/blocks/TextBlock.vue.d.ts.map +1 -0
  50. package/dist/components/message/blocks/index.d.ts +6 -0
  51. package/dist/components/message/blocks/index.d.ts.map +1 -0
  52. package/dist/components/message/parts/CollapsibleCard.vue.d.ts +45 -0
  53. package/dist/components/message/parts/CollapsibleCard.vue.d.ts.map +1 -0
  54. package/dist/components/{chat/ToolCallBlock.vue.d.ts → message/parts/ErrorPart.vue.d.ts} +4 -5
  55. package/dist/components/message/parts/ErrorPart.vue.d.ts.map +1 -0
  56. package/dist/components/{chat/ThinkingBlock.vue.d.ts → message/parts/ImagePart.vue.d.ts} +3 -3
  57. package/dist/components/message/parts/ImagePart.vue.d.ts.map +1 -0
  58. package/dist/components/message/parts/SearchPart.vue.d.ts +12 -0
  59. package/dist/components/message/parts/SearchPart.vue.d.ts.map +1 -0
  60. package/dist/components/{chat/messages/ExecutionSteps.vue.d.ts → message/parts/TextPart.vue.d.ts} +2 -9
  61. package/dist/components/message/parts/TextPart.vue.d.ts.map +1 -0
  62. package/dist/components/message/parts/ThinkingPart.vue.d.ts +12 -0
  63. package/dist/components/message/parts/ThinkingPart.vue.d.ts.map +1 -0
  64. package/dist/components/message/parts/ToolCallPart.vue.d.ts +19 -0
  65. package/dist/components/message/parts/ToolCallPart.vue.d.ts.map +1 -0
  66. package/dist/components/message/parts/ToolResultPart.vue.d.ts +14 -0
  67. package/dist/components/message/parts/ToolResultPart.vue.d.ts.map +1 -0
  68. package/dist/components/message/parts/index.d.ts +12 -0
  69. package/dist/components/message/parts/index.d.ts.map +1 -0
  70. package/dist/components/message/tool-results/DefaultToolResult.vue.d.ts +4 -0
  71. package/dist/components/message/tool-results/DefaultToolResult.vue.d.ts.map +1 -0
  72. package/dist/components/message/tool-results/SearchResults.vue.d.ts +4 -0
  73. package/dist/components/message/tool-results/SearchResults.vue.d.ts.map +1 -0
  74. package/dist/components/message/tool-results/WeatherCard.vue.d.ts +4 -0
  75. package/dist/components/message/tool-results/WeatherCard.vue.d.ts.map +1 -0
  76. package/dist/components/message/tool-results/index.d.ts +7 -0
  77. package/dist/components/message/tool-results/index.d.ts.map +1 -0
  78. package/dist/components/message/welcome-types.d.ts +28 -0
  79. package/dist/components/message/welcome-types.d.ts.map +1 -0
  80. package/dist/composables/useChat.d.ts +99 -44
  81. package/dist/composables/useChat.d.ts.map +1 -1
  82. package/dist/composables/useImageUpload.d.ts +55 -0
  83. package/dist/composables/useImageUpload.d.ts.map +1 -0
  84. package/dist/index.d.ts +25 -26
  85. package/dist/index.d.ts.map +1 -1
  86. package/dist/index.js +55871 -1252
  87. package/dist/style.css +1 -1
  88. package/dist/types/index.d.ts +113 -53
  89. package/dist/types/index.d.ts.map +1 -1
  90. package/dist/utils/fileIcon.d.ts +13 -0
  91. package/dist/utils/fileIcon.d.ts.map +1 -0
  92. package/package.json +12 -6
  93. package/src/adapter.ts +12 -70
  94. package/src/components/ChatPanel.vue +329 -110
  95. package/src/components/common/ConfirmDialog.vue +208 -0
  96. package/src/components/common/CopyButton.vue +71 -0
  97. package/src/components/common/IndexingSettings.vue +580 -0
  98. package/src/components/common/SettingsPanel.vue +293 -0
  99. package/src/components/common/Toast.vue +90 -0
  100. package/src/components/common/ToggleSwitch.vue +75 -0
  101. package/src/components/{chat/ui → header}/ChatHeader.vue +170 -93
  102. package/src/components/input/AtFilePicker.vue +657 -0
  103. package/src/components/input/ChatInput.vue +653 -0
  104. package/src/components/input/DropdownSelector.vue +322 -0
  105. package/src/components/input/ImagePreviewModal.vue +238 -0
  106. package/src/components/input/at-views/AtBranchView.vue +63 -0
  107. package/src/components/input/at-views/AtBrowserView.vue +63 -0
  108. package/src/components/input/at-views/AtChatsView.vue +63 -0
  109. package/src/components/input/at-views/AtDocsView.vue +63 -0
  110. package/src/components/input/at-views/AtFilesView.vue +255 -0
  111. package/src/components/input/at-views/AtTerminalsView.vue +63 -0
  112. package/src/components/message/ContentRenderer.vue +61 -0
  113. package/src/components/message/MessageBubble.vue +411 -0
  114. package/src/components/message/PartsRenderer.vue +101 -0
  115. package/src/components/message/ToolResultRenderer.vue +27 -0
  116. package/src/components/message/WelcomeMessage.vue +308 -0
  117. package/src/components/message/blocks/CodeBlock.vue +113 -0
  118. package/src/components/message/blocks/TextBlock.vue +21 -0
  119. package/src/components/message/blocks/index.ts +6 -0
  120. package/src/components/message/parts/CollapsibleCard.vue +135 -0
  121. package/src/components/message/parts/ErrorPart.vue +51 -0
  122. package/src/components/message/parts/ImagePart.vue +98 -0
  123. package/src/components/message/parts/SearchPart.vue +101 -0
  124. package/src/components/message/parts/TextPart.vue +28 -0
  125. package/src/components/message/parts/ThinkingPart.vue +54 -0
  126. package/src/components/message/parts/ToolCallPart.vue +460 -0
  127. package/src/components/message/parts/ToolResultPart.vue +78 -0
  128. package/src/components/message/parts/index.ts +13 -0
  129. package/src/components/message/tool-results/DefaultToolResult.vue +43 -0
  130. package/src/components/message/tool-results/SearchResults.vue +133 -0
  131. package/src/components/message/tool-results/WeatherCard.vue +139 -0
  132. package/src/components/message/tool-results/index.ts +7 -0
  133. package/src/components/message/welcome-types.ts +47 -0
  134. package/src/composables/useChat.ts +807 -155
  135. package/src/composables/useImageUpload.ts +228 -0
  136. package/src/index.ts +93 -46
  137. package/src/styles.css +47 -0
  138. package/src/types/index.ts +146 -98
  139. package/src/utils/fileIcon.ts +49 -0
  140. package/dist/components/ChatInput.vue.d.ts.map +0 -1
  141. package/dist/components/chat/SearchResultBlock.vue.d.ts.map +0 -1
  142. package/dist/components/chat/ThinkingBlock.vue.d.ts.map +0 -1
  143. package/dist/components/chat/ToolCallBlock.vue.d.ts.map +0 -1
  144. package/dist/components/chat/messages/ExecutionSteps.vue.d.ts.map +0 -1
  145. package/dist/components/chat/messages/MessageBubble.vue.d.ts +0 -28
  146. package/dist/components/chat/messages/MessageBubble.vue.d.ts.map +0 -1
  147. package/dist/components/chat/ui/ChatHeader.vue.d.ts.map +0 -1
  148. package/dist/components/chat/ui/WelcomeMessage.vue.d.ts +0 -7
  149. package/dist/components/chat/ui/WelcomeMessage.vue.d.ts.map +0 -1
  150. package/dist/preload/preload.d.ts +0 -6
  151. package/dist/preload/preload.d.ts.map +0 -1
  152. package/src/components/ChatInput.vue +0 -649
  153. package/src/components/chat/SearchResultBlock.vue +0 -155
  154. package/src/components/chat/ThinkingBlock.vue +0 -109
  155. package/src/components/chat/ToolCallBlock.vue +0 -213
  156. package/src/components/chat/messages/ExecutionSteps.vue +0 -281
  157. package/src/components/chat/messages/MessageBubble.vue +0 -272
  158. package/src/components/chat/ui/WelcomeMessage.vue +0 -135
  159. package/src/preload/preload.ts +0 -79
@@ -19,7 +19,7 @@
19
19
  @click.stop="handleHideTab(session.id)"
20
20
  title="关闭标签"
21
21
  >
22
- <X :size="12" />
22
+ <Icon icon="lucide:x" width="18" />
23
23
  </button>
24
24
  </div>
25
25
  </template>
@@ -33,7 +33,7 @@
33
33
  title="新建对话"
34
34
  @click="$emit('new-session')"
35
35
  >
36
- <Plus :size="14" />
36
+ <Icon icon="lucide:plus" width="18" />
37
37
  </button>
38
38
 
39
39
  <!-- 历史记录 -->
@@ -43,7 +43,7 @@
43
43
  title="历史记录"
44
44
  @click.stop="toggleHistory"
45
45
  >
46
- <Clock :size="14" />
46
+ <Icon icon="lucide:clock" width="18" />
47
47
  </button>
48
48
 
49
49
  <!-- 历史记录面板 -->
@@ -51,26 +51,29 @@
51
51
  <div class="panel-header">
52
52
  <span>历史记录</span>
53
53
  <button class="icon-btn small" title="新建对话" @click="handleNewFromHistory">
54
- <Plus :size="12" />
54
+ <Icon icon="lucide:plus" width="18" />
55
55
  </button>
56
56
  </div>
57
- <div class="panel-content">
58
- <div
59
- v-for="session in sessions"
60
- :key="session.id"
61
- :class="['history-item', { active: session.id === currentSessionId }]"
62
- @click="handleSelectHistory(session.id)"
63
- >
64
- <span class="history-title">{{ session.title }}</span>
65
- <span class="history-date">{{ formatDate(session.updatedAt) }}</span>
66
- <button
67
- class="history-delete"
68
- title="删除"
69
- @click.stop="$emit('delete-session', session.id)"
57
+ <div class="panel-content chat-scrollbar">
58
+ <template v-for="group in groupedSessions" :key="group.label">
59
+ <div class="history-group-label">{{ group.label }}</div>
60
+ <div
61
+ v-for="session in group.sessions"
62
+ :key="session.id"
63
+ :class="['history-item', { active: session.id === currentSessionId }]"
64
+ @click="handleSelectHistory(session.id)"
70
65
  >
71
- <Trash2 :size="12" />
72
- </button>
73
- </div>
66
+ <span class="history-title">{{ session.title }}</span>
67
+ <span class="history-time">{{ formatTime(session.updatedAt) }}</span>
68
+ <button
69
+ class="history-action-btn delete"
70
+ title="删除"
71
+ @click.stop="$emit('delete-session', session.id)"
72
+ >
73
+ <Icon icon="lucide:trash-2" width="18" />
74
+ </button>
75
+ </div>
76
+ </template>
74
77
  <div v-if="sessions.length === 0" class="empty-state">
75
78
  暂无历史对话
76
79
  </div>
@@ -85,35 +88,35 @@
85
88
  title="更多选项"
86
89
  @click.stop="toggleMore"
87
90
  >
88
- <MoreHorizontal :size="14" />
91
+ <Icon icon="lucide:more-horizontal" width="18" />
89
92
  </button>
90
93
 
91
94
  <!-- 更多选项菜单 -->
92
95
  <div v-if="moreOpen" class="dropdown-panel more-panel">
93
96
  <button class="menu-item" @click="handleMenuAction('clear-all')">
94
- <Trash2 :size="14" />
97
+ <Icon icon="lucide:trash-2" width="18" />
95
98
  <span>清空所有对话</span>
96
99
  </button>
97
100
  <button class="menu-item" @click="handleMenuAction('close-others')">
98
- <XCircle :size="14" />
101
+ <Icon icon="lucide:x-circle" width="18" />
99
102
  <span>关闭其他对话</span>
100
103
  </button>
101
104
  <div class="menu-divider"></div>
102
105
  <button class="menu-item" @click="handleMenuAction('export')">
103
- <Download :size="14" />
106
+ <Icon icon="lucide:download" width="18" />
104
107
  <span>导出对话</span>
105
108
  </button>
106
109
  <button class="menu-item" @click="handleMenuAction('copy-id')">
107
- <Copy :size="14" />
110
+ <Icon icon="lucide:copy" width="18" />
108
111
  <span>复制请求 ID</span>
109
112
  </button>
110
113
  <div class="menu-divider"></div>
111
114
  <button class="menu-item" @click="handleMenuAction('feedback')">
112
- <MessageSquare :size="14" />
115
+ <Icon icon="lucide:message-square" width="18" />
113
116
  <span>反馈</span>
114
117
  </button>
115
118
  <button class="menu-item" @click="handleMenuAction('settings')">
116
- <Settings :size="14" />
119
+ <Icon icon="lucide:settings" width="18" />
117
120
  <span>Agent 设置</span>
118
121
  </button>
119
122
  </div>
@@ -126,7 +129,16 @@
126
129
  title="关闭"
127
130
  @click="$emit('close')"
128
131
  >
129
- <X :size="14" />
132
+ <Icon icon="lucide:x" width="18" />
133
+ </button>
134
+
135
+ <!-- 设置按钮 -->
136
+ <button
137
+ class="icon-btn"
138
+ title="设置"
139
+ @click="$emit('settings')"
140
+ >
141
+ <Icon icon="lucide:settings" width="18" />
130
142
  </button>
131
143
  </div>
132
144
  </div>
@@ -134,19 +146,8 @@
134
146
 
135
147
  <script setup lang="ts">
136
148
  import { ref, computed, onMounted, onUnmounted } from 'vue';
137
- import {
138
- X,
139
- Plus,
140
- Clock,
141
- MoreHorizontal,
142
- Trash2,
143
- XCircle,
144
- Download,
145
- Copy,
146
- MessageSquare,
147
- Settings,
148
- } from 'lucide-vue-next';
149
- import type { SessionRecord } from '../../../types';
149
+ import { Icon } from '@iconify/vue';
150
+ import type { SessionRecord } from '../../types';
150
151
 
151
152
  const props = defineProps<{
152
153
  sessions: SessionRecord[];
@@ -158,6 +159,7 @@ const emit = defineEmits<{
158
159
  'new-session': [];
159
160
  'switch-session': [sessionId: string];
160
161
  'delete-session': [sessionId: string];
162
+ 'hide-session': [sessionId: string, hidden: boolean];
161
163
  'close': [];
162
164
  'clear-all': [];
163
165
  'close-others': [];
@@ -167,12 +169,9 @@ const emit = defineEmits<{
167
169
  'settings': [];
168
170
  }>();
169
171
 
170
- // 隐藏的 tab(关闭但不删除)
171
- const hiddenTabs = ref<Set<string>>(new Set());
172
-
173
172
  // 可见的会话(过滤掉隐藏的)
174
173
  const visibleSessions = computed(() =>
175
- props.sessions.filter((s) => !hiddenTabs.value.has(s.id))
174
+ props.sessions.filter((s) => !s.hidden)
176
175
  );
177
176
 
178
177
  // 下拉菜单状态
@@ -186,28 +185,72 @@ function getDisplayTitle(title: string) {
186
185
  return title === '新对话' ? 'New Chat' : title;
187
186
  }
188
187
 
189
- // 格式化日期
190
- function formatDate(date: Date) {
188
+ // 格式化时间(HH:mm)
189
+ function formatTime(date: Date): string {
190
+ const d = new Date(date);
191
+ const hours = String(d.getHours()).padStart(2, '0');
192
+ const minutes = String(d.getMinutes()).padStart(2, '0');
193
+ return `${hours}:${minutes}`;
194
+ }
195
+
196
+ // 获取日期分组标签
197
+ function getDateGroupLabel(date: Date): string {
191
198
  const d = new Date(date);
192
199
  const now = new Date();
193
- const diff = now.getTime() - d.getTime();
200
+
201
+ // 重置时间部分,只比较日期
202
+ const today = new Date(now.getFullYear(), now.getMonth(), now.getDate());
203
+ const target = new Date(d.getFullYear(), d.getMonth(), d.getDate());
204
+ const diff = today.getTime() - target.getTime();
194
205
  const days = Math.floor(diff / (1000 * 60 * 60 * 24));
195
206
 
196
207
  if (days === 0) return '今天';
197
208
  if (days === 1) return '昨天';
198
- if (days < 7) return `${days}天前`;
199
- return d.toLocaleDateString();
209
+
210
+ // 其他显示具体日期
211
+ const month = d.getMonth() + 1;
212
+ const day = d.getDate();
213
+ return `${month}月${day}日`;
200
214
  }
201
215
 
202
- // 隐藏 tab
216
+ // 按日期分组会话
217
+ interface SessionGroup {
218
+ label: string;
219
+ sessions: SessionRecord[];
220
+ }
221
+
222
+ const groupedSessions = computed<SessionGroup[]>(() => {
223
+ const groups = new Map<string, SessionRecord[]>();
224
+
225
+ for (const session of props.sessions) {
226
+ const label = getDateGroupLabel(session.updatedAt);
227
+ const group = groups.get(label);
228
+ if (group) {
229
+ group.push(session);
230
+ } else {
231
+ groups.set(label, [session]);
232
+ }
233
+ }
234
+
235
+ // 转换为数组,保持顺序
236
+ return Array.from(groups.entries()).map(([label, sessions]) => ({
237
+ label,
238
+ sessions,
239
+ }));
240
+ });
241
+
242
+ // 隐藏 tab(触发事件由父组件持久化到数据库)
203
243
  function handleHideTab(sessionId: string) {
204
- hiddenTabs.value = new Set([...hiddenTabs.value, sessionId]);
244
+ emit('hide-session', sessionId, true);
205
245
  if (sessionId === props.currentSessionId) {
206
246
  const remaining = props.sessions.filter(
207
- (s) => s.id !== sessionId && !hiddenTabs.value.has(s.id)
247
+ (s) => s.id !== sessionId && !s.hidden
208
248
  );
209
249
  if (remaining.length > 0) {
210
250
  emit('switch-session', remaining[0].id);
251
+ } else {
252
+ // 最后一个 tab 被关闭,创建新会话
253
+ emit('new-session');
211
254
  }
212
255
  }
213
256
  }
@@ -232,13 +275,12 @@ function handleNewFromHistory() {
232
275
 
233
276
  // 选择历史
234
277
  function handleSelectHistory(sessionId: string) {
235
- emit('switch-session', sessionId);
278
+ const session = props.sessions.find((s) => s.id === sessionId);
236
279
  // 如果被隐藏了,恢复显示
237
- if (hiddenTabs.value.has(sessionId)) {
238
- const newHidden = new Set(hiddenTabs.value);
239
- newHidden.delete(sessionId);
240
- hiddenTabs.value = newHidden;
280
+ if (session?.hidden) {
281
+ emit('hide-session', sessionId, false);
241
282
  }
283
+ emit('switch-session', sessionId);
242
284
  historyOpen.value = false;
243
285
  }
244
286
 
@@ -315,13 +357,12 @@ onUnmounted(() => {
315
357
  position: relative;
316
358
  display: flex;
317
359
  align-items: center;
318
- gap: 4px;
319
360
  padding: 4px 8px;
320
361
  background: transparent;
321
362
  border: none;
322
363
  border-radius: 4px;
323
364
  color: var(--chat-text-muted, #888);
324
- font-size: 13px;
365
+ font-size: 14px;
325
366
  font-weight: 500;
326
367
  cursor: pointer;
327
368
  transition: all 0.15s;
@@ -346,27 +387,35 @@ onUnmounted(() => {
346
387
  }
347
388
 
348
389
  .tab-close {
349
- display: none;
350
- width: 16px;
351
- height: 16px;
390
+ position: absolute;
391
+ right: 0;
392
+ top: 0;
393
+ bottom: 0;
394
+ display: flex;
395
+ align-items: center;
396
+ justify-content: center;
397
+ width: 32px;
352
398
  padding: 0;
353
- background: transparent;
399
+ padding-left: 8px;
400
+ background: linear-gradient(to right, transparent, var(--chat-bg, #1e1e1e) 50%);
354
401
  border: none;
355
- border-radius: 2px;
356
- color: var(--chat-text-muted, #888);
402
+ border-radius: 0 4px 4px 0;
403
+ color: var(--chat-text-muted, #666);
357
404
  cursor: pointer;
358
- align-items: center;
359
- justify-content: center;
360
- flex-shrink: 0;
405
+ transition: all 0.15s;
406
+ opacity: 0;
361
407
  }
362
408
 
363
409
  .tab-item:hover .tab-close {
364
- display: flex;
410
+ opacity: 1;
411
+ }
412
+
413
+ .tab-item.active .tab-close {
414
+ background: linear-gradient(to right, transparent, var(--chat-muted, #3c3c3c) 50%);
365
415
  }
366
416
 
367
417
  .tab-close:hover {
368
418
  color: var(--chat-text, #fff);
369
- background: var(--chat-muted, #444);
370
419
  }
371
420
 
372
421
  .header-actions {
@@ -419,7 +468,7 @@ onUnmounted(() => {
419
468
  }
420
469
 
421
470
  .history-panel {
422
- width: 280px;
471
+ width: 360px;
423
472
  }
424
473
 
425
474
  .more-panel {
@@ -433,21 +482,36 @@ onUnmounted(() => {
433
482
  justify-content: space-between;
434
483
  padding: 10px 12px;
435
484
  border-bottom: 1px solid var(--chat-border, #333);
436
- font-size: 12px;
485
+ font-size: 14px;
437
486
  font-weight: 500;
438
487
  color: var(--chat-text-muted, #888);
439
488
  }
440
489
 
441
490
  .panel-content {
442
- max-height: 300px;
491
+ max-height: 400px;
443
492
  overflow-y: auto;
444
493
  }
445
494
 
495
+
496
+ /* 日期分组标签 */
497
+ .history-group-label {
498
+ padding: 8px 12px 4px;
499
+ font-size: 14px;
500
+ font-weight: 500;
501
+ color: var(--chat-text-muted, #666);
502
+ }
503
+
504
+ .history-group-label:not(:first-child) {
505
+ border-top: 1px solid var(--chat-border, #333);
506
+ margin-top: 4px;
507
+ padding-top: 12px;
508
+ }
509
+
446
510
  .history-item {
447
511
  display: flex;
448
512
  align-items: center;
449
- gap: 8px;
450
- padding: 10px 12px;
513
+ gap: 4px;
514
+ padding: 6px 12px;
451
515
  cursor: pointer;
452
516
  transition: background 0.15s;
453
517
  }
@@ -462,47 +526,60 @@ onUnmounted(() => {
462
526
 
463
527
  .history-title {
464
528
  flex: 1;
465
- font-size: 13px;
529
+ font-size: 14px;
466
530
  color: var(--chat-text, #ccc);
467
531
  overflow: hidden;
468
532
  text-overflow: ellipsis;
469
533
  white-space: nowrap;
470
534
  }
471
535
 
472
- .history-date {
473
- font-size: 11px;
536
+ .history-time {
537
+ font-size: 14px;
474
538
  color: var(--chat-text-muted, #666);
475
539
  flex-shrink: 0;
540
+ margin-right: 10px;
476
541
  }
477
542
 
478
- .history-delete {
479
- display: none;
480
- width: 20px;
481
- height: 20px;
543
+ .history-actions {
544
+ display: flex;
545
+ align-items: center;
546
+ gap: 2px;
547
+ flex-shrink: 0;
548
+ opacity: 0;
549
+ transition: opacity 0.15s;
550
+ }
551
+
552
+ .history-item:hover .history-actions {
553
+ opacity: 1;
554
+ }
555
+
556
+ .history-action-btn {
557
+ display: flex;
558
+ align-items: center;
559
+ justify-content: center;
560
+ width: 28px;
561
+ height: 28px;
482
562
  padding: 0;
483
563
  background: transparent;
484
564
  border: none;
485
- border-radius: 4px;
565
+ border-radius: 6px;
486
566
  color: var(--chat-text-muted, #666);
487
567
  cursor: pointer;
488
- align-items: center;
489
- justify-content: center;
490
- flex-shrink: 0;
568
+ transition: all 0.15s;
491
569
  }
492
570
 
493
- .history-item:hover .history-delete {
494
- display: flex;
571
+ .history-action-btn:hover {
572
+ color: var(--chat-text, #fff);
495
573
  }
496
574
 
497
- .history-delete:hover {
575
+ .history-action-btn.delete:hover {
498
576
  color: var(--chat-destructive, #ef4444);
499
- background: var(--chat-destructive, #ef444420);
500
577
  }
501
578
 
502
579
  .empty-state {
503
580
  padding: 20px;
504
581
  text-align: center;
505
- font-size: 13px;
582
+ font-size: 14px;
506
583
  color: var(--chat-text-muted, #666);
507
584
  }
508
585
 
@@ -515,7 +592,7 @@ onUnmounted(() => {
515
592
  background: transparent;
516
593
  border: none;
517
594
  border-radius: 4px;
518
- font-size: 13px;
595
+ font-size: 14px;
519
596
  color: var(--chat-text-muted, #999);
520
597
  cursor: pointer;
521
598
  transition: all 0.15s;