@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.
Files changed (32) hide show
  1. package/components/bubble-list/index.scss +254 -0
  2. package/components/bubble-list/index.vue +505 -0
  3. package/components/bubble-list/types.ts +110 -0
  4. package/components/bubble-list/use-typewriter.ts +193 -0
  5. package/components/bubble-wrapper/index.scss +34 -0
  6. package/components/bubble-wrapper/index.vue +97 -0
  7. package/components/history/index.scss +87 -0
  8. package/components/history/index.vue +118 -0
  9. package/components/history/types.ts +16 -0
  10. package/components/index.ts +4 -0
  11. package/components/prompts/index.scss +30 -0
  12. package/components/prompts/index.vue +46 -0
  13. package/components/prompts/types.ts +11 -0
  14. package/components/sender/image-picker.ts +100 -0
  15. package/components/sender/index.scss +288 -0
  16. package/components/sender/index.vue +374 -0
  17. package/components/sender/types.ts +56 -0
  18. package/composables/index.ts +6 -0
  19. package/composables/use-inner-audio/audio-manager.ts +22 -0
  20. package/composables/use-inner-audio/enums.ts +15 -0
  21. package/composables/use-inner-audio/types.ts +8 -0
  22. package/composables/use-inner-audio/use-inner-audio.ts +149 -0
  23. package/composables/use-prefix.ts +54 -0
  24. package/composables/use-recorder/h5-recorder-manager.ts +492 -0
  25. package/composables/use-recorder/native-recorder-manager.ts +181 -0
  26. package/composables/use-recorder/recorder-manager.ts +40 -0
  27. package/composables/use-recorder/types.ts +73 -0
  28. package/composables/use-recorder/use-recorder.ts +205 -0
  29. package/configs/index.ts +5 -0
  30. package/index.ts +3 -0
  31. package/package.json +90 -0
  32. package/styles/_base.scss +8 -0
