byt-lingxiao-ai 0.3.0 → 0.3.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.
- package/components/AiMessage.vue +12 -18
- package/components/ChatAvatar.vue +8 -1
- package/components/ChatMessageList.vue +29 -10
- package/components/ChatWindow.vue +107 -4
- package/components/ChatWindowHeader.vue +1 -0
- package/components/assets/arrow.png +0 -0
- package/components/assets/empty.png +0 -0
- package/components/assets/entering.png +0 -0
- package/components/assets/logo.png +0 -0
- package/components/assets/normal.png +0 -0
- package/components/assets/output.png +0 -0
- package/components/assets/speaking.png +0 -0
- package/components/assets/think.png +0 -0
- package/components/assets/thinking.png +0 -0
- package/components/assets/waiting.png +0 -0
- package/components/mixins/messageMixin.js +11 -5
- package/dist/img/empty.f36cb82e.png +0 -0
- package/dist/img/entering.4ef198fb.png +0 -0
- package/dist/img/normal.30197a82.png +0 -0
- package/dist/img/output.1dfa94eb.png +0 -0
- package/dist/img/speaking.fa87fedb.png +0 -0
- package/dist/img/thinking.21ad5ca5.png +0 -0
- package/dist/img/waiting.460478ef.png +0 -0
- package/dist/index.common.js +189 -79
- package/dist/index.common.js.map +1 -1
- package/dist/index.css +2 -2
- package/dist/index.umd.js +189 -79
- package/dist/index.umd.js.map +1 -1
- package/dist/index.umd.min.js +1 -1
- package/dist/index.umd.min.js.map +1 -1
- package/package.json +1 -1
- package/components/assets/byt.mp3 +0 -0
- package/dist/img/entering.42f05909.png +0 -0
- package/dist/img/normal.13f08ecb.png +0 -0
- package/dist/img/output.85c6bd8b.png +0 -0
- package/dist/img/speaking.3ce8b666.png +0 -0
- package/dist/img/thinking.05f29a84.png +0 -0
- package/dist/img/waiting.ac21d76e.png +0 -0
package/components/AiMessage.vue
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
<template>
|
|
2
2
|
<div class="chat-window-message-ai">
|
|
3
3
|
<div class="ai-render">
|
|
4
|
-
<div class="ai-loading" v-if="
|
|
4
|
+
<div class="ai-loading" v-if="isLoading">
|
|
5
5
|
<div class="dot"></div>
|
|
6
6
|
<div class="dot"></div>
|
|
7
7
|
<div class="dot"></div>
|
|
8
8
|
</div>
|
|
9
|
-
<div class="ai-thinking" @click="$emit('thinking-
|
|
10
|
-
<div class="ai-thinking-time"
|
|
11
|
-
<div class="ai-thinking-content" v-if="
|
|
9
|
+
<div class="ai-thinking" @click="$emit('thinking-toggle')" v-if="message.thinking">
|
|
10
|
+
<div class="ai-thinking-time">{{ message.time ? `思考用时${message.time}秒` : '思考中...' }}</div>
|
|
11
|
+
<div class="ai-thinking-content" v-if="thinkingExpanded">{{ message.thinking }}</div>
|
|
12
12
|
</div>
|
|
13
13
|
<div class="ai-content markdown-body" v-html="renderedContent"></div>
|
|
14
14
|
</div>
|
|
@@ -30,7 +30,6 @@ marked.setOptions({
|
|
|
30
30
|
breaks: true,
|
|
31
31
|
gfm: true
|
|
32
32
|
});
|
|
33
|
-
|
|
34
33
|
function parseMarkdown(text) {
|
|
35
34
|
if (!text) return '';
|
|
36
35
|
let html = text;
|
|
@@ -65,11 +64,10 @@ function parseMarkdown(text) {
|
|
|
65
64
|
|
|
66
65
|
html = html.replace(/^-{3,}$/gm, '<hr>');
|
|
67
66
|
|
|
68
|
-
html = html.replace(/\n/g, '<br>');
|
|
67
|
+
// html = html.replace(/\n/g, '<br>');
|
|
69
68
|
|
|
70
69
|
return html;
|
|
71
70
|
}
|
|
72
|
-
|
|
73
71
|
// 解析表格
|
|
74
72
|
function parseTable(text) {
|
|
75
73
|
const lines = text.split('\n');
|
|
@@ -108,7 +106,6 @@ function parseTable(text) {
|
|
|
108
106
|
|
|
109
107
|
return result.join('\n');
|
|
110
108
|
}
|
|
111
|
-
|
|
112
109
|
// 渲染表格
|
|
113
110
|
function renderTable(rows) {
|
|
114
111
|
if (rows.length < 2) return rows.join('\n');
|
|
@@ -140,7 +137,6 @@ function renderTable(rows) {
|
|
|
140
137
|
html += '</table></div>';
|
|
141
138
|
return html;
|
|
142
139
|
}
|
|
143
|
-
|
|
144
140
|
// HTML 转义
|
|
145
141
|
function escapeHtml(text) {
|
|
146
142
|
const map = {
|
|
@@ -159,19 +155,17 @@ export default {
|
|
|
159
155
|
message: {
|
|
160
156
|
type: Object,
|
|
161
157
|
required: true
|
|
162
|
-
},
|
|
163
|
-
thinkStatus: {
|
|
164
|
-
type: Boolean,
|
|
165
|
-
default: true
|
|
166
|
-
},
|
|
167
|
-
loading: {
|
|
168
|
-
type: Boolean,
|
|
169
|
-
default: false
|
|
170
158
|
}
|
|
171
159
|
},
|
|
172
160
|
computed: {
|
|
161
|
+
thinkingExpanded() {
|
|
162
|
+
return this.message.thinkingExpanded !== false;
|
|
163
|
+
},
|
|
173
164
|
renderedContent() {
|
|
174
165
|
return parseMarkdown(this.message.content);
|
|
166
|
+
},
|
|
167
|
+
isLoading() {
|
|
168
|
+
return this.message.loading === true;
|
|
175
169
|
}
|
|
176
170
|
},
|
|
177
171
|
watch: {
|
|
@@ -459,6 +453,6 @@ export default {
|
|
|
459
453
|
}
|
|
460
454
|
|
|
461
455
|
.markdown-body ::v-deep br {
|
|
462
|
-
line-height:
|
|
456
|
+
line-height: 0.8em;
|
|
463
457
|
}
|
|
464
458
|
</style>
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
<div class="chat-ai" @
|
|
2
|
+
<div class="chat-ai" @mousedown="$emit('mousedown')">
|
|
3
3
|
<div :class="['chat-ai-avater', status]"></div>
|
|
4
4
|
<div class="chat-ai-text">{{ statusText }}</div>
|
|
5
5
|
</div>
|
|
@@ -41,6 +41,13 @@ export default {
|
|
|
41
41
|
box-shadow: 0 2px 11.6px 0 rgba(0, 0, 0, 0.1);
|
|
42
42
|
cursor: pointer;
|
|
43
43
|
user-select: none;
|
|
44
|
+
transition: box-shadow 0.2s;
|
|
45
|
+
}
|
|
46
|
+
.chat-ai:hover {
|
|
47
|
+
box-shadow: 0 4px 16px 0 rgba(0, 0, 0, 0.2);
|
|
48
|
+
}
|
|
49
|
+
.chat-ai:active {
|
|
50
|
+
cursor: grabbing;
|
|
44
51
|
}
|
|
45
52
|
|
|
46
53
|
.chat-ai-avater {
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
<template>
|
|
2
2
|
<div ref="chatArea" class="chat-window-content scrollbar-hide">
|
|
3
|
+
<div v-if="messages.length === 0" class="chat-window-empty"></div>
|
|
3
4
|
<div
|
|
4
5
|
class="chat-window-message"
|
|
5
6
|
v-for="message in messages"
|
|
@@ -13,8 +14,7 @@
|
|
|
13
14
|
v-else
|
|
14
15
|
:message="message"
|
|
15
16
|
:think-status="thinkStatus"
|
|
16
|
-
|
|
17
|
-
@thinking-click="$emit('thinking-click')"
|
|
17
|
+
@thinking-toggle="handleThinkingToggle(message)"
|
|
18
18
|
/>
|
|
19
19
|
</div>
|
|
20
20
|
</div>
|
|
@@ -38,13 +38,22 @@ export default {
|
|
|
38
38
|
thinkStatus: {
|
|
39
39
|
type: Boolean,
|
|
40
40
|
default: true
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
41
|
+
}
|
|
42
|
+
},
|
|
43
|
+
computed: {
|
|
44
|
+
lastMessageObject() {
|
|
45
|
+
const len = this.messages.length;
|
|
46
|
+
if (len === 0) {
|
|
47
|
+
return null;
|
|
48
|
+
}
|
|
49
|
+
return this.messages[len - 1];
|
|
45
50
|
}
|
|
46
51
|
},
|
|
47
52
|
methods: {
|
|
53
|
+
handleThinkingToggle(message) {
|
|
54
|
+
console.log('handleThinkingToggle', message)
|
|
55
|
+
this.$set(message, 'thinkingExpanded', !message.thinkingExpanded);
|
|
56
|
+
},
|
|
48
57
|
scrollToBottom() {
|
|
49
58
|
this.$nextTick(() => {
|
|
50
59
|
const chatArea = this.$refs.chatArea
|
|
@@ -55,17 +64,27 @@ export default {
|
|
|
55
64
|
}
|
|
56
65
|
},
|
|
57
66
|
watch: {
|
|
58
|
-
|
|
59
|
-
handler() {
|
|
60
|
-
|
|
67
|
+
lastMessageObject: {
|
|
68
|
+
handler(newMsg) {
|
|
69
|
+
if (newMsg) {
|
|
70
|
+
this.scrollToBottom();
|
|
71
|
+
}
|
|
61
72
|
},
|
|
62
|
-
deep: true
|
|
73
|
+
deep: true,
|
|
74
|
+
immediate: true
|
|
63
75
|
}
|
|
64
76
|
}
|
|
65
77
|
}
|
|
66
78
|
</script>
|
|
67
79
|
|
|
68
80
|
<style scoped>
|
|
81
|
+
.chat-window-empty{
|
|
82
|
+
width: 300px;
|
|
83
|
+
height: 300px;
|
|
84
|
+
background: url('./assets/empty.png') no-repeat;
|
|
85
|
+
background-size: cover;
|
|
86
|
+
margin: auto;
|
|
87
|
+
}
|
|
69
88
|
.chat-window-content {
|
|
70
89
|
flex: 1;
|
|
71
90
|
padding: 16px;
|
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
<div
|
|
2
|
+
<div
|
|
3
|
+
class="chat"
|
|
4
|
+
:style="chatStyle"
|
|
5
|
+
>
|
|
3
6
|
<!-- 隐藏的音频播放器 -->
|
|
4
7
|
<audio
|
|
5
8
|
ref="audioPlayer"
|
|
@@ -21,7 +24,7 @@
|
|
|
21
24
|
<ChatAvatar
|
|
22
25
|
v-else
|
|
23
26
|
:status="avaterStatus"
|
|
24
|
-
@
|
|
27
|
+
@mousedown="startDrag"
|
|
25
28
|
/>
|
|
26
29
|
|
|
27
30
|
<!-- 聊天窗口 -->
|
|
@@ -30,7 +33,6 @@
|
|
|
30
33
|
:messages="messages"
|
|
31
34
|
:input-message="inputMessage"
|
|
32
35
|
:think-status="thinkStatus"
|
|
33
|
-
:loading="isLoading"
|
|
34
36
|
@update:inputMessage="inputMessage = $event"
|
|
35
37
|
@send="handleSend"
|
|
36
38
|
@thinking-click="handleThinkingClick"
|
|
@@ -77,7 +79,31 @@ export default {
|
|
|
77
79
|
thinkStatus: true,
|
|
78
80
|
jumpedTimePoints: new Set(),
|
|
79
81
|
SAMPLE_RATE,
|
|
80
|
-
FRAME_SIZE
|
|
82
|
+
FRAME_SIZE,
|
|
83
|
+
dragThreshold: 5, // 拖拽阈值
|
|
84
|
+
isDragging: false,
|
|
85
|
+
dragStartX: 0,
|
|
86
|
+
dragStartY: 0,
|
|
87
|
+
currentX: 10,
|
|
88
|
+
currentY: 20,
|
|
89
|
+
initialX: 10,
|
|
90
|
+
initialY: 20,
|
|
91
|
+
hasMoved: false,
|
|
92
|
+
}
|
|
93
|
+
},
|
|
94
|
+
computed: {
|
|
95
|
+
chatStyle() {
|
|
96
|
+
if (this.robotStatus === 'leaving' && !this.visible) {
|
|
97
|
+
return {
|
|
98
|
+
right: `${this.currentX}px`,
|
|
99
|
+
bottom: `${this.currentY}px`,
|
|
100
|
+
cursor: this.isDragging ? 'grabbing' : 'grab',
|
|
101
|
+
transition: this.isDragging ? 'none' : 'right 0.3s ease, bottom 0.3s ease',
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
return {
|
|
105
|
+
cursor: 'pointer',
|
|
106
|
+
};
|
|
81
107
|
}
|
|
82
108
|
},
|
|
83
109
|
mounted() {
|
|
@@ -85,6 +111,13 @@ export default {
|
|
|
85
111
|
if (this.appendToBody) {
|
|
86
112
|
this.appendToBodyHandler()
|
|
87
113
|
}
|
|
114
|
+
|
|
115
|
+
this.$nextTick(() => {
|
|
116
|
+
const chatEl = this.$el;
|
|
117
|
+
const style = window.getComputedStyle(chatEl);
|
|
118
|
+
this.initialX = this.currentX = parseInt(style.right, 10) || 10;
|
|
119
|
+
this.initialY = this.currentY = parseInt(style.bottom, 10) || 20;
|
|
120
|
+
})
|
|
88
121
|
},
|
|
89
122
|
beforeDestroy() {
|
|
90
123
|
if (this.appendToBody && this.$el.parentElement === document.body) {
|
|
@@ -92,10 +125,80 @@ export default {
|
|
|
92
125
|
}
|
|
93
126
|
this.closeWebSocket()
|
|
94
127
|
this.stopRecording()
|
|
128
|
+
|
|
129
|
+
// 移除全局事件监听器
|
|
130
|
+
document.removeEventListener('mousemove', this.onDrag)
|
|
131
|
+
document.removeEventListener('mouseup', this.stopDrag)
|
|
95
132
|
},
|
|
96
133
|
methods: {
|
|
97
134
|
toggleWindow() {
|
|
98
135
|
this.visible = !this.visible
|
|
136
|
+
|
|
137
|
+
if (this.visible) {
|
|
138
|
+
this.currentX = this.initialX
|
|
139
|
+
this.currentY = this.initialY
|
|
140
|
+
}
|
|
141
|
+
},
|
|
142
|
+
startDrag() {
|
|
143
|
+
console.log('startDrag')
|
|
144
|
+
if (this.robotStatus !== 'leaving' && this.visible) return;
|
|
145
|
+
|
|
146
|
+
this.isDragging = true;
|
|
147
|
+
this.hasMoved = false;
|
|
148
|
+
|
|
149
|
+
// 记录鼠标的初始位置
|
|
150
|
+
this.dragStartX = event.clientX;
|
|
151
|
+
this.dragStartY = event.clientY;
|
|
152
|
+
|
|
153
|
+
// 绑定全局事件监听器
|
|
154
|
+
document.addEventListener('mousemove', this.onDrag);
|
|
155
|
+
document.addEventListener('mouseup', this.stopDrag);
|
|
156
|
+
},
|
|
157
|
+
onDrag(event) {
|
|
158
|
+
if (!this.isDragging) return;
|
|
159
|
+
|
|
160
|
+
// 鼠标位移量
|
|
161
|
+
const deltaX = event.clientX - this.dragStartX;
|
|
162
|
+
const deltaY = event.clientY - this.dragStartY;
|
|
163
|
+
|
|
164
|
+
if (Math.abs(deltaX) > this.dragThreshold || Math.abs(deltaY) > this.dragThreshold) {
|
|
165
|
+
console.log('移动超过阈值')
|
|
166
|
+
this.hasMoved = true; // 只要移动超过阈值,就标记为拖拽
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
// 获取 .chat 容器的尺寸
|
|
170
|
+
const chatEl = this.$el;
|
|
171
|
+
const chatWidth = chatEl.offsetWidth;
|
|
172
|
+
const chatHeight = chatEl.offsetHeight;
|
|
173
|
+
|
|
174
|
+
let newX = this.currentX - deltaX;
|
|
175
|
+
let newY = this.currentY - deltaY;
|
|
176
|
+
|
|
177
|
+
// 视口宽度和高度
|
|
178
|
+
const viewportWidth = window.innerWidth;
|
|
179
|
+
const viewportHeight = window.innerHeight;
|
|
180
|
+
|
|
181
|
+
newX = Math.max(0, newX);
|
|
182
|
+
newX = Math.min(viewportWidth - chatWidth, newX);
|
|
183
|
+
|
|
184
|
+
newY = Math.max(0, newY);
|
|
185
|
+
newY = Math.min(viewportHeight - chatHeight, newY);
|
|
186
|
+
|
|
187
|
+
// 更新位置
|
|
188
|
+
this.currentX = newX;
|
|
189
|
+
this.currentY = newY;
|
|
190
|
+
|
|
191
|
+
// 重新设置新的拖拽起始点,实现平滑拖拽
|
|
192
|
+
this.dragStartX = event.clientX;
|
|
193
|
+
this.dragStartY = event.clientY;
|
|
194
|
+
},
|
|
195
|
+
stopDrag() {
|
|
196
|
+
this.isDragging = false;
|
|
197
|
+
document.removeEventListener('mousemove', this.onDrag);
|
|
198
|
+
document.removeEventListener('mouseup', this.stopDrag);
|
|
199
|
+
if (!this.hasMoved) {
|
|
200
|
+
this.toggleWindow()
|
|
201
|
+
}
|
|
99
202
|
},
|
|
100
203
|
handleThinkingClick() {
|
|
101
204
|
this.thinkStatus = !this.thinkStatus
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -4,8 +4,7 @@ import { API_URL } from '../config/index.js'
|
|
|
4
4
|
export default {
|
|
5
5
|
data() {
|
|
6
6
|
return {
|
|
7
|
-
streamParser: null
|
|
8
|
-
isLoading: false
|
|
7
|
+
streamParser: null
|
|
9
8
|
}
|
|
10
9
|
},
|
|
11
10
|
|
|
@@ -15,7 +14,6 @@ export default {
|
|
|
15
14
|
updateInterval: 16, // 约60fps
|
|
16
15
|
debug: process.env.NODE_ENV === 'development'
|
|
17
16
|
});
|
|
18
|
-
this.isLoading = true;
|
|
19
17
|
},
|
|
20
18
|
|
|
21
19
|
methods: {
|
|
@@ -28,6 +26,8 @@ export default {
|
|
|
28
26
|
thinking: '',
|
|
29
27
|
charts: [],
|
|
30
28
|
content: '',
|
|
29
|
+
loading: true,
|
|
30
|
+
thinkingExpanded: true
|
|
31
31
|
};
|
|
32
32
|
this.messages.push(message);
|
|
33
33
|
this.currentMessage = message;
|
|
@@ -95,7 +95,6 @@ export default {
|
|
|
95
95
|
|
|
96
96
|
console.log(`流处理完成,总耗时: ${duration}ms`);
|
|
97
97
|
this.avaterStatus = 'normal';
|
|
98
|
-
this.isLoading = false;
|
|
99
98
|
|
|
100
99
|
} catch (error) {
|
|
101
100
|
console.error('发送消息失败:', error);
|
|
@@ -103,7 +102,11 @@ export default {
|
|
|
103
102
|
this.currentMessage.content = '抱歉,发生了错误,请重试。';
|
|
104
103
|
this.$forceUpdate();
|
|
105
104
|
}
|
|
106
|
-
|
|
105
|
+
} finally {
|
|
106
|
+
// 确保加载状态关闭
|
|
107
|
+
if (this.currentMessage) {
|
|
108
|
+
this.currentMessage.loading = false;
|
|
109
|
+
}
|
|
107
110
|
}
|
|
108
111
|
},
|
|
109
112
|
|
|
@@ -141,6 +144,9 @@ export default {
|
|
|
141
144
|
|
|
142
145
|
console.log('收到更新:', result);
|
|
143
146
|
|
|
147
|
+
if (this.currentMessage.loading) {
|
|
148
|
+
this.currentMessage.loading = false;
|
|
149
|
+
}
|
|
144
150
|
// 更新思考内容
|
|
145
151
|
if (result.thinking) {
|
|
146
152
|
this.currentMessage.thinking += result.thinking;
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|