@foundbyte/uni-agent 1.0.0-alpha.0
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/bubble-list/index.scss +254 -0
- package/components/bubble-list/index.vue +505 -0
- package/components/bubble-list/types.ts +110 -0
- package/components/bubble-list/use-typewriter.ts +193 -0
- package/components/bubble-wrapper/index.scss +34 -0
- package/components/bubble-wrapper/index.vue +97 -0
- package/components/history/index.scss +87 -0
- package/components/history/index.vue +118 -0
- package/components/history/types.ts +16 -0
- package/components/index.ts +4 -0
- package/components/prompts/index.scss +30 -0
- package/components/prompts/index.vue +46 -0
- package/components/prompts/types.ts +11 -0
- package/components/sender/image-picker.ts +100 -0
- package/components/sender/index.scss +288 -0
- package/components/sender/index.vue +374 -0
- package/components/sender/types.ts +56 -0
- package/composables/index.ts +6 -0
- package/composables/use-inner-audio/audio-manager.ts +22 -0
- package/composables/use-inner-audio/enums.ts +15 -0
- package/composables/use-inner-audio/types.ts +8 -0
- package/composables/use-inner-audio/use-inner-audio.ts +149 -0
- package/composables/use-prefix.ts +54 -0
- package/composables/use-recorder/h5-recorder-manager.ts +492 -0
- package/composables/use-recorder/native-recorder-manager.ts +181 -0
- package/composables/use-recorder/recorder-manager.ts +40 -0
- package/composables/use-recorder/types.ts +73 -0
- package/composables/use-recorder/use-recorder.ts +205 -0
- package/configs/index.ts +5 -0
- package/index.ts +3 -0
- package/package.json +90 -0
- package/styles/_base.scss +8 -0
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 录音相关类型定义
|
|
3
|
+
* 供 recorder-manager.ts 和 use-recorder.ts 共享使用
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
/** 录音停止回调结果(原生平台格式) */
|
|
7
|
+
export interface RecorderStopResult {
|
|
8
|
+
/** 录音文件的临时路径 */
|
|
9
|
+
tempFilePath: string
|
|
10
|
+
/** 录音时长,单位 s(原生平台返回秒) */
|
|
11
|
+
duration: number
|
|
12
|
+
/** 录音文件大小,单位 Byte */
|
|
13
|
+
fileSize: number
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/** 录音结果(use-recorder 使用的格式) */
|
|
17
|
+
export interface RecorderResult {
|
|
18
|
+
/** 录音文件的临时路径 */
|
|
19
|
+
tempFilePath: string
|
|
20
|
+
/** 录音时长,单位 ms(转换为毫秒) */
|
|
21
|
+
duration: number
|
|
22
|
+
/** 录音文件大小,单位 Byte */
|
|
23
|
+
fileSize: number
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/** 录音错误回调结果 */
|
|
27
|
+
export interface RecorderErrorResult {
|
|
28
|
+
/** 错误信息 */
|
|
29
|
+
errMsg: string
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/** 录音帧回调结果 */
|
|
33
|
+
export interface RecorderFrameResult {
|
|
34
|
+
/** 录音帧数据 */
|
|
35
|
+
frameBuffer: Blob
|
|
36
|
+
/** 是否最后一帧 */
|
|
37
|
+
isLastFrame: boolean
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/** 开始录音回调函数类型 */
|
|
41
|
+
export type StartCallback = () => void
|
|
42
|
+
|
|
43
|
+
/** 暂停录音回调函数类型 */
|
|
44
|
+
export type PauseCallback = () => void
|
|
45
|
+
|
|
46
|
+
/** 继续录音回调函数类型 */
|
|
47
|
+
export type ResumeCallback = () => void
|
|
48
|
+
|
|
49
|
+
/** 停止录音回调函数类型 */
|
|
50
|
+
export type StopCallback = (result: RecorderStopResult) => void
|
|
51
|
+
|
|
52
|
+
/** 录音错误回调函数类型 */
|
|
53
|
+
export type ErrorCallback = (error: RecorderErrorResult) => void
|
|
54
|
+
|
|
55
|
+
/** 录音帧录制回调函数类型 */
|
|
56
|
+
export type FrameRecordedCallback = (result: RecorderFrameResult) => void
|
|
57
|
+
|
|
58
|
+
/** 录音中断回调函数类型 */
|
|
59
|
+
export type InterruptionCallback = () => void
|
|
60
|
+
|
|
61
|
+
export type RecorderManagerOptions = UniApp.RecorderManagerStartOptions
|
|
62
|
+
|
|
63
|
+
/** 回调函数集合 */
|
|
64
|
+
export interface RecorderCallbacks {
|
|
65
|
+
onStart: Array<() => void>
|
|
66
|
+
onPause: Array<() => void>
|
|
67
|
+
onResume: Array<() => void>
|
|
68
|
+
onStop: Array<(result: RecorderStopResult) => void>
|
|
69
|
+
onError: Array<(error: RecorderErrorResult) => void>
|
|
70
|
+
onFrameRecorded: Array<(result: RecorderFrameResult) => void>
|
|
71
|
+
onInterruptionBegin: Array<() => void>
|
|
72
|
+
onInterruptionEnd: Array<() => void>
|
|
73
|
+
}
|
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
/* eslint-disable no-restricted-imports */
|
|
2
|
+
import { onUnmounted, ref } from 'vue'
|
|
3
|
+
import { getRecorderManager } from './recorder-manager'
|
|
4
|
+
import type { RecorderManagerOptions, RecorderResult } from './types'
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* 录音功能 Hooks
|
|
8
|
+
* @returns 录音控制方法和状态
|
|
9
|
+
*
|
|
10
|
+
* @example
|
|
11
|
+
* const { isRecording, recordingTime, start, stop, cancel } = useRecorder()
|
|
12
|
+
*
|
|
13
|
+
* // 开始录音
|
|
14
|
+
* await start({
|
|
15
|
+
* duration: 60000,
|
|
16
|
+
* format: 'aac'
|
|
17
|
+
* })
|
|
18
|
+
*
|
|
19
|
+
* // 结束录音并获取结果
|
|
20
|
+
* const result = await stop()
|
|
21
|
+
* if (result) {
|
|
22
|
+
* console.log('录音文件路径:', result.tempFilePath)
|
|
23
|
+
* console.log('录音时长:', result.duration)
|
|
24
|
+
* }
|
|
25
|
+
*/
|
|
26
|
+
export function useRecorder() {
|
|
27
|
+
// 录音管理器实例
|
|
28
|
+
const recorderManager = getRecorderManager()
|
|
29
|
+
|
|
30
|
+
// 是否正在录音
|
|
31
|
+
const isRecording = ref(false)
|
|
32
|
+
|
|
33
|
+
// 录音时长(毫秒)
|
|
34
|
+
const recordingTime = ref(0)
|
|
35
|
+
|
|
36
|
+
// 录音计时器
|
|
37
|
+
let timer: ReturnType<typeof setInterval> | null = null
|
|
38
|
+
|
|
39
|
+
// 录音完成的 Promise 解析函数
|
|
40
|
+
let resolvePromise: ((value: RecorderResult | null) => void) | null = null
|
|
41
|
+
|
|
42
|
+
// 录音失败的 Promise 拒绝函数
|
|
43
|
+
let rejectPromise: ((reason: Error) => void) | null = null
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* 开始计时
|
|
47
|
+
*/
|
|
48
|
+
const startTimer = () => {
|
|
49
|
+
recordingTime.value = 0
|
|
50
|
+
timer = setInterval(() => {
|
|
51
|
+
recordingTime.value += 100
|
|
52
|
+
}, 100)
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* 停止计时
|
|
57
|
+
*/
|
|
58
|
+
const stopTimer = () => {
|
|
59
|
+
if (timer) {
|
|
60
|
+
clearInterval(timer)
|
|
61
|
+
timer = null
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* 录音停止事件处理
|
|
67
|
+
*/
|
|
68
|
+
recorderManager.onStop((res) => {
|
|
69
|
+
isRecording.value = false
|
|
70
|
+
stopTimer()
|
|
71
|
+
|
|
72
|
+
if (resolvePromise) {
|
|
73
|
+
resolvePromise({
|
|
74
|
+
tempFilePath: res.tempFilePath,
|
|
75
|
+
duration: res.duration * 1000, // 转换为毫秒
|
|
76
|
+
fileSize: res.fileSize,
|
|
77
|
+
})
|
|
78
|
+
resolvePromise = null
|
|
79
|
+
rejectPromise = null
|
|
80
|
+
}
|
|
81
|
+
})
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* 录音错误事件处理
|
|
85
|
+
*/
|
|
86
|
+
recorderManager.onError((err) => {
|
|
87
|
+
isRecording.value = false
|
|
88
|
+
stopTimer()
|
|
89
|
+
|
|
90
|
+
console.error('录音错误:', err)
|
|
91
|
+
|
|
92
|
+
if (rejectPromise) {
|
|
93
|
+
rejectPromise(new Error(err.errMsg || '录音发生错误'))
|
|
94
|
+
resolvePromise = null
|
|
95
|
+
rejectPromise = null
|
|
96
|
+
}
|
|
97
|
+
})
|
|
98
|
+
|
|
99
|
+
// 用于标记是否已处理开始回调
|
|
100
|
+
let startHandled = false
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* 开始录音
|
|
104
|
+
* @param options 录音配置选项
|
|
105
|
+
*/
|
|
106
|
+
const start = async (options: RecorderManagerOptions = {}): Promise<void> => {
|
|
107
|
+
if (isRecording.value) {
|
|
108
|
+
throw new Error('正在录音中,请先停止当前录音')
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
const {
|
|
112
|
+
duration = 60000,
|
|
113
|
+
sampleRate = 16000,
|
|
114
|
+
numberOfChannels = 1,
|
|
115
|
+
encodeBitRate = 64000,
|
|
116
|
+
format = 'aac',
|
|
117
|
+
frameSize,
|
|
118
|
+
} = options
|
|
119
|
+
|
|
120
|
+
return new Promise((resolve, reject) => {
|
|
121
|
+
startHandled = false
|
|
122
|
+
|
|
123
|
+
// 监听录音开始成功事件
|
|
124
|
+
const onStart = () => {
|
|
125
|
+
if (startHandled) return
|
|
126
|
+
startHandled = true
|
|
127
|
+
isRecording.value = true
|
|
128
|
+
startTimer()
|
|
129
|
+
resolve()
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
// 监听录音开始失败事件
|
|
133
|
+
const onStartError = (err: { errMsg: string }) => {
|
|
134
|
+
if (startHandled) return
|
|
135
|
+
startHandled = true
|
|
136
|
+
reject(new Error(err.errMsg || '启动录音失败'))
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
// 使用 once 方式监听,通过标志位防止重复触发
|
|
140
|
+
recorderManager.onStart(onStart)
|
|
141
|
+
recorderManager.onError(onStartError)
|
|
142
|
+
|
|
143
|
+
// 开始录音
|
|
144
|
+
recorderManager.start({
|
|
145
|
+
duration,
|
|
146
|
+
sampleRate,
|
|
147
|
+
numberOfChannels,
|
|
148
|
+
encodeBitRate,
|
|
149
|
+
format,
|
|
150
|
+
frameSize,
|
|
151
|
+
})
|
|
152
|
+
})
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
/**
|
|
156
|
+
* 结束录音
|
|
157
|
+
* @returns 录音结果,包含临时文件路径、时长和文件大小
|
|
158
|
+
*/
|
|
159
|
+
const stop = async (): Promise<RecorderResult | null> => {
|
|
160
|
+
if (!isRecording.value) {
|
|
161
|
+
console.warn('当前没有在录音')
|
|
162
|
+
return null
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
return new Promise((resolve, reject) => {
|
|
166
|
+
resolvePromise = resolve
|
|
167
|
+
rejectPromise = reject
|
|
168
|
+
recorderManager.stop()
|
|
169
|
+
})
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
/**
|
|
173
|
+
* 取消录音
|
|
174
|
+
*/
|
|
175
|
+
const cancel = (): void => {
|
|
176
|
+
if (!isRecording.value) {
|
|
177
|
+
return
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
// 设置标志,让 onStop 回调返回 null
|
|
181
|
+
resolvePromise = () => {
|
|
182
|
+
// 取消时返回 null
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
stopTimer()
|
|
186
|
+
isRecording.value = false
|
|
187
|
+
recorderManager.stop()
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
// 组件卸载时清理资源
|
|
191
|
+
onUnmounted(() => {
|
|
192
|
+
stopTimer()
|
|
193
|
+
if (isRecording.value) {
|
|
194
|
+
recorderManager.stop()
|
|
195
|
+
}
|
|
196
|
+
})
|
|
197
|
+
|
|
198
|
+
return {
|
|
199
|
+
isRecording,
|
|
200
|
+
recordingTime,
|
|
201
|
+
start,
|
|
202
|
+
stop,
|
|
203
|
+
cancel,
|
|
204
|
+
}
|
|
205
|
+
}
|
package/configs/index.ts
ADDED
package/index.ts
ADDED
package/package.json
ADDED
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@foundbyte/uni-agent",
|
|
3
|
+
"version": "1.0.0-alpha.0",
|
|
4
|
+
"private": false,
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "index.ts",
|
|
7
|
+
"module": "index.ts",
|
|
8
|
+
"browser": "index.ts",
|
|
9
|
+
"types": "index.d.ts",
|
|
10
|
+
"publishConfig": {
|
|
11
|
+
"access": "public"
|
|
12
|
+
},
|
|
13
|
+
"engines": {
|
|
14
|
+
"HBuilderX": "^4.07",
|
|
15
|
+
"uni-app": "^4.07",
|
|
16
|
+
"uni-app-x": ""
|
|
17
|
+
},
|
|
18
|
+
"uni_modules": {
|
|
19
|
+
"dependencies": [],
|
|
20
|
+
"encrypt": [],
|
|
21
|
+
"platforms": {
|
|
22
|
+
"cloud": {
|
|
23
|
+
"tcb": "√",
|
|
24
|
+
"aliyun": "√",
|
|
25
|
+
"alipay": "√"
|
|
26
|
+
},
|
|
27
|
+
"client": {
|
|
28
|
+
"uni-app": {
|
|
29
|
+
"vue": {
|
|
30
|
+
"vue2": "x",
|
|
31
|
+
"vue3": "√"
|
|
32
|
+
},
|
|
33
|
+
"web": {
|
|
34
|
+
"safari": "√",
|
|
35
|
+
"chrome": "√"
|
|
36
|
+
},
|
|
37
|
+
"app": {
|
|
38
|
+
"vue": "√",
|
|
39
|
+
"nvue": "-",
|
|
40
|
+
"android": "√",
|
|
41
|
+
"ios": "√",
|
|
42
|
+
"harmony": "√"
|
|
43
|
+
},
|
|
44
|
+
"mp": {
|
|
45
|
+
"weixin": "√",
|
|
46
|
+
"alipay": "√",
|
|
47
|
+
"toutiao": "√",
|
|
48
|
+
"baidu": "-",
|
|
49
|
+
"kuaishou": "-",
|
|
50
|
+
"jd": "-",
|
|
51
|
+
"harmony": "√",
|
|
52
|
+
"qq": "√",
|
|
53
|
+
"lark": "-",
|
|
54
|
+
"xhs": "-"
|
|
55
|
+
},
|
|
56
|
+
"quickapp": {
|
|
57
|
+
"huawei": "-",
|
|
58
|
+
"union": "-"
|
|
59
|
+
}
|
|
60
|
+
},
|
|
61
|
+
"uni-app-x": {
|
|
62
|
+
"web": {
|
|
63
|
+
"safari": "-",
|
|
64
|
+
"chrome": "-"
|
|
65
|
+
},
|
|
66
|
+
"app": {
|
|
67
|
+
"android": "-",
|
|
68
|
+
"ios": "-",
|
|
69
|
+
"harmony": "-"
|
|
70
|
+
},
|
|
71
|
+
"mp": {
|
|
72
|
+
"weixin": "-"
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
},
|
|
78
|
+
"dependencies": {
|
|
79
|
+
"vue": "3.5.30",
|
|
80
|
+
"@foundbyte/uni-types": "^1.0.0",
|
|
81
|
+
"@foundbyte/uni-utils": "^1.1.0"
|
|
82
|
+
},
|
|
83
|
+
"devDependencies": {
|
|
84
|
+
"@dcloudio/types": "3.4.30"
|
|
85
|
+
},
|
|
86
|
+
"scripts": {
|
|
87
|
+
"publish:npm": "pnpm publish",
|
|
88
|
+
"sync:sync": "cnpm sync @foundbyte/uni-agent"
|
|
89
|
+
}
|
|
90
|
+
}
|