@huyooo/ai-chat-frontend-vue 0.1.2

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 (45) hide show
  1. package/dist/adapter.d.ts +87 -0
  2. package/dist/adapter.d.ts.map +1 -0
  3. package/dist/components/ChatInput.vue.d.ts +54 -0
  4. package/dist/components/ChatInput.vue.d.ts.map +1 -0
  5. package/dist/components/ChatPanel.vue.d.ts +38 -0
  6. package/dist/components/ChatPanel.vue.d.ts.map +1 -0
  7. package/dist/components/chat/SearchResultBlock.vue.d.ts +8 -0
  8. package/dist/components/chat/SearchResultBlock.vue.d.ts.map +1 -0
  9. package/dist/components/chat/ThinkingBlock.vue.d.ts +7 -0
  10. package/dist/components/chat/ThinkingBlock.vue.d.ts.map +1 -0
  11. package/dist/components/chat/ToolCallBlock.vue.d.ts +9 -0
  12. package/dist/components/chat/ToolCallBlock.vue.d.ts.map +1 -0
  13. package/dist/components/chat/messages/ExecutionSteps.vue.d.ts +13 -0
  14. package/dist/components/chat/messages/ExecutionSteps.vue.d.ts.map +1 -0
  15. package/dist/components/chat/messages/MessageBubble.vue.d.ts +28 -0
  16. package/dist/components/chat/messages/MessageBubble.vue.d.ts.map +1 -0
  17. package/dist/components/chat/ui/ChatHeader.vue.d.ts +34 -0
  18. package/dist/components/chat/ui/ChatHeader.vue.d.ts.map +1 -0
  19. package/dist/components/chat/ui/WelcomeMessage.vue.d.ts +7 -0
  20. package/dist/components/chat/ui/WelcomeMessage.vue.d.ts.map +1 -0
  21. package/dist/composables/useChat.d.ts +96 -0
  22. package/dist/composables/useChat.d.ts.map +1 -0
  23. package/dist/index.d.ts +37 -0
  24. package/dist/index.d.ts.map +1 -0
  25. package/dist/index.js +1497 -0
  26. package/dist/preload/preload.d.ts +6 -0
  27. package/dist/preload/preload.d.ts.map +1 -0
  28. package/dist/style.css +1 -0
  29. package/dist/types/index.d.ts +107 -0
  30. package/dist/types/index.d.ts.map +1 -0
  31. package/package.json +59 -0
  32. package/src/adapter.ts +160 -0
  33. package/src/components/ChatInput.vue +649 -0
  34. package/src/components/ChatPanel.vue +309 -0
  35. package/src/components/chat/SearchResultBlock.vue +155 -0
  36. package/src/components/chat/ThinkingBlock.vue +109 -0
  37. package/src/components/chat/ToolCallBlock.vue +213 -0
  38. package/src/components/chat/messages/ExecutionSteps.vue +281 -0
  39. package/src/components/chat/messages/MessageBubble.vue +272 -0
  40. package/src/components/chat/ui/ChatHeader.vue +535 -0
  41. package/src/components/chat/ui/WelcomeMessage.vue +135 -0
  42. package/src/composables/useChat.ts +423 -0
  43. package/src/index.ts +82 -0
  44. package/src/preload/preload.ts +79 -0
  45. package/src/types/index.ts +164 -0