@@ -0,0 +1,100 @@
1
+ import type { ChooseImageOptions, ChooseImageResult } from './types'
2
+
3
+ export interface ChooseImageCallbacks {
4
+ /** 选择成功回调 */
5
+ onSuccess?: (result: ChooseImageResult) => void
6
+ /** 选择失败回调 */
7
+ onError?: (error: string) => void
8
+ /** 用户取消回调 */
9
+ onCancel?: () => void
10
+ }
11
+
12
+ /**
13
+ * 选择图片 - 兼容小程序和APP
14
+ * @param options 图片选择选项
15
+ * @param callbacks 回调函数
16
+ */
17
+ export function chooseImage(
18
+ options: ChooseImageOptions = {},
19
+ callbacks: ChooseImageCallbacks = {},
20
+ ): void {
21
+ const { count = 9, sizeType = ['compressed'], sourceType = ['album', 'camera'] } = options
22
+
23
+ const { onSuccess, onError, onCancel } = callbacks
24
+
25
+ uni.chooseImage({
26
+ count,
27
+ sizeType,
28
+ sourceType,
29
+ success: (res) => {
30
+ // 处理 tempFilePaths - 可能是 string 或 string[]
31
+ const tempFilePaths = Array.isArray(res.tempFilePaths)
32
+ ? res.tempFilePaths
33
+ : [res.tempFilePaths]
34
+
35
+ // 处理 tempFiles - 可能是对象或对象数组
36
+ const tempFilesRaw = Array.isArray(res.tempFiles) ? res.tempFiles : [res.tempFiles]
37
+ const tempFiles = tempFilesRaw.map((file: any) => ({
38
+ path: file.path,
39
+ size: file.size,
40
+ }))
41
+
42
+ const result: ChooseImageResult = {
43
+ tempFilePaths,
44
+ tempFiles,
45
+ }
46
+
47
+ onSuccess?.(result)
48
+ },
49
+ fail: (err) => {
50
+ // 用户取消选择
51
+ if (err.errMsg?.includes('cancel')) {
52
+ onCancel?.()
53
+ return
54
+ }
55
+ onError?.(err.errMsg || '选择图片失败')
56
+ },
57
+ })
58
+ }
59
+
60
+ /**
61
+ * 选择图片 - Promise 版本
62
+ * @param options 图片选择选项
63
+ * @returns Promise<ChooseImageResult>
64
+ *
65
+ * @example
66
+ * ```ts
67
+ * try {
68
+ * const result = await chooseImageAsync({ count: 1, sourceType: ['camera'] })
69
+ * console.log('拍照结果:', result.tempFilePaths[0])
70
+ * } catch (error) {
71
+ * console.error('选择失败:', error)
72
+ * }
73
+ * ```
74
+ */
75
+ export function chooseImageAsync(options: ChooseImageOptions = {}): Promise<ChooseImageResult> {
76
+ return new Promise((resolve, reject) => {
77
+ chooseImage(options, {
78
+ onSuccess: resolve,
79
+ onError: reject,
80
+ onCancel: () => reject(new Error('用户取消选择')),
81
+ })
82
+ })
83
+ }
84
+
85
+ /**
86
+ * 从相册选择图片
87
+ * @param count 最多选择的图片数量
88
+ * @returns Promise<ChooseImageResult>
89
+ */
90
+ export function chooseFromAlbum(count: number = 9): Promise<ChooseImageResult> {
91
+ return chooseImageAsync({ count, sourceType: ['album'] })
92
+ }
93
+
94
+ /**
95
+ * 拍照
96
+ * @returns Promise<ChooseImageResult>
97
+ */
98
+ export function takePhoto(): Promise<ChooseImageResult> {
99
+ return chooseImageAsync({ count: 1, sourceType: ['camera'] })
100
+ }
@@ -0,0 +1,288 @@
1
+ @use '../../styles/base' as base;
2
+
3
+ // 输入区域
4
+ .#{base.$prefix}-sender {
5
+ padding: 24rpx;
6
+
7
+ &__input-wrapper {
8
+ display: flex;
9
+ align-items: flex-end;
10
+ gap: 24rpx;
11
+ padding: 22rpx 24rpx;
12
+ background-color: #fff;
13
+ min-height: 100rpx;
14
+ border-radius: 50rpx;
15
+ box-shadow: 1px 4rpx 25rpx 0 rgba(0, 0, 0, 0.1);
16
+ transition: border-radius 0.3s;
17
+
18
+ &--multi-line {
19
+ border-radius: 24rpx;
20
+ }
21
+ }
22
+
23
+ // 输入模式切换
24
+ &__mode-toggle {
25
+ display: flex;
26
+ align-items: center;
27
+ justify-content: center;
28
+
29
+ &:active {
30
+ opacity: 0.7;
31
+ }
32
+ }
33
+
34
+ &__toggle-icon {
35
+ width: 56rpx;
36
+ height: 56rpx;
37
+
38
+ &--disabled {
39
+ opacity: 0.5;
40
+ }
41
+ }
42
+
43
+ &__chat-input {
44
+ flex: 1;
45
+ padding: 8rpx 0;
46
+ font-size: 28rpx;
47
+ color: rgba(0, 0, 0, 0.85);
48
+ text-align: justify;
49
+ line-height: 40rpx;
50
+ /** 最多4行 */
51
+ max-height: calc(40rpx * 4 + 16rpx);
52
+ overflow: auto;
53
+ }
54
+
55
+ // 语音输入按钮
56
+ &__voice-input-btn {
57
+ flex: 1;
58
+ display: flex;
59
+ align-items: center;
60
+ justify-content: center;
61
+ height: 56rpx;
62
+ border-radius: 36rpx;
63
+ transition: all 0.2s;
64
+ font-size: 28rpx;
65
+ color: rgba(0, 0, 0, 0.85);
66
+ line-height: 38rpx;
67
+ font-weight: 400;
68
+
69
+ &--recording {
70
+ .#{base.$prefix}-sender__voice-text {
71
+ color: #ff8041;
72
+ }
73
+ }
74
+
75
+ &--recording#{&}--will-cancel,
76
+ &--will-cancel {
77
+ .#{base.$prefix}-sender__voice-text {
78
+ color: #ff4141;
79
+ }
80
+ }
81
+
82
+ &--disabled {
83
+ opacity: 0.5;
84
+ }
85
+ }
86
+
87
+ &__send-btn {
88
+ width: 56rpx;
89
+ height: 56rpx;
90
+ flex-shrink: 0;
91
+
92
+ &--disabled,
93
+ &[disabled] {
94
+ opacity: 0.5;
95
+ }
96
+ }
97
+
98
+ // 附件按钮 (+号)
99
+ &__attachment-toggle {
100
+ display: flex;
101
+ align-items: center;
102
+ justify-content: center;
103
+
104
+ &:active {
105
+ opacity: 0.7;
106
+ }
107
+
108
+ &--active {
109
+ .#{base.$prefix}-sender__attachment-icon {
110
+ transform: rotate(225deg);
111
+ }
112
+ }
113
+
114
+ &--disabled {
115
+ opacity: 0.5;
116
+ }
117
+ }
118
+
119
+ &__attachment-icon {
120
+ transition: all 0.3s;
121
+ width: 56rpx;
122
+ height: 56rpx;
123
+ }
124
+
125
+ // 附件选择面板
126
+ &__attachment-panel {
127
+ max-height: 0;
128
+ overflow: hidden;
129
+ transition: all 0.3s ease;
130
+
131
+ &--visible {
132
+ max-height: 160rpx;
133
+ }
134
+ }
135
+
136
+ &__attachment-options {
137
+ padding: 24rpx 8rpx 0;
138
+ display: flex;
139
+ gap: 80rpx;
140
+ }
141
+
142
+ &__attachment-item {
143
+ height: 136rpx;
144
+ border-radius: 12rpx;
145
+ display: flex;
146
+ flex-direction: column;
147
+ align-items: center;
148
+ gap: 8rpx;
149
+
150
+ &:active {
151
+ opacity: 0.7;
152
+ }
153
+ }
154
+
155
+ &__attachment-item-icon {
156
+ width: 52rpx;
157
+ height: 52rpx;
158
+
159
+ &-wrap {
160
+ width: 90rpx;
161
+ height: 90rpx;
162
+ background: #fefaf7;
163
+ border-radius: 32rpx;
164
+ display: flex;
165
+ align-items: center;
166
+ justify-content: center;
167
+ border: 2rpx solid #fff;
168
+ }
169
+ }
170
+
171
+ &__attachment-item-label {
172
+ font-size: 20rpx;
173
+ color: rgba(0, 0, 0, 0.85);
174
+ line-height: 36rpx;
175
+ }
176
+
177
+ // 录音遮罩层(微信风格)
178
+ &__voice-overlay {
179
+ position: fixed;
180
+ top: 0;
181
+ left: 0;
182
+ right: 0;
183
+ bottom: 0;
184
+ z-index: 9999;
185
+ display: flex;
186
+ flex-direction: column;
187
+ justify-content: flex-end;
188
+ // background-color: rgba(0, 0, 0, 0.5);
189
+
190
+ &__content {
191
+ display: flex;
192
+ flex-direction: column;
193
+ align-items: center;
194
+ justify-content: center;
195
+ }
196
+ }
197
+
198
+ &__voice-output-text {
199
+ font-size: 28rpx;
200
+ color: #fff;
201
+ font-weight: 600;
202
+ margin-bottom: 48rpx;
203
+ text-align: center;
204
+ }
205
+
206
+ &__voice-mic {
207
+ width: 100%;
208
+ padding: 76rpx 0;
209
+ display: flex;
210
+ flex-direction: column;
211
+ align-items: center;
212
+ justify-content: center;
213
+ gap: 56rpx;
214
+ position: relative;
215
+ }
216
+
217
+ &__voice-mic-bg {
218
+ position: absolute;
219
+ inset: 0;
220
+ background: #fff;
221
+
222
+ &--will-cancel {
223
+ background: #fff;
224
+ }
225
+ }
226
+
227
+ &__voice-mic-text {
228
+ font-size: 24rpx;
229
+ color: rgba(0, 0, 0, 0.45);
230
+ font-weight: 400;
231
+ position: relative;
232
+ }
233
+
234
+ // 录音动画
235
+ &__voice-marquee-viewport {
236
+ width: 272rpx;
237
+ /* 图片宽度 */
238
+ height: 28rpx;
239
+ /* 图片高度 */
240
+ overflow: hidden;
241
+ }
242
+
243
+ &__voice-marquee-track {
244
+ display: flex;
245
+ width: fit-content;
246
+ // animation: scrollSingle 4s linear infinite;
247
+ will-change: transform;
248
+
249
+ &--paused {
250
+ animation-play-state: paused;
251
+ }
252
+ }
253
+
254
+ &__voice-marquee-item {
255
+ flex: 0 0 auto;
256
+ width: 272rpx;
257
+ /* 与视口宽度一致 */
258
+ height: 28rpx;
259
+
260
+ /* 与视口高度一致 */
261
+ &:first-child {
262
+ margin-left: -272rpx;
263
+ }
264
+
265
+ & + & {
266
+ margin-left: 8rpx;
267
+ }
268
+ }
269
+
270
+ &__voice-marquee-img {
271
+ display: block;
272
+ width: 100%;
273
+ height: 100%;
274
+ object-fit: cover;
275
+ /* 或根据需求改为 fill / contain */
276
+ }
277
+ }
278
+
279
+ /* 关键帧 — 移动距离 = 单张图片宽度 */
280
+ @keyframes scrollSingle {
281
+ 0% {
282
+ transform: translateX(0);
283
+ }
284
+
285
+ 100% {
286
+ transform: translateX(272rpx + 8rpx);
287
+ }
288
+ }