@tencentcloud/roomkit-electron-vue3 2.7.0 → 2.7.1

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 (50) hide show
  1. package/es/components/AITools/AISubtitles.vue.mjs +1 -1
  2. package/es/components/AITools/AISubtitles.vue2.mjs +18 -19
  3. package/es/components/AITools/AITranscription.vue.mjs +1 -1
  4. package/es/components/AITools/AITranscription.vue2.mjs +65 -76
  5. package/es/components/ManageMember/MemberControl/index.vue.mjs +1 -1
  6. package/es/components/ManageMember/MemberControl/index.vue2.mjs +6 -4
  7. package/es/components/ManageMember/MemberItemCommon/MemberInfo.vue.mjs +1 -1
  8. package/es/components/RoomFooter/BasicBeauty.vue.mjs +1 -1
  9. package/es/components/RoomFooter/BasicBeauty.vue2.mjs +3 -1
  10. package/es/components/RoomFooter/ChatControl.vue.mjs +2 -0
  11. package/es/components/ScheduleConference/ScheduleConferencePanel/index.vue2.mjs +1 -1
  12. package/es/components/ScheduleConference/ScheduleRoomControl.vue2.mjs +1 -1
  13. package/es/components/ScheduleConference/ScheduleRoomList.vue2.mjs +1 -1
  14. package/es/index.mjs +118 -110
  15. package/es/services/function/aiTask.d.ts +13 -17
  16. package/es/services/function/aiTask.mjs +63 -100
  17. package/es/services/manager/dataReportManager.d.ts +4 -1
  18. package/es/services/manager/dataReportManager.mjs +3 -0
  19. package/es/utils/utils.d.ts +2 -0
  20. package/es/utils/utils.mjs +33 -0
  21. package/lib/components/AITools/AISubtitles.vue.js +1 -1
  22. package/lib/components/AITools/AISubtitles.vue2.js +17 -18
  23. package/lib/components/AITools/AITranscription.vue.js +1 -1
  24. package/lib/components/AITools/AITranscription.vue2.js +64 -75
  25. package/lib/components/ManageMember/MemberControl/index.vue.js +1 -1
  26. package/lib/components/ManageMember/MemberControl/index.vue2.js +5 -3
  27. package/lib/components/ManageMember/MemberItemCommon/MemberInfo.vue.js +1 -1
  28. package/lib/components/RoomFooter/BasicBeauty.vue.js +1 -1
  29. package/lib/components/RoomFooter/BasicBeauty.vue2.js +3 -1
  30. package/lib/components/RoomFooter/ChatControl.vue.js +2 -0
  31. package/lib/components/ScheduleConference/ScheduleConferencePanel/index.vue2.js +1 -1
  32. package/lib/components/ScheduleConference/ScheduleRoomControl.vue2.js +1 -1
  33. package/lib/components/ScheduleConference/ScheduleRoomList.vue2.js +1 -1
  34. package/lib/index.js +118 -110
  35. package/lib/services/function/aiTask.d.ts +13 -17
  36. package/lib/services/function/aiTask.js +63 -100
  37. package/lib/services/manager/dataReportManager.d.ts +4 -1
  38. package/lib/services/manager/dataReportManager.js +3 -0
  39. package/lib/utils/utils.d.ts +2 -0
  40. package/lib/utils/utils.js +33 -0
  41. package/package.json +2 -2
  42. package/src/TUIRoom/components/AITools/AISubtitles.vue +26 -23
  43. package/src/TUIRoom/components/AITools/AITranscription.vue +106 -101
  44. package/src/TUIRoom/components/ManageMember/MemberControl/index.vue +8 -3
  45. package/src/TUIRoom/components/ManageMember/MemberItemCommon/MemberInfo.vue +1 -1
  46. package/src/TUIRoom/components/RoomFooter/BasicBeauty.vue +2 -1
  47. package/src/TUIRoom/components/RoomFooter/ChatControl.vue +2 -1
  48. package/src/TUIRoom/services/function/aiTask.ts +79 -113
  49. package/src/TUIRoom/services/manager/dataReportManager.ts +3 -0
  50. package/src/TUIRoom/utils/utils.ts +47 -0