@@ -0,0 +1,272 @@
1
+ <template>
2
+ <div :class="['message-bubble', role]">
3
+ <!-- 用户消息 - 复用 ChatInput 组件 -->
4
+ <template v-if="role === 'user'">
5
+ <ChatInput
6
+ v-if="onSend"
7
+ variant="message"
8
+ :value="content"
9
+ :selected-images="images"
10
+ @send="(text) => $emit('send', text)"
11
+ />
12
+ <div v-else class="user-content">
13
+ <div class="user-text">{{ content }}</div>
14
+ <div v-if="images && images.length > 0" class="user-images">
15
+ <img
16
+ v-for="(img, i) in images"
17
+ :key="i"
18
+ :src="img"
19
+ class="user-image"
20
+ @click="$emit('view-image', img)"
21
+ />
22
+ </div>
23
+ </div>
24
+ </template>
25
+
26
+ <!-- 助手消息 -->
27
+ <template v-else>
28
+ <div class="assistant-content">
29
+ <!-- 执行步骤列表 -->
30
+ <ExecutionSteps
31
+ :loading="loading"
32
+ :has-content="!!content"
33
+ :thinking="thinking"
34
+ :thinking-complete="thinkingComplete"
35
+ :searching="searching"
36
+ :search-results="searchResults"
37
+ :tool-calls="toolCalls"
38
+ />
39
+
40
+ <!-- 文本内容 -->
41
+ <div v-if="content" class="assistant-text" v-html="renderedContent"></div>
42
+
43
+ <!-- 加载中 -->
44
+ <div v-if="loading && !content && !thinking && !toolCalls?.length" class="loading-indicator">
45
+ <span class="loading-dot"></span>
46
+ <span class="loading-dot"></span>
47
+ <span class="loading-dot"></span>
48
+ </div>
49
+ </div>
50
+
51
+ <!-- 操作按钮 -->
52
+ <div v-if="content && !loading" class="message-actions">
53
+ <button class="action-btn" title="复制" @click="$emit('copy')">
54
+ <component :is="copied ? Check : Copy" :size="14" />
55
+ </button>
56
+ <button class="action-btn" title="重新生成" @click="$emit('regenerate')">
57
+ <RefreshCw :size="14" />
58
+ </button>
59
+ </div>
60
+ </template>
61
+ </div>
62
+ </template>
63
+
64
+ <script setup lang="ts">
65
+ import { computed } from 'vue';
66
+ import { Copy, Check, RefreshCw } from 'lucide-vue-next';
67
+ import ChatInput from '../../ChatInput.vue';
68
+ import ExecutionSteps from './ExecutionSteps.vue';
69
+ import type { ToolCall, SearchResult } from '../../../types';
70
+
71
+ const props = defineProps<{
72
+ role: 'user' | 'assistant';
73
+ content: string;
74
+ images?: string[];
75
+ thinking?: string;
76
+ thinkingComplete?: boolean;
77
+ searchResults?: SearchResult[];
78
+ searching?: boolean;
79
+ toolCalls?: ToolCall[];
80
+ loading?: boolean;
81
+ copied?: boolean;
82
+ /** 是否支持重新发送(仅用户消息) */
83
+ onSend?: (text: string) => void;
84
+ }>();
85
+
86
+ defineEmits<{
87
+ copy: [];
88
+ regenerate: [];
89
+ 'view-image': [url: string];
90
+ send: [text: string];
91
+ }>();
92
+
93
+ // Markdown 渲染
94
+ const renderedContent = computed(() => {
95
+ let html = props.content;
96
+
97
+ // 代码块
98
+ html = html.replace(/```(\w*)\n([\s\S]*?)```/g, '<pre class="code-block"><code>$2</code></pre>');
99
+
100
+ // 行内代码
101
+ html = html.replace(/`([^`]+)`/g, '<code class="inline-code">$1</code>');
102
+
103
+ // 粗体
104
+ html = html.replace(/\*\*([^*]+)\*\*/g, '<strong>$1</strong>');
105
+
106
+ // 斜体
107
+ html = html.replace(/\*([^*]+)\*/g, '<em>$1</em>');
108
+
109
+ // 换行
110
+ html = html.replace(/\n/g, '<br>');
111
+
112
+ return html;
113
+ });
114
+ </script>
115
+
116
+ <style scoped>
117
+ .message-bubble {
118
+ padding: 8px 0;
119
+ animation: fadeIn 0.2s ease;
120
+ }
121
+
122
+ @keyframes fadeIn {
123
+ from {
124
+ opacity: 0;
125
+ transform: translateY(4px);
126
+ }
127
+ to {
128
+ opacity: 1;
129
+ transform: translateY(0);
130
+ }
131
+ }
132
+
133
+ /* 用户消息 */
134
+ .message-bubble.user {
135
+ width: 100%;
136
+ }
137
+
138
+ .user-content {
139
+ width: 100%;
140
+ background: var(--chat-muted, #2d2d2d);
141
+ color: var(--chat-text, #ccc);
142
+ padding: 12px;
143
+ border-radius: 12px;
144
+ border: 1px solid var(--chat-border, #444);
145
+ }
146
+
147
+ .user-text {
148
+ font-size: 14px;
149
+ line-height: 1.5;
150
+ white-space: pre-wrap;
151
+ word-break: break-word;
152
+ }
153
+
154
+ .user-images {
155
+ display: flex;
156
+ gap: 8px;
157
+ margin-top: 8px;
158
+ flex-wrap: wrap;
159
+ }
160
+
161
+ .user-image {
162
+ width: 80px;
163
+ height: 80px;
164
+ object-fit: cover;
165
+ border-radius: 8px;
166
+ cursor: pointer;
167
+ transition: transform 0.15s;
168
+ }
169
+
170
+ .user-image:hover {
171
+ transform: scale(1.05);
172
+ }
173
+
174
+ /* 助手消息 */
175
+ .message-bubble.assistant {
176
+ position: relative;
177
+ }
178
+
179
+ .assistant-content {
180
+ max-width: 100%;
181
+ }
182
+
183
+ .assistant-text {
184
+ font-size: 14px;
185
+ line-height: 1.7;
186
+ color: var(--chat-text, #ccc);
187
+ font-weight: 500;
188
+ letter-spacing: 0.01em;
189
+ }
190
+
191
+ .assistant-text :deep(.code-block) {
192
+ background: var(--chat-code-bg, #1f2937);
193
+ color: var(--chat-code-text, #e5e7eb);
194
+ padding: 12px;
195
+ border-radius: 8px;
196
+ margin: 8px 0;
197
+ overflow-x: auto;
198
+ font-family: 'SF Mono', Monaco, monospace;
199
+ font-size: 13px;
200
+ }
201
+
202
+ .assistant-text :deep(.inline-code) {
203
+ background: var(--chat-muted, #3c3c3c);
204
+ color: var(--chat-text, #e5e7eb);
205
+ padding: 2px 6px;
206
+ border-radius: 4px;
207
+ font-family: 'SF Mono', Monaco, monospace;
208
+ font-size: 13px;
209
+ }
210
+
211
+ /* 加载动画 */
212
+ .loading-indicator {
213
+ display: flex;
214
+ gap: 4px;
215
+ padding: 8px 0;
216
+ }
217
+
218
+ .loading-dot {
219
+ width: 6px;
220
+ height: 6px;
221
+ background: var(--chat-text-muted, #666);
222
+ border-radius: 50%;
223
+ animation: pulse 1.4s ease-in-out infinite;
224
+ }
225
+
226
+ .loading-dot:nth-child(2) {
227
+ animation-delay: 0.2s;
228
+ }
229
+
230
+ .loading-dot:nth-child(3) {
231
+ animation-delay: 0.4s;
232
+ }
233
+
234
+ @keyframes pulse {
235
+ 0%,
236
+ 100% {
237
+ opacity: 0.4;
238
+ transform: scale(0.8);
239
+ }
240
+ 50% {
241
+ opacity: 1;
242
+ transform: scale(1);
243
+ }
244
+ }
245
+
246
+ /* 操作按钮 */
247
+ .message-actions {
248
+ display: flex;
249
+ gap: 4px;
250
+ margin-top: 8px;
251
+ justify-content: flex-end;
252
+ }
253
+
254
+ .action-btn {
255
+ display: flex;
256
+ align-items: center;
257
+ justify-content: center;
258
+ width: 24px;
259
+ height: 24px;
260
+ border: none;
261
+ background: transparent;
262
+ border-radius: 4px;
263
+ color: var(--chat-text-muted, #666);
264
+ cursor: pointer;
265
+ transition: all 0.15s;
266
+ }
267
+
268
+ .action-btn:hover {
269
+ background: var(--chat-muted, #3c3c3c);
270
+ color: var(--chat-text, #ccc);
271
+ }
272
+ </style>