@@ -5,18 +5,20 @@ var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "sy
5
5
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
6
6
  const mitt = require("mitt");
7
7
  const environment = require("../../utils/environment.js");
8
+ const utils = require("../../utils/utils.js");
8
9
  var AI_TASK = /* @__PURE__ */ ((AI_TASK2) => {
9
10
  AI_TASK2["TRANSCRIPTION_TASK"] = "transcription";
10
11
  return AI_TASK2;
11
12
  })(AI_TASK || {});
13
+ const ASR_EVENT_CODE = 1e4;
12
14
  class AITask {
13
15
  constructor(service) {
14
16
  __publicField(this, "emitter", mitt());
15
17
  __publicField(this, "trtc");
16
18
  __publicField(this, "service");
17
- __publicField(this, "subtitleMsg", []);
18
- __publicField(this, "subtitleText", { value: "" });
19
- __publicField(this, "transcriptionText", { value: "" });
19
+ __publicField(this, "subtitleMessages", {});
20
+ __publicField(this, "transcribedMessageList", []);
21
+ __publicField(this, "subtitleTimeout", {});
20
22
  this.service = service;
21
23
  if (environment.isElectron || environment.isMobile) return;
22
24
  this.bindCtx();
@@ -45,126 +47,87 @@ class AITask {
45
47
  if (typeof ((_a = this.service.roomEngine.instance) == null ? void 0 : _a.getTRTCCloud) === "undefined" || typeof ((_c = (_b = this.service.roomEngine.instance) == null ? void 0 : _b.getTRTCCloud()) == null ? void 0 : _c._trtc) === "undefined") {
46
48
  return;
47
49
  }
48
- const trtc = (_d = this.service.roomEngine.instance) == null ? void 0 : _d.getTRTCCloud()._trtc;
49
- this.trtc = trtc;
50
- trtc.on("custom-message", this.handleAIMessage);
50
+ this.trtc = (_d = this.service.roomEngine.instance) == null ? void 0 : _d.getTRTCCloud()._trtc;
51
+ this.trtc.on("custom-message", this.handleAIMessage);
51
52
  }
52
53
  handleUnmount() {
53
54
  var _a;
54
- this.subtitleMsg = [];
55
- this.subtitleText.value = "";
56
- this.transcriptionText.value = "";
55
+ this.subtitleMessages = {};
56
+ this.transcribedMessageList = [];
57
57
  (_a = this.trtc) == null ? void 0 : _a.off("custom-message", this.handleAIMessage);
58
58
  }
59
59
  bindEvent() {
60
60
  this.service.lifeCycleManager.on("mount", this.handleMount);
61
61
  this.service.lifeCycleManager.on("unmount", this.handleUnmount);
62
62
  }
63
- // todo trtc defines this type as any
63
+ resetSubtitleTimeout(id, fn) {
64
+ if (this.subtitleTimeout[id]) {
65
+ clearTimeout(this.subtitleTimeout[id]);
66
+ }
67
+ this.subtitleTimeout[id] = setTimeout(fn, 3e3);
68
+ }
64
69
  handleAIMessage(event) {
65
70
  if (event.cmdId !== 1) return;
66
71
  const data = new TextDecoder().decode(event.data);
67
72
  const jsonData = JSON.parse(data);
68
73
  this.handleMessage(jsonData);
69
74
  this.emit("transcription", {
70
- subtitleText: this.subtitleText,
71
- transcriptionText: this.transcriptionText,
72
- subtitleMsg: this.subtitleMsg
75
+ subtitleMessages: this.subtitleMessages,
76
+ transcribedMessageList: this.transcribedMessageList
73
77
  });
74
78
  }
75
79
  handleMessage(data) {
76
- const refreshSubtitle = () => {
77
- let displayText = "";
78
- for (let i = 0; i < this.subtitleMsg.length; i++) {
79
- displayText += `${this.service.roomStore.getDisplayName(this.subtitleMsg[i].userid)}: ${this.subtitleMsg[i].text}
80
- `;
81
- if (this.subtitleMsg[i].translation_text !== "") {
82
- displayText += `${this.service.roomStore.getDisplayName(this.subtitleMsg[i].userid)}: ${this.subtitleMsg[i].translation_text}
83
- `;
84
- }
85
- }
86
- this.subtitleText.value = displayText;
80
+ if (data.type !== ASR_EVENT_CODE) return;
81
+ const { sender, payload } = data;
82
+ const { end } = payload;
83
+ const createSubtitleMsg = () => {
84
+ return {
85
+ sender,
86
+ text: payload.text,
87
+ translationText: payload.translation_text,
88
+ startMsTs: data.start_ms_ts,
89
+ end
90
+ };
87
91
  };
88
- if (data.type === 1e4 && data.payload.end === false) {
89
- let exist = false;
90
- for (let i = 0; i < this.subtitleMsg.length; i++) {
91
- if (data.sender === this.subtitleMsg[i].userid) {
92
- this.subtitleMsg[i].text = data.payload.text;
93
- this.subtitleMsg[i].translation_text = data.payload.translation_text;
94
- exist = true;
95
- break;
96
- }
97
- }
98
- if (!exist) {
99
- this.subtitleMsg.push({
100
- userid: data.sender,
101
- text: data.payload.text,
102
- translation_text: data.payload.translation_text
103
- });
104
- }
105
- refreshSubtitle();
106
- } else if (data.type === 1e4 && data.payload.end === true) {
107
- for (let i = 0; i < this.subtitleMsg.length; i++) {
108
- if (data.sender === this.subtitleMsg[i].userid) {
109
- this.subtitleMsg[i].text = data.payload.text;
110
- this.subtitleMsg[i].translation_text = data.payload.translation_text;
111
- break;
112
- }
113
- }
114
- refreshSubtitle();
115
- let content = `${data.payload.start_time}->${data.payload.end_time} ${data.sender}: ${data.payload.text}
116
- `;
117
- if (data.payload.translation_text !== "") {
118
- content += `${data.payload.start_time}->${data.payload.end_time} ${data.sender}: ${data.payload.translation_text}
119
- `;
92
+ const updateMsg = (msg) => {
93
+ msg.text = payload.text;
94
+ msg.translationText = payload.translation_text;
95
+ msg.end = end;
96
+ };
97
+ const appendMsg = (msg, target) => {
98
+ if (Array.isArray(target)) {
99
+ target.push(msg);
100
+ } else if (typeof target === "object") {
101
+ const recordTarget = target;
102
+ recordTarget[msg.sender] = msg;
103
+ } else {
104
+ throw new Error("Invalid target type");
120
105
  }
121
- this.transcriptionText.value += content;
106
+ };
107
+ const existingSubtitle = this.subtitleMessages[sender];
108
+ if (existingSubtitle) {
109
+ updateMsg(existingSubtitle);
110
+ } else {
111
+ appendMsg(createSubtitleMsg(), this.subtitleMessages);
122
112
  }
123
- if (data.type === "subtitle") {
124
- let exist = false;
125
- for (let i = 0; i < this.subtitleMsg.length; i++) {
126
- if (data.userid === this.subtitleMsg[i].userid) {
127
- this.subtitleMsg[i].text = data.text;
128
- this.subtitleMsg[i].translation_text = data.translation_text;
129
- exist = true;
130
- break;
131
- }
132
- }
133
- if (!exist) {
134
- this.subtitleMsg.push({
135
- userid: data.userid,
136
- text: data.text,
137
- translation_text: data.translation_text
138
- });
139
- }
140
- refreshSubtitle();
141
- } else if (data.type === "transcription") {
142
- for (let i = 0; i < this.subtitleMsg.length; i++) {
143
- if (data.userid === this.subtitleMsg[i].userid) {
144
- this.subtitleMsg[i].text = data.text;
145
- this.subtitleMsg[i].translation_text = data.translation_text;
146
- break;
147
- }
148
- }
149
- refreshSubtitle();
150
- let content = `${formatTimestampToTime(data.start_ms_ts)}->${formatTimestampToTime(data.end_ms_ts)} ${this.service.roomStore.getDisplayName(data.userid)}: ${data.text}
151
- `;
152
- if (data.translation_text !== "") {
153
- content += `${formatTimestampToTime(data.start_ms_ts)}->${formatTimestampToTime(data.end_ms_ts)} ${this.service.roomStore.getDisplayName(data.userid)}: ${data.translation_text}
154
- `;
155
- }
156
- this.transcriptionText.value += content;
113
+ const transcriptionIndex = utils.findLastIndex(
114
+ this.transcribedMessageList,
115
+ (msg) => msg.sender === sender && !msg.end
116
+ );
117
+ if (transcriptionIndex !== -1) {
118
+ updateMsg(this.transcribedMessageList[transcriptionIndex]);
119
+ } else {
120
+ appendMsg(createSubtitleMsg(), this.transcribedMessageList);
157
121
  }
122
+ this.resetSubtitleTimeout(sender, () => {
123
+ if (!end) return;
124
+ delete this.subtitleMessages[sender];
125
+ this.emit("transcription", {
126
+ subtitleMessages: this.subtitleMessages,
127
+ transcribedMessageList: this.transcribedMessageList
128
+ });
129
+ });
158
130
  }
159
- StartAITranscription() {
160
- }
161
- }
162
- function formatTimestampToTime(timestamp) {
163
- const date = new Date(timestamp);
164
- const hours = date.getHours().toString().padStart(2, "0");
165
- const minutes = date.getMinutes().toString().padStart(2, "0");
166
- const seconds = date.getSeconds().toString().padStart(2, "0");
167
- return `${hours}:${minutes}:${seconds}`;
168
131
  }
169
132
  exports.AITask = AITask;
170
133
  exports.AI_TASK = AI_TASK;
@@ -10,7 +10,10 @@ export declare enum MetricsKey {
10
10
  disableScreenSharing = 106053,
11
11
  enableWatermark = 106054,
12
12
  enableVirtualBackground = 106055,
13
- hideFeatureButton = 106056
13
+ hideFeatureButton = 106056,
14
+ openChat = 106057,
15
+ setBasicBeauty = 106058,
16
+ aiTask = 106059
14
17
  }
15
18
  export declare class DataReportManager {
16
19
  private taskQueue;
@@ -18,6 +18,9 @@ var MetricsKey = /* @__PURE__ */ ((MetricsKey2) => {
18
18
  MetricsKey2[MetricsKey2["enableWatermark"] = 106054] = "enableWatermark";
19
19
  MetricsKey2[MetricsKey2["enableVirtualBackground"] = 106055] = "enableVirtualBackground";
20
20
  MetricsKey2[MetricsKey2["hideFeatureButton"] = 106056] = "hideFeatureButton";
21
+ MetricsKey2[MetricsKey2["openChat"] = 106057] = "openChat";
22
+ MetricsKey2[MetricsKey2["setBasicBeauty"] = 106058] = "setBasicBeauty";
23
+ MetricsKey2[MetricsKey2["aiTask"] = 106059] = "aiTask";
21
24
  return MetricsKey2;
22
25
  })(MetricsKey || {});
23
26
  class DataReportManager {
@@ -60,3 +60,5 @@ export declare function convertSecondsToHMS(seconds: number): {
60
60
  seconds: number;
61
61
  };
62
62
  export declare function getNanoId(size?: number): string;
63
+ export declare function findLastIndex<T>(array: T[], predicate: (value: T, index: number, obj: T[]) => boolean, thisArg?: any): number;
64
+ export declare function formatTimestampToTime(timestamp: number, format?: string): string;
@@ -147,12 +147,45 @@ function getNanoId(size = 21) {
147
147
  }
148
148
  return id;
149
149
  }
150
+ function findLastIndex(array, predicate, thisArg) {
151
+ const len = array.length >>> 0;
152
+ let k = len - 1;
153
+ while (k >= 0) {
154
+ const kValue = array[k];
155
+ if (predicate.call(thisArg, kValue, k, array)) {
156
+ return k;
157
+ }
158
+ k = k - 1;
159
+ }
160
+ return -1;
161
+ }
162
+ function formatTimestampToTime(timestamp, format = "MM-DD HH:mm") {
163
+ const date = new Date(timestamp);
164
+ const padStart = (value, length = 2) => value.toString().padStart(length, "0");
165
+ const replacements = {
166
+ YYYY: date.getFullYear().toString(),
167
+ YY: (date.getFullYear() % 100).toString().padStart(2, "0"),
168
+ MM: padStart(date.getMonth() + 1),
169
+ DD: padStart(date.getDate()),
170
+ HH: padStart(date.getHours()),
171
+ hh: padStart(date.getHours() % 12),
172
+ mm: padStart(date.getMinutes()),
173
+ ss: padStart(date.getSeconds()),
174
+ A: date.getHours() >= 12 ? "PM" : "AM"
175
+ };
176
+ return format.replace(
177
+ /YYYY|YY|MM|DD|HH|hh|mm|ss|A/g,
178
+ (match) => replacements[match]
179
+ );
180
+ }
150
181
  exports.addSuffix = addSuffix;
151
182
  exports.calculateByteLength = calculateByteLength;
152
183
  exports.convertSecondsToHMS = convertSecondsToHMS;
153
184
  exports.debounce = debounce;
154
185
  exports.deepClone = deepClone;
155
186
  exports.exitFullScreen = exitFullScreen;
187
+ exports.findLastIndex = findLastIndex;
188
+ exports.formatTimestampToTime = formatTimestampToTime;
156
189
  exports.getNanoId = getNanoId;
157
190
  exports.getUrlParam = getUrlParam;
158
191
  exports.getUrlWithRoomId = getUrlWithRoomId;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tencentcloud/roomkit-electron-vue3",
3
- "version": "2.7.0",
3
+ "version": "2.7.1",
4
4
  "main": "./lib/index.js",
5
5
  "module": "./es/index.mjs",
6
6
  "types": "./es/index.d.ts",
@@ -20,7 +20,7 @@
20
20
  "dependencies": {
21
21
  "@tencentcloud/chat": "latest",
22
22
  "@tencentcloud/tui-core": "latest",
23
- "@tencentcloud/tuiroom-engine-electron": "^2.7.0",
23
+ "@tencentcloud/tuiroom-engine-electron": "^2.7.1",
24
24
  "@tencentcloud/chat-uikit-engine": "2.2.7",
25
25
  "@tencentcloud/chat-uikit-vue": "2.2.7",
26
26
  "@tencentcloud/universal-api": "^2.0.9",
@@ -1,37 +1,40 @@
1
1
  <template>
2
2
  <div v-if="subtitleLines.length" class="ai-subtitles">
3
- <div v-for="(line, index) in subtitleLines" :key="index">{{ line }}</div>
3
+ <div v-for="line in subtitleLines" :key="line.sender">
4
+ {{
5
+ `${roomService.roomStore.getDisplayName(line.sender)}: ${line?.text}`
6
+ }}
7
+ </div>
4
8
  </div>
5
9
  </template>
6
10
 
7
11
  <script setup lang="ts">
8
- import { ref, computed, onUnmounted } from 'vue';
9
- import { roomService, AI_TASK, AITaskEvent } from '../../services';
10
-
11
- const rawSubtitleText = ref('');
12
- const subtitleLines = computed(() =>
13
- rawSubtitleText.value.split('\n').filter(item => item)
14
- );
15
-
16
- let subtitleTimeout: ReturnType<typeof setTimeout> | null = null;
17
-
18
- const resetSubtitleTimeout = () => {
19
- if (subtitleTimeout) {
20
- clearTimeout(subtitleTimeout);
21
- }
22
-
23
- subtitleTimeout = setTimeout(() => {
24
- rawSubtitleText.value = '';
25
- }, 3000);
26
- };
12
+ import { computed, onMounted, onUnmounted, ref } from 'vue';
13
+ import {
14
+ AI_TASK,
15
+ AITaskEvent,
16
+ MetricsKey,
17
+ roomService,
18
+ SubtitleMessage,
19
+ } from '../../services';
20
+
21
+ const subtitleMessages = ref<Record<string, SubtitleMessage>>({});
22
+ const subtitleLines = computed(() => {
23
+ const arr = Object.keys(subtitleMessages.value).map(userId => {
24
+ return subtitleMessages.value[userId];
25
+ });
26
+ return arr.sort((a, b) => a.startMsTs - b.startMsTs);
27
+ });
27
28
 
28
29
  const handleAISubtitles = (data?: AITaskEvent[AI_TASK.TRANSCRIPTION_TASK]) => {
29
30
  if (!data) return;
30
- rawSubtitleText.value = data.subtitleText.value;
31
- resetSubtitleTimeout();
31
+ subtitleMessages.value = Object.assign({}, data.subtitleMessages);
32
32
  };
33
33
 
34
- roomService.aiTask.on(AI_TASK.TRANSCRIPTION_TASK, handleAISubtitles);
34
+ onMounted(() => {
35
+ roomService.dataReportManager.reportCount(MetricsKey.AITask);
36
+ roomService.aiTask.on(AI_TASK.TRANSCRIPTION_TASK, handleAISubtitles);
37
+ });
35
38
 
36
39
  onUnmounted(() => {
37
40
  roomService.aiTask.off(AI_TASK.TRANSCRIPTION_TASK, handleAISubtitles);
@@ -1,141 +1,138 @@
1
1
  <template>
2
- <div class="conversation" ref="conversationContainer">
2
+ <div
3
+ class="conversation"
4
+ ref="conversationContainerRef"
5
+ @scroll="handleScroll"
6
+ >
3
7
  <div
4
- v-for="(item, index) in conversations"
5
- :key="index"
6
- class="conversation-item"
8
+ v-for="group in transcribedMessageList"
9
+ :key="group.startMsTs"
10
+ class="conversation-group"
7
11
  >
8
12
  <div class="title">
9
- <span class="speaker">{{ item.speaker }}</span>
10
- <span class="timestamp">{{ item.timestamp }}</span>
13
+ <span class="speaker">{{
14
+ roomService.roomStore.getDisplayName(group.sender)
15
+ }}</span>
16
+ <span class="timestamp">
17
+ {{ formatTimestampToTime(group.startMsTs) }}
18
+ </span>
11
19
  </div>
12
- <div class="content">
13
- <div v-for="(msg, msgIndex) in item.messages" :key="msgIndex">
14
- {{ msg }}
15
- </div>
20
+ <div
21
+ v-for="(message, messageIndex) in group.messages"
22
+ :key="messageIndex"
23
+ class="content"
24
+ >
25
+ {{ message.text }}
16
26
  </div>
17
27
  </div>
18
28
  </div>
19
29
  </template>
20
30
 
21
31
  <script setup lang="ts">
22
- import { ref, computed, onMounted, nextTick } from 'vue';
23
- import { roomService, AI_TASK } from '../../services';
24
-
25
- const conversationContainer = ref<HTMLElement | null>(null);
26
-
27
- onMounted(() => scrollToBottom());
28
-
29
- const processedConversations = ref<
30
- {
31
- timestamp: string;
32
- speaker: string;
33
- content: string;
34
- }[]
35
- >(processConversation(roomService.aiTask.transcriptionText.value));
32
+ import { ref, onMounted, computed, nextTick, watch, onUnmounted } from 'vue';
33
+ import {
34
+ roomService,
35
+ AI_TASK,
36
+ SubtitleMessage,
37
+ MetricsKey,
38
+ } from '../../services';
39
+ import { formatTimestampToTime } from '../../utils/utils.ts';
40
+
41
+ const conversationContainerRef = ref<HTMLElement>();
42
+ const isUserScrolling = ref(false);
43
+ const timeInterval = 60 * 1000;
44
+ const rawTranscribedMessageList = ref<SubtitleMessage[]>(
45
+ roomService.aiTask.transcribedMessageList
46
+ );
47
+
48
+ onMounted(() => {
49
+ scrollToBottom();
50
+ });
36
51
 
37
52
  const scrollToBottom = async () => {
38
- if (conversationContainer.value) {
53
+ if (conversationContainerRef.value && !isUserScrolling.value) {
39
54
  await nextTick();
40
- conversationContainer.value.scrollIntoView({ block: 'end' });
55
+ conversationContainerRef.value.scrollTop =
56
+ conversationContainerRef.value.scrollHeight;
41
57
  }
42
58
  };
43
59
 
44
- const conversations = computed(() => {
45
- const result = [];
46
- let currentSpeaker: string | null = null;
47
- let currentTimestamp: string | null = null;
48
- let currentMessages: string[] = [];
60
+ const handleScroll = () => {
61
+ if (conversationContainerRef.value) {
62
+ const isAtBottom =
63
+ conversationContainerRef.value.scrollTop +
64
+ conversationContainerRef.value.clientHeight >=
65
+ conversationContainerRef.value.scrollHeight - 5;
66
+ if (isAtBottom) {
67
+ isUserScrolling.value = false;
68
+ scrollToBottom();
69
+ } else {
70
+ isUserScrolling.value = true;
71
+ }
72
+ }
73
+ };
49
74
 
50
- processedConversations.value.forEach(item => {
51
- const [start, end] = item.timestamp.split('->');
52
- const timestampMinute = start.slice(0, 5); // Take the "HH:MM" part
75
+ const transcribedMessageList = computed(() => {
76
+ const aggregatedMessageList: {
77
+ sender: string;
78
+ startMsTs: number;
79
+ messages: SubtitleMessage[];
80
+ }[] = [];
81
+ let currentAggregatedMessage: {
82
+ sender: string;
83
+ startMsTs: number;
84
+ messages: SubtitleMessage[];
85
+ } | null = null;
53
86
 
87
+ for (const message of rawTranscribedMessageList.value) {
54
88
  if (
55
- item.speaker === currentSpeaker &&
56
- timestampMinute === currentTimestamp
89
+ !currentAggregatedMessage ||
90
+ message.sender !== currentAggregatedMessage.sender ||
91
+ message.startMsTs - currentAggregatedMessage.startMsTs > timeInterval
57
92
  ) {
58
- // Aggregate messages from the same speaker and within the same minute
59
- currentMessages.push(item.content);
93
+ currentAggregatedMessage = {
94
+ messages: [message],
95
+ sender: message.sender,
96
+ startMsTs: message.startMsTs,
97
+ };
98
+ aggregatedMessageList.push(currentAggregatedMessage);
60
99
  } else {
61
- // New speakers or different minutes, save previous aggregation results
62
- if (currentSpeaker !== null) {
63
- result.push({
64
- timestamp: currentTimestamp,
65
- speaker: currentSpeaker,
66
- messages: currentMessages,
67
- });
68
- }
69
- // Update active speaker, timestamp, and message
70
- currentSpeaker = item.speaker;
71
- currentTimestamp = timestampMinute;
72
- currentMessages = [item.content];
100
+ currentAggregatedMessage.messages.push(message);
73
101
  }
74
- });
75
-
76
- // Save the last set of aggregation results
77
- if (currentSpeaker !== null) {
78
- result.push({
79
- timestamp: currentTimestamp,
80
- speaker: currentSpeaker,
81
- messages: currentMessages,
82
- });
83
102
  }
84
103
 
85
- return result;
104
+ return aggregatedMessageList;
86
105
  });
87
106
 
88
- roomService.aiTask.on(AI_TASK.TRANSCRIPTION_TASK, data => {
89
- if (!data) return;
90
- processedConversations.value = processConversation(
91
- data.transcriptionText.value
92
- );
107
+ watch(rawTranscribedMessageList, () => {
108
+ scrollToBottom();
93
109
  });
94
110
 
95
- function processConversation(data: string) {
96
- const pattern =
97
- /(\d{2}:\d{2}:\d{2}->\d{2}:\d{2}:\d{2})\s+([\u4e00-\u9fa5\w]+):\s*(.*)/;
98
-
99
- const conversations: {
100
- timestamp: string;
101
- speaker: string;
102
- content: string;
103
- }[] = [];
104
-
105
- const lines = data.split('\n');
106
-
107
- lines.forEach(line => {
108
- const match = pattern.exec(line);
109
- if (match) {
110
- const timestamp = match[1];
111
- const speaker = match[2];
112
- const content = match[3];
113
- conversations.push({
114
- timestamp,
115
- speaker,
116
- content,
117
- });
118
- }
119
- });
120
-
121
- conversations.sort((a, b) => {
122
- const timeA = a.timestamp.split('->')[0];
123
- const timeB = b.timestamp.split('->')[0];
124
- return timeA.localeCompare(timeB);
125
- });
126
-
127
- return conversations;
128
- }
111
+ const handleAITranscriptionTask = async (data?: {
112
+ subtitleMessages: { [key: string]: SubtitleMessage };
113
+ transcribedMessageList: SubtitleMessage[];
114
+ }) => {
115
+ if (!data) return;
116
+ rawTranscribedMessageList.value = [...data.transcribedMessageList];
117
+ };
118
+ onMounted(() => {
119
+ roomService.dataReportManager.reportCount(MetricsKey.AITask);
120
+ roomService.aiTask.on(AI_TASK.TRANSCRIPTION_TASK, handleAITranscriptionTask);
121
+ });
122
+ onUnmounted(() => {
123
+ roomService.aiTask.off(AI_TASK.TRANSCRIPTION_TASK, handleAITranscriptionTask);
124
+ });
129
125
  </script>
130
126
 
131
127
  <style scoped lang="scss">
132
128
  .conversation {
129
+ height: 100%;
133
130
  padding: 20px;
134
131
  overflow-y: auto;
135
132
  }
136
133
 
137
- .conversation-item {
138
- margin-bottom: 10px;
134
+ .conversation-group {
135
+ margin-bottom: 20px;
139
136
  }
140
137
 
141
138
  .title {
@@ -147,10 +144,18 @@ function processConversation(data: string) {
147
144
  line-height: 22px;
148
145
  color: var(--font-color-4);
149
146
  text-align: left;
147
+
148
+ .speaker {
149
+ max-width: 150px;
150
+ overflow: hidden;
151
+ text-overflow: ellipsis;
152
+ white-space: nowrap;
153
+ }
150
154
  }
151
155
 
152
156
  .content {
153
157
  padding: 8px;
158
+ margin-top: 5px;
154
159
  font-size: 14px;
155
160
  font-weight: 400;
156
161
  line-height: 22px;
@@ -1,10 +1,10 @@
1
1
  <template>
2
2
  <div class="member-control-container">
3
3
  <tui-button
4
+ v-if="singleControl"
4
5
  class="button"
5
6
  size="default"
6
7
  @click="singleControl.func(props.userInfo)"
7
- v-if="!isCanOperateMySelf"
8
8
  >
9
9
  {{ singleControl?.title }}
10
10
  </tui-button>
@@ -105,9 +105,14 @@ const {
105
105
 
106
106
  const { isCanOperateMySelf } = useMemberItemHooks(props.userInfo);
107
107
 
108
- const singleControl = computed(() => controlList.value[0]);
108
+ const singleControl = computed(() => {
109
+ return isCanOperateMySelf.value ? null : controlList.value[0];
110
+ });
111
+
109
112
  const moreControlList = computed(() => {
110
- return isCanOperateMySelf ? controlList.value : controlList.value.slice(1);
113
+ return isCanOperateMySelf.value
114
+ ? controlList.value
115
+ : controlList.value.slice(1);
111
116
  });
112
117
  const dropdownClass = ref('down');
113
118
  const moreBtnRef = ref();
@@ -221,7 +221,7 @@ const iconList = computed(() => {
221
221
  height: 100%;
222
222
  color: var(--icon-color);
223
223
 
224
- .state-icon .member-invite {
224
+ .state-icon {
225
225
  margin-left: 16px;
226
226
  }
227
227