koishi-plugin-chat-patch 1.0.18 → 1.1.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/client/vue/chat-logic.ts +273 -83
- package/client/vue/index.vue +7 -0
- package/client/vue/style.css +24 -0
- package/dist/index.js +7 -7
- package/dist/style.css +1 -1
- package/lib/index.d.ts +17 -0
- package/lib/index.js +14 -5
- package/package.json +2 -2
- package/src/api-handlers.ts +23 -7
- package/src/index.ts +7 -1
- package/src/message-handler.ts +1 -0
package/client/vue/chat-logic.ts
CHANGED
|
@@ -449,7 +449,7 @@ export function useChatLogic() {
|
|
|
449
449
|
isExpanded.value = !isExpanded.value
|
|
450
450
|
}
|
|
451
451
|
|
|
452
|
-
const getPreviewText = () => {
|
|
452
|
+
const getPreviewText = (): { previews: string[]; messageCount: number } => {
|
|
453
453
|
if (!props.element.children || props.element.children.length === 0) {
|
|
454
454
|
return {
|
|
455
455
|
previews: [],
|
|
@@ -488,7 +488,7 @@ export function useChatLogic() {
|
|
|
488
488
|
}
|
|
489
489
|
}
|
|
490
490
|
|
|
491
|
-
const renderForwardedMessage = (message: any, index: number) => {
|
|
491
|
+
const renderForwardedMessage = (message: any, index: number): any => {
|
|
492
492
|
const nickname = message.attrs?.nickname || '用户'
|
|
493
493
|
const userId = message.attrs?.userId || 'unknown'
|
|
494
494
|
|
|
@@ -551,7 +551,7 @@ export function useChatLogic() {
|
|
|
551
551
|
}
|
|
552
552
|
},
|
|
553
553
|
setup(props) {
|
|
554
|
-
const renderElement = (element: MessageElement) => {
|
|
554
|
+
const renderElement = (element: MessageElement): any => {
|
|
555
555
|
switch (element.type) {
|
|
556
556
|
case 'text':
|
|
557
557
|
return h('span', { class: 'message-text-content' }, element.attrs.content || '')
|
|
@@ -614,7 +614,7 @@ export function useChatLogic() {
|
|
|
614
614
|
case 'p':
|
|
615
615
|
// 处理段落元素,递归渲染子元素
|
|
616
616
|
if (element.children && element.children.length > 0) {
|
|
617
|
-
const childElements = element.children.map((child, index) =>
|
|
617
|
+
const childElements: any[] = element.children.map((child, index) =>
|
|
618
618
|
h(MessageElement, {
|
|
619
619
|
key: index,
|
|
620
620
|
element: child,
|
|
@@ -654,6 +654,13 @@ export function useChatLogic() {
|
|
|
654
654
|
// 存储每个频道的真实消息数量
|
|
655
655
|
const channelMessageCounts = ref<Record<string, number>>({})
|
|
656
656
|
|
|
657
|
+
// 存储每个频道的分页状态
|
|
658
|
+
const channelPagination = ref<Record<string, {
|
|
659
|
+
offset: number
|
|
660
|
+
hasMore: boolean
|
|
661
|
+
loading: boolean
|
|
662
|
+
}>>({})
|
|
663
|
+
|
|
657
664
|
const pluginConfig = ref<{
|
|
658
665
|
maxMessagesPerChannel: number
|
|
659
666
|
keepMessagesOnClear: number
|
|
@@ -682,6 +689,8 @@ export function useChatLogic() {
|
|
|
682
689
|
|
|
683
690
|
// 内存中的URL缓存
|
|
684
691
|
const imageBlobUrls = ref<Record<string, string>>({})
|
|
692
|
+
// 正在加载的图片URL锁,防止并发请求
|
|
693
|
+
const loadingImages = new Map<string, Promise<string | null>>()
|
|
685
694
|
|
|
686
695
|
// 内存管理配置
|
|
687
696
|
const MAX_MEMORY_USAGE = 100 * 1024 * 1024 // 100MB 最大内存使用量
|
|
@@ -736,6 +745,7 @@ export function useChatLogic() {
|
|
|
736
745
|
const showScrollButton = ref<boolean>(false)
|
|
737
746
|
const isUserScrolling = ref<boolean>(false)
|
|
738
747
|
const isSending = ref<boolean>(false)
|
|
748
|
+
const isLoadingMore = ref<boolean>(false)
|
|
739
749
|
|
|
740
750
|
// 拖拽相关状态
|
|
741
751
|
const draggingChannel = ref<string>('')
|
|
@@ -1113,9 +1123,13 @@ export function useChatLogic() {
|
|
|
1113
1123
|
mobileView.value = 'messages'
|
|
1114
1124
|
}
|
|
1115
1125
|
|
|
1116
|
-
//
|
|
1126
|
+
// 清除该频道的分页状态
|
|
1127
|
+
const channelKey = `${selectedBot.value}:${channelId}`
|
|
1128
|
+
delete channelPagination.value[channelKey]
|
|
1129
|
+
|
|
1130
|
+
// 先获取历史消息(只加载最新的50条)
|
|
1117
1131
|
if (selectedBot.value) {
|
|
1118
|
-
await loadHistoryMessages(selectedBot.value, channelId)
|
|
1132
|
+
await loadHistoryMessages(selectedBot.value, channelId, 50, 0)
|
|
1119
1133
|
}
|
|
1120
1134
|
|
|
1121
1135
|
nextTick(() => {
|
|
@@ -1366,6 +1380,59 @@ export function useChatLogic() {
|
|
|
1366
1380
|
// 滚动到底部时,重置滚动状态
|
|
1367
1381
|
isUserScrolling.value = false
|
|
1368
1382
|
}
|
|
1383
|
+
|
|
1384
|
+
// 检查是否滚动到顶部,如果是则加载更多消息
|
|
1385
|
+
if (scrollTop <= 10 && selectedBot.value && selectedChannel.value) {
|
|
1386
|
+
loadMoreMessages()
|
|
1387
|
+
}
|
|
1388
|
+
}
|
|
1389
|
+
}
|
|
1390
|
+
|
|
1391
|
+
// 加载更多消息的函数
|
|
1392
|
+
async function loadMoreMessages() {
|
|
1393
|
+
if (!selectedBot.value || !selectedChannel.value) return
|
|
1394
|
+
|
|
1395
|
+
const channelKey = `${selectedBot.value}:${selectedChannel.value}`
|
|
1396
|
+
|
|
1397
|
+
// 检查是否正在加载或没有更多消息
|
|
1398
|
+
const pagination = channelPagination.value[channelKey]
|
|
1399
|
+
if (isLoadingMore.value || (pagination && !pagination.hasMore)) return
|
|
1400
|
+
|
|
1401
|
+
// 更新加载状态
|
|
1402
|
+
if (!channelPagination.value[channelKey]) {
|
|
1403
|
+
channelPagination.value[channelKey] = {
|
|
1404
|
+
offset: 0,
|
|
1405
|
+
hasMore: true,
|
|
1406
|
+
loading: false
|
|
1407
|
+
}
|
|
1408
|
+
}
|
|
1409
|
+
|
|
1410
|
+
// 设置为加载中状态
|
|
1411
|
+
isLoadingMore.value = true
|
|
1412
|
+
channelPagination.value[channelKey].loading = true
|
|
1413
|
+
|
|
1414
|
+
try {
|
|
1415
|
+
// 获取当前offset
|
|
1416
|
+
const currentOffset = pagination?.offset || 0
|
|
1417
|
+
|
|
1418
|
+
// 加载下一批消息(50条)
|
|
1419
|
+
const result = await loadHistoryMessages(selectedBot.value, selectedChannel.value, 50, currentOffset)
|
|
1420
|
+
|
|
1421
|
+
if (result) {
|
|
1422
|
+
// 更新分页状态已在loadHistoryMessages中处理
|
|
1423
|
+
} else {
|
|
1424
|
+
// 加载失败,重置加载状态
|
|
1425
|
+
channelPagination.value[channelKey].loading = false
|
|
1426
|
+
}
|
|
1427
|
+
} catch (error) {
|
|
1428
|
+
console.error('加载更多消息失败:', error)
|
|
1429
|
+
// 加载失败,重置加载状态
|
|
1430
|
+
if (channelPagination.value[channelKey]) {
|
|
1431
|
+
channelPagination.value[channelKey].loading = false
|
|
1432
|
+
}
|
|
1433
|
+
} finally {
|
|
1434
|
+
// 重置加载状态
|
|
1435
|
+
isLoadingMore.value = false
|
|
1369
1436
|
}
|
|
1370
1437
|
}
|
|
1371
1438
|
|
|
@@ -2197,102 +2264,177 @@ export function useChatLogic() {
|
|
|
2197
2264
|
|
|
2198
2265
|
// 图片缓存管理函数
|
|
2199
2266
|
async function getCachedImageUrl(channelKey: string, originalUrl: string): Promise<string | null> {
|
|
2267
|
+
// 检查是否正在加载中,如果是则等待加载完成
|
|
2268
|
+
if (loadingImages.has(originalUrl)) {
|
|
2269
|
+
return loadingImages.get(originalUrl) || null
|
|
2270
|
+
}
|
|
2271
|
+
|
|
2200
2272
|
// 首先检查内存中的blob URL
|
|
2201
2273
|
const existingBlobUrl = imageBlobUrls.value[originalUrl]
|
|
2202
2274
|
if (existingBlobUrl) {
|
|
2203
2275
|
return existingBlobUrl
|
|
2204
2276
|
}
|
|
2205
2277
|
|
|
2206
|
-
//
|
|
2207
|
-
const
|
|
2208
|
-
|
|
2278
|
+
// 使用立即执行的异步函数来处理加载过程
|
|
2279
|
+
const loadingPromise = (async () => {
|
|
2280
|
+
try {
|
|
2281
|
+
// 再次检查内存中的blob URL(防止在创建Promise期间其他地方已经缓存了)
|
|
2282
|
+
const cachedBlobUrl = imageBlobUrls.value[originalUrl]
|
|
2283
|
+
if (cachedBlobUrl) {
|
|
2284
|
+
return cachedBlobUrl
|
|
2285
|
+
}
|
|
2286
|
+
|
|
2287
|
+
// 从IndexedDB获取
|
|
2288
|
+
const cacheItem = await getImageFromDB(originalUrl)
|
|
2289
|
+
if (!cacheItem) {
|
|
2290
|
+
return null
|
|
2291
|
+
}
|
|
2292
|
+
|
|
2293
|
+
// 检查内存使用情况
|
|
2294
|
+
checkAndCleanupMemory()
|
|
2295
|
+
|
|
2296
|
+
// 创建blob URL并缓存到内存
|
|
2297
|
+
const blobUrl = URL.createObjectURL(cacheItem.blob)
|
|
2298
|
+
imageBlobUrls.value[originalUrl] = blobUrl
|
|
2209
2299
|
|
|
2210
|
-
|
|
2211
|
-
|
|
2300
|
+
// 更新内存使用量
|
|
2301
|
+
updateMemoryUsage(estimateBlobSize(cacheItem.blob))
|
|
2212
2302
|
|
|
2213
|
-
|
|
2214
|
-
|
|
2215
|
-
|
|
2303
|
+
// 更新访问时间
|
|
2304
|
+
cacheItem.timestamp = Date.now()
|
|
2305
|
+
await saveImageToDB(cacheItem)
|
|
2216
2306
|
|
|
2217
|
-
|
|
2218
|
-
|
|
2307
|
+
return blobUrl
|
|
2308
|
+
} catch (error) {
|
|
2309
|
+
console.error('获取缓存图片失败:', error)
|
|
2310
|
+
return null
|
|
2311
|
+
} finally {
|
|
2312
|
+
// 加载完成后从loadingImages中移除
|
|
2313
|
+
loadingImages.delete(originalUrl)
|
|
2314
|
+
}
|
|
2315
|
+
})()
|
|
2219
2316
|
|
|
2220
|
-
//
|
|
2221
|
-
|
|
2222
|
-
await saveImageToDB(cacheItem)
|
|
2317
|
+
// 立即添加到loadingImages中,防止并发请求
|
|
2318
|
+
loadingImages.set(originalUrl, loadingPromise)
|
|
2223
2319
|
|
|
2224
|
-
|
|
2320
|
+
// 等待加载完成并返回结果
|
|
2321
|
+
return loadingPromise
|
|
2225
2322
|
}
|
|
2226
2323
|
|
|
2227
2324
|
async function cacheImage(channelKey: string, originalUrl: string): Promise<string | null> {
|
|
2228
|
-
|
|
2229
|
-
|
|
2230
|
-
|
|
2231
|
-
|
|
2232
|
-
return cached
|
|
2233
|
-
}
|
|
2325
|
+
// 检查是否正在加载中,如果是则等待加载完成
|
|
2326
|
+
if (loadingImages.has(originalUrl)) {
|
|
2327
|
+
return loadingImages.get(originalUrl) || null
|
|
2328
|
+
}
|
|
2234
2329
|
|
|
2235
|
-
|
|
2236
|
-
|
|
2330
|
+
// 首先检查内存中的blob URL
|
|
2331
|
+
const existingBlobUrl = imageBlobUrls.value[originalUrl]
|
|
2332
|
+
if (existingBlobUrl) {
|
|
2333
|
+
return existingBlobUrl
|
|
2334
|
+
}
|
|
2237
2335
|
|
|
2238
|
-
|
|
2239
|
-
|
|
2240
|
-
|
|
2241
|
-
|
|
2336
|
+
// 使用立即执行的异步函数来处理加载过程
|
|
2337
|
+
const loadingPromise = (async () => {
|
|
2338
|
+
try {
|
|
2339
|
+
// 再次检查内存中的blob URL(防止在创建Promise期间其他地方已经缓存了)
|
|
2340
|
+
const cachedBlobUrl = imageBlobUrls.value[originalUrl]
|
|
2341
|
+
if (cachedBlobUrl) {
|
|
2342
|
+
return cachedBlobUrl
|
|
2343
|
+
}
|
|
2242
2344
|
|
|
2243
|
-
|
|
2244
|
-
|
|
2245
|
-
|
|
2345
|
+
// 从IndexedDB获取
|
|
2346
|
+
const cachedItem = await getImageFromDB(originalUrl)
|
|
2347
|
+
if (cachedItem) {
|
|
2348
|
+
// 检查内存使用情况
|
|
2349
|
+
checkAndCleanupMemory()
|
|
2246
2350
|
|
|
2247
|
-
|
|
2248
|
-
|
|
2249
|
-
|
|
2250
|
-
for (let i = 0; i < byteCharacters.length; i++) {
|
|
2251
|
-
byteNumbers[i] = byteCharacters.charCodeAt(i)
|
|
2252
|
-
}
|
|
2253
|
-
const byteArray = new Uint8Array(byteNumbers)
|
|
2351
|
+
// 创建blob URL并缓存到内存
|
|
2352
|
+
const blobUrl = URL.createObjectURL(cachedItem.blob)
|
|
2353
|
+
imageBlobUrls.value[originalUrl] = blobUrl
|
|
2254
2354
|
|
|
2255
|
-
|
|
2256
|
-
|
|
2355
|
+
// 更新内存使用量
|
|
2356
|
+
updateMemoryUsage(estimateBlobSize(cachedItem.blob))
|
|
2257
2357
|
|
|
2258
|
-
|
|
2259
|
-
|
|
2260
|
-
|
|
2261
|
-
return null
|
|
2262
|
-
}
|
|
2358
|
+
// 更新访问时间
|
|
2359
|
+
cachedItem.timestamp = Date.now()
|
|
2360
|
+
await saveImageToDB(cachedItem)
|
|
2263
2361
|
|
|
2264
|
-
|
|
2265
|
-
|
|
2266
|
-
url: originalUrl,
|
|
2267
|
-
blob: blob,
|
|
2268
|
-
timestamp: Date.now(),
|
|
2269
|
-
size: blob.size,
|
|
2270
|
-
channelKey: channelKey
|
|
2271
|
-
}
|
|
2362
|
+
return blobUrl
|
|
2363
|
+
}
|
|
2272
2364
|
|
|
2273
|
-
|
|
2274
|
-
|
|
2275
|
-
if (!saved) {
|
|
2276
|
-
console.error('保存图片到IndexedDB失败')
|
|
2277
|
-
return null
|
|
2278
|
-
}
|
|
2365
|
+
// 获取图片数据
|
|
2366
|
+
const result = await (send as any)('fetch-image', { url: originalUrl })
|
|
2279
2367
|
|
|
2280
|
-
|
|
2281
|
-
|
|
2368
|
+
if (!result.success) {
|
|
2369
|
+
return null
|
|
2370
|
+
}
|
|
2282
2371
|
|
|
2283
|
-
|
|
2284
|
-
|
|
2285
|
-
|
|
2372
|
+
// 将base64转换为blob
|
|
2373
|
+
const base64Data = result.base64
|
|
2374
|
+
const contentType = result.contentType || 'image/jpeg'
|
|
2286
2375
|
|
|
2287
|
-
|
|
2288
|
-
|
|
2376
|
+
// 解码base64
|
|
2377
|
+
const byteCharacters = atob(base64Data)
|
|
2378
|
+
const byteNumbers = new Array(byteCharacters.length)
|
|
2379
|
+
for (let i = 0; i < byteCharacters.length; i++) {
|
|
2380
|
+
byteNumbers[i] = byteCharacters.charCodeAt(i)
|
|
2381
|
+
}
|
|
2382
|
+
const byteArray = new Uint8Array(byteNumbers)
|
|
2289
2383
|
|
|
2290
|
-
|
|
2384
|
+
// 保持原始MIME类型,特别是对于GIF
|
|
2385
|
+
const blob = new Blob([byteArray], { type: contentType })
|
|
2291
2386
|
|
|
2292
|
-
|
|
2293
|
-
|
|
2294
|
-
|
|
2295
|
-
|
|
2387
|
+
// 检查blob大小
|
|
2388
|
+
if (blob.size > MAX_IMAGE_SIZE) {
|
|
2389
|
+
return null
|
|
2390
|
+
}
|
|
2391
|
+
|
|
2392
|
+
// 再次检查内存中是否已经存在(防止并发请求)
|
|
2393
|
+
const concurrentBlobUrl = imageBlobUrls.value[originalUrl]
|
|
2394
|
+
if (concurrentBlobUrl) {
|
|
2395
|
+
return concurrentBlobUrl
|
|
2396
|
+
}
|
|
2397
|
+
|
|
2398
|
+
// 创建缓存项
|
|
2399
|
+
const cacheItem: ImageCacheItem = {
|
|
2400
|
+
url: originalUrl,
|
|
2401
|
+
blob: blob,
|
|
2402
|
+
timestamp: Date.now(),
|
|
2403
|
+
size: blob.size,
|
|
2404
|
+
channelKey: channelKey
|
|
2405
|
+
}
|
|
2406
|
+
|
|
2407
|
+
// 保存到IndexedDB
|
|
2408
|
+
const saved = await saveImageToDB(cacheItem)
|
|
2409
|
+
if (!saved) {
|
|
2410
|
+
return null
|
|
2411
|
+
}
|
|
2412
|
+
|
|
2413
|
+
// 检查内存使用情况
|
|
2414
|
+
checkAndCleanupMemory()
|
|
2415
|
+
|
|
2416
|
+
// 创建blob URL并缓存到内存
|
|
2417
|
+
const blobUrl = URL.createObjectURL(blob)
|
|
2418
|
+
imageBlobUrls.value[originalUrl] = blobUrl
|
|
2419
|
+
|
|
2420
|
+
// 更新内存使用量
|
|
2421
|
+
updateMemoryUsage(estimateBlobSize(blob))
|
|
2422
|
+
|
|
2423
|
+
return blobUrl
|
|
2424
|
+
} catch (error) {
|
|
2425
|
+
console.error('缓存图片失败:', error)
|
|
2426
|
+
return null
|
|
2427
|
+
} finally {
|
|
2428
|
+
// 加载完成后从loadingImages中移除
|
|
2429
|
+
loadingImages.delete(originalUrl)
|
|
2430
|
+
}
|
|
2431
|
+
})()
|
|
2432
|
+
|
|
2433
|
+
// 立即添加到loadingImages中,防止并发请求
|
|
2434
|
+
loadingImages.set(originalUrl, loadingPromise)
|
|
2435
|
+
|
|
2436
|
+
// 等待加载完成并返回结果
|
|
2437
|
+
return loadingPromise
|
|
2296
2438
|
}
|
|
2297
2439
|
|
|
2298
2440
|
// 清理频道的所有图片缓存
|
|
@@ -2808,13 +2950,21 @@ export function useChatLogic() {
|
|
|
2808
2950
|
}
|
|
2809
2951
|
|
|
2810
2952
|
// 获取历史消息
|
|
2811
|
-
async function loadHistoryMessages(botId: string, channelId: string) {
|
|
2953
|
+
async function loadHistoryMessages(botId: string, channelId: string, limit?: number, offset?: number) {
|
|
2812
2954
|
try {
|
|
2813
2955
|
|
|
2814
|
-
const
|
|
2956
|
+
const requestData: any = {
|
|
2815
2957
|
selfId: botId,
|
|
2816
2958
|
channelId: channelId
|
|
2817
|
-
}
|
|
2959
|
+
}
|
|
2960
|
+
|
|
2961
|
+
// 如果提供了分页参数,则添加到请求中
|
|
2962
|
+
if (limit !== undefined) {
|
|
2963
|
+
requestData.limit = limit
|
|
2964
|
+
requestData.offset = offset || 0
|
|
2965
|
+
}
|
|
2966
|
+
|
|
2967
|
+
const result = await (send as any)('get-history-messages', requestData)
|
|
2818
2968
|
|
|
2819
2969
|
if (result.success && result.messages) {
|
|
2820
2970
|
const channelKey = `${botId}:${channelId}`
|
|
@@ -2834,14 +2984,51 @@ export function useChatLogic() {
|
|
|
2834
2984
|
quote: msg.quote
|
|
2835
2985
|
}))
|
|
2836
2986
|
|
|
2837
|
-
//
|
|
2838
|
-
|
|
2987
|
+
// 确保offset有默认值
|
|
2988
|
+
const actualOffset = offset || 0
|
|
2839
2989
|
|
|
2840
|
-
//
|
|
2841
|
-
|
|
2990
|
+
// 如果是分页加载,需要合并到现有消息中
|
|
2991
|
+
if (limit !== undefined) {
|
|
2992
|
+
// 按时间戳排序
|
|
2993
|
+
messages.sort((a, b) => a.timestamp - b.timestamp)
|
|
2994
|
+
|
|
2995
|
+
// 如果是第一页(offset为0),则替换现有消息
|
|
2996
|
+
if (offset === 0) {
|
|
2997
|
+
chatData.value.messages[channelKey] = messages
|
|
2998
|
+
// 初始化分页状态
|
|
2999
|
+
channelPagination.value[channelKey] = {
|
|
3000
|
+
offset: messages.length,
|
|
3001
|
+
hasMore: messages.length >= limit && result.total > messages.length,
|
|
3002
|
+
loading: false
|
|
3003
|
+
}
|
|
3004
|
+
} else {
|
|
3005
|
+
// 否则是加载更多,需要合并到现有消息的开头(因为是历史消息)
|
|
3006
|
+
const existingMessages = chatData.value.messages[channelKey] || []
|
|
3007
|
+
chatData.value.messages[channelKey] = [...messages, ...existingMessages]
|
|
3008
|
+
// 更新分页状态
|
|
3009
|
+
const actualOffset = offset || 0
|
|
3010
|
+
channelPagination.value[channelKey] = {
|
|
3011
|
+
offset: actualOffset + messages.length,
|
|
3012
|
+
hasMore: messages.length >= limit && result.total > (actualOffset + messages.length),
|
|
3013
|
+
loading: false
|
|
3014
|
+
}
|
|
3015
|
+
}
|
|
3016
|
+
} else {
|
|
3017
|
+
// 按时间戳排序
|
|
3018
|
+
messages.sort((a, b) => a.timestamp - b.timestamp)
|
|
3019
|
+
|
|
3020
|
+
// 设置历史消息
|
|
3021
|
+
chatData.value.messages[channelKey] = messages
|
|
3022
|
+
// 初始化分页状态
|
|
3023
|
+
channelPagination.value[channelKey] = {
|
|
3024
|
+
offset: messages.length,
|
|
3025
|
+
hasMore: false, // 不分页时没有更多消息
|
|
3026
|
+
loading: false
|
|
3027
|
+
}
|
|
3028
|
+
}
|
|
2842
3029
|
|
|
2843
3030
|
// 更新频道消息数量缓存
|
|
2844
|
-
channelMessageCounts.value[channelKey] = messages.length
|
|
3031
|
+
channelMessageCounts.value[channelKey] = result.total || messages.length
|
|
2845
3032
|
|
|
2846
3033
|
// 触发响应式更新
|
|
2847
3034
|
chatData.value = { ...chatData.value }
|
|
@@ -3120,6 +3307,7 @@ export function useChatLogic() {
|
|
|
3120
3307
|
// 响应式数据
|
|
3121
3308
|
chatData,
|
|
3122
3309
|
channelMessageCounts,
|
|
3310
|
+
channelPagination,
|
|
3123
3311
|
pluginConfig,
|
|
3124
3312
|
selectedBot,
|
|
3125
3313
|
selectedChannel,
|
|
@@ -3140,6 +3328,7 @@ export function useChatLogic() {
|
|
|
3140
3328
|
showScrollButton,
|
|
3141
3329
|
isUserScrolling,
|
|
3142
3330
|
isSending,
|
|
3331
|
+
isLoadingMore,
|
|
3143
3332
|
draggingChannel,
|
|
3144
3333
|
dragStartPos,
|
|
3145
3334
|
dragCurrentPos,
|
|
@@ -3204,6 +3393,7 @@ export function useChatLogic() {
|
|
|
3204
3393
|
handleTouchMove,
|
|
3205
3394
|
handleTouchEnd,
|
|
3206
3395
|
handleInputFocus,
|
|
3396
|
+
loadMoreMessages,
|
|
3207
3397
|
|
|
3208
3398
|
// 图片缓存相关
|
|
3209
3399
|
getCachedImageUrl,
|
package/client/vue/index.vue
CHANGED
|
@@ -66,6 +66,12 @@
|
|
|
66
66
|
<div v-else class="message-content">
|
|
67
67
|
<!-- 消息历史 -->
|
|
68
68
|
<div class="message-history" ref="messageHistory">
|
|
69
|
+
<!-- 加载更多指示器 -->
|
|
70
|
+
<div v-if="isLoadingMore" class="loading-more-indicator">
|
|
71
|
+
<div class="loading-spinner"></div>
|
|
72
|
+
<span>加载更多消息中...</span>
|
|
73
|
+
</div>
|
|
74
|
+
|
|
69
75
|
<div v-for="message in currentMessages" :key="message.id"
|
|
70
76
|
:class="['message-item', { 'bot-message': message.isBot }]">
|
|
71
77
|
<div class="message-avatar">
|
|
@@ -245,6 +251,7 @@ const {
|
|
|
245
251
|
showScrollButton,
|
|
246
252
|
isUserScrolling,
|
|
247
253
|
isSending,
|
|
254
|
+
isLoadingMore,
|
|
248
255
|
draggingChannel,
|
|
249
256
|
dragStartPos,
|
|
250
257
|
dragCurrentPos,
|
package/client/vue/style.css
CHANGED
|
@@ -1918,3 +1918,27 @@ body.dragging-bubble-global {
|
|
|
1918
1918
|
background-color: rgba(255, 255, 255, .7)
|
|
1919
1919
|
}
|
|
1920
1920
|
}
|
|
1921
|
+
|
|
1922
|
+
.loading-more-indicator {
|
|
1923
|
+
display: flex;
|
|
1924
|
+
align-items: center;
|
|
1925
|
+
justify-content: center;
|
|
1926
|
+
padding: 12px;
|
|
1927
|
+
color: #6c757d;
|
|
1928
|
+
font-size: 14px;
|
|
1929
|
+
}
|
|
1930
|
+
|
|
1931
|
+
.loading-spinner {
|
|
1932
|
+
width: 20px;
|
|
1933
|
+
height: 20px;
|
|
1934
|
+
border: 2px solid #f3f3f3;
|
|
1935
|
+
border-top: 2px solid #2196f3;
|
|
1936
|
+
border-radius: 50%;
|
|
1937
|
+
animation: spin 1s linear infinite;
|
|
1938
|
+
margin-right: 8px;
|
|
1939
|
+
}
|
|
1940
|
+
|
|
1941
|
+
@keyframes spin {
|
|
1942
|
+
0% { transform: rotate(0deg); }
|
|
1943
|
+
100% { transform: rotate(360deg); }
|
|
1944
|
+
}
|
package/dist/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import{defineComponent as Ce,ref as v,onMounted as st,h,computed as E,watch as St,nextTick as ye,onUnmounted as ga,createElementBlock as x,openBlock as y,unref as r,normalizeStyle as ot,normalizeClass as le,createCommentVNode as B,createElementVNode as f,Fragment as ee,renderList as _e,createBlock as Re,toDisplayString as _,withDirectives as Bt,createTextVNode as rt,vShow as ya,withModifiers as $t,withKeys as pa,isRef as wa,vModelText as Ia,resolveComponent as xa}from"vue";import{receive as lt,send as P,icons as Ca}from"@koishijs/client";function ka(){function te(e){try{return new URL(e).protocol==="file:"}catch{return false}}const ce=Ce({props:{src:{type:String,required:true},alt:{type:String,default:"头像"},channelKey:{type:String,required:true}},setup(e){const t=v("loading"),a=v(e.src),n=v(""),o=async()=>{try{t.value="loading";const i=await Ve(e.channelKey,e.src);if(i){a.value=i,t.value="loaded";return}const c=new Image;c.crossOrigin="anonymous",c.referrerPolicy="no-referrer",c.draggable=false;const d=new Promise((k,w)=>{c.onload=()=>k(),c.onerror=()=>w(new Error("Direct load failed")),c.src=e.src}),p=new Promise((k,w)=>{setTimeout(()=>w(new Error("Timeout")),3e3)});try{await Promise.race([d,p]),a.value=e.src,t.value="loaded",xe(e.channelKey,e.src).catch(k=>{console.warn("异步缓存头像失败:",k)})}catch{await s()}}catch(i){console.error("头像加载失败:",i),t.value="error",n.value="头像加载失败"}},s=async()=>{try{t.value="caching";const i=await xe(e.channelKey,e.src);if(i)a.value=i,t.value="loaded";else throw new Error("缓存系统加载失败")}catch(i){console.error("缓存系统加载头像失败:",i),t.value="error",n.value=(i==null?void 0:i.message)||"缓存加载失败"}};return st(()=>{o()}),()=>{switch(t.value){case"loading":case"caching":return h("div",{class:"avatar-placeholder"},e.alt.charAt(0).toUpperCase());case"loaded":return h("img",{src:a.value,alt:e.alt,draggable:false,style:{width:"100%",height:"100%","object-fit":"cover"}});case"error":return h("div",{class:"avatar-placeholder"},e.alt.charAt(0).toUpperCase());default:return h("div",{class:"avatar-placeholder"},e.alt.charAt(0).toUpperCase())}}}}),Y=Ce({props:{src:{type:String,required:true},alt:{type:String,default:"图片"},filename:{type:String,default:""},channelKey:{type:String,required:true}},setup(e){const t=v("loading"),a=v(e.src),n=v(""),o=v(null),s=async()=>{try{t.value="loading";const c=await Ve(e.channelKey,e.src);if(c){a.value=c,t.value="loaded";return}if(te(e.src)){console.log("ImageComponent: 检测到本地文件,使用代理请求:",e.src),await i();return}const d=new Image;d.crossOrigin="anonymous",d.referrerPolicy="no-referrer",d.draggable=false;const p=new Promise((w,O)=>{d.onload=()=>w(),d.onerror=()=>O(new Error("Direct load failed")),d.src=e.src}),k=new Promise((w,O)=>{setTimeout(()=>O(new Error("Timeout")),3e3)});try{await Promise.race([p,k]),a.value=e.src,t.value="loaded",xe(e.channelKey,e.src).catch(w=>{console.warn("异步缓存图片失败:",w)})}catch{await i()}}catch(c){console.error("图片加载失败:",c),t.value="error",n.value="图片加载失败"}},i=async()=>{try{t.value="caching";const c=await xe(e.channelKey,e.src);if(c)a.value=c,t.value="loaded";else throw new Error("缓存系统加载失败")}catch(c){console.error("缓存系统加载图片失败:",c),t.value="error",n.value=(c==null?void 0:c.message)||"缓存加载失败"}};return st(()=>{s()}),()=>{switch(t.value){case"loading":return h("div",{class:"message-image-loading"},"加载中...");case"caching":return h("div",{class:"message-image-loading"},"[图片加载缓存中...]");case"loaded":return h("img",{src:a.value,alt:e.alt,class:"message-image",loading:"lazy",ref:o,draggable:false,style:{"max-width":"min(400px, 66.67vw)","max-height":"200px",width:"auto",height:"auto","object-fit":"contain"},onLoad:()=>{o.value&&e.src.toLowerCase().includes(".gif")&&(o.value.style.imageRendering="auto")}});case"error":return h("div",{class:"message-image-error"},["图片加载失败",h("br"),h("small",e.filename||e.alt||"未知图片"),h("br"),h("small",{style:"color: #ff9800;"},n.value)]);default:return h("div",{class:"message-image-error"},"未知状态")}}}}),pe=Ce({props:{data:{type:String,required:true},channelKey:{type:String,required:true}},setup(e){const a=(()=>{try{const o=JSON.parse(e.data);if(o.meta&&o.meta.detail_1){const s=o.meta.detail_1;return{type:"share_card",title:s.title||o.prompt||"分享内容",desc:s.desc||"",preview:s.preview?s.preview.replace(/\\\//g,"/"):"",icon:s.icon?s.icon.replace(/\\\//g,"/"):"",url:s.qqdocurl?s.qqdocurl.replace(/\\\//g,"/"):s.url?s.url.replace(/\\\//g,"/"):"",appName:s.title||"应用"}}return{type:"raw",data:o}}catch(o){return console.error("解析JSON数据失败:",o),{type:"error",error:"无法解析的JSON数据"}}})(),n=()=>{a.type==="share_card"&&a.url&&window.open(a.url,"_blank","noopener,noreferrer")};return()=>a.type==="share_card"&&a.preview?h("img",{src:a.preview,alt:a.title||"[分享小程序]",class:"message-image",loading:"lazy",draggable:false,onClick:n,style:{"max-width":"400px","max-height":"200px",width:"auto",height:"auto","object-fit":"contain",cursor:a.url?"pointer":"default"},title:a.url?`点击打开: ${a.title||"链接"}`:a.title,onError:o=>{const i=o.target.parentElement;i&&(i.style.display="none")}}):a.type==="error"?h("div",{class:"message-json-error"},[h("span",{class:"json-error-text"},a.error),h("details",{class:"json-raw-data"},[h("summary","查看原始数据"),h("pre",{class:"json-raw-content"},e.data)])]):h("div",{class:"message-json-raw"},[h("div",{class:"json-label"},"[JSON数据]"),h("details",{class:"json-raw-data"},[h("summary","查看详情"),h("pre",{class:"json-raw-content"},JSON.stringify(a.data,null,2))])])}}),Z=Ce({props:{element:{type:Object,required:true},channelKey:{type:String,required:true}},setup(e){const t=v(false),a=()=>{t.value=!t.value},n=()=>{if(!e.element.children||e.element.children.length===0)return{previews:[],messageCount:0};const s=e.element.children.filter(d=>d.type==="message"),i=s.length;return{previews:s.slice(0,3).map(d=>{var w,O;const p=((w=d.attrs)==null?void 0:w.nickname)||"用户";let k="";if(d.children&&d.children.length>0){const z=d.children[0];z.type==="text"?(k=(((O=z.attrs)==null?void 0:O.content)||"").substring(0,20),k.length>15&&(k+="...")):z.type==="img"?k="[图片]":z.type==="video"?k="[视频]":k=`[${z.type}]`}return`${p}:${k}`}),messageCount:i}},o=(s,i)=>{var p,k,w;const c=((p=s.attrs)==null?void 0:p.nickname)||"用户",d=((k=s.attrs)==null?void 0:k.userId)||"unknown";return h("div",{key:i,class:"forwarded-message-item"},[h("div",{class:"forwarded-message-header"},[h("span",{class:"forwarded-message-nickname"},c),h("span",{class:"forwarded-message-userid"},`(${d})`)]),h("div",{class:"forwarded-message-content"},((w=s.children)==null?void 0:w.map((O,z)=>h(ke,{key:z,element:O,channelKey:e.channelKey})))||[])])};return()=>{var c;const{previews:s,messageCount:i}=n();return h("div",{class:"forward-message-container"},[h("div",{class:"forward-message-preview",onClick:a},[h("div",{class:"forward-message-title"},"聊天记录"),...s.map((d,p)=>h("div",{key:p,class:"forward-message-preview-item"},d)),h("div",{class:"forward-message-footer"},[h("span",{class:"forward-message-count"},`查看${i}条转发消息`),h("span",{class:"forward-message-toggle"},t.value?"▲":"▼")])]),t.value&&h("div",{class:"forward-message-expanded"},((c=e.element.children)==null?void 0:c.filter(d=>d.type==="message").map((d,p)=>o(d,p)))||[])])}}}),ke=Ce({props:{element:{type:Object,required:true},channelKey:{type:String,required:true}},setup(e){const t=a=>{var n,o,s,i,c,d;switch(a.type){case"text":return h("span",{class:"message-text-content"},a.attrs.content||"");case"forward":return h("span",{class:"message-text-content"},`[转发消息 ${a.attrs.id}]`||"[转发消息]");case"img":case"image":const p=a.attrs.src||a.attrs.url||a.attrs.file;return h("div",{class:"message-image-container"},[h(Y,{src:p,alt:a.attrs.summary||"图片",filename:a.attrs.filename||a.attrs.summary||"",channelKey:e.channelKey})]);case"mface":const k=a.attrs.src||a.attrs.url||a.attrs.file;return h("div",{class:"message-image-container"},[h(Y,{src:k,alt:a.attrs.summary||"表情",filename:a.attrs.emojiId||a.attrs.summary||"",channelKey:e.channelKey})]);case"face":if((o=(n=a.children[0])==null?void 0:n.attrs)!=null&&o.src){const w=((i=(s=a.children[0])==null?void 0:s.attrs)==null?void 0:i.src)||((d=(c=a.children[0])==null?void 0:c.attrs)==null?void 0:d.url);return h("div",{class:"message-image-container"},[h(Y,{src:w,alt:a.attrs.name||a.attrs.id||"[表情]",filename:a.attrs.name||a.attrs.id||"[表情]",channelKey:e.channelKey})])}else return h("span",{class:"message-text-content"},`[${a.attrs.name||a.attrs.id}]`||"[表情]");case"at":return h("span",{class:"message-at",title:a.attrs.name},`${a.attrs.name||a.attrs.id}`);case"json":return h("div",{class:"message-image-container"},[h(pe,{data:a.attrs.data||"",channelKey:e.channelKey})]);case"p":if(a.children&&a.children.length>0){const w=a.children.map((O,z)=>h(ke,{key:z,element:O,channelKey:e.channelKey}));return h("div",{class:"message-paragraph"},w)}else return h("div",{class:"message-paragraph"},"");case"figure":return h(Z,{element:a,channelKey:e.channelKey});default:return h("span",{class:"message-unknown",title:`未知消息类型: ${a.type}`},a.attrs.content||`[${a.type}]`)}};return()=>t(e.element)}}),u=v({bots:{},channels:{},messages:{}}),U=v({}),ie=v({maxMessagesPerChannel:1e3,keepMessagesOnClear:50,loggerinfo:false,blockedPlatforms:[],chatContainerHeight:80}),b=v({}),be=100*1024*1024,ue=50;let we=0,I=null;const Ne="ChatImageCache",Oe=2,$="images",ae=50*1024*1024,M=100,de=500,Le=12*1024*1024,Fe=.8,We=60*1e3;let F=0,K=0,He=0;const g=v(""),S=v(""),Se=v(""),V=v([]),fe=v(false),Te=v(),q=v(false),W=v("bots"),H=v(null),ne=v(null),Be=v(false),he=v({show:false,text:""}),D=v(),me=v(),$e=v(false),se=v(false),ve=v(false),oe=v(""),j=v({x:0,y:0}),A=v({x:0,y:0}),De=v({x:0,y:0}),Ie=v({x:0,y:0}),X=80,qe=v(0),R=v(null),Q=v(false),C=v(null),N=v({show:false,x:0,y:0,type:null,targetId:"",isSecondClick:false}),J=v(new Set),re=v(new Set),m=E(()=>Object.values(u.value.bots).sort((t,a)=>{const n=J.value.has(t.selfId),o=J.value.has(a.selfId);return n&&!o?-1:!n&&o?1:0})),l=E(()=>!g.value||!u.value.channels[g.value]?[]:Object.values(u.value.channels[g.value]).sort((t,a)=>{const n=re.value.has(`${g.value}:${t.id}`),o=re.value.has(`${g.value}:${a.id}`);return n&&!o?-1:!n&&o?1:0})),L=E(()=>{if(!g.value||!S.value)return[];const e=`${g.value}:${S.value}`,t=u.value.messages[e]||[];return t.filter(a=>a.quote),t}),Ue=E(()=>{var t;if(!g.value||!S.value)return"";const e=u.value.channels[g.value];return((t=e==null?void 0:e[S.value])==null?void 0:t.name)||""}),_t=E(()=>!g.value||!S.value?"":`${g.value}:${S.value}`),ct=E(()=>g.value&&S.value&&(Se.value.trim()||V.value.length>0)&&!ve.value),Lt=E(()=>g.value&&S.value&&!ve.value),Tt=E(()=>{if(!q.value)return"";switch(W.value){case"channels":return"show-channels";case"messages":return"show-messages";default:return""}}),qt=E(()=>q.value?"输入消息...(屏幕左滑返回)":"输入消息..."),Ut=E(()=>({}));function it(e){return e.size||0}function Xe(e){we+=e,ie.value.loggerinfo&&console.log(`内存使用量变化: ${e>0?"+":""}${(e/1024/1024).toFixed(2)}MB, 总计: ${(we/1024/1024).toFixed(2)}MB`)}function ut(e=10){const t=Object.entries(b.value);if(t.length<=e)return;const a=t.slice(0,t.length-e);let n=0;a.forEach(([o,s])=>{URL.revokeObjectURL(s),delete b.value[o],n+=500*1024,ie.value.loggerinfo&&console.log("清理旧blob URL:",o)}),Xe(-n)}function Je(){Object.keys(b.value).length>ue&&ut(Math.floor(ue*.7)),we>be&&ut(Math.floor(ue*.5))}function Mt(e){g.value=e,S.value="",at(),q.value&&(W.value="channels")}function Kt(e,t){if(e.preventDefault(),e.stopPropagation(),N.value.show&&N.value.type==="bot"&&N.value.targetId===t){G();return}Ge(e,"bot",t)}function jt(e,t){if(e.preventDefault(),e.stopPropagation(),N.value.show&&N.value.type==="channel"&&N.value.targetId===t){G();return}Ge(e,"channel",t)}function Ge(e,t,a){let s=e.clientX,i=e.clientY;s+180>window.innerWidth&&(s=window.innerWidth-180-10),i+80>window.innerHeight&&(i=window.innerHeight-80-10),N.value={show:true,x:s,y:i,type:t,targetId:a,isSecondClick:false},document.addEventListener("click",G,{once:true}),document.addEventListener("keydown",Ze)}function G(){N.value.show=false,document.removeEventListener("click",G),document.removeEventListener("keydown",Ze)}function Ze(e){e.key==="Escape"&&N.value.show&&G()}async function At(e){J.value.has(e)?J.value.delete(e):J.value.add(e),await P("set-pinned-bots",{pinnedBots:Array.from(J.value)}),G()}async function zt(e){const t=`${g.value}:${e}`;re.value.has(t)?re.value.delete(t):re.value.add(t),await P("set-pinned-channels",{pinnedChannels:Array.from(re.value)}),G()}async function Pt(e){try{const t=await P("delete-bot-data",{selfId:e});if(t.success){const a=Object.keys(u.value.messages).filter(n=>n.startsWith(`${e}:`));for(const n of a)delete u.value.messages[n],delete U.value[n],await Pe(n);delete u.value.bots[e],delete u.value.channels[e],g.value===e&&(g.value="",S.value=""),T(t.message||"已删除该机器人的所有数据","success")}else throw new Error(t.error||"删除失败")}catch(t){console.error("删除机器人数据失败:",t),T("删除失败: "+((t==null?void 0:t.message)||String(t)),"error")}G()}async function Rt(e){try{const t=await P("delete-channel-data",{selfId:g.value,channelId:e});if(t.success){const a=`${g.value}:${e}`;delete u.value.messages[a],delete U.value[a],u.value.channels[g.value]&&delete u.value.channels[g.value][e],await Pe(a),S.value===e&&(S.value=""),T(t.message||"已删除该频道的所有数据","success")}else throw new Error(t.error||"删除失败")}catch(t){console.error("删除频道数据失败:",t),T("删除失败: "+((t==null?void 0:t.message)||String(t)),"error")}G()}async function Nt(e){S.value=e,se.value=false,at(),q.value&&(W.value="messages"),g.value&&await Ct(g.value,e),ye(()=>{ge(),!q.value&&me.value&&me.value.focus()})}async function Ot(){if(!ct.value)return;const e=Se.value.trim();if(!e&&V.value.length===0)return;ve.value=true;const t=[...V.value];try{const a=await P("send-message",{selfId:g.value,channelId:S.value,content:e,images:t.map(n=>({tempId:n.tempId,filename:n.filename}))});if(a.success){if(Se.value="",t.forEach(n=>{URL.revokeObjectURL(n.preview)}),V.value=[],fe.value=false,a.tempImageIds&&a.tempImageIds.length>0)try{await P("cleanup-temp-images",{tempImageIds:a.tempImageIds}),console.log("临时图片清理完成:",a.tempImageIds)}catch(n){console.warn("清理临时图片失败:",n)}}else console.error("消息发送失败:",a.error),T("发送失败: "+a.error,"error")}catch(a){console.error("发送消息时出错:",a),T("发送失败: "+((a==null?void 0:a.message)||String(a)),"error")}finally{ve.value=false}}function Ft(){fe.value=!fe.value}function Ht(){Te.value&&Te.value.click(),fe.value=false}async function Xt(e){const t=e.target,a=t.files;if(!(!a||a.length===0)){for(const n of Array.from(a))await Qe(n);t.value=""}}async function Yt(e){var a;const t=(a=e.clipboardData)==null?void 0:a.items;if(t){for(const n of Array.from(t))if(n.type.startsWith("image/")){e.preventDefault();const o=n.getAsFile();if(o){const s=n.type==="image/gif"?".gif":n.type==="image/png"?".png":(n.type==="image/jpeg",".jpg"),i=`pasted-image-${Date.now()}${s}`,c=new File([o],i,{type:n.type,lastModified:Date.now()});await Qe(c)}}}}async function Qe(e){try{if(e.size>10*1024*1024){T("图片文件过大,请选择小于10MB的图片","error");return}if(!e.type.startsWith("image/")){T("请选择图片文件","error");return}const t=await dt(e),a=URL.createObjectURL(e),n=await P("upload-image",{file:t,filename:e.name,mimeType:e.type,isGif:e.type==="image/gif"});n.success?V.value.push({tempId:n.tempId,filename:e.name,preview:a,size:e.size}):(URL.revokeObjectURL(a),T("图片上传失败: "+n.error,"error"))}catch(t){console.error("上传图片失败:",t),T("图片上传失败: "+((t==null?void 0:t.message)||String(t)),"error")}}async function Vt(e){try{const t=V.value.findIndex(a=>a.tempId===e);if(t!==-1){const a=V.value[t];URL.revokeObjectURL(a.preview),V.value.splice(t,1)}await P("delete-temp-image",{tempId:e})}catch(t){console.error("删除图片失败:",t)}}function dt(e){return new Promise((t,a)=>{const n=new FileReader;n.onload=()=>t(n.result),n.onerror=a,n.readAsDataURL(e)})}function Ee(e){const t=e.target;t.closest(".input-actions")||(fe.value=false),t.closest(".context-menu")||G()}function Wt(e){return new Date(e).toLocaleTimeString("zh-CN",{hour:"2-digit",minute:"2-digit"})}function Jt(e){if(typeof e=="number")switch(e){case 0:return"文本";case 1:return"私聊";default:return"未知"}return String(e)}function ge(){D.value&&(D.value.scrollTop=D.value.scrollHeight,$e.value=false,se.value=false)}function Me(){if(D.value){const{scrollTop:e,scrollHeight:t,clientHeight:a}=D.value,o=t-(e+a)<=50,s=!o;$e.value=s,o?se.value=false:se.value=true}}function Ke(){if(!D.value)return true;const{scrollTop:e,scrollHeight:t,clientHeight:a}=D.value;return t-(e+a)<=200}function ft(e){var n;if(!g.value)return 0;const t=`${g.value}:${e}`,a=U.value[t];return a!==void 0?a:((n=u.value.messages[t])==null?void 0:n.length)||0}function Gt(e,t){e.preventDefault(),e.stopPropagation();const a="touches"in e?e.touches[0].clientX:e.clientX,n="touches"in e?e.touches[0].clientY:e.clientY;qe.value=Date.now(),j.value={x:a,y:n},A.value={x:a,y:n},Q.value=false;const s=e.target.getBoundingClientRect();De.value={x:s.left,y:s.top},Ie.value={x:a-s.left,y:n-s.top},R.value=window.setTimeout(()=>{if(qe.value>0){Q.value=true,oe.value=t;const i=e.target,c=i.cloneNode(true);c.classList.add("dragging-clone"),c.style.position="fixed",c.style.zIndex="1000",c.style.pointerEvents="none";const d=i.getBoundingClientRect();c.style.left=`${d.left}px`,c.style.top=`${d.top}px`,c.style.width=`${d.width}px`,c.style.height=`${d.height}px`,document.body.appendChild(c),C.value=c,document.body.style.userSelect="none",document.body.style.cursor="grabbing",document.body.classList.add("dragging-bubble-global"),mt(j.value.x,j.value.y)}},60),document.addEventListener("mousemove",je),document.addEventListener("mouseup",Ae),document.addEventListener("touchmove",je),document.addEventListener("touchend",Ae)}function je(e){if(!Q.value||!C.value)return;e.preventDefault();const t="touches"in e?e.touches[0].clientX:e.clientX,a="touches"in e?e.touches[0].clientY:e.clientY;A.value={x:t,y:a};const n=A.value.x-j.value.x,o=A.value.y-j.value.y,s=Math.sqrt(n*n+o*o),i=Math.max(.3,1-s/(X*2)),c=Math.max(.8,1-s/(X*3)),d=s>X,p=A.value.x-Ie.value.x,k=A.value.y-Ie.value.y;C.value.style.left=`${p}px`,C.value.style.top=`${k}px`,C.value.style.transform=`scale(${c})`,C.value.style.opacity=`${i}`,C.value.style.backgroundColor=d?"#f44336":"#2196f3",C.value.style.boxShadow=d?"0 4px 12px rgba(244, 67, 54, 0.4)":"0 4px 12px rgba(33, 150, 243, 0.4)",d?C.value.classList.add("will-delete"):C.value.classList.remove("will-delete")}function Ae(e){if(R.value&&(clearTimeout(R.value),R.value=null),R.value&&(clearTimeout(R.value),R.value=null),!Q.value||!oe.value){ze();return}const t=oe.value;if(Math.sqrt(Math.pow(A.value.x-j.value.x,2)+Math.pow(A.value.y-j.value.y,2))>X)ht(t),ze();else if(C.value){C.value.style.transition="all 0.3s cubic-bezier(0.68, -0.55, 0.265, 1.55)";const n=document.querySelector(`[data-channel-id="${t}"] .channel-message-count`);if(n){const o=n.getBoundingClientRect();C.value.style.left=`${o.left}px`,C.value.style.top=`${o.top}px`,C.value.style.transform="scale(1)",C.value.style.opacity="1",C.value.style.backgroundColor="#2196f3",C.value.style.boxShadow="0 4px 12px rgba(33, 150, 243, 0.4)"}setTimeout(()=>{ze()},300)}else ze()}function ze(){R.value&&(clearTimeout(R.value),R.value=null),oe.value="",j.value={x:0,y:0},A.value={x:0,y:0},De.value={x:0,y:0},qe.value=0,Q.value=false,document.removeEventListener("mousemove",je),document.removeEventListener("mouseup",Ae),document.removeEventListener("touchmove",je),document.removeEventListener("touchend",Ae),document.body.style.userSelect="",document.body.style.cursor="",document.body.classList.remove("dragging-bubble-global"),C.value&&C.value.parentNode&&(C.value.parentNode.removeChild(C.value),C.value=null),vt()}function Zt(e){return oe.value===e&&Q.value?{visibility:"hidden",pointerEvents:"none",transition:"none"}:{}}function Qt(e){if(oe.value!==e)return 0;const t=A.value.x-j.value.x,a=A.value.y-j.value.y;return Math.sqrt(t*t+a*a)}async function ht(e){if(g.value)try{const t=`${g.value}:${e}`,a=ft(e),n=ie.value.keepMessagesOnClear;if(n>0&&a<=n){T("当前消息还很少诶~ 无需清理","success");return}const o=await P("clear-channel-history",{selfId:g.value,channelId:e});if(o.success)if(o.clearedCount&&o.clearedCount>0){if(u.value.messages[t]){const i=[...u.value.messages[t]].sort((c,d)=>c.timestamp-d.timestamp);u.value.messages[t]=i.slice(-o.keptCount)}U.value[t]=o.keptCount,await Pe(t),T(`历史记录已清理,清理了 ${o.clearedCount} 条消息,保留最新 ${o.keptCount} 条`,"success")}else n===0?(u.value.messages[t]&&(u.value.messages[t]=[]),U.value[t]=0,await Pe(t),T("历史记录已清理,所有消息已删除","success")):T("当前消息还很少诶~ 无需清理","success");else console.error("清理历史记录失败:",o.error),T("清理失败: "+o.error,"error")}catch(t){console.error("清理历史记录时出错:",t),T("清理失败: "+((t==null?void 0:t.message)||String(t)),"error")}}function T(e,t="success"){const a=document.createElement("div");a.className=`notification ${t}`,a.textContent=e;let n="#4caf50";switch(t){case"info":n="#2196f3";break;case"warn":n="#ff9800";break;case"error":n="#f44336";break;case"success":n="#4caf50";break}a.style.cssText=`
|
|
1
|
+
import{defineComponent as Ce,ref as g,onMounted as it,h as m,computed as te,watch as Tt,nextTick as me,onUnmounted as ba,createElementBlock as C,openBlock as w,unref as c,normalizeStyle as ut,normalizeClass as re,createCommentVNode as B,createElementVNode as f,Fragment as ae,renderList as Le,createBlock as Fe,toDisplayString as D,withDirectives as Mt,createTextVNode as dt,vShow as ka,withModifiers as qt,withKeys as Sa,isRef as Ba,vModelText as $a,resolveComponent as Da}from"vue";import{receive as ft,send as z,icons as _a}from"@koishijs/client";function La(){function ne(e){try{return new URL(e).protocol==="file:"}catch{return false}}const ce=Ce({props:{src:{type:String,required:true},alt:{type:String,default:"头像"},channelKey:{type:String,required:true}},setup(e){const t=g("loading"),a=g(e.src),n=g(""),o=async()=>{try{t.value="loading";const i=await ot(e.channelKey,e.src);if(i){a.value=i,t.value="loaded";return}const l=new Image;l.crossOrigin="anonymous",l.referrerPolicy="no-referrer",l.draggable=false;const d=new Promise((p,I)=>{l.onload=()=>p(),l.onerror=()=>I(new Error("Direct load failed")),l.src=e.src}),v=new Promise((p,I)=>{setTimeout(()=>I(new Error("Timeout")),3e3)});try{await Promise.race([d,v]),a.value=e.src,t.value="loaded",Ie(e.channelKey,e.src).catch(p=>{console.warn("异步缓存头像失败:",p)})}catch{await s()}}catch(i){console.error("头像加载失败:",i),t.value="error",n.value="头像加载失败"}},s=async()=>{try{t.value="caching";const i=await Ie(e.channelKey,e.src);if(i)a.value=i,t.value="loaded";else throw new Error("缓存系统加载失败")}catch(i){console.error("缓存系统加载头像失败:",i),t.value="error",n.value=(i==null?void 0:i.message)||"缓存加载失败"}};return it(()=>{o()}),()=>{switch(t.value){case"loading":case"caching":return m("div",{class:"avatar-placeholder"},e.alt.charAt(0).toUpperCase());case"loaded":return m("img",{src:a.value,alt:e.alt,draggable:false,style:{width:"100%",height:"100%","object-fit":"cover"}});case"error":return m("div",{class:"avatar-placeholder"},e.alt.charAt(0).toUpperCase());default:return m("div",{class:"avatar-placeholder"},e.alt.charAt(0).toUpperCase())}}}}),X=Ce({props:{src:{type:String,required:true},alt:{type:String,default:"图片"},filename:{type:String,default:""},channelKey:{type:String,required:true}},setup(e){const t=g("loading"),a=g(e.src),n=g(""),o=g(null),s=async()=>{try{t.value="loading";const l=await ot(e.channelKey,e.src);if(l){a.value=l,t.value="loaded";return}if(ne(e.src)){console.log("ImageComponent: 检测到本地文件,使用代理请求:",e.src),await i();return}const d=new Image;d.crossOrigin="anonymous",d.referrerPolicy="no-referrer",d.draggable=false;const v=new Promise((I,q)=>{d.onload=()=>I(),d.onerror=()=>q(new Error("Direct load failed")),d.src=e.src}),p=new Promise((I,q)=>{setTimeout(()=>q(new Error("Timeout")),3e3)});try{await Promise.race([v,p]),a.value=e.src,t.value="loaded",Ie(e.channelKey,e.src).catch(I=>{console.warn("异步缓存图片失败:",I)})}catch{await i()}}catch(l){console.error("图片加载失败:",l),t.value="error",n.value="图片加载失败"}},i=async()=>{try{t.value="caching";const l=await Ie(e.channelKey,e.src);if(l)a.value=l,t.value="loaded";else throw new Error("缓存系统加载失败")}catch(l){console.error("缓存系统加载图片失败:",l),t.value="error",n.value=(l==null?void 0:l.message)||"缓存加载失败"}};return it(()=>{s()}),()=>{switch(t.value){case"loading":return m("div",{class:"message-image-loading"},"加载中...");case"caching":return m("div",{class:"message-image-loading"},"[图片加载缓存中...]");case"loaded":return m("img",{src:a.value,alt:e.alt,class:"message-image",loading:"lazy",ref:o,draggable:false,style:{"max-width":"min(400px, 66.67vw)","max-height":"200px",width:"auto",height:"auto","object-fit":"contain"},onLoad:()=>{o.value&&e.src.toLowerCase().includes(".gif")&&(o.value.style.imageRendering="auto")}});case"error":return m("div",{class:"message-image-error"},["图片加载失败",m("br"),m("small",e.filename||e.alt||"未知图片"),m("br"),m("small",{style:"color: #ff9800;"},n.value)]);default:return m("div",{class:"message-image-error"},"未知状态")}}}}),ve=Ce({props:{data:{type:String,required:true},channelKey:{type:String,required:true}},setup(e){const a=(()=>{try{const o=JSON.parse(e.data);if(o.meta&&o.meta.detail_1){const s=o.meta.detail_1;return{type:"share_card",title:s.title||o.prompt||"分享内容",desc:s.desc||"",preview:s.preview?s.preview.replace(/\\\//g,"/"):"",icon:s.icon?s.icon.replace(/\\\//g,"/"):"",url:s.qqdocurl?s.qqdocurl.replace(/\\\//g,"/"):s.url?s.url.replace(/\\\//g,"/"):"",appName:s.title||"应用"}}return{type:"raw",data:o}}catch(o){return console.error("解析JSON数据失败:",o),{type:"error",error:"无法解析的JSON数据"}}})(),n=()=>{a.type==="share_card"&&a.url&&window.open(a.url,"_blank","noopener,noreferrer")};return()=>a.type==="share_card"&&a.preview?m("img",{src:a.preview,alt:a.title||"[分享小程序]",class:"message-image",loading:"lazy",draggable:false,onClick:n,style:{"max-width":"400px","max-height":"200px",width:"auto",height:"auto","object-fit":"contain",cursor:a.url?"pointer":"default"},title:a.url?`点击打开: ${a.title||"链接"}`:a.title,onError:o=>{const i=o.target.parentElement;i&&(i.style.display="none")}}):a.type==="error"?m("div",{class:"message-json-error"},[m("span",{class:"json-error-text"},a.error),m("details",{class:"json-raw-data"},[m("summary","查看原始数据"),m("pre",{class:"json-raw-content"},e.data)])]):m("div",{class:"message-json-raw"},[m("div",{class:"json-label"},"[JSON数据]"),m("details",{class:"json-raw-data"},[m("summary","查看详情"),m("pre",{class:"json-raw-content"},JSON.stringify(a.data,null,2))])])}}),E=Ce({props:{element:{type:Object,required:true},channelKey:{type:String,required:true}},setup(e){const t=g(false),a=()=>{t.value=!t.value},n=()=>{if(!e.element.children||e.element.children.length===0)return{previews:[],messageCount:0};const s=e.element.children.filter(d=>d.type==="message"),i=s.length;return{previews:s.slice(0,3).map(d=>{var I,q;const v=((I=d.attrs)==null?void 0:I.nickname)||"用户";let p="";if(d.children&&d.children.length>0){const H=d.children[0];H.type==="text"?(p=(((q=H.attrs)==null?void 0:q.content)||"").substring(0,20),p.length>15&&(p+="...")):H.type==="img"?p="[图片]":H.type==="video"?p="[视频]":p=`[${H.type}]`}return`${v}:${p}`}),messageCount:i}},o=(s,i)=>{var v,p,I;const l=((v=s.attrs)==null?void 0:v.nickname)||"用户",d=((p=s.attrs)==null?void 0:p.userId)||"unknown";return m("div",{key:i,class:"forwarded-message-item"},[m("div",{class:"forwarded-message-header"},[m("span",{class:"forwarded-message-nickname"},l),m("span",{class:"forwarded-message-userid"},`(${d})`)]),m("div",{class:"forwarded-message-content"},((I=s.children)==null?void 0:I.map((q,H)=>m(be,{key:H,element:q,channelKey:e.channelKey})))||[])])};return()=>{var l;const{previews:s,messageCount:i}=n();return m("div",{class:"forward-message-container"},[m("div",{class:"forward-message-preview",onClick:a},[m("div",{class:"forward-message-title"},"聊天记录"),...s.map((d,v)=>m("div",{key:v,class:"forward-message-preview-item"},d)),m("div",{class:"forward-message-footer"},[m("span",{class:"forward-message-count"},`查看${i}条转发消息`),m("span",{class:"forward-message-toggle"},t.value?"▲":"▼")])]),t.value&&m("div",{class:"forward-message-expanded"},((l=e.element.children)==null?void 0:l.filter(d=>d.type==="message").map((d,v)=>o(d,v)))||[])])}}}),be=Ce({props:{element:{type:Object,required:true},channelKey:{type:String,required:true}},setup(e){const t=a=>{var n,o,s,i,l,d;switch(a.type){case"text":return m("span",{class:"message-text-content"},a.attrs.content||"");case"forward":return m("span",{class:"message-text-content"},`[转发消息 ${a.attrs.id}]`||"[转发消息]");case"img":case"image":const v=a.attrs.src||a.attrs.url||a.attrs.file;return m("div",{class:"message-image-container"},[m(X,{src:v,alt:a.attrs.summary||"图片",filename:a.attrs.filename||a.attrs.summary||"",channelKey:e.channelKey})]);case"mface":const p=a.attrs.src||a.attrs.url||a.attrs.file;return m("div",{class:"message-image-container"},[m(X,{src:p,alt:a.attrs.summary||"表情",filename:a.attrs.emojiId||a.attrs.summary||"",channelKey:e.channelKey})]);case"face":if((o=(n=a.children[0])==null?void 0:n.attrs)!=null&&o.src){const I=((i=(s=a.children[0])==null?void 0:s.attrs)==null?void 0:i.src)||((d=(l=a.children[0])==null?void 0:l.attrs)==null?void 0:d.url);return m("div",{class:"message-image-container"},[m(X,{src:I,alt:a.attrs.name||a.attrs.id||"[表情]",filename:a.attrs.name||a.attrs.id||"[表情]",channelKey:e.channelKey})])}else return m("span",{class:"message-text-content"},`[${a.attrs.name||a.attrs.id}]`||"[表情]");case"at":return m("span",{class:"message-at",title:a.attrs.name},`${a.attrs.name||a.attrs.id}`);case"json":return m("div",{class:"message-image-container"},[m(ve,{data:a.attrs.data||"",channelKey:e.channelKey})]);case"p":if(a.children&&a.children.length>0){const I=a.children.map((q,H)=>m(be,{key:H,element:q,channelKey:e.channelKey}));return m("div",{class:"message-paragraph"},I)}else return m("div",{class:"message-paragraph"},"");case"figure":return m(E,{element:a,channelKey:e.channelKey});default:return m("span",{class:"message-unknown",title:`未知消息类型: ${a.type}`},a.attrs.content||`[${a.type}]`)}};return()=>t(e.element)}}),u=g({bots:{},channels:{},messages:{}}),U=g({}),K=g({}),ie=g({maxMessagesPerChannel:1e3,keepMessagesOnClear:50,loggerinfo:false,blockedPlatforms:[],chatContainerHeight:80}),b=g({}),Y=new Map,Te=100*1024*1024,ge=50;let ye=0,x=null;const He="ChatImageCache",Me=2,$="images",_=50*1024*1024,Xe=100,ue=500,qe=12*1024*1024,Ye=.8,Ge=60*1e3;let j=0,V=0,Ve=0;const y=g(""),S=g(""),ke=g(""),W=g([]),de=g(false),Ke=g(),P=g(false),J=g("bots"),N=g(null),se=g(null),Se=g(false),G=g({show:false,text:""}),L=g(),fe=g(),Be=g(false),oe=g(false),pe=g(false),we=g(false),le=g(""),R=g({x:0,y:0}),A=g({x:0,y:0}),$e=g({x:0,y:0}),De=g({x:0,y:0}),F=80,_e=g(0),O=g(null),ee=g(false),k=g(null),Z=g({show:false,x:0,y:0,type:null,targetId:"",isSecondClick:false}),h=g(new Set),r=g(new Set),M=te(()=>Object.values(u.value.bots).sort((t,a)=>{const n=h.value.has(t.selfId),o=h.value.has(a.selfId);return n&&!o?-1:!n&&o?1:0})),je=te(()=>!y.value||!u.value.channels[y.value]?[]:Object.values(u.value.channels[y.value]).sort((t,a)=>{const n=r.value.has(`${y.value}:${t.id}`),o=r.value.has(`${y.value}:${a.id}`);return n&&!o?-1:!n&&o?1:0})),ht=te(()=>{if(!y.value||!S.value)return[];const e=`${y.value}:${S.value}`,t=u.value.messages[e]||[];return t.filter(a=>a.quote),t}),jt=te(()=>{var t;if(!y.value||!S.value)return"";const e=u.value.channels[y.value];return((t=e==null?void 0:e[S.value])==null?void 0:t.name)||""}),Pt=te(()=>!y.value||!S.value?"":`${y.value}:${S.value}`),mt=te(()=>y.value&&S.value&&(ke.value.trim()||W.value.length>0)&&!pe.value),At=te(()=>y.value&&S.value&&!pe.value),Ut=te(()=>{if(!P.value)return"";switch(J.value){case"channels":return"show-channels";case"messages":return"show-messages";default:return""}}),Rt=te(()=>P.value?"输入消息...(屏幕左滑返回)":"输入消息..."),zt=te(()=>({}));function Ze(e){return e.size||0}function Pe(e){ye+=e,ie.value.loggerinfo&&console.log(`内存使用量变化: ${e>0?"+":""}${(e/1024/1024).toFixed(2)}MB, 总计: ${(ye/1024/1024).toFixed(2)}MB`)}function vt(e=10){const t=Object.entries(b.value);if(t.length<=e)return;const a=t.slice(0,t.length-e);let n=0;a.forEach(([o,s])=>{URL.revokeObjectURL(s),delete b.value[o],n+=500*1024,ie.value.loggerinfo&&console.log("清理旧blob URL:",o)}),Pe(-n)}function We(){Object.keys(b.value).length>ge&&vt(Math.floor(ge*.7)),ye>Te&&vt(Math.floor(ge*.5))}function Ot(e){y.value=e,S.value="",lt(),P.value&&(J.value="channels")}function Nt(e,t){if(e.preventDefault(),e.stopPropagation(),Z.value.show&&Z.value.type==="bot"&&Z.value.targetId===t){Q();return}Qe(e,"bot",t)}function Ft(e,t){if(e.preventDefault(),e.stopPropagation(),Z.value.show&&Z.value.type==="channel"&&Z.value.targetId===t){Q();return}Qe(e,"channel",t)}function Qe(e,t,a){let s=e.clientX,i=e.clientY;s+180>window.innerWidth&&(s=window.innerWidth-180-10),i+80>window.innerHeight&&(i=window.innerHeight-80-10),Z.value={show:true,x:s,y:i,type:t,targetId:a,isSecondClick:false},document.addEventListener("click",Q,{once:true}),document.addEventListener("keydown",Ee)}function Q(){Z.value.show=false,document.removeEventListener("click",Q),document.removeEventListener("keydown",Ee)}function Ee(e){e.key==="Escape"&&Z.value.show&&Q()}async function Ht(e){h.value.has(e)?h.value.delete(e):h.value.add(e),await z("set-pinned-bots",{pinnedBots:Array.from(h.value)}),Q()}async function Xt(e){const t=`${y.value}:${e}`;r.value.has(t)?r.value.delete(t):r.value.add(t),await z("set-pinned-channels",{pinnedChannels:Array.from(r.value)}),Q()}async function Yt(e){try{const t=await z("delete-bot-data",{selfId:e});if(t.success){const a=Object.keys(u.value.messages).filter(n=>n.startsWith(`${e}:`));for(const n of a)delete u.value.messages[n],delete U.value[n],await Ne(n);delete u.value.bots[e],delete u.value.channels[e],y.value===e&&(y.value="",S.value=""),T(t.message||"已删除该机器人的所有数据","success")}else throw new Error(t.error||"删除失败")}catch(t){console.error("删除机器人数据失败:",t),T("删除失败: "+((t==null?void 0:t.message)||String(t)),"error")}Q()}async function Vt(e){try{const t=await z("delete-channel-data",{selfId:y.value,channelId:e});if(t.success){const a=`${y.value}:${e}`;delete u.value.messages[a],delete U.value[a],u.value.channels[y.value]&&delete u.value.channels[y.value][e],await Ne(a),S.value===e&&(S.value=""),T(t.message||"已删除该频道的所有数据","success")}else throw new Error(t.error||"删除失败")}catch(t){console.error("删除频道数据失败:",t),T("删除失败: "+((t==null?void 0:t.message)||String(t)),"error")}Q()}async function Wt(e){S.value=e,oe.value=false,lt(),P.value&&(J.value="messages");const t=`${y.value}:${e}`;delete K.value[t],y.value&&await rt(y.value,e,50,0),me(()=>{he(),!P.value&&fe.value&&fe.value.focus()})}async function Jt(){if(!mt.value)return;const e=ke.value.trim();if(!e&&W.value.length===0)return;pe.value=true;const t=[...W.value];try{const a=await z("send-message",{selfId:y.value,channelId:S.value,content:e,images:t.map(n=>({tempId:n.tempId,filename:n.filename}))});if(a.success){if(ke.value="",t.forEach(n=>{URL.revokeObjectURL(n.preview)}),W.value=[],de.value=false,a.tempImageIds&&a.tempImageIds.length>0)try{await z("cleanup-temp-images",{tempImageIds:a.tempImageIds}),console.log("临时图片清理完成:",a.tempImageIds)}catch(n){console.warn("清理临时图片失败:",n)}}else console.error("消息发送失败:",a.error),T("发送失败: "+a.error,"error")}catch(a){console.error("发送消息时出错:",a),T("发送失败: "+((a==null?void 0:a.message)||String(a)),"error")}finally{pe.value=false}}function Gt(){de.value=!de.value}function Zt(){Ke.value&&Ke.value.click(),de.value=false}async function Qt(e){const t=e.target,a=t.files;if(!(!a||a.length===0)){for(const n of Array.from(a))await et(n);t.value=""}}async function Et(e){var a;const t=(a=e.clipboardData)==null?void 0:a.items;if(t){for(const n of Array.from(t))if(n.type.startsWith("image/")){e.preventDefault();const o=n.getAsFile();if(o){const s=n.type==="image/gif"?".gif":n.type==="image/png"?".png":(n.type==="image/jpeg",".jpg"),i=`pasted-image-${Date.now()}${s}`,l=new File([o],i,{type:n.type,lastModified:Date.now()});await et(l)}}}}async function et(e){try{if(e.size>10*1024*1024){T("图片文件过大,请选择小于10MB的图片","error");return}if(!e.type.startsWith("image/")){T("请选择图片文件","error");return}const t=await gt(e),a=URL.createObjectURL(e),n=await z("upload-image",{file:t,filename:e.name,mimeType:e.type,isGif:e.type==="image/gif"});n.success?W.value.push({tempId:n.tempId,filename:e.name,preview:a,size:e.size}):(URL.revokeObjectURL(a),T("图片上传失败: "+n.error,"error"))}catch(t){console.error("上传图片失败:",t),T("图片上传失败: "+((t==null?void 0:t.message)||String(t)),"error")}}async function ea(e){try{const t=W.value.findIndex(a=>a.tempId===e);if(t!==-1){const a=W.value[t];URL.revokeObjectURL(a.preview),W.value.splice(t,1)}await z("delete-temp-image",{tempId:e})}catch(t){console.error("删除图片失败:",t)}}function gt(e){return new Promise((t,a)=>{const n=new FileReader;n.onload=()=>t(n.result),n.onerror=a,n.readAsDataURL(e)})}function tt(e){const t=e.target;t.closest(".input-actions")||(de.value=false),t.closest(".context-menu")||Q()}function ta(e){return new Date(e).toLocaleTimeString("zh-CN",{hour:"2-digit",minute:"2-digit"})}function aa(e){if(typeof e=="number")switch(e){case 0:return"文本";case 1:return"私聊";default:return"未知"}return String(e)}function he(){L.value&&(L.value.scrollTop=L.value.scrollHeight,Be.value=false,oe.value=false)}function Ae(){if(L.value){const{scrollTop:e,scrollHeight:t,clientHeight:a}=L.value,o=t-(e+a)<=50,s=!o;Be.value=s,o?oe.value=false:oe.value=true,e<=10&&y.value&&S.value&&yt()}}async function yt(){if(!y.value||!S.value)return;const e=`${y.value}:${S.value}`,t=K.value[e];if(!(we.value||t&&!t.hasMore)){K.value[e]||(K.value[e]={offset:0,hasMore:true,loading:false}),we.value=true,K.value[e].loading=true;try{const a=(t==null?void 0:t.offset)||0;await rt(y.value,S.value,50,a)||(K.value[e].loading=false)}catch(a){console.error("加载更多消息失败:",a),K.value[e]&&(K.value[e].loading=false)}finally{we.value=false}}}function Ue(){if(!L.value)return true;const{scrollTop:e,scrollHeight:t,clientHeight:a}=L.value;return t-(e+a)<=200}function pt(e){var n;if(!y.value)return 0;const t=`${y.value}:${e}`,a=U.value[t];return a!==void 0?a:((n=u.value.messages[t])==null?void 0:n.length)||0}function na(e,t){e.preventDefault(),e.stopPropagation();const a="touches"in e?e.touches[0].clientX:e.clientX,n="touches"in e?e.touches[0].clientY:e.clientY;_e.value=Date.now(),R.value={x:a,y:n},A.value={x:a,y:n},ee.value=false;const s=e.target.getBoundingClientRect();$e.value={x:s.left,y:s.top},De.value={x:a-s.left,y:n-s.top},O.value=window.setTimeout(()=>{if(_e.value>0){ee.value=true,le.value=t;const i=e.target,l=i.cloneNode(true);l.classList.add("dragging-clone"),l.style.position="fixed",l.style.zIndex="1000",l.style.pointerEvents="none";const d=i.getBoundingClientRect();l.style.left=`${d.left}px`,l.style.top=`${d.top}px`,l.style.width=`${d.width}px`,l.style.height=`${d.height}px`,document.body.appendChild(l),k.value=l,document.body.style.userSelect="none",document.body.style.cursor="grabbing",document.body.classList.add("dragging-bubble-global"),It(R.value.x,R.value.y)}},60),document.addEventListener("mousemove",Re),document.addEventListener("mouseup",ze),document.addEventListener("touchmove",Re),document.addEventListener("touchend",ze)}function Re(e){if(!ee.value||!k.value)return;e.preventDefault();const t="touches"in e?e.touches[0].clientX:e.clientX,a="touches"in e?e.touches[0].clientY:e.clientY;A.value={x:t,y:a};const n=A.value.x-R.value.x,o=A.value.y-R.value.y,s=Math.sqrt(n*n+o*o),i=Math.max(.3,1-s/(F*2)),l=Math.max(.8,1-s/(F*3)),d=s>F,v=A.value.x-De.value.x,p=A.value.y-De.value.y;k.value.style.left=`${v}px`,k.value.style.top=`${p}px`,k.value.style.transform=`scale(${l})`,k.value.style.opacity=`${i}`,k.value.style.backgroundColor=d?"#f44336":"#2196f3",k.value.style.boxShadow=d?"0 4px 12px rgba(244, 67, 54, 0.4)":"0 4px 12px rgba(33, 150, 243, 0.4)",d?k.value.classList.add("will-delete"):k.value.classList.remove("will-delete")}function ze(e){if(O.value&&(clearTimeout(O.value),O.value=null),O.value&&(clearTimeout(O.value),O.value=null),!ee.value||!le.value){Oe();return}const t=le.value;if(Math.sqrt(Math.pow(A.value.x-R.value.x,2)+Math.pow(A.value.y-R.value.y,2))>F)wt(t),Oe();else if(k.value){k.value.style.transition="all 0.3s cubic-bezier(0.68, -0.55, 0.265, 1.55)";const n=document.querySelector(`[data-channel-id="${t}"] .channel-message-count`);if(n){const o=n.getBoundingClientRect();k.value.style.left=`${o.left}px`,k.value.style.top=`${o.top}px`,k.value.style.transform="scale(1)",k.value.style.opacity="1",k.value.style.backgroundColor="#2196f3",k.value.style.boxShadow="0 4px 12px rgba(33, 150, 243, 0.4)"}setTimeout(()=>{Oe()},300)}else Oe()}function Oe(){O.value&&(clearTimeout(O.value),O.value=null),le.value="",R.value={x:0,y:0},A.value={x:0,y:0},$e.value={x:0,y:0},_e.value=0,ee.value=false,document.removeEventListener("mousemove",Re),document.removeEventListener("mouseup",ze),document.removeEventListener("touchmove",Re),document.removeEventListener("touchend",ze),document.body.style.userSelect="",document.body.style.cursor="",document.body.classList.remove("dragging-bubble-global"),k.value&&k.value.parentNode&&(k.value.parentNode.removeChild(k.value),k.value=null),xt()}function sa(e){return le.value===e&&ee.value?{visibility:"hidden",pointerEvents:"none",transition:"none"}:{}}function oa(e){if(le.value!==e)return 0;const t=A.value.x-R.value.x,a=A.value.y-R.value.y;return Math.sqrt(t*t+a*a)}async function wt(e){if(y.value)try{const t=`${y.value}:${e}`,a=pt(e),n=ie.value.keepMessagesOnClear;if(n>0&&a<=n){T("当前消息还很少诶~ 无需清理","success");return}const o=await z("clear-channel-history",{selfId:y.value,channelId:e});if(o.success)if(o.clearedCount&&o.clearedCount>0){if(u.value.messages[t]){const i=[...u.value.messages[t]].sort((l,d)=>l.timestamp-d.timestamp);u.value.messages[t]=i.slice(-o.keptCount)}U.value[t]=o.keptCount,await Ne(t),T(`历史记录已清理,清理了 ${o.clearedCount} 条消息,保留最新 ${o.keptCount} 条`,"success")}else n===0?(u.value.messages[t]&&(u.value.messages[t]=[]),U.value[t]=0,await Ne(t),T("历史记录已清理,所有消息已删除","success")):T("当前消息还很少诶~ 无需清理","success");else console.error("清理历史记录失败:",o.error),T("清理失败: "+o.error,"error")}catch(t){console.error("清理历史记录时出错:",t),T("清理失败: "+((t==null?void 0:t.message)||String(t)),"error")}}function T(e,t="success"){const a=document.createElement("div");a.className=`notification ${t}`,a.textContent=e;let n="#4caf50";switch(t){case"info":n="#2196f3";break;case"warn":n="#ff9800";break;case"error":n="#f44336";break;case"success":n="#4caf50";break}a.style.cssText=`
|
|
2
2
|
position: fixed;
|
|
3
3
|
top: 20px;
|
|
4
4
|
right: 20px;
|
|
@@ -19,9 +19,9 @@ import{defineComponent as Ce,ref as v,onMounted as st,h,computed as E,watch as S
|
|
|
19
19
|
from { transform: translateX(0); opacity: 1; }
|
|
20
20
|
to { transform: translateX(100%); opacity: 0; }
|
|
21
21
|
}
|
|
22
|
-
`,document.head.appendChild(o),document.body.appendChild(a),setTimeout(()=>{a.style.animation="slideOut 0.3s ease-in",setTimeout(()=>{a.parentNode&&a.parentNode.removeChild(a),o.parentNode&&o.parentNode.removeChild(o)},300)},3e3)}function
|
|
23
|
-
left: ${e-
|
|
24
|
-
top: ${t-
|
|
25
|
-
width: ${
|
|
26
|
-
height: ${
|
|
27
|
-
`,document.body.appendChild(a),window.dragThresholdCircle=a}function vt(){const e=window.dragThresholdCircle;e&&e.parentNode&&(e.parentNode.removeChild(e),window.dragThresholdCircle=null)}async function et(){try{if(!I)return false;const e=Date.now();if(e-He<We)return true;He=e;const t=await gt();F=t.totalSize,K=t.totalImages,console.log("数据库健康检查:",{大小:`${(F/1024/1024).toFixed(2)}MB / ${(ae/1024/1024).toFixed(2)}MB`,图片数量:`${K} / ${de}`,使用率:`${(F/ae*100).toFixed(1)}%`});const a=F/ae,n=K/de;return(a>Fe||n>Fe)&&(console.warn("数据库使用率过高,开始自动清理"),await Ye()),(a>.95||n>.95)&&(console.error("数据库接近极限,执行紧急清理"),await Et()),true}catch(e){return console.error("数据库健康检查失败:",e),false}}async function gt(){return I?new Promise(e=>{const n=I.transaction([$],"readonly").objectStore($).getAll();n.onsuccess=()=>{const o=n.result||[];let s=0;const i={};o.forEach(c=>{s+=c.size||0,i[c.channelKey]=(i[c.channelKey]||0)+1}),e({totalSize:s,totalImages:o.length,channelStats:i})},n.onerror=()=>{console.error("获取数据库统计失败:",n.error),e({totalSize:0,totalImages:0,channelStats:{}})}}):{totalSize:0,totalImages:0,channelStats:{}}}async function Ye(){try{console.log("开始自动清理...");const e=await yt();if(e.length===0)return;const t={};e.forEach(o=>{t[o.channelKey]||(t[o.channelKey]=[]),t[o.channelKey].push(o)});let a=0,n=0;for(const[o,s]of Object.entries(t))if(s.length>M){s.sort((c,d)=>c.timestamp-d.timestamp);const i=s.slice(0,s.length-M);for(const c of i)await tt(c.url),a++,n+=c.size||0,b.value[c.url]&&(URL.revokeObjectURL(b.value[c.url]),delete b.value[c.url])}console.log(`自动清理完成: 清理了 ${a} 张图片,释放了 ${(n/1024/1024).toFixed(2)}MB`),K-=a,F-=n}catch(e){console.error("自动清理失败:",e)}}async function Et(){try{console.log("开始紧急清理...");const e=await yt();if(e.length===0)return;e.sort((s,i)=>i.timestamp-s.timestamp);const t=Math.floor(de*.3),a=e.slice(t);let n=0,o=0;for(const s of a)await tt(s.url),n++,o+=s.size||0,b.value[s.url]&&(URL.revokeObjectURL(b.value[s.url]),delete b.value[s.url]);console.log(`紧急清理完成: 清理了 ${n} 张图片,释放了 ${(o/1024/1024).toFixed(2)}MB`),K=t,F-=o}catch(e){console.error("紧急清理失败:",e)}}async function yt(){return I?new Promise(e=>{const n=I.transaction([$],"readonly").objectStore($).getAll();n.onsuccess=()=>{e(n.result||[])},n.onerror=()=>{console.error("获取所有图片失败:",n.error),e([])}}):[]}async function ea(){return new Promise(e=>{try{I&&(I.close(),I=null);const t=indexedDB.deleteDatabase(Ne);t.onsuccess=()=>{console.log("IndexedDB数据库已完全清理"),F=0,K=0,e(true)},t.onerror=()=>{console.error("清理IndexedDB数据库失败:",t.error),e(false)},t.onblocked=()=>{console.warn("IndexedDB数据库删除被阻塞,可能有其他连接正在使用"),setTimeout(()=>{e(false)},5e3)}}catch(t){console.error("清理数据库时出错:",t),e(false)}})}async function ta(){try{return await pt()?(setTimeout(async()=>{const t=await gt();console.log("数据库初始状态:",{大小:`${(t.totalSize/1024/1024).toFixed(2)}MB`,图片数量:t.totalImages,频道分布:t.channelStats}),(t.totalSize>ae*.9||t.totalImages>de*.9)&&(console.warn("数据库初始状态接近限制,执行清理"),await Ye())},1e3),true):(console.warn("数据库打开失败,尝试清理后重新初始化"),await ea(),await pt())}catch(e){return console.error("IndexedDB初始化出错:",e),false}}async function pt(){return new Promise(e=>{try{const t=indexedDB.open(Ne,Oe);t.onerror=()=>{console.error("IndexedDB打开失败:",t.error),e(false)},t.onsuccess=()=>{I=t.result,I.onerror=a=>{console.error("IndexedDB运行时错误:",a)},I.onversionchange=()=>{console.warn("IndexedDB版本变更,关闭连接"),I==null||I.close(),I=null},e(true)},t.onupgradeneeded=a=>{const n=a.target.result;if(!n.objectStoreNames.contains($)){const o=n.createObjectStore($,{keyPath:"url"});o.createIndex("channelKey","channelKey",{unique:false}),o.createIndex("timestamp","timestamp",{unique:false}),o.createIndex("size","size",{unique:false}),console.log("IndexedDB对象存储创建完成")}},t.onblocked=()=>{console.warn("IndexedDB打开被阻塞"),e(false)}}catch(t){console.error("打开数据库时出错:",t),e(false)}})}async function aa(e){return I?new Promise((t,a)=>{const s=I.transaction([$],"readonly").objectStore($).get(e);s.onsuccess=()=>{t(s.result||null)},s.onerror=()=>{console.error("从IndexedDB获取图片失败:",s.error),t(null)}}):null}async function wt(e){if(!I)return false;try{return e.size>Le?(console.warn(`图片过大,跳过缓存: ${(e.size/1024/1024).toFixed(2)}MB > ${(Le/1024/1024).toFixed(2)}MB`),false):(await et(),F+e.size>ae&&(console.warn("添加图片会超过数据库大小限制,执行清理"),await Ye(),F+e.size>ae)?(console.warn("清理后仍会超过限制,跳过此图片"),false):K>=de&&(console.warn("图片数量已达上限,执行清理"),await Ye(),K>=de)?(console.warn("清理后仍达上限,跳过此图片"),false):new Promise(t=>{const o=I.transaction([$],"readwrite").objectStore($).put(e);o.onsuccess=()=>{F+=e.size,K+=1,t(true)},o.onerror=()=>{console.error("保存图片到IndexedDB失败:",o.error),t(false)}}))}catch(t){return console.error("保存图片时出错:",t),false}}async function tt(e){return I?new Promise(t=>{const o=I.transaction([$],"readwrite").objectStore($).delete(e);o.onsuccess=()=>{t(true)},o.onerror=()=>{console.error("从IndexedDB删除图片失败:",o.error),t(false)}}):false}async function na(e){return I?new Promise(t=>{const s=I.transaction([$],"readonly").objectStore($).index("channelKey").getAll(e);s.onsuccess=()=>{t(s.result||[])},s.onerror=()=>{console.error("获取频道图片失败:",s.error),t([])}}):[]}async function Ve(e,t){const a=b.value[t];if(a)return a;const n=await aa(t);if(!n)return null;Je();const o=URL.createObjectURL(n.blob);return b.value[t]=o,Xe(it(n.blob)),n.timestamp=Date.now(),await wt(n),o}async function xe(e,t){try{const a=await Ve(e,t);if(a)return a;const n=await P("fetch-image",{url:t});if(!n.success)return console.error("获取图片失败:",n.error),null;const o=n.base64,s=n.contentType||"image/jpeg",i=atob(o),c=new Array(i.length);for(let z=0;z<i.length;z++)c[z]=i.charCodeAt(z);const d=new Uint8Array(c),p=new Blob([d],{type:s});if(p.size>Le)return console.warn(`图片过大,跳过缓存: ${(p.size/1024/1024).toFixed(2)}MB`),null;const k={url:t,blob:p,timestamp:Date.now(),size:p.size,channelKey:e};if(!await wt(k))return console.error("保存图片到IndexedDB失败"),null;Je();const O=URL.createObjectURL(p);return b.value[t]=O,Xe(it(p)),O}catch(a){return console.error("缓存图片失败:",a),null}}async function Pe(e){try{const t=await na(e);let a=0;for(const n of t)await tt(n.url),b.value[n.url]&&(URL.revokeObjectURL(b.value[n.url]),delete b.value[n.url],a+=n.size||0);a>0&&Xe(-a)}catch(t){console.error("清理频道图片缓存失败:",t)}}function sa(){const e=Object.keys(b.value).length;return{blobCount:e,estimatedMemoryUsage:we,maxMemoryLimit:be,maxBlobLimit:ue,memoryUsagePercent:(we/be*100).toFixed(1),blobUsagePercent:(e/ue*100).toFixed(1)}}async function oa(){return I?new Promise(e=>{const n=I.transaction([$],"readonly").objectStore($).getAll();n.onsuccess=()=>{const o=n.result||[],s=new Set;let i=0;o.forEach(c=>{s.add(c.channelKey),i+=c.size}),e({totalImages:o.length,totalSize:i,channels:s.size})},n.onerror=()=>{console.error("获取缓存统计失败:",n.error),e({totalImages:0,totalSize:0,channels:0})}}):{totalImages:0,totalSize:0,channels:0}}function at(){g.value&&S.value&&(localStorage.setItem("chat-selected-bot",g.value),localStorage.setItem("chat-selected-channel",S.value))}function It(){const e=localStorage.getItem("chat-selected-bot"),t=localStorage.getItem("chat-selected-channel");return e&&t&&u.value.bots[e]&&u.value.channels[e]&&u.value.channels[e][t]?(g.value=e,S.value=t,true):false}function xt(e){var t,a,n,o;if(!u.value.bots[e.selfId])u.value.bots[e.selfId]={selfId:e.selfId,platform:e.platform,username:((t=e.bot)==null?void 0:t.name)||`Bot-${e.selfId}`,avatar:(a=e.bot)==null?void 0:a.avatar,status:"online"};else{const s=u.value.bots[e.selfId];s.status="online",(n=e.bot)!=null&&n.name&&s.username!==e.bot.name&&(s.username=e.bot.name),(o=e.bot)!=null&&o.avatar&&s.avatar!==e.bot.avatar&&(s.avatar=e.bot.avatar)}if(u.value.channels[e.selfId]||(u.value.channels[e.selfId]={}),e.channelId&&!u.value.channels[e.selfId][e.channelId]){const s=e.isDirect?`私信 ${e.channelId}`:`${e.guildName||e.channelId} (${e.channelId})`;u.value.channels[e.selfId][e.channelId]={id:e.channelId,name:s,type:e.channelType||0,channelId:e.channelId,guildName:e.guildName||"群聊",isDirect:e.isDirect}}if(e.messageId&&e.content&&e.channelId){const s=`${e.selfId}:${e.channelId}`;if(u.value.messages[s]||(u.value.messages[s]=[]),!u.value.messages[s].find(c=>c.id===e.messageId)){const c={id:e.messageId,content:e.content,userId:e.userId,username:e.username,avatar:e.avatar,timestamp:e.timestamp,channelId:e.channelId,selfId:e.selfId,elements:e.elements,isBot:false,quote:e.quote},d=u.value.messages[s];let p=d.length;for(let w=d.length-1;w>=0;w--){if(d[w].timestamp<=e.timestamp){p=w+1;break}w===0&&(p=0)}d.splice(p,0,c),d.length>100&&(u.value.messages[s]=d.slice(-100)),U.value[s]=d.length;const k=Ke();ye(()=>{setTimeout(()=>{k&&ge()},10)})}}if(e.elements&&e.elements.length>0){const s=`${e.selfId}:${e.channelId}`;e.elements.forEach(i=>{if((i.type==="img"||i.type==="image"||i.type==="mface")&&i.attrs){const c=i.attrs.src||i.attrs.url||i.attrs.file;c&&xe(s,c).catch(d=>{console.warn("预缓存图片失败:",c,d)})}})}u.value={...u.value}}function ra(e){const t=`${e.selfId}:${e.channelId}`;if(u.value.messages[t]||(u.value.messages[t]=[]),!u.value.messages[t].find(n=>n.id===e.messageId)){const n={id:e.messageId,content:e.content,userId:e.selfId,username:e.botUsername,avatar:e.botAvatar,timestamp:e.timestamp,channelId:e.channelId,selfId:e.selfId,elements:e.elements,isBot:true,quote:e.quote},o=u.value.messages[t];let s=o.length;for(let c=o.length-1;c>=0;c--){if(o[c].timestamp<=e.timestamp){s=c+1;break}c===0&&(s=0)}o.splice(s,0,n),o.length>100&&(u.value.messages[t]=o.slice(-100)),U.value[t]=o.length;const i=Ke();ye(()=>{setTimeout(()=>{i&&ge()},10)})}u.value={...u.value}}function la(e){var t,a,n,o;if(!u.value.bots[e.selfId])u.value.bots[e.selfId]={selfId:e.selfId,platform:e.platform,username:((t=e.bot)==null?void 0:t.name)||`Bot-${e.selfId}`,avatar:(a=e.bot)==null?void 0:a.avatar,status:"online"};else{const s=u.value.bots[e.selfId];s.status="online",(n=e.bot)!=null&&n.name&&s.username!==e.bot.name&&(s.username=e.bot.name),(o=e.bot)!=null&&o.avatar&&s.avatar!==e.bot.avatar&&(s.avatar=e.bot.avatar)}if(u.value.channels[e.selfId]||(u.value.channels[e.selfId]={}),e.channelId&&!u.value.channels[e.selfId][e.channelId]){const s=e.isDirect?`私信 ${e.channelId}`:`${e.guildName||e.channelId} (${e.channelId})`;u.value.channels[e.selfId][e.channelId]={id:e.channelId,name:s,type:e.channelType||0,channelId:e.channelId,guildName:e.guildName||"群聊",isDirect:e.isDirect}}if(e.messageId&&e.content&&e.channelId){const s=`${e.selfId}:${e.channelId}`;if(u.value.messages[s]||(u.value.messages[s]=[]),!u.value.messages[s].find(c=>c.id===e.messageId)){const c={id:e.messageId,content:e.content,userId:e.userId,username:e.username,avatar:e.avatar,timestamp:e.timestamp,channelId:e.channelId,selfId:e.selfId,elements:e.elements,isBot:true,quote:e.quote},d=u.value.messages[s];let p=d.length;for(let w=d.length-1;w>=0;w--){if(d[w].timestamp<=e.timestamp){p=w+1;break}w===0&&(p=0)}d.splice(p,0,c),d.length>100&&(u.value.messages[s]=d.slice(-100)),U.value[s]=d.length;const k=Ke();ye(()=>{setTimeout(()=>{k&&ge()},10)})}}if(e.elements&&e.elements.length>0){const s=`${e.selfId}:${e.channelId}`;e.elements.forEach(i=>{if((i.type==="img"||i.type==="image"||i.type==="mface")&&i.attrs){const c=i.attrs.src||i.attrs.url||i.attrs.file;c&&xe(s,c).catch(d=>{console.warn("预缓存图片失败:",c,d)})}})}u.value={...u.value}}async function ca(){try{const e=await P("get-chat-data");if(e.success&&e.data){const t={};J.value=new Set(e.data.pinnedBots||[]),re.value=new Set(e.data.pinnedChannels||[]);for(const[a,n]of Object.entries(e.data.messages||{})){const o=n.map(s=>({id:s.id,content:s.content,userId:s.userId,username:s.username,avatar:s.avatar,timestamp:s.timestamp,channelId:s.channelId,selfId:s.selfId,elements:s.elements,isBot:s.type==="bot",quote:s.quote}));o.sort((s,i)=>s.timestamp-i.timestamp),t[a]=o}return u.value={bots:e.data.bots||{},channels:e.data.channels||{},messages:t},await ia(),true}else return console.warn("获取聊天数据失败:",e.error),false}catch(e){return console.error("获取聊天数据时出错:",e),false}}async function ia(){try{const e=await P("get-all-channel-message-counts");if(e.success&&e.counts){const t={};for(const[a,n]of Object.entries(e.counts))t[a]=n;U.value=t}else console.warn("获取频道消息数量失败:",e.error)}catch(e){console.error("获取频道消息数量时出错:",e)}}async function ua(){try{const e=await P("get-plugin-config");e.success&&e.config?ie.value=e.config:console.warn("获取插件配置失败:",e.error)}catch(e){console.error("获取插件配置时出错:",e)}}async function Ct(e,t){try{const a=await P("get-history-messages",{selfId:e,channelId:t});if(a.success&&a.messages){const n=`${e}:${t}`,o=a.messages.map(s=>({id:s.id,content:s.content,userId:s.userId,username:s.username,avatar:s.avatar,timestamp:s.timestamp,channelId:s.channelId,selfId:s.selfId,elements:s.elements,isBot:s.type==="bot",quote:s.quote}));return o.sort((s,i)=>s.timestamp-i.timestamp),u.value.messages[n]=o,U.value[n]=o.length,u.value={...u.value},true}else return console.warn("获取历史消息失败:",a.error),false}catch(a){return console.error("获取历史消息时出错:",a),false}}function da(e){if(!q.value||e.touches.length!==1)return;const t=e.touches[0];H.value={x:t.clientX,y:t.clientY,time:Date.now()},ne.value={x:t.clientX,y:t.clientY},Be.value=false}function fa(e){if(!q.value||!H.value||e.touches.length!==1)return;const t=e.touches[0];ne.value={x:t.clientX,y:t.clientY};const a=t.clientX-H.value.x,n=t.clientY-H.value.y;if(Math.abs(a)>Math.abs(n)&&Math.abs(a)>30){const o=a>0,s=W.value==="messages"||W.value==="channels";if(o&&s){Be.value=true;const i=Math.min(a,200),c=150;i>c?he.value={show:true,text:"松开返回"}:he.value={show:true,text:`滑动返回 ${Math.round(i/c*100)}%`},e.preventDefault()}else he.value={show:false,text:""}}else he.value={show:false,text:""}}function ha(e){if(!q.value||!H.value)return;const a=Date.now()-H.value.time;if(ne.value){const n=ne.value.x-H.value.x,o=ne.value.y-H.value.y,s=n>150,i=Math.abs(n)>Math.abs(o),c=a<300&&n>80;(s&&i||c)&&ma()}H.value=null,ne.value=null,Be.value=false,he.value={show:false,text:""}}function ma(){switch(W.value){case"messages":W.value="channels";break;case"channels":W.value="bots",g.value="",S.value="";break}}function nt(){q.value=window.innerWidth<=768}function kt(){return!se.value||Ke()}const bt=()=>{q.value&&D.value&&ye(()=>{kt()&&ge()})},va=()=>{q.value&&setTimeout(()=>{D.value&&kt()&&ge()},300)};return St(L,(e,t)=>{t.length===0&&e.length>0&&ye(()=>{ge()})}),st(async()=>{nt(),window.addEventListener("resize",nt),window.visualViewport&&window.visualViewport.addEventListener("resize",bt),document.addEventListener("click",Ee),await ta()?(console.log("IndexedDB初始化成功"),setTimeout(async()=>{await et()},2e3),setInterval(async()=>{await et()},5*60*1e3)):console.warn("IndexedDB初始化失败,图片缓存功能将不可用"),await ua(),await ca(),ye(()=>{It()});const t=lt("chat-message-event",xt),a=lt("bot-message-sent-event",ra),n=lt("chat-bot-message-event",la);St(S,o=>{o&&ye(()=>{D.value&&(D.value.removeEventListener("scroll",Me),D.value.addEventListener("scroll",Me),Me()),!q.value&&me.value&&me.value.focus()})},{immediate:true}),setInterval(()=>{Je()},2*60*1e3),ga(()=>{window.removeEventListener("resize",nt),window.visualViewport&&window.visualViewport.removeEventListener("resize",bt),document.removeEventListener("click",Ee),t&&typeof t=="function"&&t(),a&&typeof a=="function"&&a(),n&&typeof n=="function"&&n(),D.value&&D.value.removeEventListener("scroll",Me),Object.values(b.value).forEach(o=>{URL.revokeObjectURL(o)}),b.value={},I&&(I.close(),I=null)})}),{AvatarComponent:ce,ImageComponent:Y,JsonCardComponent:pe,ForwardMessageComponent:Z,MessageElement:ke,chatData:u,channelMessageCounts:U,pluginConfig:ie,selectedBot:g,selectedChannel:S,inputMessage:Se,imageBlobUrls:b,pinnedBots:J,pinnedChannels:re,uploadedImages:V,showActionMenu:fe,isMobile:q,mobileView:W,touchStart:H,touchCurrent:ne,isSwipeActive:Be,swipeIndicator:he,messageHistory:D,messageInput:me,showScrollButton:$e,isUserScrolling:se,isSending:ve,draggingChannel:oe,dragStartPos:j,dragCurrentPos:A,dragElementInitialPos:De,dragOffset:Ie,dragThreshold:X,isDragReady:Q,draggedBubbleElement:C,contextMenu:N,fileInput:Te,bots:m,currentChannels:l,currentMessages:L,currentChannelName:Ue,currentChannelKey:_t,canSendMessage:ct,canInputMessage:Lt,mobileViewClass:Tt,inputPlaceholder:qt,chatContainerStyle:Ut,selectBot:Mt,selectChannel:Nt,handleBotRightClick:Kt,handleChannelRightClick:jt,showContextMenu:Ge,hideContextMenu:G,handleKeyDown:Ze,toggleBotPin:At,toggleChannelPin:zt,deleteBotMessages:Pt,deleteChannelMessages:Rt,sendMessage:Ot,toggleActionMenu:Ft,triggerImageUpload:Ht,handleFileSelect:Xt,handlePaste:Yt,uploadImage:Qe,removeImage:Vt,fileToBase64:dt,handleClickOutside:Ee,formatTime:Wt,getChannelTypeText:Jt,scrollToBottom:ge,checkScrollPosition:Me,isNearBottom:Ke,getChannelMessageCount:ft,startDrag:Gt,handleDragMove:je,handleDragEnd:Ae,resetDragState:ze,getDragStyle:Zt,getDragDistance:Qt,clearChannelHistory:ht,showNotification:T,createThresholdCircle:mt,removeThresholdCircle:vt,handleTouchStart:da,handleTouchMove:fa,handleTouchEnd:ha,handleInputFocus:va,getCachedImageUrl:Ve,cacheImage:xe,clearChannelImageCache:Pe,getMemoryStats:sa,getCacheStats:oa,isFileUrl:te,loadHistoryMessages:Ct,handleMessageEvent:xt,saveSelectionState:at,restoreSelectionState:It}}const ba={class:"bot-list"},Sa={class:"bot-items"},Ba=["onClick","onContextmenu"],$a={class:"bot-avatar"},Da={key:1,class:"avatar-placeholder"},_a={class:"bot-info"},La={class:"bot-name"},Ta={class:"bot-platform"},qa={class:"channel-list"},Ua={key:0,class:"empty-state"},Ma={key:1,class:"channel-items"},Ka=["data-channel-id","onClick","onContextmenu"],ja={class:"channel-info"},Aa={class:"channel-name"},za={class:"channel-type"},Pa=["onMousedown","onTouchstart","title"],Ra={class:"message-area"},Na={class:"panel-header"},Oa={key:0,class:"empty-state"},Fa={key:1,class:"message-content"},Ha={class:"message-avatar"},Xa={key:1,class:"avatar-placeholder"},Ya={class:"message-content-wrapper"},Va={class:"message-header"},Wa={class:"message-username"},Ja={class:"message-time"},Ga={key:0,class:"message-quote"},Za={class:"quote-header"},Qa={class:"quote-avatar"},Ea={key:1,class:"avatar-placeholder"},en={class:"quote-username"},tn={class:"quote-time"},an={class:"quote-content"},nn={class:"message-text"},sn={class:"message-input"},on={key:0,class:"image-preview-container"},rn=["src","alt"],ln=["onClick"],cn={class:"input-row"},un={class:"input-actions"},dn=["placeholder","disabled"],fn=["disabled"],hn=Ce({__name:"index",setup(te){const ce=ka(),{AvatarComponent:Y,MessageElement:pe,selectedBot:Z,selectedChannel:ke,inputMessage:u,pinnedBots:U,pinnedChannels:ie,uploadedImages:b,showActionMenu:be,swipeIndicator:ue,messageHistory:we,messageInput:I,showScrollButton:Ne,isSending:Oe,draggingChannel:$,dragThreshold:ae,contextMenu:M,fileInput:de,bots:Le,currentChannels:Fe,currentMessages:We,currentChannelName:F,currentChannelKey:K,canSendMessage:He,canInputMessage:g,mobileViewClass:S,inputPlaceholder:Se,chatContainerStyle:V,selectBot:fe,selectChannel:Te,handleBotRightClick:q,handleChannelRightClick:W,toggleBotPin:H,toggleChannelPin:ne,deleteBotMessages:Be,deleteChannelMessages:he,sendMessage:D,toggleActionMenu:me,triggerImageUpload:$e,handleFileSelect:se,handlePaste:ve,removeImage:oe,formatTime:j,getChannelTypeText:A,scrollToBottom:De,getChannelMessageCount:Ie,startDrag:X,getDragStyle:qe,getDragDistance:R,handleTouchStart:Q,handleTouchMove:C,handleTouchEnd:N,handleInputFocus:J}=ce;return(re,m)=>(y(),x("div",{class:le(["chat-container",r(S)]),style:ot(r(V)),onTouchstart:m[15]||(m[15]=(...l)=>r(Q)&&r(Q)(...l)),onTouchmove:m[16]||(m[16]=(...l)=>r(C)&&r(C)(...l)),onTouchend:m[17]||(m[17]=(...l)=>r(N)&&r(N)(...l))},[B(" 左侧机器人列表 "),f("div",ba,[m[18]||(m[18]=f("div",{class:"panel-header"},[f("h3",null,"机器人")],-1)),f("div",Sa,[(y(true),x(ee,null,_e(r(Le),l=>(y(),x("div",{key:l.selfId,class:le(["bot-item",{active:r(Z)===l.selfId,pinned:r(U).has(l.selfId)}]),onClick:L=>r(fe)(l.selfId),onContextmenu:L=>r(q)(L,l.selfId)},[f("div",$a,[l.avatar?(y(),Re(r(Y),{key:0,src:l.avatar,alt:l.username,"channel-key":"bot-list"},null,8,["src","alt"])):(y(),x("div",Da,_(l.username.charAt(0).toUpperCase()),1))]),f("div",_a,[f("div",La,_(l.username),1),f("div",Ta,_(l.platform),1)]),f("div",{class:le(["bot-status",l.status])},null,2)],42,Ba))),128))])]),B(" 中间频道列表 "),f("div",qa,[m[19]||(m[19]=f("div",{class:"panel-header"},[f("h3",null,"频道")],-1)),r(Z)?(y(),x("div",Ma,[(y(true),x(ee,null,_e(r(Fe),l=>(y(),x("div",{key:l.id,class:le(["channel-item",{active:r(ke)===l.id,pinned:r(ie).has(`${r(Z)}:${l.id}`)}]),"data-channel-id":l.id,onClick:L=>r(Te)(l.id),onContextmenu:L=>r(W)(L,l.id)},[f("div",ja,[f("div",Aa,_(l.name),1),f("div",za,_(r(A)(l.type)),1)]),r(Ie)(l.id)>0?(y(),x("div",{key:0,class:le(["channel-message-count draggable-bubble",{dragging:r($)===l.id,"will-delete":r($)===l.id&&r(R)(l.id)>r(ae)}]),onMousedown:L=>r(X)(L,l.id),onTouchstart:L=>r(X)(L,l.id),style:ot(r(qe)(l.id)),title:r($)===l.id?r(R)(l.id)>r(ae)?"松开清理历史记录":"拖拽更远以清理历史记录":"拖拽清理历史记录"},_(r(Ie)(l.id)),47,Pa)):B("v-if",true)],42,Ka))),128))])):(y(),x("div",Ua," 请选择一个机器人 "))]),B(" 右侧消息区域 "),f("div",Ra,[f("div",Na,[f("h3",null,_(r(F)||"选择频道"),1)]),!r(Z)||!r(ke)?(y(),x("div",Oa," 请选择机器人和频道 ")):(y(),x("div",Fa,[B(" 消息历史 "),f("div",{class:"message-history",ref_key:"messageHistory",ref:we},[(y(true),x(ee,null,_e(r(We),l=>(y(),x("div",{key:l.id,class:le(["message-item",{"bot-message":l.isBot}])},[f("div",Ha,[l.avatar?(y(),Re(r(Y),{key:0,src:l.avatar,alt:l.username,"channel-key":r(K)},null,8,["src","alt","channel-key"])):(y(),x("div",Xa,_(l.username.charAt(0).toUpperCase()),1))]),f("div",Ya,[f("div",Va,[f("span",Wa,_(l.username),1),f("span",Ja,_(r(j)(l.timestamp)),1)]),B(" 引用消息显示 "),l.quote?(y(),x("div",Ga,[f("div",Za,[f("div",Qa,[l.quote.user.avatar?(y(),Re(r(Y),{key:0,src:l.quote.user.avatar,alt:l.quote.user.username,"channel-key":r(K)},null,8,["src","alt","channel-key"])):(y(),x("div",Ea,_(l.quote.user.username.charAt(0).toUpperCase()),1))]),f("span",en,_(l.quote.user.username),1),f("span",tn,_(r(j)(l.quote.timestamp)),1)]),f("div",an,[l.quote.elements&&l.quote.elements.length>0?(y(true),x(ee,{key:0},_e(l.quote.elements,(L,Ue)=>(y(),Re(r(pe),{key:`quote-${Ue}`,element:L,"channel-key":r(K)},null,8,["element","channel-key"]))),128)):(y(),x(ee,{key:1},[rt(_(l.quote.content),1)],64))])])):B("v-if",true),f("div",nn,[l.elements&&l.elements.length>0?(y(true),x(ee,{key:0},_e(l.elements,(L,Ue)=>(y(),Re(r(pe),{key:Ue,element:L,"channel-key":r(K)},null,8,["element","channel-key"]))),128)):(y(),x(ee,{key:1},[rt(_(l.content),1)],64))])])],2))),128))],512),B(" 悬浮的滚动到底部按钮 "),Bt(f("div",{class:"floating-scroll-button",onClick:m[0]||(m[0]=(...l)=>r(De)&&r(De)(...l))},m[20]||(m[20]=[f("svg",{width:"20",height:"20",viewBox:"0 0 24 24",fill:"currentColor"},[f("path",{d:"M7 10l5 5 5-5z"})],-1)]),512),[[ya,r(Ne)]]),B(" 输入框 "),f("div",sn,[B(" 图片预览区域 "),r(b).length>0?(y(),x("div",on,[(y(true),x(ee,null,_e(r(b),l=>(y(),x("div",{key:l.tempId,class:"image-preview-item"},[f("img",{src:l.preview,alt:l.filename,class:"preview-image"},null,8,rn),f("button",{class:"remove-image-btn",onClick:L=>r(oe)(l.tempId),title:"删除图片"},m[21]||(m[21]=[f("svg",{width:"12",height:"12",viewBox:"0 0 24 24",fill:"currentColor"},[f("path",{d:"M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z"})],-1)]),8,ln)]))),128))])):B("v-if",true),f("div",cn,[B(" 加号按钮 "),f("div",un,[f("button",{class:le(["add-button",{active:r(be)}]),onClick:m[1]||(m[1]=(...l)=>r(me)&&r(me)(...l)),title:"更多操作"},m[22]||(m[22]=[f("svg",{width:"18",height:"18",viewBox:"0 0 24 24",fill:"none",stroke:"currentColor","stroke-width":"2.5","stroke-linecap":"round","stroke-linejoin":"round"},[f("line",{x1:"12",y1:"5",x2:"12",y2:"19"}),f("line",{x1:"5",y1:"12",x2:"19",y2:"12"})],-1)]),2),B(" 操作菜单 "),r(be)?(y(),x("div",{key:0,class:"action-menu",onClick:m[3]||(m[3]=$t(()=>{},["stop"]))},[f("button",{class:"action-menu-item",onClick:m[2]||(m[2]=(...l)=>r($e)&&r($e)(...l))},m[23]||(m[23]=[f("svg",{width:"18",height:"18",viewBox:"0 0 24 24",fill:"none",stroke:"currentColor","stroke-width":"2","stroke-linecap":"round","stroke-linejoin":"round"},[f("rect",{x:"3",y:"3",width:"18",height:"18",rx:"2",ry:"2"}),f("circle",{cx:"8.5",cy:"8.5",r:"1.5"}),f("polyline",{points:"21,15 16,10 5,21"})],-1),rt(" 上传图片 ",-1)]))])):B("v-if",true)]),Bt(f("input",{"onUpdate:modelValue":m[4]||(m[4]=l=>wa(u)?u.value=l:null),type:"text",placeholder:r(Se),onKeyup:m[5]||(m[5]=pa((...l)=>r(D)&&r(D)(...l),["enter"])),disabled:!r(g),ref_key:"messageInput",ref:I,onPaste:m[6]||(m[6]=(...l)=>r(ve)&&r(ve)(...l)),onFocus:m[7]||(m[7]=(...l)=>r(J)&&r(J)(...l))},null,40,dn),[[Ia,r(u)]]),f("button",{onClick:m[8]||(m[8]=(...l)=>r(D)&&r(D)(...l)),disabled:!r(He),class:le({"is-sending":r(Oe)})},_(r(Oe)?"发送中...":"发送"),11,fn)]),B(" 隐藏的文件输入 "),f("input",{type:"file",ref_key:"fileInput",ref:de,onChange:m[9]||(m[9]=(...l)=>r(se)&&r(se)(...l)),accept:"image/*",multiple:"",style:{display:"none"}},null,544)])]))]),B(" 右键菜单 "),r(M).show?(y(),x("div",{key:0,class:"context-menu",style:ot({left:r(M).x+"px",top:r(M).y+"px"}),onClick:m[14]||(m[14]=$t(()=>{},["stop"]))},[B(" 机器人右键菜单 "),r(M).type==="bot"?(y(),x(ee,{key:0},[f("div",{class:"context-menu-item",onClick:m[10]||(m[10]=l=>r(H)(r(M).targetId))},_(r(U).has(r(M).targetId)?"取消置顶":"置顶"),1),f("div",{class:"context-menu-item danger",onClick:m[11]||(m[11]=l=>r(Be)(r(M).targetId))}," 彻底删除此机器人所有数据 ")],64)):B("v-if",true),B(" 频道右键菜单 "),r(M).type==="channel"?(y(),x(ee,{key:1},[f("div",{class:"context-menu-item",onClick:m[12]||(m[12]=l=>r(ne)(r(M).targetId))},_(r(ie).has(`${r(Z)}:${r(M).targetId}`)?"取消置顶":"置顶"),1),f("div",{class:"context-menu-item danger",onClick:m[13]||(m[13]=l=>r(he)(r(M).targetId))}," 彻底删除此频道所有数据 ")],64)):B("v-if",true)],4)):B("v-if",true),B(" 滑动指示器 "),f("div",{class:le(["swipe-indicator",{show:r(ue).show}])},_(r(ue).text),3)],38))}}),Dt=(te,ce)=>{const Y=te.__vccOpts||te;for(const[pe,Z]of ce)Y[pe]=Z;return Y},mn=Dt(hn,[["__scopeId","data-v-de93bb69"]]),vn={},gn={class:"k-icon",xmlns:"http://www.w3.org/2000/svg",viewBox:"0 0 24 24",fill:"none"};function yn(te,ce){return y(),x("svg",gn,ce[0]||(ce[0]=[f("path",{d:"M8 10.5H16",stroke:"currentColor","stroke-width":"1.5","stroke-linecap":"round"},null,-1),f("path",{d:"M8 14H13.5",stroke:"currentColor","stroke-width":"1.5","stroke-linecap":"round"},null,-1),f("path",{d:"M17 3.33782C15.5291 2.48697 13.8214 2 12 2C6.47715 2 2 6.47715 2 12C2 13.5997 2.37562 15.1116 3.04346 16.4525C3.22094 16.8088 3.28001 17.2161 3.17712 17.6006L2.58151 19.8267C2.32295 20.793 3.20701 21.677 4.17335 21.4185L6.39939 20.8229C6.78393 20.72 7.19121 20.7791 7.54753 20.9565C8.88837 21.6244 10.4003 22 12 22C17.5228 22 22 17.5228 22 12C22 10.1786 21.513 8.47087 20.6622 7",stroke:"currentColor","stroke-width":"1.5","stroke-linecap":"round"},null,-1)]))}const pn=Dt(vn,[["render",yn]]);Ca.register("activity:chat",pn);const xn=te=>{te.page({name:"聊天室",path:"/chat-patch",desc:"",authority:4,icon:"activity:chat",component:Ce({setup(){return()=>h(xa("k-layout"),{},{default:()=>h(mn)})}})})};export{xn as default};
|
|
22
|
+
`,document.head.appendChild(o),document.body.appendChild(a),setTimeout(()=>{a.style.animation="slideOut 0.3s ease-in",setTimeout(()=>{a.parentNode&&a.parentNode.removeChild(a),o.parentNode&&o.parentNode.removeChild(o)},300)},3e3)}function It(e,t){const a=document.createElement("div");a.className="drag-threshold-circle",a.style.cssText=`
|
|
23
|
+
left: ${e-F}px;
|
|
24
|
+
top: ${t-F}px;
|
|
25
|
+
width: ${F*2}px;
|
|
26
|
+
height: ${F*2}px;
|
|
27
|
+
`,document.body.appendChild(a),window.dragThresholdCircle=a}function xt(){const e=window.dragThresholdCircle;e&&e.parentNode&&(e.parentNode.removeChild(e),window.dragThresholdCircle=null)}async function at(){try{if(!x)return false;const e=Date.now();if(e-Ve<Ge)return true;Ve=e;const t=await Ct();j=t.totalSize,V=t.totalImages,console.log("数据库健康检查:",{大小:`${(j/1024/1024).toFixed(2)}MB / ${(_/1024/1024).toFixed(2)}MB`,图片数量:`${V} / ${ue}`,使用率:`${(j/_*100).toFixed(1)}%`});const a=j/_,n=V/ue;return(a>Ye||n>Ye)&&(console.warn("数据库使用率过高,开始自动清理"),await Je()),(a>.95||n>.95)&&(console.error("数据库接近极限,执行紧急清理"),await la()),true}catch(e){return console.error("数据库健康检查失败:",e),false}}async function Ct(){return x?new Promise(e=>{const n=x.transaction([$],"readonly").objectStore($).getAll();n.onsuccess=()=>{const o=n.result||[];let s=0;const i={};o.forEach(l=>{s+=l.size||0,i[l.channelKey]=(i[l.channelKey]||0)+1}),e({totalSize:s,totalImages:o.length,channelStats:i})},n.onerror=()=>{console.error("获取数据库统计失败:",n.error),e({totalSize:0,totalImages:0,channelStats:{}})}}):{totalSize:0,totalImages:0,channelStats:{}}}async function Je(){try{console.log("开始自动清理...");const e=await bt();if(e.length===0)return;const t={};e.forEach(o=>{t[o.channelKey]||(t[o.channelKey]=[]),t[o.channelKey].push(o)});let a=0,n=0;for(const[o,s]of Object.entries(t))if(s.length>Xe){s.sort((l,d)=>l.timestamp-d.timestamp);const i=s.slice(0,s.length-Xe);for(const l of i)await st(l.url),a++,n+=l.size||0,b.value[l.url]&&(URL.revokeObjectURL(b.value[l.url]),delete b.value[l.url])}console.log(`自动清理完成: 清理了 ${a} 张图片,释放了 ${(n/1024/1024).toFixed(2)}MB`),V-=a,j-=n}catch(e){console.error("自动清理失败:",e)}}async function la(){try{console.log("开始紧急清理...");const e=await bt();if(e.length===0)return;e.sort((s,i)=>i.timestamp-s.timestamp);const t=Math.floor(ue*.3),a=e.slice(t);let n=0,o=0;for(const s of a)await st(s.url),n++,o+=s.size||0,b.value[s.url]&&(URL.revokeObjectURL(b.value[s.url]),delete b.value[s.url]);console.log(`紧急清理完成: 清理了 ${n} 张图片,释放了 ${(o/1024/1024).toFixed(2)}MB`),V=t,j-=o}catch(e){console.error("紧急清理失败:",e)}}async function bt(){return x?new Promise(e=>{const n=x.transaction([$],"readonly").objectStore($).getAll();n.onsuccess=()=>{e(n.result||[])},n.onerror=()=>{console.error("获取所有图片失败:",n.error),e([])}}):[]}async function ra(){return new Promise(e=>{try{x&&(x.close(),x=null);const t=indexedDB.deleteDatabase(He);t.onsuccess=()=>{console.log("IndexedDB数据库已完全清理"),j=0,V=0,e(true)},t.onerror=()=>{console.error("清理IndexedDB数据库失败:",t.error),e(false)},t.onblocked=()=>{console.warn("IndexedDB数据库删除被阻塞,可能有其他连接正在使用"),setTimeout(()=>{e(false)},5e3)}}catch(t){console.error("清理数据库时出错:",t),e(false)}})}async function ca(){try{return await kt()?(setTimeout(async()=>{const t=await Ct();console.log("数据库初始状态:",{大小:`${(t.totalSize/1024/1024).toFixed(2)}MB`,图片数量:t.totalImages,频道分布:t.channelStats}),(t.totalSize>_*.9||t.totalImages>ue*.9)&&(console.warn("数据库初始状态接近限制,执行清理"),await Je())},1e3),true):(console.warn("数据库打开失败,尝试清理后重新初始化"),await ra(),await kt())}catch(e){return console.error("IndexedDB初始化出错:",e),false}}async function kt(){return new Promise(e=>{try{const t=indexedDB.open(He,Me);t.onerror=()=>{console.error("IndexedDB打开失败:",t.error),e(false)},t.onsuccess=()=>{x=t.result,x.onerror=a=>{console.error("IndexedDB运行时错误:",a)},x.onversionchange=()=>{console.warn("IndexedDB版本变更,关闭连接"),x==null||x.close(),x=null},e(true)},t.onupgradeneeded=a=>{const n=a.target.result;if(!n.objectStoreNames.contains($)){const o=n.createObjectStore($,{keyPath:"url"});o.createIndex("channelKey","channelKey",{unique:false}),o.createIndex("timestamp","timestamp",{unique:false}),o.createIndex("size","size",{unique:false}),console.log("IndexedDB对象存储创建完成")}},t.onblocked=()=>{console.warn("IndexedDB打开被阻塞"),e(false)}}catch(t){console.error("打开数据库时出错:",t),e(false)}})}async function St(e){return x?new Promise((t,a)=>{const s=x.transaction([$],"readonly").objectStore($).get(e);s.onsuccess=()=>{t(s.result||null)},s.onerror=()=>{console.error("从IndexedDB获取图片失败:",s.error),t(null)}}):null}async function nt(e){if(!x)return false;try{return e.size>qe?(console.warn(`图片过大,跳过缓存: ${(e.size/1024/1024).toFixed(2)}MB > ${(qe/1024/1024).toFixed(2)}MB`),false):(await at(),j+e.size>_&&(console.warn("添加图片会超过数据库大小限制,执行清理"),await Je(),j+e.size>_)?(console.warn("清理后仍会超过限制,跳过此图片"),false):V>=ue&&(console.warn("图片数量已达上限,执行清理"),await Je(),V>=ue)?(console.warn("清理后仍达上限,跳过此图片"),false):new Promise(t=>{const o=x.transaction([$],"readwrite").objectStore($).put(e);o.onsuccess=()=>{j+=e.size,V+=1,t(true)},o.onerror=()=>{console.error("保存图片到IndexedDB失败:",o.error),t(false)}}))}catch(t){return console.error("保存图片时出错:",t),false}}async function st(e){return x?new Promise(t=>{const o=x.transaction([$],"readwrite").objectStore($).delete(e);o.onsuccess=()=>{t(true)},o.onerror=()=>{console.error("从IndexedDB删除图片失败:",o.error),t(false)}}):false}async function ia(e){return x?new Promise(t=>{const s=x.transaction([$],"readonly").objectStore($).index("channelKey").getAll(e);s.onsuccess=()=>{t(s.result||[])},s.onerror=()=>{console.error("获取频道图片失败:",s.error),t([])}}):[]}async function ot(e,t){if(Y.has(t))return Y.get(t)||null;const a=b.value[t];if(a)return a;const n=(async()=>{try{const o=b.value[t];if(o)return o;const s=await St(t);if(!s)return null;We();const i=URL.createObjectURL(s.blob);return b.value[t]=i,Pe(Ze(s.blob)),s.timestamp=Date.now(),await nt(s),i}catch(o){return console.error("获取缓存图片失败:",o),null}finally{Y.delete(t)}})();return Y.set(t,n),n}async function Ie(e,t){if(Y.has(t))return Y.get(t)||null;const a=b.value[t];if(a)return a;const n=(async()=>{try{const o=b.value[t];if(o)return o;const s=await St(t);if(s){We();const xe=URL.createObjectURL(s.blob);return b.value[t]=xe,Pe(Ze(s.blob)),s.timestamp=Date.now(),await nt(s),xe}const i=await z("fetch-image",{url:t});if(!i.success)return null;const l=i.base64,d=i.contentType||"image/jpeg",v=atob(l),p=new Array(v.length);for(let xe=0;xe<v.length;xe++)p[xe]=v.charCodeAt(xe);const I=new Uint8Array(p),q=new Blob([I],{type:d});if(q.size>qe)return null;const H=b.value[t];if(H)return H;const Ca={url:t,blob:q,timestamp:Date.now(),size:q.size,channelKey:e};if(!await nt(Ca))return null;We();const Lt=URL.createObjectURL(q);return b.value[t]=Lt,Pe(Ze(q)),Lt}catch(o){return console.error("缓存图片失败:",o),null}finally{Y.delete(t)}})();return Y.set(t,n),n}async function Ne(e){try{const t=await ia(e);let a=0;for(const n of t)await st(n.url),b.value[n.url]&&(URL.revokeObjectURL(b.value[n.url]),delete b.value[n.url],a+=n.size||0);a>0&&Pe(-a)}catch(t){console.error("清理频道图片缓存失败:",t)}}function ua(){const e=Object.keys(b.value).length;return{blobCount:e,estimatedMemoryUsage:ye,maxMemoryLimit:Te,maxBlobLimit:ge,memoryUsagePercent:(ye/Te*100).toFixed(1),blobUsagePercent:(e/ge*100).toFixed(1)}}async function da(){return x?new Promise(e=>{const n=x.transaction([$],"readonly").objectStore($).getAll();n.onsuccess=()=>{const o=n.result||[],s=new Set;let i=0;o.forEach(l=>{s.add(l.channelKey),i+=l.size}),e({totalImages:o.length,totalSize:i,channels:s.size})},n.onerror=()=>{console.error("获取缓存统计失败:",n.error),e({totalImages:0,totalSize:0,channels:0})}}):{totalImages:0,totalSize:0,channels:0}}function lt(){y.value&&S.value&&(localStorage.setItem("chat-selected-bot",y.value),localStorage.setItem("chat-selected-channel",S.value))}function Bt(){const e=localStorage.getItem("chat-selected-bot"),t=localStorage.getItem("chat-selected-channel");return e&&t&&u.value.bots[e]&&u.value.channels[e]&&u.value.channels[e][t]?(y.value=e,S.value=t,true):false}function $t(e){var t,a,n,o;if(!u.value.bots[e.selfId])u.value.bots[e.selfId]={selfId:e.selfId,platform:e.platform,username:((t=e.bot)==null?void 0:t.name)||`Bot-${e.selfId}`,avatar:(a=e.bot)==null?void 0:a.avatar,status:"online"};else{const s=u.value.bots[e.selfId];s.status="online",(n=e.bot)!=null&&n.name&&s.username!==e.bot.name&&(s.username=e.bot.name),(o=e.bot)!=null&&o.avatar&&s.avatar!==e.bot.avatar&&(s.avatar=e.bot.avatar)}if(u.value.channels[e.selfId]||(u.value.channels[e.selfId]={}),e.channelId&&!u.value.channels[e.selfId][e.channelId]){const s=e.isDirect?`私信 ${e.channelId}`:`${e.guildName||e.channelId} (${e.channelId})`;u.value.channels[e.selfId][e.channelId]={id:e.channelId,name:s,type:e.channelType||0,channelId:e.channelId,guildName:e.guildName||"群聊",isDirect:e.isDirect}}if(e.messageId&&e.content&&e.channelId){const s=`${e.selfId}:${e.channelId}`;if(u.value.messages[s]||(u.value.messages[s]=[]),!u.value.messages[s].find(l=>l.id===e.messageId)){const l={id:e.messageId,content:e.content,userId:e.userId,username:e.username,avatar:e.avatar,timestamp:e.timestamp,channelId:e.channelId,selfId:e.selfId,elements:e.elements,isBot:false,quote:e.quote},d=u.value.messages[s];let v=d.length;for(let I=d.length-1;I>=0;I--){if(d[I].timestamp<=e.timestamp){v=I+1;break}I===0&&(v=0)}d.splice(v,0,l),d.length>100&&(u.value.messages[s]=d.slice(-100)),U.value[s]=d.length;const p=Ue();me(()=>{setTimeout(()=>{p&&he()},10)})}}if(e.elements&&e.elements.length>0){const s=`${e.selfId}:${e.channelId}`;e.elements.forEach(i=>{if((i.type==="img"||i.type==="image"||i.type==="mface")&&i.attrs){const l=i.attrs.src||i.attrs.url||i.attrs.file;l&&Ie(s,l).catch(d=>{console.warn("预缓存图片失败:",l,d)})}})}u.value={...u.value}}function fa(e){const t=`${e.selfId}:${e.channelId}`;if(u.value.messages[t]||(u.value.messages[t]=[]),!u.value.messages[t].find(n=>n.id===e.messageId)){const n={id:e.messageId,content:e.content,userId:e.selfId,username:e.botUsername,avatar:e.botAvatar,timestamp:e.timestamp,channelId:e.channelId,selfId:e.selfId,elements:e.elements,isBot:true,quote:e.quote},o=u.value.messages[t];let s=o.length;for(let l=o.length-1;l>=0;l--){if(o[l].timestamp<=e.timestamp){s=l+1;break}l===0&&(s=0)}o.splice(s,0,n),o.length>100&&(u.value.messages[t]=o.slice(-100)),U.value[t]=o.length;const i=Ue();me(()=>{setTimeout(()=>{i&&he()},10)})}u.value={...u.value}}function ha(e){var t,a,n,o;if(!u.value.bots[e.selfId])u.value.bots[e.selfId]={selfId:e.selfId,platform:e.platform,username:((t=e.bot)==null?void 0:t.name)||`Bot-${e.selfId}`,avatar:(a=e.bot)==null?void 0:a.avatar,status:"online"};else{const s=u.value.bots[e.selfId];s.status="online",(n=e.bot)!=null&&n.name&&s.username!==e.bot.name&&(s.username=e.bot.name),(o=e.bot)!=null&&o.avatar&&s.avatar!==e.bot.avatar&&(s.avatar=e.bot.avatar)}if(u.value.channels[e.selfId]||(u.value.channels[e.selfId]={}),e.channelId&&!u.value.channels[e.selfId][e.channelId]){const s=e.isDirect?`私信 ${e.channelId}`:`${e.guildName||e.channelId} (${e.channelId})`;u.value.channels[e.selfId][e.channelId]={id:e.channelId,name:s,type:e.channelType||0,channelId:e.channelId,guildName:e.guildName||"群聊",isDirect:e.isDirect}}if(e.messageId&&e.content&&e.channelId){const s=`${e.selfId}:${e.channelId}`;if(u.value.messages[s]||(u.value.messages[s]=[]),!u.value.messages[s].find(l=>l.id===e.messageId)){const l={id:e.messageId,content:e.content,userId:e.userId,username:e.username,avatar:e.avatar,timestamp:e.timestamp,channelId:e.channelId,selfId:e.selfId,elements:e.elements,isBot:true,quote:e.quote},d=u.value.messages[s];let v=d.length;for(let I=d.length-1;I>=0;I--){if(d[I].timestamp<=e.timestamp){v=I+1;break}I===0&&(v=0)}d.splice(v,0,l),d.length>100&&(u.value.messages[s]=d.slice(-100)),U.value[s]=d.length;const p=Ue();me(()=>{setTimeout(()=>{p&&he()},10)})}}if(e.elements&&e.elements.length>0){const s=`${e.selfId}:${e.channelId}`;e.elements.forEach(i=>{if((i.type==="img"||i.type==="image"||i.type==="mface")&&i.attrs){const l=i.attrs.src||i.attrs.url||i.attrs.file;l&&Ie(s,l).catch(d=>{console.warn("预缓存图片失败:",l,d)})}})}u.value={...u.value}}async function ma(){try{const e=await z("get-chat-data");if(e.success&&e.data){const t={};h.value=new Set(e.data.pinnedBots||[]),r.value=new Set(e.data.pinnedChannels||[]);for(const[a,n]of Object.entries(e.data.messages||{})){const o=n.map(s=>({id:s.id,content:s.content,userId:s.userId,username:s.username,avatar:s.avatar,timestamp:s.timestamp,channelId:s.channelId,selfId:s.selfId,elements:s.elements,isBot:s.type==="bot",quote:s.quote}));o.sort((s,i)=>s.timestamp-i.timestamp),t[a]=o}return u.value={bots:e.data.bots||{},channels:e.data.channels||{},messages:t},await va(),true}else return console.warn("获取聊天数据失败:",e.error),false}catch(e){return console.error("获取聊天数据时出错:",e),false}}async function va(){try{const e=await z("get-all-channel-message-counts");if(e.success&&e.counts){const t={};for(const[a,n]of Object.entries(e.counts))t[a]=n;U.value=t}else console.warn("获取频道消息数量失败:",e.error)}catch(e){console.error("获取频道消息数量时出错:",e)}}async function ga(){try{const e=await z("get-plugin-config");e.success&&e.config?ie.value=e.config:console.warn("获取插件配置失败:",e.error)}catch(e){console.error("获取插件配置时出错:",e)}}async function rt(e,t,a,n){try{const o={selfId:e,channelId:t};a!==void 0&&(o.limit=a,o.offset=n||0);const s=await z("get-history-messages",o);if(s.success&&s.messages){const i=`${e}:${t}`,l=s.messages.map(v=>({id:v.id,content:v.content,userId:v.userId,username:v.username,avatar:v.avatar,timestamp:v.timestamp,channelId:v.channelId,selfId:v.selfId,elements:v.elements,isBot:v.type==="bot",quote:v.quote})),d=n||0;if(a!==void 0)if(l.sort((v,p)=>v.timestamp-p.timestamp),n===0)u.value.messages[i]=l,K.value[i]={offset:l.length,hasMore:l.length>=a&&s.total>l.length,loading:false};else{const v=u.value.messages[i]||[];u.value.messages[i]=[...l,...v];const p=n||0;K.value[i]={offset:p+l.length,hasMore:l.length>=a&&s.total>p+l.length,loading:false}}else l.sort((v,p)=>v.timestamp-p.timestamp),u.value.messages[i]=l,K.value[i]={offset:l.length,hasMore:false,loading:false};return U.value[i]=s.total||l.length,u.value={...u.value},true}else return console.warn("获取历史消息失败:",s.error),false}catch(o){return console.error("获取历史消息时出错:",o),false}}function ya(e){if(!P.value||e.touches.length!==1)return;const t=e.touches[0];N.value={x:t.clientX,y:t.clientY,time:Date.now()},se.value={x:t.clientX,y:t.clientY},Se.value=false}function pa(e){if(!P.value||!N.value||e.touches.length!==1)return;const t=e.touches[0];se.value={x:t.clientX,y:t.clientY};const a=t.clientX-N.value.x,n=t.clientY-N.value.y;if(Math.abs(a)>Math.abs(n)&&Math.abs(a)>30){const o=a>0,s=J.value==="messages"||J.value==="channels";if(o&&s){Se.value=true;const i=Math.min(a,200),l=150;i>l?G.value={show:true,text:"松开返回"}:G.value={show:true,text:`滑动返回 ${Math.round(i/l*100)}%`},e.preventDefault()}else G.value={show:false,text:""}}else G.value={show:false,text:""}}function wa(e){if(!P.value||!N.value)return;const a=Date.now()-N.value.time;if(se.value){const n=se.value.x-N.value.x,o=se.value.y-N.value.y,s=n>150,i=Math.abs(n)>Math.abs(o),l=a<300&&n>80;(s&&i||l)&&Ia()}N.value=null,se.value=null,Se.value=false,G.value={show:false,text:""}}function Ia(){switch(J.value){case"messages":J.value="channels";break;case"channels":J.value="bots",y.value="",S.value="";break}}function ct(){P.value=window.innerWidth<=768}function Dt(){return!oe.value||Ue()}const _t=()=>{P.value&&L.value&&me(()=>{Dt()&&he()})},xa=()=>{P.value&&setTimeout(()=>{L.value&&Dt()&&he()},300)};return Tt(ht,(e,t)=>{t.length===0&&e.length>0&&me(()=>{he()})}),it(async()=>{ct(),window.addEventListener("resize",ct),window.visualViewport&&window.visualViewport.addEventListener("resize",_t),document.addEventListener("click",tt),await ca()?(console.log("IndexedDB初始化成功"),setTimeout(async()=>{await at()},2e3),setInterval(async()=>{await at()},5*60*1e3)):console.warn("IndexedDB初始化失败,图片缓存功能将不可用"),await ga(),await ma(),me(()=>{Bt()});const t=ft("chat-message-event",$t),a=ft("bot-message-sent-event",fa),n=ft("chat-bot-message-event",ha);Tt(S,o=>{o&&me(()=>{L.value&&(L.value.removeEventListener("scroll",Ae),L.value.addEventListener("scroll",Ae),Ae()),!P.value&&fe.value&&fe.value.focus()})},{immediate:true}),setInterval(()=>{We()},2*60*1e3),ba(()=>{window.removeEventListener("resize",ct),window.visualViewport&&window.visualViewport.removeEventListener("resize",_t),document.removeEventListener("click",tt),t&&typeof t=="function"&&t(),a&&typeof a=="function"&&a(),n&&typeof n=="function"&&n(),L.value&&L.value.removeEventListener("scroll",Ae),Object.values(b.value).forEach(o=>{URL.revokeObjectURL(o)}),b.value={},x&&(x.close(),x=null)})}),{AvatarComponent:ce,ImageComponent:X,JsonCardComponent:ve,ForwardMessageComponent:E,MessageElement:be,chatData:u,channelMessageCounts:U,channelPagination:K,pluginConfig:ie,selectedBot:y,selectedChannel:S,inputMessage:ke,imageBlobUrls:b,pinnedBots:h,pinnedChannels:r,uploadedImages:W,showActionMenu:de,isMobile:P,mobileView:J,touchStart:N,touchCurrent:se,isSwipeActive:Se,swipeIndicator:G,messageHistory:L,messageInput:fe,showScrollButton:Be,isUserScrolling:oe,isSending:pe,isLoadingMore:we,draggingChannel:le,dragStartPos:R,dragCurrentPos:A,dragElementInitialPos:$e,dragOffset:De,dragThreshold:F,isDragReady:ee,draggedBubbleElement:k,contextMenu:Z,fileInput:Ke,bots:M,currentChannels:je,currentMessages:ht,currentChannelName:jt,currentChannelKey:Pt,canSendMessage:mt,canInputMessage:At,mobileViewClass:Ut,inputPlaceholder:Rt,chatContainerStyle:zt,selectBot:Ot,selectChannel:Wt,handleBotRightClick:Nt,handleChannelRightClick:Ft,showContextMenu:Qe,hideContextMenu:Q,handleKeyDown:Ee,toggleBotPin:Ht,toggleChannelPin:Xt,deleteBotMessages:Yt,deleteChannelMessages:Vt,sendMessage:Jt,toggleActionMenu:Gt,triggerImageUpload:Zt,handleFileSelect:Qt,handlePaste:Et,uploadImage:et,removeImage:ea,fileToBase64:gt,handleClickOutside:tt,formatTime:ta,getChannelTypeText:aa,scrollToBottom:he,checkScrollPosition:Ae,isNearBottom:Ue,getChannelMessageCount:pt,startDrag:na,handleDragMove:Re,handleDragEnd:ze,resetDragState:Oe,getDragStyle:sa,getDragDistance:oa,clearChannelHistory:wt,showNotification:T,createThresholdCircle:It,removeThresholdCircle:xt,handleTouchStart:ya,handleTouchMove:pa,handleTouchEnd:wa,handleInputFocus:xa,loadMoreMessages:yt,getCachedImageUrl:ot,cacheImage:Ie,clearChannelImageCache:Ne,getMemoryStats:ua,getCacheStats:da,isFileUrl:ne,loadHistoryMessages:rt,handleMessageEvent:$t,saveSelectionState:lt,restoreSelectionState:Bt}}const Ta={class:"bot-list"},Ma={class:"bot-items"},qa=["onClick","onContextmenu"],Ka={class:"bot-avatar"},ja={key:1,class:"avatar-placeholder"},Pa={class:"bot-info"},Aa={class:"bot-name"},Ua={class:"bot-platform"},Ra={class:"channel-list"},za={key:0,class:"empty-state"},Oa={key:1,class:"channel-items"},Na=["data-channel-id","onClick","onContextmenu"],Fa={class:"channel-info"},Ha={class:"channel-name"},Xa={class:"channel-type"},Ya=["onMousedown","onTouchstart","title"],Va={class:"message-area"},Wa={class:"panel-header"},Ja={key:0,class:"empty-state"},Ga={key:1,class:"message-content"},Za={key:0,class:"loading-more-indicator"},Qa={class:"message-avatar"},Ea={key:1,class:"avatar-placeholder"},en={class:"message-content-wrapper"},tn={class:"message-header"},an={class:"message-username"},nn={class:"message-time"},sn={key:0,class:"message-quote"},on={class:"quote-header"},ln={class:"quote-avatar"},rn={key:1,class:"avatar-placeholder"},cn={class:"quote-username"},un={class:"quote-time"},dn={class:"quote-content"},fn={class:"message-text"},hn={class:"message-input"},mn={key:0,class:"image-preview-container"},vn=["src","alt"],gn=["onClick"],yn={class:"input-row"},pn={class:"input-actions"},wn=["placeholder","disabled"],In=["disabled"],xn=Ce({__name:"index",setup(ne){const ce=La(),{AvatarComponent:X,MessageElement:ve,selectedBot:E,selectedChannel:be,inputMessage:u,pinnedBots:U,pinnedChannels:K,uploadedImages:ie,showActionMenu:b,swipeIndicator:Y,messageHistory:Te,messageInput:ge,showScrollButton:ye,isSending:x,isLoadingMore:He,draggingChannel:Me,dragThreshold:$,contextMenu:_,fileInput:Xe,bots:ue,currentChannels:qe,currentMessages:Ye,currentChannelName:Ge,currentChannelKey:j,canSendMessage:V,canInputMessage:Ve,mobileViewClass:y,inputPlaceholder:S,chatContainerStyle:ke,selectBot:W,selectChannel:de,handleBotRightClick:Ke,handleChannelRightClick:P,toggleBotPin:J,toggleChannelPin:N,deleteBotMessages:se,deleteChannelMessages:Se,sendMessage:G,toggleActionMenu:L,triggerImageUpload:fe,handleFileSelect:Be,handlePaste:oe,removeImage:pe,formatTime:we,getChannelTypeText:le,scrollToBottom:R,getChannelMessageCount:A,startDrag:$e,getDragStyle:De,getDragDistance:F,handleTouchStart:_e,handleTouchMove:O,handleTouchEnd:ee,handleInputFocus:k}=ce;return(Z,h)=>(w(),C("div",{class:re(["chat-container",c(y)]),style:ut(c(ke)),onTouchstart:h[15]||(h[15]=(...r)=>c(_e)&&c(_e)(...r)),onTouchmove:h[16]||(h[16]=(...r)=>c(O)&&c(O)(...r)),onTouchend:h[17]||(h[17]=(...r)=>c(ee)&&c(ee)(...r))},[B(" 左侧机器人列表 "),f("div",Ta,[h[18]||(h[18]=f("div",{class:"panel-header"},[f("h3",null,"机器人")],-1)),f("div",Ma,[(w(true),C(ae,null,Le(c(ue),r=>(w(),C("div",{key:r.selfId,class:re(["bot-item",{active:c(E)===r.selfId,pinned:c(U).has(r.selfId)}]),onClick:M=>c(W)(r.selfId),onContextmenu:M=>c(Ke)(M,r.selfId)},[f("div",Ka,[r.avatar?(w(),Fe(c(X),{key:0,src:r.avatar,alt:r.username,"channel-key":"bot-list"},null,8,["src","alt"])):(w(),C("div",ja,D(r.username.charAt(0).toUpperCase()),1))]),f("div",Pa,[f("div",Aa,D(r.username),1),f("div",Ua,D(r.platform),1)]),f("div",{class:re(["bot-status",r.status])},null,2)],42,qa))),128))])]),B(" 中间频道列表 "),f("div",Ra,[h[19]||(h[19]=f("div",{class:"panel-header"},[f("h3",null,"频道")],-1)),c(E)?(w(),C("div",Oa,[(w(true),C(ae,null,Le(c(qe),r=>(w(),C("div",{key:r.id,class:re(["channel-item",{active:c(be)===r.id,pinned:c(K).has(`${c(E)}:${r.id}`)}]),"data-channel-id":r.id,onClick:M=>c(de)(r.id),onContextmenu:M=>c(P)(M,r.id)},[f("div",Fa,[f("div",Ha,D(r.name),1),f("div",Xa,D(c(le)(r.type)),1)]),c(A)(r.id)>0?(w(),C("div",{key:0,class:re(["channel-message-count draggable-bubble",{dragging:c(Me)===r.id,"will-delete":c(Me)===r.id&&c(F)(r.id)>c($)}]),onMousedown:M=>c($e)(M,r.id),onTouchstart:M=>c($e)(M,r.id),style:ut(c(De)(r.id)),title:c(Me)===r.id?c(F)(r.id)>c($)?"松开清理历史记录":"拖拽更远以清理历史记录":"拖拽清理历史记录"},D(c(A)(r.id)),47,Ya)):B("v-if",true)],42,Na))),128))])):(w(),C("div",za," 请选择一个机器人 "))]),B(" 右侧消息区域 "),f("div",Va,[f("div",Wa,[f("h3",null,D(c(Ge)||"选择频道"),1)]),!c(E)||!c(be)?(w(),C("div",Ja," 请选择机器人和频道 ")):(w(),C("div",Ga,[B(" 消息历史 "),f("div",{class:"message-history",ref_key:"messageHistory",ref:Te},[B(" 加载更多指示器 "),c(He)?(w(),C("div",Za,h[20]||(h[20]=[f("div",{class:"loading-spinner"},null,-1),f("span",null,"加载更多消息中...",-1)]))):B("v-if",true),(w(true),C(ae,null,Le(c(Ye),r=>(w(),C("div",{key:r.id,class:re(["message-item",{"bot-message":r.isBot}])},[f("div",Qa,[r.avatar?(w(),Fe(c(X),{key:0,src:r.avatar,alt:r.username,"channel-key":c(j)},null,8,["src","alt","channel-key"])):(w(),C("div",Ea,D(r.username.charAt(0).toUpperCase()),1))]),f("div",en,[f("div",tn,[f("span",an,D(r.username),1),f("span",nn,D(c(we)(r.timestamp)),1)]),B(" 引用消息显示 "),r.quote?(w(),C("div",sn,[f("div",on,[f("div",ln,[r.quote.user.avatar?(w(),Fe(c(X),{key:0,src:r.quote.user.avatar,alt:r.quote.user.username,"channel-key":c(j)},null,8,["src","alt","channel-key"])):(w(),C("div",rn,D(r.quote.user.username.charAt(0).toUpperCase()),1))]),f("span",cn,D(r.quote.user.username),1),f("span",un,D(c(we)(r.quote.timestamp)),1)]),f("div",dn,[r.quote.elements&&r.quote.elements.length>0?(w(true),C(ae,{key:0},Le(r.quote.elements,(M,je)=>(w(),Fe(c(ve),{key:`quote-${je}`,element:M,"channel-key":c(j)},null,8,["element","channel-key"]))),128)):(w(),C(ae,{key:1},[dt(D(r.quote.content),1)],64))])])):B("v-if",true),f("div",fn,[r.elements&&r.elements.length>0?(w(true),C(ae,{key:0},Le(r.elements,(M,je)=>(w(),Fe(c(ve),{key:je,element:M,"channel-key":c(j)},null,8,["element","channel-key"]))),128)):(w(),C(ae,{key:1},[dt(D(r.content),1)],64))])])],2))),128))],512),B(" 悬浮的滚动到底部按钮 "),Mt(f("div",{class:"floating-scroll-button",onClick:h[0]||(h[0]=(...r)=>c(R)&&c(R)(...r))},h[21]||(h[21]=[f("svg",{width:"20",height:"20",viewBox:"0 0 24 24",fill:"currentColor"},[f("path",{d:"M7 10l5 5 5-5z"})],-1)]),512),[[ka,c(ye)]]),B(" 输入框 "),f("div",hn,[B(" 图片预览区域 "),c(ie).length>0?(w(),C("div",mn,[(w(true),C(ae,null,Le(c(ie),r=>(w(),C("div",{key:r.tempId,class:"image-preview-item"},[f("img",{src:r.preview,alt:r.filename,class:"preview-image"},null,8,vn),f("button",{class:"remove-image-btn",onClick:M=>c(pe)(r.tempId),title:"删除图片"},h[22]||(h[22]=[f("svg",{width:"12",height:"12",viewBox:"0 0 24 24",fill:"currentColor"},[f("path",{d:"M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z"})],-1)]),8,gn)]))),128))])):B("v-if",true),f("div",yn,[B(" 加号按钮 "),f("div",pn,[f("button",{class:re(["add-button",{active:c(b)}]),onClick:h[1]||(h[1]=(...r)=>c(L)&&c(L)(...r)),title:"更多操作"},h[23]||(h[23]=[f("svg",{width:"18",height:"18",viewBox:"0 0 24 24",fill:"none",stroke:"currentColor","stroke-width":"2.5","stroke-linecap":"round","stroke-linejoin":"round"},[f("line",{x1:"12",y1:"5",x2:"12",y2:"19"}),f("line",{x1:"5",y1:"12",x2:"19",y2:"12"})],-1)]),2),B(" 操作菜单 "),c(b)?(w(),C("div",{key:0,class:"action-menu",onClick:h[3]||(h[3]=qt(()=>{},["stop"]))},[f("button",{class:"action-menu-item",onClick:h[2]||(h[2]=(...r)=>c(fe)&&c(fe)(...r))},h[24]||(h[24]=[f("svg",{width:"18",height:"18",viewBox:"0 0 24 24",fill:"none",stroke:"currentColor","stroke-width":"2","stroke-linecap":"round","stroke-linejoin":"round"},[f("rect",{x:"3",y:"3",width:"18",height:"18",rx:"2",ry:"2"}),f("circle",{cx:"8.5",cy:"8.5",r:"1.5"}),f("polyline",{points:"21,15 16,10 5,21"})],-1),dt(" 上传图片 ",-1)]))])):B("v-if",true)]),Mt(f("input",{"onUpdate:modelValue":h[4]||(h[4]=r=>Ba(u)?u.value=r:null),type:"text",placeholder:c(S),onKeyup:h[5]||(h[5]=Sa((...r)=>c(G)&&c(G)(...r),["enter"])),disabled:!c(Ve),ref_key:"messageInput",ref:ge,onPaste:h[6]||(h[6]=(...r)=>c(oe)&&c(oe)(...r)),onFocus:h[7]||(h[7]=(...r)=>c(k)&&c(k)(...r))},null,40,wn),[[$a,c(u)]]),f("button",{onClick:h[8]||(h[8]=(...r)=>c(G)&&c(G)(...r)),disabled:!c(V),class:re({"is-sending":c(x)})},D(c(x)?"发送中...":"发送"),11,In)]),B(" 隐藏的文件输入 "),f("input",{type:"file",ref_key:"fileInput",ref:Xe,onChange:h[9]||(h[9]=(...r)=>c(Be)&&c(Be)(...r)),accept:"image/*",multiple:"",style:{display:"none"}},null,544)])]))]),B(" 右键菜单 "),c(_).show?(w(),C("div",{key:0,class:"context-menu",style:ut({left:c(_).x+"px",top:c(_).y+"px"}),onClick:h[14]||(h[14]=qt(()=>{},["stop"]))},[B(" 机器人右键菜单 "),c(_).type==="bot"?(w(),C(ae,{key:0},[f("div",{class:"context-menu-item",onClick:h[10]||(h[10]=r=>c(J)(c(_).targetId))},D(c(U).has(c(_).targetId)?"取消置顶":"置顶"),1),f("div",{class:"context-menu-item danger",onClick:h[11]||(h[11]=r=>c(se)(c(_).targetId))}," 彻底删除此机器人所有数据 ")],64)):B("v-if",true),B(" 频道右键菜单 "),c(_).type==="channel"?(w(),C(ae,{key:1},[f("div",{class:"context-menu-item",onClick:h[12]||(h[12]=r=>c(N)(c(_).targetId))},D(c(K).has(`${c(E)}:${c(_).targetId}`)?"取消置顶":"置顶"),1),f("div",{class:"context-menu-item danger",onClick:h[13]||(h[13]=r=>c(Se)(c(_).targetId))}," 彻底删除此频道所有数据 ")],64)):B("v-if",true)],4)):B("v-if",true),B(" 滑动指示器 "),f("div",{class:re(["swipe-indicator",{show:c(Y).show}])},D(c(Y).text),3)],38))}}),Kt=(ne,ce)=>{const X=ne.__vccOpts||ne;for(const[ve,E]of ce)X[ve]=E;return X},Cn=Kt(xn,[["__scopeId","data-v-f8e0fc57"]]),bn={},kn={class:"k-icon",xmlns:"http://www.w3.org/2000/svg",viewBox:"0 0 24 24",fill:"none"};function Sn(ne,ce){return w(),C("svg",kn,ce[0]||(ce[0]=[f("path",{d:"M8 10.5H16",stroke:"currentColor","stroke-width":"1.5","stroke-linecap":"round"},null,-1),f("path",{d:"M8 14H13.5",stroke:"currentColor","stroke-width":"1.5","stroke-linecap":"round"},null,-1),f("path",{d:"M17 3.33782C15.5291 2.48697 13.8214 2 12 2C6.47715 2 2 6.47715 2 12C2 13.5997 2.37562 15.1116 3.04346 16.4525C3.22094 16.8088 3.28001 17.2161 3.17712 17.6006L2.58151 19.8267C2.32295 20.793 3.20701 21.677 4.17335 21.4185L6.39939 20.8229C6.78393 20.72 7.19121 20.7791 7.54753 20.9565C8.88837 21.6244 10.4003 22 12 22C17.5228 22 22 17.5228 22 12C22 10.1786 21.513 8.47087 20.6622 7",stroke:"currentColor","stroke-width":"1.5","stroke-linecap":"round"},null,-1)]))}const Bn=Kt(bn,[["render",Sn]]);_a.register("activity:chat",Bn);const Ln=ne=>{ne.page({name:"聊天室",path:"/chat-patch",desc:"",authority:4,icon:"activity:chat",component:Ce({setup(){return()=>m(Da("k-layout"),{},{default:()=>m(Cn)})}})})};export{Ln as default};
|
package/dist/style.css
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
.message-quote[data-v-de93bb69]{margin:8px 0;padding:8px 12px;background-color:#bebebe1a;border-left:4px solid rgba(151,152,152,.45);border-radius:6px;font-size:.9em;position:relative}.quote-indicator[data-v-de93bb69]{font-size:.75em;color:#007bff;font-weight:600;margin-bottom:4px;text-transform:uppercase}.quote-header[data-v-de93bb69]{display:flex;align-items:center;margin-bottom:4px;gap:6px}.quote-avatar[data-v-de93bb69]{width:20px;height:20px;border-radius:50%;overflow:hidden;flex-shrink:0}.quote-avatar img[data-v-de93bb69]{width:100%;height:100%;object-fit:cover}.quote-avatar .avatar-placeholder[data-v-de93bb69]{width:100%;height:100%;display:flex;align-items:center;justify-content:center;background-color:#6c757d;color:#fff;font-size:10px;font-weight:700}.quote-username[data-v-de93bb69]{font-weight:600;color:#007bff;font-size:.85em}.quote-time[data-v-de93bb69]{color:#6c757d;font-size:.75em;margin-left:auto}.quote-content[data-v-de93bb69]{color:#7276b7;line-height:1.3;word-wrap:break-word;word-break:break-all;overflow-wrap:anywhere;-webkit-hyphens:auto;hyphens:auto}.quote-content .message-image[data-v-de93bb69]{max-width:min(200px,50vw);max-height:100px}.quote-content .message-image-container[data-v-de93bb69]{margin:2px 0}@media (prefers-color-scheme:dark){.message-quote[data-v-de93bb69]{background-color:#ffffff14;border-left-color:#0d6efd}.quote-content[data-v-de93bb69]{color:#adb5bd}.quote-username[data-v-de93bb69]{color:#0d6efd}}.chat-container[data-v-de93bb69]{display:flex;height:100vh;background:0 0;margin-left:0;width:100%;overflow:hidden;color:inherit;position:relative}.bot-list[data-v-de93bb69],.channel-list[data-v-de93bb69],.message-area[data-v-de93bb69]{display:flex;flex-direction:column;background:0 0;border-right:1px solid rgba(128,128,128,.2)}.bot-list[data-v-de93bb69]{width:250px;min-width:250px;max-width:250px;flex-shrink:0}.channel-list[data-v-de93bb69]{width:300px;min-width:300px;max-width:300px;flex-shrink:0}.message-area[data-v-de93bb69]{flex:1;border-right:none}.panel-header[data-v-de93bb69]{padding:16px;border-bottom:1px solid rgba(128,128,128,.2);background:0 0;display:flex;align-items:center;justify-content:space-between}.panel-header h3[data-v-de93bb69]{margin:0;font-size:16px;font-weight:600;color:currentColor}.back-button[data-v-de93bb69]{display:none;background:0 0;border:none;color:currentColor;font-size:18px;cursor:pointer;padding:4px 8px;border-radius:4px;transition:background-color .2s}.back-button[data-v-de93bb69]:hover{background:#8080801a}.bot-items[data-v-de93bb69],.channel-items[data-v-de93bb69]{flex:1;overflow-y:auto;overflow-x:hidden;max-height:calc(100vh - 140px);-webkit-overflow-scrolling:touch;scrollbar-width:thin;scrollbar-color:rgba(144,147,153,.4) rgba(0,0,0,.05);scroll-behavior:smooth;overscroll-behavior:contain;padding-bottom:8px}.bot-item[data-v-de93bb69],.channel-item[data-v-de93bb69]{display:flex;align-items:center;padding:12px 16px;cursor:pointer;border-bottom:1px solid rgba(128,128,128,.1);transition:background-color .2s}.bot-item[data-v-de93bb69]:hover,.channel-item[data-v-de93bb69]:hover{background:#8080801a}.bot-item.active[data-v-de93bb69],.channel-item.active[data-v-de93bb69]{background:#2196f31a;border-right:3px solid #2196f3}.bot-avatar[data-v-de93bb69]{width:40px;height:40px;margin-right:12px;border-radius:50%;overflow:hidden;display:flex;align-items:center;justify-content:center;background:#e0e0e0}.bot-avatar img[data-v-de93bb69]{width:100%;height:100%;object-fit:cover}.avatar-placeholder[data-v-de93bb69]{font-size:16px;font-weight:700;color:currentColor;opacity:.7}.bot-info[data-v-de93bb69]{flex:1}.bot-name[data-v-de93bb69]{font-weight:500;color:currentColor;margin-bottom:2px}.bot-platform[data-v-de93bb69]{font-size:12px;color:currentColor;opacity:.7}.bot-status[data-v-de93bb69]{width:8px;height:8px;border-radius:50%;margin-left:8px}.bot-status.online[data-v-de93bb69]{background:#4caf50}.bot-status.offline[data-v-de93bb69]{background:#f44336}.channel-info[data-v-de93bb69]{flex:1}.channel-name[data-v-de93bb69]{font-weight:500;color:currentColor;margin-bottom:2px}.channel-type[data-v-de93bb69]{font-size:12px;color:currentColor;opacity:.7}.channel-message-count[data-v-de93bb69]{background:#2196f3;color:#fff;font-size:12px;padding:2px 6px;border-radius:10px;min-width:18px;text-align:center;position:relative;transition:all .2s ease}.channel-message-count.draggable-bubble[data-v-de93bb69]{cursor:grab;user-select:none;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none}.channel-message-count.draggable-bubble[data-v-de93bb69]:hover{background:#1976d2;transform:scale(1.1);box-shadow:0 2px 8px #2196f34d}.channel-message-count.draggable-bubble.dragging[data-v-de93bb69],.channel-message-count.draggable-bubble[data-v-de93bb69]:active{cursor:grabbing;background:#1565c0;box-shadow:0 4px 12px #2196f366}.dragging-clone[data-v-de93bb69]{position:fixed;z-index:1000;pointer-events:none;transition:none;box-sizing:border-box;display:flex;align-items:center;justify-content:center;background:#2196f3;color:#fff;font-size:12px;padding:2px 6px;border-radius:10px;min-width:18px;text-align:center;box-shadow:0 4px 12px #2196f366}.dragging-clone.will-delete[data-v-de93bb69]{background:#f44336!important;animation:shake-de93bb69 .3s ease-in-out infinite}@keyframes pulseThreshold-de93bb69{0%,to{opacity:.4;transform:scale(.9);border-color:#f4433699;box-shadow:0 0 15px #f4433633}50%{opacity:.8;transform:scale(1.1);border-color:#f44336;box-shadow:0 0 25px #f4433666}}@keyframes menuSlideIn-de93bb69{0%{transform:translateY(8px);opacity:0}to{transform:translateY(0);opacity:1}}.message-content[data-v-de93bb69]{display:flex;flex-direction:column;height:calc(100vh - 60px);overflow:hidden;position:relative}.message-history[data-v-de93bb69]{flex:1;overflow-y:auto;padding:16px 16px 8px;max-height:calc(100vh - 200px)}.message-item[data-v-de93bb69]{display:flex;margin-bottom:16px;padding:8px;border-radius:8px;transition:background-color .3s ease}.message-item[data-v-de93bb69]:hover{background-color:#8080801a}.message-item.new-message[data-v-de93bb69]{background-color:#4caf5033;animation:highlight-de93bb69 2s ease-out}.message-item.bot-message[data-v-de93bb69]{flex-direction:row-reverse;justify-content:flex-start}.message-item.bot-message .message-avatar[data-v-de93bb69]{margin-right:0;margin-left:12px}.message-item.bot-message .message-content-wrapper[data-v-de93bb69]{display:flex;flex-direction:column;align-items:flex-end}.message-item.bot-message .message-header[data-v-de93bb69]{display:flex;align-items:center;justify-content:flex-end;margin-bottom:4px}.message-item.bot-message .message-username[data-v-de93bb69]{margin-right:0;margin-left:8px;order:2}.message-item.bot-message .message-time[data-v-de93bb69]{order:1}.message-item.bot-message .message-text[data-v-de93bb69]{background-color:#2196f31a;padding:8px 12px;border-radius:12px 12px 4px;display:inline-block;max-width:80%;text-align:left;word-wrap:break-word;word-break:break-all;overflow-wrap:anywhere}@keyframes highlight-de93bb69{0%{background-color:#4caf504d}to{background-color:transparent}}.message-avatar[data-v-de93bb69]{width:32px;height:32px;margin-right:12px;border-radius:50%;overflow:hidden;display:flex;align-items:center;justify-content:center;background:#e0e0e0;flex-shrink:0}.message-avatar img[data-v-de93bb69]{width:100%;height:100%;object-fit:cover}.message-avatar .avatar-placeholder[data-v-de93bb69]{font-size:12px;font-weight:700;color:currentColor;opacity:.7}.message-content-wrapper[data-v-de93bb69]{flex:1}.message-header[data-v-de93bb69]{display:flex;align-items:center;margin-bottom:4px}.message-username[data-v-de93bb69]{font-weight:500;color:currentColor;margin-right:8px}.message-time[data-v-de93bb69]{font-size:12px;color:currentColor;opacity:.5}.message-text[data-v-de93bb69]{color:currentColor;line-height:1.4;word-wrap:break-word;word-break:break-all;white-space:pre-wrap;max-width:100%;overflow-wrap:anywhere;-webkit-hyphens:auto;hyphens:auto}.message-item:not(.bot-message) .message-text[data-v-de93bb69]{background-color:#8080801a;padding:8px 12px;border-radius:12px 12px 12px 4px;display:inline-block;max-width:80%;word-wrap:break-word;word-break:break-all;overflow-wrap:anywhere}.message-input[data-v-de93bb69]{display:flex;flex-direction:column;padding:12px 16px 16px;border-top:1px solid rgba(128,128,128,.2);background:0 0;flex-shrink:0;position:sticky;bottom:0;margin-top:-10px}.input-row[data-v-de93bb69]{display:flex;gap:8px;align-items:center}.floating-scroll-button[data-v-de93bb69]{position:absolute;right:24px;bottom:18%;width:48px;height:48px;background:#2196f3;border-radius:50%;display:flex;align-items:center;justify-content:center;cursor:pointer;box-shadow:0 4px 12px #2196f34d;color:#fff;transition:all .3s ease;z-index:100;border:2px solid rgba(255,255,255,.2)}.floating-scroll-button[data-v-de93bb69]:hover{background:#1976d2;transform:scale(1.1);box-shadow:0 6px 16px #2196f366}.floating-scroll-button[data-v-de93bb69]:active{transform:scale(.95)}.input-row button[data-v-de93bb69]{padding:8px 16px;background:#2196f3;color:#fff;border:none;border-radius:4px;cursor:pointer;transition:background-color .2s}.message-image-container[data-v-de93bb69]{margin:8px 0;max-width:100%;display:block}.message-image[data-v-de93bb69]{border-radius:8px;object-fit:contain;display:block;cursor:pointer;transition:transform .2s ease;max-width:min(400px,66.67vw);max-height:200px;width:auto;height:auto;image-rendering:auto;-webkit-image-rendering:auto;-moz-image-rendering:auto;-o-image-rendering:auto}.message-image[src*=".gif"][data-v-de93bb69],.message-image[src*="image/gif"][data-v-de93bb69]{image-rendering:auto!important;-webkit-image-rendering:auto!important;animation-play-state:running!important}.message-image-loading[data-v-de93bb69]{display:inline-block;padding:8px 12px;background:#8080801a;border-radius:4px;color:currentColor;opacity:.7;font-size:12px}.message-image-error[data-v-de93bb69]{display:inline-block;padding:8px 12px;background:#f443361a;border:1px solid rgba(244,67,54,.3);border-radius:4px;color:#f44336;font-size:12px;max-width:300px;word-break:break-word}.message-paragraph[data-v-de93bb69]{display:block;margin-bottom:4px}.message-paragraph[data-v-de93bb69]:last-child{margin-bottom:0}.forward-message-container[data-v-de93bb69]{border:1px solid #fff;border-radius:8px;margin:4px 0;overflow:hidden;background:#f9f9f900}.forward-message-preview[data-v-de93bb69]{padding:12px;cursor:pointer;transition:background-color .2s}.forward-message-preview[data-v-de93bb69]:hover{background:#f0f0f0}.forward-message-title[data-v-de93bb69]{font-weight:600;color:#333;margin-bottom:8px;font-size:14px}.forward-message-preview-item[data-v-de93bb69]{color:#666;font-size:13px;margin:2px 0;line-height:1.4;word-wrap:break-word;word-break:break-all;overflow-wrap:anywhere}.forward-message-footer[data-v-de93bb69]{display:flex;justify-content:space-between;align-items:center;margin-top:8px;padding-top:8px;border-top:1px solid #e8e8e8}.forward-message-count[data-v-de93bb69]{color:#2196f3;font-size:12px}.forward-message-toggle[data-v-de93bb69]{color:#666;font-size:12px}.forward-message-expanded[data-v-de93bb69]{border-top:1px solid #e0e0e0;background:#fff;max-height:400px;overflow-y:auto}.forwarded-message-item[data-v-de93bb69]{padding:12px;border-bottom:1px solid #f0f0f0}.forwarded-message-item[data-v-de93bb69]:last-child{border-bottom:none}.forwarded-message-header[data-v-de93bb69]{display:flex;align-items:center;margin-bottom:6px}.forwarded-message-nickname[data-v-de93bb69]{font-weight:600;color:#333;font-size:13px}.forwarded-message-userid[data-v-de93bb69]{color:#999;font-size:11px;margin-left:6px}.forwarded-message-content[data-v-de93bb69]{color:#555;font-size:13px;line-height:1.4;word-wrap:break-word;word-break:break-all;overflow-wrap:anywhere}.message-at[data-v-de93bb69]{background:#2196f31a;color:#2196f3;padding:2px 4px;border-radius:3px;font-weight:500;cursor:pointer}.message-unknown[data-v-de93bb69]{background:#ff98001a;color:#ff9800;padding:2px 4px;border-radius:3px;font-style:italic;cursor:help}.input-row input[data-v-de93bb69]{flex:1;min-width:0;padding:8px 12px;border:1px solid rgba(128,128,128,.3);border-radius:4px;outline:0;background:0 0;color:currentColor}.input-row input[data-v-de93bb69]:focus{border-color:#2196f3;box-shadow:0 0 0 2px #2196f333}.input-row input[data-v-de93bb69]::placeholder{color:currentColor;opacity:.5}.input-row button[data-v-de93bb69]{padding:8px 16px;background:#2196f3;color:#fff;border:none;border-radius:4px;cursor:pointer;transition:background-color .2s;flex-shrink:0;white-space:nowrap}.input-row button[data-v-de93bb69]:hover:not(:disabled){background:#1976d2}.input-row button[data-v-de93bb69]:disabled{background:#8080804d;cursor:not-allowed}.input-row button.is-sending[data-v-de93bb69]{background:#ffc107!important;color:#333!important;cursor:default!important}.message-input .input-row button.is-sending[data-v-de93bb69]{background:#ffc107!important;color:#333!important;cursor:default!important;border-color:#ffc107!important}.empty-state[data-v-de93bb69]{display:flex;align-items:center;justify-content:center;height:100%;color:currentColor;opacity:.5;font-size:14px}.message-text-content[data-v-de93bb69]{display:inline;word-wrap:break-word;word-break:break-all;overflow-wrap:anywhere;-webkit-hyphens:auto;hyphens:auto}.message-image-container[data-v-de93bb69]{display:block;margin:4px 0}.message-image[data-v-de93bb69]{border-radius:8px;margin:4px 0;display:block;cursor:pointer;transition:transform .2s ease;object-fit:contain;background:#8080801a}.message-image[data-v-de93bb69]:hover{transform:scale(1.02)}.message-image-loading[data-v-de93bb69]{height:60px;width:200px;border-radius:8px;margin:4px 0;display:flex;align-items:center;justify-content:center;background:#8080801a;color:currentColor;opacity:.7;font-size:14px;border:1px dashed rgba(128,128,128,.3)}.message-image-error[data-v-de93bb69]{min-height:60px;width:200px;border-radius:8px;margin:4px 0;display:flex;flex-direction:column;align-items:center;justify-content:center;background:#8080801a;color:currentColor;opacity:.7;font-size:14px;border:1px dashed rgba(128,128,128,.3);padding:8px;text-align:center}.message-at[data-v-de93bb69]{background:#2196f31a;color:#2196f3;padding:2px 6px;border-radius:4px;font-weight:500;cursor:pointer;display:inline-block;margin:0 2px}.message-at[data-v-de93bb69]:hover{background:#2196f333}.message-unknown[data-v-de93bb69]{background:#ff98001a;color:#ff9800;padding:2px 6px;border-radius:4px;font-size:12px;display:inline-block;margin:0 2px;cursor:help}@keyframes pulse-de93bb69{0%,to{transform:translate(-50%,-50%) scale(1)}50%{transform:translate(-50%,-50%) scale(1.1)}}.channel-message-count.will-delete[data-v-de93bb69]{background:#f44336!important;animation:shake-de93bb69 .3s ease-in-out infinite}@keyframes shake-de93bb69{0%,to{transform:translate(0)}25%{transform:translate(-2px)}75%{transform:translate(2px)}}body.dragging-bubble-global[data-v-de93bb69]{user-select:none;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;cursor:grabbing}.drag-threshold-circle[data-v-de93bb69]{position:fixed;pointer-events:none;border:3px dashed rgba(244,67,54,.8);border-radius:50%;z-index:999;background:#f443361a;box-shadow:0 0 20px #f443364d;animation:pulseThreshold-de93bb69 1.5s ease-in-out infinite}.context-menu[data-v-de93bb69]{position:fixed;background:#ffffffa6;border:1px solid rgba(0,0,0,.1);border-radius:8px;box-shadow:0 8px 32px #00000026;z-index:10000;min-width:180px;backdrop-filter:blur(16px);-webkit-backdrop-filter:blur(16px);overflow:hidden}.context-menu-item[data-v-de93bb69]{padding:12px 16px;cursor:pointer;color:#333;font-size:14px;font-weight:500;transition:all .2s ease;border-bottom:1px solid rgba(0,0,0,.08);position:relative}.context-menu-item[data-v-de93bb69]:last-child{border-bottom:none}.context-menu-item[data-v-de93bb69]:hover{background:#2196f31a;color:#1976d2;transform:translate(2px)}.context-menu-item.danger[data-v-de93bb69]{color:#d32f2f}.context-menu-item.danger[data-v-de93bb69]:hover{background:#f443361a;color:#c62828}.bot-item.pinned[data-v-de93bb69],.channel-item.pinned[data-v-de93bb69]{background:#ffc1071a;border-left:3px solid #ffc107}.bot-item.pinned[data-v-de93bb69]:before,.channel-item.pinned[data-v-de93bb69]:before{content:"📌";position:absolute;left:4px;top:50%;transform:translateY(-50%);font-size:12px;opacity:.7}.bot-item.pinned[data-v-de93bb69],.channel-item.pinned[data-v-de93bb69]{position:relative;padding-left:24px}@media (prefers-color-scheme:dark){.context-menu[data-v-de93bb69]{background:#ffffff26;border-color:#fff3;box-shadow:0 8px 32px #0006}.context-menu-item[data-v-de93bb69]{color:#fff;border-bottom-color:#ffffff1a}.context-menu-item[data-v-de93bb69]:hover{background:#2196f333;color:#64b5f6}.context-menu-item.danger[data-v-de93bb69]{color:#ff8a80}.context-menu-item.danger[data-v-de93bb69]:hover{background:#f4433633;color:#ffab91}.bot-item.pinned[data-v-de93bb69],.channel-item.pinned[data-v-de93bb69]{background:#ffc10726}}.message-share-card[data-v-de93bb69]{display:inline-block;width:75px;margin:8px 0;border:1px solid rgba(128,128,128,.2);border-radius:12px;background:#ffffff0d;transition:all .3s ease;cursor:pointer;backdrop-filter:blur(8px);-webkit-backdrop-filter:blur(8px)}.message-share-card[data-v-de93bb69]:hover{transform:translateY(-2px);box-shadow:0 8px 24px #00000026;border-color:#2196f34d}.share-card-preview[data-v-de93bb69]{width:100%;position:relative;display:flex;align-items:center;justify-content:center;padding:4px;border-radius:12px 12px 0 0}.share-card-image[data-v-de93bb69]{max-width:calc(100% - 8px);width:auto;height:auto;object-fit:contain;transition:transform .3s ease;display:block;border-radius:4px}.message-share-card:hover .share-card-image[data-v-de93bb69]{transform:scale(1.05)}.share-card-content[data-v-de93bb69]{padding:8px}.share-card-header[data-v-de93bb69]{display:flex;align-items:center;margin-bottom:12px;gap:12px}.share-card-icon[data-v-de93bb69]{width:32px;height:32px;border-radius:8px;object-fit:cover;flex-shrink:0}.share-card-app-info[data-v-de93bb69]{flex:1;min-width:0}.share-card-app-name[data-v-de93bb69]{font-size:12px;color:currentColor;opacity:.7;margin-bottom:2px;font-weight:500}.share-card-title[data-v-de93bb69]{font-size:10px;font-weight:600;color:currentColor;line-height:1.2;word-wrap:break-word;display:-webkit-box;-webkit-box-orient:vertical;overflow:hidden}.share-card-desc[data-v-de93bb69]{font-size:14px;color:currentColor;opacity:.8;line-height:1.4;margin-bottom:12px;display:-webkit-box;-webkit-box-orient:vertical;overflow:hidden;word-wrap:break-word}.share-card-link-hint[data-v-de93bb69]{font-size:12px;color:#2196f3;font-weight:500;display:flex;align-items:center;gap:4px}.share-card-link-hint[data-v-de93bb69]:after{content:"→";transition:transform .2s ease}.message-share-card:hover .share-card-link-hint[data-v-de93bb69]:after{transform:translate(2px)}.message-json-error[data-v-de93bb69],.message-json-raw[data-v-de93bb69]{display:inline-block;margin:8px 0;max-width:400px}.json-error-text[data-v-de93bb69]{color:#f44336;font-size:14px;font-weight:500}.json-label[data-v-de93bb69]{color:currentColor;opacity:.7;font-size:12px;font-weight:500;margin-bottom:8px}.json-raw-data[data-v-de93bb69]{margin-top:8px}.json-raw-data summary[data-v-de93bb69]{cursor:pointer;font-size:12px;color:#2196f3;font-weight:500;padding:4px 8px;border-radius:4px;background:#2196f31a;display:inline-block;transition:background-color .2s ease}.json-raw-data summary[data-v-de93bb69]:hover{background:#2196f333}.json-raw-content[data-v-de93bb69]{margin-top:8px;padding:12px;background:#8080801a;border-radius:8px;font-family:Courier New,monospace;font-size:12px;color:currentColor;opacity:.8;white-space:pre-wrap;word-wrap:break-word;word-break:break-all;overflow-wrap:anywhere;max-height:200px;overflow-y:auto;border:1px solid rgba(128,128,128,.2)}@media (prefers-color-scheme:dark){.message-share-card[data-v-de93bb69]{background:#ffffff14;border-color:#ffffff26}.message-share-card[data-v-de93bb69]:hover{border-color:#2196f380;box-shadow:0 8px 24px #0000004d}.json-raw-content[data-v-de93bb69]{background:#ffffff0d;border-color:#ffffff1a}}@media (max-width:768px){.chat-container[data-v-de93bb69]{position:relative;height:100vh;height:100dvh}.bot-list[data-v-de93bb69],.channel-list[data-v-de93bb69],.message-area[data-v-de93bb69]{position:absolute;top:0;left:0;width:100%;height:100%;transition:transform .3s ease;z-index:1}.bot-list[data-v-de93bb69]{transform:translate(0);z-index:3}.channel-list[data-v-de93bb69]{transform:translate(100%);z-index:2}.message-area[data-v-de93bb69]{transform:translate(200%);z-index:1}.chat-container.show-channels .bot-list[data-v-de93bb69]{transform:translate(-100%)}.chat-container.show-channels .channel-list[data-v-de93bb69]{transform:translate(0);z-index:3}.chat-container.show-channels .message-area[data-v-de93bb69]{transform:translate(100%)}.chat-container.show-messages .bot-list[data-v-de93bb69]{transform:translate(-200%)}.chat-container.show-messages .channel-list[data-v-de93bb69]{transform:translate(-100%)}.chat-container.show-messages .message-area[data-v-de93bb69]{transform:translate(0);z-index:3}.back-button[data-v-de93bb69]{display:none!important}.panel-header[data-v-de93bb69]{flex-direction:row-reverse;position:sticky;top:0;z-index:5;background:inherit}.panel-header h3[data-v-de93bb69]{text-align:right}.floating-scroll-button[data-v-de93bb69]{right:16px;bottom:calc(90px + env(safe-area-inset-bottom,0px));width:40px;height:40px;transition:bottom .3s ease}.message-input[data-v-de93bb69]{padding:8px 12px calc(12px + env(safe-area-inset-bottom,0px)) 12px;position:fixed;bottom:20px;left:0;right:0;background:inherit;border-top:1px solid rgba(128,128,128,.2);z-index:10;min-height:calc(60px + env(safe-area-inset-bottom,0px));transition:transform .3s ease;transform:translateZ(0)}.input-row input[data-v-de93bb69]{font-size:16px;padding:10px 12px}.input-row button[data-v-de93bb69]{padding:10px 16px;font-size:16px}.message-history[data-v-de93bb69]{padding:12px;max-height:calc(100vh - 140px - env(safe-area-inset-bottom,0px));max-height:calc(100dvh - 140px - env(safe-area-inset-bottom,0px));padding-bottom:calc(80px + env(safe-area-inset-bottom,0px));overflow-y:auto;-webkit-overflow-scrolling:touch;transition:max-height .3s ease}.message-item[data-v-de93bb69]{margin-bottom:12px;padding:6px}.message-text[data-v-de93bb69]{max-width:90%!important}.message-avatar[data-v-de93bb69]{width:28px;height:28px;margin-right:8px}.message-item.bot-message .message-avatar[data-v-de93bb69]{margin-left:8px}.message-image[data-v-de93bb69]{max-width:min(280px,66.67vw);max-height:160px}.quote-content .message-image[data-v-de93bb69]{max-width:min(150px,50vw);max-height:80px}.context-menu[data-v-de93bb69]{min-width:160px;backdrop-filter:blur(12px);-webkit-backdrop-filter:blur(12px)}.context-menu-item[data-v-de93bb69]{padding:14px 16px;font-size:16px}.bot-item[data-v-de93bb69],.channel-item[data-v-de93bb69]{padding:14px 16px}.bot-avatar[data-v-de93bb69]{width:36px;height:36px;margin-right:10px}.bot-name[data-v-de93bb69],.channel-name[data-v-de93bb69]{font-size:15px}.bot-platform[data-v-de93bb69],.channel-type[data-v-de93bb69]{font-size:13px}.bot-items[data-v-de93bb69],.channel-items[data-v-de93bb69]{flex:1;overflow-y:auto;overflow-x:hidden;max-height:calc(100vh - 140px);max-height:calc(100dvh - 140px);-webkit-overflow-scrolling:touch;scrollbar-width:thin;scrollbar-color:rgba(144,147,153,.4) rgba(0,0,0,.05);scroll-behavior:smooth;overscroll-behavior:contain;padding-bottom:8px}}@media (min-width:769px) and (max-width:1024px){.bot-list[data-v-de93bb69]{width:220px;min-width:220px;max-width:220px}.channel-list[data-v-de93bb69]{width:280px;min-width:280px;max-width:280px}.message-image[data-v-de93bb69]{max-width:min(350px,66.67vw);max-height:180px}}.chat-container.swiping[data-v-de93bb69]{overflow:hidden}.bot-list.swiping[data-v-de93bb69],.channel-list.swiping[data-v-de93bb69],.message-area.swiping[data-v-de93bb69]{transition:none!important}.swipe-indicator[data-v-de93bb69]{position:fixed;top:50%;left:20px;transform:translateY(-50%);background:#2196f3cc;color:#fff;padding:8px 12px;border-radius:20px;font-size:14px;z-index:1000;opacity:0;transition:opacity .2s ease;pointer-events:none;backdrop-filter:blur(8px);-webkit-backdrop-filter:blur(8px)}.swipe-indicator.show[data-v-de93bb69]{opacity:1}.message-area .message-content[data-v-de93bb69]{padding-bottom:80px}.image-preview-container[data-v-de93bb69]{display:flex;flex-wrap:wrap;gap:8px;padding:8px 0;margin-bottom:8px;border-bottom:1px solid rgba(128,128,128,.1)}.image-preview-item[data-v-de93bb69]{position:relative;width:60px;height:60px;border-radius:8px;overflow:hidden;border:2px solid rgba(33,150,243,.3);background:#8080801a}.preview-image[data-v-de93bb69]{width:100%;height:100%;object-fit:cover;display:block}.remove-image-btn[data-v-de93bb69]{position:absolute;top:-6px;right:-6px;width:20px;height:20px;border-radius:50%;background:#f44336;color:#fff;border:none;cursor:pointer;display:flex;align-items:center;justify-content:center;font-size:12px;transition:all .2s ease;box-shadow:0 2px 4px #0003}.remove-image-btn[data-v-de93bb69]:hover{background:#d32f2f;transform:scale(1.1)}.input-actions[data-v-de93bb69]{position:relative;margin-right:8px}.add-button[data-v-de93bb69]{width:36px;height:36px;border-radius:12px;background:linear-gradient(135deg,#667eea 0,#764ba2);border:none;color:#fff;cursor:pointer;display:flex;align-items:center;justify-content:center;transition:all .3s cubic-bezier(.4,0,.2,1);flex-shrink:0;box-shadow:0 2px 8px #667eea4d;position:relative;overflow:hidden}.add-button[data-v-de93bb69]:before{content:"";position:absolute;top:0;left:0;right:0;bottom:0;background:linear-gradient(135deg,#fff3 0,#ffffff1a);border-radius:12px;opacity:0;transition:opacity .3s ease}.add-button[data-v-de93bb69]:hover{transform:translateY(-2px) scale(1.05);box-shadow:0 4px 16px #667eea66}.add-button[data-v-de93bb69]:hover:before{opacity:1}.add-button.active[data-v-de93bb69]{background:linear-gradient(135deg,#f093fb 0,#f5576c);transform:translateY(-2px) scale(1.05) rotate(45deg);box-shadow:0 4px 16px #f5576c66}.add-button[data-v-de93bb69]:active{transform:translateY(0) scale(.98);transition:all .1s ease}.action-menu[data-v-de93bb69]{position:absolute;bottom:100%;left:0;margin-bottom:12px;background:#fffffffa;border:none;border-radius:16px;box-shadow:0 8px 32px #0000001f,0 2px 8px #00000014;backdrop-filter:blur(20px);-webkit-backdrop-filter:blur(20px);overflow:hidden;z-index:1000;min-width:140px;transform:translateY(8px);opacity:0;animation:menuSlideIn-de93bb69 .3s cubic-bezier(.4,0,.2,1) forwards}.action-menu-item[data-v-de93bb69]{display:flex;align-items:center;gap:12px;padding:14px 18px;background:0 0;border:none;color:#333;font-size:14px;font-weight:500;cursor:pointer;transition:all .2s cubic-bezier(.4,0,.2,1);width:100%;text-align:left;position:relative}.action-menu-item[data-v-de93bb69]:before{content:"";position:absolute;left:0;top:0;bottom:0;width:3px;background:linear-gradient(135deg,#667eea 0,#764ba2);transform:scaleY(0);transition:transform .2s cubic-bezier(.4,0,.2,1)}.action-menu-item[data-v-de93bb69]:hover{background:linear-gradient(90deg,#667eea14 0,#764ba214);color:#667eea;transform:translate(4px)}.action-menu-item[data-v-de93bb69]:hover:before{transform:scaleY(1)}.action-menu-item svg[data-v-de93bb69]{flex-shrink:0}@media (prefers-color-scheme:dark){.action-menu[data-v-de93bb69]{background:#1e1e1efa;box-shadow:0 8px 32px #0000004d,0 2px 8px #0003}.action-menu-item[data-v-de93bb69]{color:#fff}.action-menu-item[data-v-de93bb69]:before{background:linear-gradient(135deg,#4facfe 0,#00f2fe)}.action-menu-item[data-v-de93bb69]:hover{background:linear-gradient(90deg,#4facfe1f 0,#00f2fe1f);color:#4facfe}.add-button[data-v-de93bb69]{background:linear-gradient(135deg,#4facfe 0,#00f2fe);box-shadow:0 2px 8px #4facfe4d}.add-button[data-v-de93bb69]:hover{box-shadow:0 4px 16px #4facfe66}.add-button.active[data-v-de93bb69]{background:linear-gradient(135deg,#fa709a 0,#fee140);box-shadow:0 4px 16px #fa709a66}.image-preview-item[data-v-de93bb69]{border-color:#64b5f680;background:#ffffff0d}}@media (max-width:768px){.image-preview-container[data-v-de93bb69]{padding:6px 0;margin-bottom:6px}.image-preview-item[data-v-de93bb69]{width:50px;height:50px}.add-button[data-v-de93bb69]{width:32px;height:32px;border-radius:10px}.action-menu[data-v-de93bb69]{min-width:100px}.action-menu-item[data-v-de93bb69]{padding:10px 12px;font-size:13px}.remove-image-btn[data-v-de93bb69]{width:18px;height:18px;top:-5px;right:-5px}}.message-history[data-v-de93bb69]::-webkit-scrollbar{width:6px}.message-history[data-v-de93bb69]::-webkit-scrollbar-track{background:0 0}.message-history[data-v-de93bb69]::-webkit-scrollbar-thumb{background-color:#9093994d;border-radius:4px;transition:background-color .3s}.message-history[data-v-de93bb69]::-webkit-scrollbar-thumb:hover{background-color:#90939980}.bot-items[data-v-de93bb69]::-webkit-scrollbar,.channel-items[data-v-de93bb69]::-webkit-scrollbar{width:8px}.bot-items[data-v-de93bb69]::-webkit-scrollbar-track,.channel-items[data-v-de93bb69]::-webkit-scrollbar-track{background:#0000000d;border-radius:4px}.bot-items[data-v-de93bb69]::-webkit-scrollbar-thumb,.channel-items[data-v-de93bb69]::-webkit-scrollbar-thumb{background-color:#90939966;border-radius:4px;transition:all .3s ease;border:1px solid rgba(255,255,255,.1);background-clip:padding-box}.bot-items[data-v-de93bb69]::-webkit-scrollbar-thumb:hover,.channel-items[data-v-de93bb69]::-webkit-scrollbar-thumb:hover{background-color:#90939999;transform:scaleX(1.2)}.bot-items[data-v-de93bb69]::-webkit-scrollbar-thumb:active,.channel-items[data-v-de93bb69]::-webkit-scrollbar-thumb:active{background-color:#909399cc}.forward-message-expanded[data-v-de93bb69]::-webkit-scrollbar{width:6px}.forward-message-expanded[data-v-de93bb69]::-webkit-scrollbar-track{background:0 0}.forward-message-expanded[data-v-de93bb69]::-webkit-scrollbar-thumb{background-color:#9093994d;border-radius:4px;transition:background-color .3s}.forward-message-expanded[data-v-de93bb69]::-webkit-scrollbar-thumb:hover{background-color:#90939980}.json-raw-content[data-v-de93bb69]::-webkit-scrollbar{width:6px}.json-raw-content[data-v-de93bb69]::-webkit-scrollbar-track{background:0 0}.json-raw-content[data-v-de93bb69]::-webkit-scrollbar-thumb{background-color:#9093994d;border-radius:4px;transition:background-color .3s}.json-raw-content[data-v-de93bb69]::-webkit-scrollbar-thumb:hover{background-color:#90939980}@media (prefers-color-scheme:dark){.bot-items[data-v-de93bb69]::-webkit-scrollbar-track,.channel-items[data-v-de93bb69]::-webkit-scrollbar-track{background:#ffffff0d}.bot-items[data-v-de93bb69]::-webkit-scrollbar-thumb,.channel-items[data-v-de93bb69]::-webkit-scrollbar-thumb,.forward-message-expanded[data-v-de93bb69]::-webkit-scrollbar-thumb,.json-raw-content[data-v-de93bb69]::-webkit-scrollbar-thumb,.message-history[data-v-de93bb69]::-webkit-scrollbar-thumb{background-color:#ffffff4d;border-color:#0000001a}.bot-items[data-v-de93bb69]::-webkit-scrollbar-thumb:hover,.channel-items[data-v-de93bb69]::-webkit-scrollbar-thumb:hover,.forward-message-expanded[data-v-de93bb69]::-webkit-scrollbar-thumb:hover,.json-raw-content[data-v-de93bb69]::-webkit-scrollbar-thumb:hover,.message-history[data-v-de93bb69]::-webkit-scrollbar-thumb:hover{background-color:#ffffff80}.bot-items[data-v-de93bb69]::-webkit-scrollbar-thumb:active,.channel-items[data-v-de93bb69]::-webkit-scrollbar-thumb:active{background-color:#ffffffb3}}.layout-iframe{width:100%;height:100%;border:none}
|
|
1
|
+
.message-quote[data-v-f8e0fc57]{margin:8px 0;padding:8px 12px;background-color:#bebebe1a;border-left:4px solid rgba(151,152,152,.45);border-radius:6px;font-size:.9em;position:relative}.quote-indicator[data-v-f8e0fc57]{font-size:.75em;color:#007bff;font-weight:600;margin-bottom:4px;text-transform:uppercase}.quote-header[data-v-f8e0fc57]{display:flex;align-items:center;margin-bottom:4px;gap:6px}.quote-avatar[data-v-f8e0fc57]{width:20px;height:20px;border-radius:50%;overflow:hidden;flex-shrink:0}.quote-avatar img[data-v-f8e0fc57]{width:100%;height:100%;object-fit:cover}.quote-avatar .avatar-placeholder[data-v-f8e0fc57]{width:100%;height:100%;display:flex;align-items:center;justify-content:center;background-color:#6c757d;color:#fff;font-size:10px;font-weight:700}.quote-username[data-v-f8e0fc57]{font-weight:600;color:#007bff;font-size:.85em}.quote-time[data-v-f8e0fc57]{color:#6c757d;font-size:.75em;margin-left:auto}.quote-content[data-v-f8e0fc57]{color:#7276b7;line-height:1.3;word-wrap:break-word;word-break:break-all;overflow-wrap:anywhere;-webkit-hyphens:auto;hyphens:auto}.quote-content .message-image[data-v-f8e0fc57]{max-width:min(200px,50vw);max-height:100px}.quote-content .message-image-container[data-v-f8e0fc57]{margin:2px 0}@media (prefers-color-scheme:dark){.message-quote[data-v-f8e0fc57]{background-color:#ffffff14;border-left-color:#0d6efd}.quote-content[data-v-f8e0fc57]{color:#adb5bd}.quote-username[data-v-f8e0fc57]{color:#0d6efd}}.chat-container[data-v-f8e0fc57]{display:flex;height:100vh;background:0 0;margin-left:0;width:100%;overflow:hidden;color:inherit;position:relative}.bot-list[data-v-f8e0fc57],.channel-list[data-v-f8e0fc57],.message-area[data-v-f8e0fc57]{display:flex;flex-direction:column;background:0 0;border-right:1px solid rgba(128,128,128,.2)}.bot-list[data-v-f8e0fc57]{width:250px;min-width:250px;max-width:250px;flex-shrink:0}.channel-list[data-v-f8e0fc57]{width:300px;min-width:300px;max-width:300px;flex-shrink:0}.message-area[data-v-f8e0fc57]{flex:1;border-right:none}.panel-header[data-v-f8e0fc57]{padding:16px;border-bottom:1px solid rgba(128,128,128,.2);background:0 0;display:flex;align-items:center;justify-content:space-between}.panel-header h3[data-v-f8e0fc57]{margin:0;font-size:16px;font-weight:600;color:currentColor}.back-button[data-v-f8e0fc57]{display:none;background:0 0;border:none;color:currentColor;font-size:18px;cursor:pointer;padding:4px 8px;border-radius:4px;transition:background-color .2s}.back-button[data-v-f8e0fc57]:hover{background:#8080801a}.bot-items[data-v-f8e0fc57],.channel-items[data-v-f8e0fc57]{flex:1;overflow-y:auto;overflow-x:hidden;max-height:calc(100vh - 140px);-webkit-overflow-scrolling:touch;scrollbar-width:thin;scrollbar-color:rgba(144,147,153,.4) rgba(0,0,0,.05);scroll-behavior:smooth;overscroll-behavior:contain;padding-bottom:8px}.bot-item[data-v-f8e0fc57],.channel-item[data-v-f8e0fc57]{display:flex;align-items:center;padding:12px 16px;cursor:pointer;border-bottom:1px solid rgba(128,128,128,.1);transition:background-color .2s}.bot-item[data-v-f8e0fc57]:hover,.channel-item[data-v-f8e0fc57]:hover{background:#8080801a}.bot-item.active[data-v-f8e0fc57],.channel-item.active[data-v-f8e0fc57]{background:#2196f31a;border-right:3px solid #2196f3}.bot-avatar[data-v-f8e0fc57]{width:40px;height:40px;margin-right:12px;border-radius:50%;overflow:hidden;display:flex;align-items:center;justify-content:center;background:#e0e0e0}.bot-avatar img[data-v-f8e0fc57]{width:100%;height:100%;object-fit:cover}.avatar-placeholder[data-v-f8e0fc57]{font-size:16px;font-weight:700;color:currentColor;opacity:.7}.bot-info[data-v-f8e0fc57]{flex:1}.bot-name[data-v-f8e0fc57]{font-weight:500;color:currentColor;margin-bottom:2px}.bot-platform[data-v-f8e0fc57]{font-size:12px;color:currentColor;opacity:.7}.bot-status[data-v-f8e0fc57]{width:8px;height:8px;border-radius:50%;margin-left:8px}.bot-status.online[data-v-f8e0fc57]{background:#4caf50}.bot-status.offline[data-v-f8e0fc57]{background:#f44336}.channel-info[data-v-f8e0fc57]{flex:1}.channel-name[data-v-f8e0fc57]{font-weight:500;color:currentColor;margin-bottom:2px}.channel-type[data-v-f8e0fc57]{font-size:12px;color:currentColor;opacity:.7}.channel-message-count[data-v-f8e0fc57]{background:#2196f3;color:#fff;font-size:12px;padding:2px 6px;border-radius:10px;min-width:18px;text-align:center;position:relative;transition:all .2s ease}.channel-message-count.draggable-bubble[data-v-f8e0fc57]{cursor:grab;user-select:none;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none}.channel-message-count.draggable-bubble[data-v-f8e0fc57]:hover{background:#1976d2;transform:scale(1.1);box-shadow:0 2px 8px #2196f34d}.channel-message-count.draggable-bubble.dragging[data-v-f8e0fc57],.channel-message-count.draggable-bubble[data-v-f8e0fc57]:active{cursor:grabbing;background:#1565c0;box-shadow:0 4px 12px #2196f366}.dragging-clone[data-v-f8e0fc57]{position:fixed;z-index:1000;pointer-events:none;transition:none;box-sizing:border-box;display:flex;align-items:center;justify-content:center;background:#2196f3;color:#fff;font-size:12px;padding:2px 6px;border-radius:10px;min-width:18px;text-align:center;box-shadow:0 4px 12px #2196f366}.dragging-clone.will-delete[data-v-f8e0fc57]{background:#f44336!important;animation:shake-f8e0fc57 .3s ease-in-out infinite}@keyframes pulseThreshold-f8e0fc57{0%,to{opacity:.4;transform:scale(.9);border-color:#f4433699;box-shadow:0 0 15px #f4433633}50%{opacity:.8;transform:scale(1.1);border-color:#f44336;box-shadow:0 0 25px #f4433666}}@keyframes menuSlideIn-f8e0fc57{0%{transform:translateY(8px);opacity:0}to{transform:translateY(0);opacity:1}}.message-content[data-v-f8e0fc57]{display:flex;flex-direction:column;height:calc(100vh - 60px);overflow:hidden;position:relative}.message-history[data-v-f8e0fc57]{flex:1;overflow-y:auto;padding:16px 16px 8px;max-height:calc(100vh - 200px)}.message-item[data-v-f8e0fc57]{display:flex;margin-bottom:16px;padding:8px;border-radius:8px;transition:background-color .3s ease}.message-item[data-v-f8e0fc57]:hover{background-color:#8080801a}.message-item.new-message[data-v-f8e0fc57]{background-color:#4caf5033;animation:highlight-f8e0fc57 2s ease-out}.message-item.bot-message[data-v-f8e0fc57]{flex-direction:row-reverse;justify-content:flex-start}.message-item.bot-message .message-avatar[data-v-f8e0fc57]{margin-right:0;margin-left:12px}.message-item.bot-message .message-content-wrapper[data-v-f8e0fc57]{display:flex;flex-direction:column;align-items:flex-end}.message-item.bot-message .message-header[data-v-f8e0fc57]{display:flex;align-items:center;justify-content:flex-end;margin-bottom:4px}.message-item.bot-message .message-username[data-v-f8e0fc57]{margin-right:0;margin-left:8px;order:2}.message-item.bot-message .message-time[data-v-f8e0fc57]{order:1}.message-item.bot-message .message-text[data-v-f8e0fc57]{background-color:#2196f31a;padding:8px 12px;border-radius:12px 12px 4px;display:inline-block;max-width:80%;text-align:left;word-wrap:break-word;word-break:break-all;overflow-wrap:anywhere}@keyframes highlight-f8e0fc57{0%{background-color:#4caf504d}to{background-color:transparent}}.message-avatar[data-v-f8e0fc57]{width:32px;height:32px;margin-right:12px;border-radius:50%;overflow:hidden;display:flex;align-items:center;justify-content:center;background:#e0e0e0;flex-shrink:0}.message-avatar img[data-v-f8e0fc57]{width:100%;height:100%;object-fit:cover}.message-avatar .avatar-placeholder[data-v-f8e0fc57]{font-size:12px;font-weight:700;color:currentColor;opacity:.7}.message-content-wrapper[data-v-f8e0fc57]{flex:1}.message-header[data-v-f8e0fc57]{display:flex;align-items:center;margin-bottom:4px}.message-username[data-v-f8e0fc57]{font-weight:500;color:currentColor;margin-right:8px}.message-time[data-v-f8e0fc57]{font-size:12px;color:currentColor;opacity:.5}.message-text[data-v-f8e0fc57]{color:currentColor;line-height:1.4;word-wrap:break-word;word-break:break-all;white-space:pre-wrap;max-width:100%;overflow-wrap:anywhere;-webkit-hyphens:auto;hyphens:auto}.message-item:not(.bot-message) .message-text[data-v-f8e0fc57]{background-color:#8080801a;padding:8px 12px;border-radius:12px 12px 12px 4px;display:inline-block;max-width:80%;word-wrap:break-word;word-break:break-all;overflow-wrap:anywhere}.message-input[data-v-f8e0fc57]{display:flex;flex-direction:column;padding:12px 16px 16px;border-top:1px solid rgba(128,128,128,.2);background:0 0;flex-shrink:0;position:sticky;bottom:0;margin-top:-10px}.input-row[data-v-f8e0fc57]{display:flex;gap:8px;align-items:center}.floating-scroll-button[data-v-f8e0fc57]{position:absolute;right:24px;bottom:18%;width:48px;height:48px;background:#2196f3;border-radius:50%;display:flex;align-items:center;justify-content:center;cursor:pointer;box-shadow:0 4px 12px #2196f34d;color:#fff;transition:all .3s ease;z-index:100;border:2px solid rgba(255,255,255,.2)}.floating-scroll-button[data-v-f8e0fc57]:hover{background:#1976d2;transform:scale(1.1);box-shadow:0 6px 16px #2196f366}.floating-scroll-button[data-v-f8e0fc57]:active{transform:scale(.95)}.input-row button[data-v-f8e0fc57]{padding:8px 16px;background:#2196f3;color:#fff;border:none;border-radius:4px;cursor:pointer;transition:background-color .2s}.message-image-container[data-v-f8e0fc57]{margin:8px 0;max-width:100%;display:block}.message-image[data-v-f8e0fc57]{border-radius:8px;object-fit:contain;display:block;cursor:pointer;transition:transform .2s ease;max-width:min(400px,66.67vw);max-height:200px;width:auto;height:auto;image-rendering:auto;-webkit-image-rendering:auto;-moz-image-rendering:auto;-o-image-rendering:auto}.message-image[src*=".gif"][data-v-f8e0fc57],.message-image[src*="image/gif"][data-v-f8e0fc57]{image-rendering:auto!important;-webkit-image-rendering:auto!important;animation-play-state:running!important}.message-image-loading[data-v-f8e0fc57]{display:inline-block;padding:8px 12px;background:#8080801a;border-radius:4px;color:currentColor;opacity:.7;font-size:12px}.message-image-error[data-v-f8e0fc57]{display:inline-block;padding:8px 12px;background:#f443361a;border:1px solid rgba(244,67,54,.3);border-radius:4px;color:#f44336;font-size:12px;max-width:300px;word-break:break-word}.message-paragraph[data-v-f8e0fc57]{display:block;margin-bottom:4px}.message-paragraph[data-v-f8e0fc57]:last-child{margin-bottom:0}.forward-message-container[data-v-f8e0fc57]{border:1px solid #fff;border-radius:8px;margin:4px 0;overflow:hidden;background:#f9f9f900}.forward-message-preview[data-v-f8e0fc57]{padding:12px;cursor:pointer;transition:background-color .2s}.forward-message-preview[data-v-f8e0fc57]:hover{background:#f0f0f0}.forward-message-title[data-v-f8e0fc57]{font-weight:600;color:#333;margin-bottom:8px;font-size:14px}.forward-message-preview-item[data-v-f8e0fc57]{color:#666;font-size:13px;margin:2px 0;line-height:1.4;word-wrap:break-word;word-break:break-all;overflow-wrap:anywhere}.forward-message-footer[data-v-f8e0fc57]{display:flex;justify-content:space-between;align-items:center;margin-top:8px;padding-top:8px;border-top:1px solid #e8e8e8}.forward-message-count[data-v-f8e0fc57]{color:#2196f3;font-size:12px}.forward-message-toggle[data-v-f8e0fc57]{color:#666;font-size:12px}.forward-message-expanded[data-v-f8e0fc57]{border-top:1px solid #e0e0e0;background:#fff;max-height:400px;overflow-y:auto}.forwarded-message-item[data-v-f8e0fc57]{padding:12px;border-bottom:1px solid #f0f0f0}.forwarded-message-item[data-v-f8e0fc57]:last-child{border-bottom:none}.forwarded-message-header[data-v-f8e0fc57]{display:flex;align-items:center;margin-bottom:6px}.forwarded-message-nickname[data-v-f8e0fc57]{font-weight:600;color:#333;font-size:13px}.forwarded-message-userid[data-v-f8e0fc57]{color:#999;font-size:11px;margin-left:6px}.forwarded-message-content[data-v-f8e0fc57]{color:#555;font-size:13px;line-height:1.4;word-wrap:break-word;word-break:break-all;overflow-wrap:anywhere}.message-at[data-v-f8e0fc57]{background:#2196f31a;color:#2196f3;padding:2px 4px;border-radius:3px;font-weight:500;cursor:pointer}.message-unknown[data-v-f8e0fc57]{background:#ff98001a;color:#ff9800;padding:2px 4px;border-radius:3px;font-style:italic;cursor:help}.input-row input[data-v-f8e0fc57]{flex:1;min-width:0;padding:8px 12px;border:1px solid rgba(128,128,128,.3);border-radius:4px;outline:0;background:0 0;color:currentColor}.input-row input[data-v-f8e0fc57]:focus{border-color:#2196f3;box-shadow:0 0 0 2px #2196f333}.input-row input[data-v-f8e0fc57]::placeholder{color:currentColor;opacity:.5}.input-row button[data-v-f8e0fc57]{padding:8px 16px;background:#2196f3;color:#fff;border:none;border-radius:4px;cursor:pointer;transition:background-color .2s;flex-shrink:0;white-space:nowrap}.input-row button[data-v-f8e0fc57]:hover:not(:disabled){background:#1976d2}.input-row button[data-v-f8e0fc57]:disabled{background:#8080804d;cursor:not-allowed}.input-row button.is-sending[data-v-f8e0fc57]{background:#ffc107!important;color:#333!important;cursor:default!important}.message-input .input-row button.is-sending[data-v-f8e0fc57]{background:#ffc107!important;color:#333!important;cursor:default!important;border-color:#ffc107!important}.empty-state[data-v-f8e0fc57]{display:flex;align-items:center;justify-content:center;height:100%;color:currentColor;opacity:.5;font-size:14px}.message-text-content[data-v-f8e0fc57]{display:inline;word-wrap:break-word;word-break:break-all;overflow-wrap:anywhere;-webkit-hyphens:auto;hyphens:auto}.message-image-container[data-v-f8e0fc57]{display:block;margin:4px 0}.message-image[data-v-f8e0fc57]{border-radius:8px;margin:4px 0;display:block;cursor:pointer;transition:transform .2s ease;object-fit:contain;background:#8080801a}.message-image[data-v-f8e0fc57]:hover{transform:scale(1.02)}.message-image-loading[data-v-f8e0fc57]{height:60px;width:200px;border-radius:8px;margin:4px 0;display:flex;align-items:center;justify-content:center;background:#8080801a;color:currentColor;opacity:.7;font-size:14px;border:1px dashed rgba(128,128,128,.3)}.message-image-error[data-v-f8e0fc57]{min-height:60px;width:200px;border-radius:8px;margin:4px 0;display:flex;flex-direction:column;align-items:center;justify-content:center;background:#8080801a;color:currentColor;opacity:.7;font-size:14px;border:1px dashed rgba(128,128,128,.3);padding:8px;text-align:center}.message-at[data-v-f8e0fc57]{background:#2196f31a;color:#2196f3;padding:2px 6px;border-radius:4px;font-weight:500;cursor:pointer;display:inline-block;margin:0 2px}.message-at[data-v-f8e0fc57]:hover{background:#2196f333}.message-unknown[data-v-f8e0fc57]{background:#ff98001a;color:#ff9800;padding:2px 6px;border-radius:4px;font-size:12px;display:inline-block;margin:0 2px;cursor:help}@keyframes pulse-f8e0fc57{0%,to{transform:translate(-50%,-50%) scale(1)}50%{transform:translate(-50%,-50%) scale(1.1)}}.channel-message-count.will-delete[data-v-f8e0fc57]{background:#f44336!important;animation:shake-f8e0fc57 .3s ease-in-out infinite}@keyframes shake-f8e0fc57{0%,to{transform:translate(0)}25%{transform:translate(-2px)}75%{transform:translate(2px)}}body.dragging-bubble-global[data-v-f8e0fc57]{user-select:none;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;cursor:grabbing}.drag-threshold-circle[data-v-f8e0fc57]{position:fixed;pointer-events:none;border:3px dashed rgba(244,67,54,.8);border-radius:50%;z-index:999;background:#f443361a;box-shadow:0 0 20px #f443364d;animation:pulseThreshold-f8e0fc57 1.5s ease-in-out infinite}.context-menu[data-v-f8e0fc57]{position:fixed;background:#ffffffa6;border:1px solid rgba(0,0,0,.1);border-radius:8px;box-shadow:0 8px 32px #00000026;z-index:10000;min-width:180px;backdrop-filter:blur(16px);-webkit-backdrop-filter:blur(16px);overflow:hidden}.context-menu-item[data-v-f8e0fc57]{padding:12px 16px;cursor:pointer;color:#333;font-size:14px;font-weight:500;transition:all .2s ease;border-bottom:1px solid rgba(0,0,0,.08);position:relative}.context-menu-item[data-v-f8e0fc57]:last-child{border-bottom:none}.context-menu-item[data-v-f8e0fc57]:hover{background:#2196f31a;color:#1976d2;transform:translate(2px)}.context-menu-item.danger[data-v-f8e0fc57]{color:#d32f2f}.context-menu-item.danger[data-v-f8e0fc57]:hover{background:#f443361a;color:#c62828}.bot-item.pinned[data-v-f8e0fc57],.channel-item.pinned[data-v-f8e0fc57]{background:#ffc1071a;border-left:3px solid #ffc107}.bot-item.pinned[data-v-f8e0fc57]:before,.channel-item.pinned[data-v-f8e0fc57]:before{content:"📌";position:absolute;left:4px;top:50%;transform:translateY(-50%);font-size:12px;opacity:.7}.bot-item.pinned[data-v-f8e0fc57],.channel-item.pinned[data-v-f8e0fc57]{position:relative;padding-left:24px}@media (prefers-color-scheme:dark){.context-menu[data-v-f8e0fc57]{background:#ffffff26;border-color:#fff3;box-shadow:0 8px 32px #0006}.context-menu-item[data-v-f8e0fc57]{color:#fff;border-bottom-color:#ffffff1a}.context-menu-item[data-v-f8e0fc57]:hover{background:#2196f333;color:#64b5f6}.context-menu-item.danger[data-v-f8e0fc57]{color:#ff8a80}.context-menu-item.danger[data-v-f8e0fc57]:hover{background:#f4433633;color:#ffab91}.bot-item.pinned[data-v-f8e0fc57],.channel-item.pinned[data-v-f8e0fc57]{background:#ffc10726}}.message-share-card[data-v-f8e0fc57]{display:inline-block;width:75px;margin:8px 0;border:1px solid rgba(128,128,128,.2);border-radius:12px;background:#ffffff0d;transition:all .3s ease;cursor:pointer;backdrop-filter:blur(8px);-webkit-backdrop-filter:blur(8px)}.message-share-card[data-v-f8e0fc57]:hover{transform:translateY(-2px);box-shadow:0 8px 24px #00000026;border-color:#2196f34d}.share-card-preview[data-v-f8e0fc57]{width:100%;position:relative;display:flex;align-items:center;justify-content:center;padding:4px;border-radius:12px 12px 0 0}.share-card-image[data-v-f8e0fc57]{max-width:calc(100% - 8px);width:auto;height:auto;object-fit:contain;transition:transform .3s ease;display:block;border-radius:4px}.message-share-card:hover .share-card-image[data-v-f8e0fc57]{transform:scale(1.05)}.share-card-content[data-v-f8e0fc57]{padding:8px}.share-card-header[data-v-f8e0fc57]{display:flex;align-items:center;margin-bottom:12px;gap:12px}.share-card-icon[data-v-f8e0fc57]{width:32px;height:32px;border-radius:8px;object-fit:cover;flex-shrink:0}.share-card-app-info[data-v-f8e0fc57]{flex:1;min-width:0}.share-card-app-name[data-v-f8e0fc57]{font-size:12px;color:currentColor;opacity:.7;margin-bottom:2px;font-weight:500}.share-card-title[data-v-f8e0fc57]{font-size:10px;font-weight:600;color:currentColor;line-height:1.2;word-wrap:break-word;display:-webkit-box;-webkit-box-orient:vertical;overflow:hidden}.share-card-desc[data-v-f8e0fc57]{font-size:14px;color:currentColor;opacity:.8;line-height:1.4;margin-bottom:12px;display:-webkit-box;-webkit-box-orient:vertical;overflow:hidden;word-wrap:break-word}.share-card-link-hint[data-v-f8e0fc57]{font-size:12px;color:#2196f3;font-weight:500;display:flex;align-items:center;gap:4px}.share-card-link-hint[data-v-f8e0fc57]:after{content:"→";transition:transform .2s ease}.message-share-card:hover .share-card-link-hint[data-v-f8e0fc57]:after{transform:translate(2px)}.message-json-error[data-v-f8e0fc57],.message-json-raw[data-v-f8e0fc57]{display:inline-block;margin:8px 0;max-width:400px}.json-error-text[data-v-f8e0fc57]{color:#f44336;font-size:14px;font-weight:500}.json-label[data-v-f8e0fc57]{color:currentColor;opacity:.7;font-size:12px;font-weight:500;margin-bottom:8px}.json-raw-data[data-v-f8e0fc57]{margin-top:8px}.json-raw-data summary[data-v-f8e0fc57]{cursor:pointer;font-size:12px;color:#2196f3;font-weight:500;padding:4px 8px;border-radius:4px;background:#2196f31a;display:inline-block;transition:background-color .2s ease}.json-raw-data summary[data-v-f8e0fc57]:hover{background:#2196f333}.json-raw-content[data-v-f8e0fc57]{margin-top:8px;padding:12px;background:#8080801a;border-radius:8px;font-family:Courier New,monospace;font-size:12px;color:currentColor;opacity:.8;white-space:pre-wrap;word-wrap:break-word;word-break:break-all;overflow-wrap:anywhere;max-height:200px;overflow-y:auto;border:1px solid rgba(128,128,128,.2)}@media (prefers-color-scheme:dark){.message-share-card[data-v-f8e0fc57]{background:#ffffff14;border-color:#ffffff26}.message-share-card[data-v-f8e0fc57]:hover{border-color:#2196f380;box-shadow:0 8px 24px #0000004d}.json-raw-content[data-v-f8e0fc57]{background:#ffffff0d;border-color:#ffffff1a}}@media (max-width:768px){.chat-container[data-v-f8e0fc57]{position:relative;height:100vh;height:100dvh}.bot-list[data-v-f8e0fc57],.channel-list[data-v-f8e0fc57],.message-area[data-v-f8e0fc57]{position:absolute;top:0;left:0;width:100%;height:100%;transition:transform .3s ease;z-index:1}.bot-list[data-v-f8e0fc57]{transform:translate(0);z-index:3}.channel-list[data-v-f8e0fc57]{transform:translate(100%);z-index:2}.message-area[data-v-f8e0fc57]{transform:translate(200%);z-index:1}.chat-container.show-channels .bot-list[data-v-f8e0fc57]{transform:translate(-100%)}.chat-container.show-channels .channel-list[data-v-f8e0fc57]{transform:translate(0);z-index:3}.chat-container.show-channels .message-area[data-v-f8e0fc57]{transform:translate(100%)}.chat-container.show-messages .bot-list[data-v-f8e0fc57]{transform:translate(-200%)}.chat-container.show-messages .channel-list[data-v-f8e0fc57]{transform:translate(-100%)}.chat-container.show-messages .message-area[data-v-f8e0fc57]{transform:translate(0);z-index:3}.back-button[data-v-f8e0fc57]{display:none!important}.panel-header[data-v-f8e0fc57]{flex-direction:row-reverse;position:sticky;top:0;z-index:5;background:inherit}.panel-header h3[data-v-f8e0fc57]{text-align:right}.floating-scroll-button[data-v-f8e0fc57]{right:16px;bottom:calc(90px + env(safe-area-inset-bottom,0px));width:40px;height:40px;transition:bottom .3s ease}.message-input[data-v-f8e0fc57]{padding:8px 12px calc(12px + env(safe-area-inset-bottom,0px)) 12px;position:fixed;bottom:20px;left:0;right:0;background:inherit;border-top:1px solid rgba(128,128,128,.2);z-index:10;min-height:calc(60px + env(safe-area-inset-bottom,0px));transition:transform .3s ease;transform:translateZ(0)}.input-row input[data-v-f8e0fc57]{font-size:16px;padding:10px 12px}.input-row button[data-v-f8e0fc57]{padding:10px 16px;font-size:16px}.message-history[data-v-f8e0fc57]{padding:12px;max-height:calc(100vh - 140px - env(safe-area-inset-bottom,0px));max-height:calc(100dvh - 140px - env(safe-area-inset-bottom,0px));padding-bottom:calc(80px + env(safe-area-inset-bottom,0px));overflow-y:auto;-webkit-overflow-scrolling:touch;transition:max-height .3s ease}.message-item[data-v-f8e0fc57]{margin-bottom:12px;padding:6px}.message-text[data-v-f8e0fc57]{max-width:90%!important}.message-avatar[data-v-f8e0fc57]{width:28px;height:28px;margin-right:8px}.message-item.bot-message .message-avatar[data-v-f8e0fc57]{margin-left:8px}.message-image[data-v-f8e0fc57]{max-width:min(280px,66.67vw);max-height:160px}.quote-content .message-image[data-v-f8e0fc57]{max-width:min(150px,50vw);max-height:80px}.context-menu[data-v-f8e0fc57]{min-width:160px;backdrop-filter:blur(12px);-webkit-backdrop-filter:blur(12px)}.context-menu-item[data-v-f8e0fc57]{padding:14px 16px;font-size:16px}.bot-item[data-v-f8e0fc57],.channel-item[data-v-f8e0fc57]{padding:14px 16px}.bot-avatar[data-v-f8e0fc57]{width:36px;height:36px;margin-right:10px}.bot-name[data-v-f8e0fc57],.channel-name[data-v-f8e0fc57]{font-size:15px}.bot-platform[data-v-f8e0fc57],.channel-type[data-v-f8e0fc57]{font-size:13px}.bot-items[data-v-f8e0fc57],.channel-items[data-v-f8e0fc57]{flex:1;overflow-y:auto;overflow-x:hidden;max-height:calc(100vh - 140px);max-height:calc(100dvh - 140px);-webkit-overflow-scrolling:touch;scrollbar-width:thin;scrollbar-color:rgba(144,147,153,.4) rgba(0,0,0,.05);scroll-behavior:smooth;overscroll-behavior:contain;padding-bottom:8px}}@media (min-width:769px) and (max-width:1024px){.bot-list[data-v-f8e0fc57]{width:220px;min-width:220px;max-width:220px}.channel-list[data-v-f8e0fc57]{width:280px;min-width:280px;max-width:280px}.message-image[data-v-f8e0fc57]{max-width:min(350px,66.67vw);max-height:180px}}.chat-container.swiping[data-v-f8e0fc57]{overflow:hidden}.bot-list.swiping[data-v-f8e0fc57],.channel-list.swiping[data-v-f8e0fc57],.message-area.swiping[data-v-f8e0fc57]{transition:none!important}.swipe-indicator[data-v-f8e0fc57]{position:fixed;top:50%;left:20px;transform:translateY(-50%);background:#2196f3cc;color:#fff;padding:8px 12px;border-radius:20px;font-size:14px;z-index:1000;opacity:0;transition:opacity .2s ease;pointer-events:none;backdrop-filter:blur(8px);-webkit-backdrop-filter:blur(8px)}.swipe-indicator.show[data-v-f8e0fc57]{opacity:1}.message-area .message-content[data-v-f8e0fc57]{padding-bottom:80px}.image-preview-container[data-v-f8e0fc57]{display:flex;flex-wrap:wrap;gap:8px;padding:8px 0;margin-bottom:8px;border-bottom:1px solid rgba(128,128,128,.1)}.image-preview-item[data-v-f8e0fc57]{position:relative;width:60px;height:60px;border-radius:8px;overflow:hidden;border:2px solid rgba(33,150,243,.3);background:#8080801a}.preview-image[data-v-f8e0fc57]{width:100%;height:100%;object-fit:cover;display:block}.remove-image-btn[data-v-f8e0fc57]{position:absolute;top:-6px;right:-6px;width:20px;height:20px;border-radius:50%;background:#f44336;color:#fff;border:none;cursor:pointer;display:flex;align-items:center;justify-content:center;font-size:12px;transition:all .2s ease;box-shadow:0 2px 4px #0003}.remove-image-btn[data-v-f8e0fc57]:hover{background:#d32f2f;transform:scale(1.1)}.input-actions[data-v-f8e0fc57]{position:relative;margin-right:8px}.add-button[data-v-f8e0fc57]{width:36px;height:36px;border-radius:12px;background:linear-gradient(135deg,#667eea 0,#764ba2);border:none;color:#fff;cursor:pointer;display:flex;align-items:center;justify-content:center;transition:all .3s cubic-bezier(.4,0,.2,1);flex-shrink:0;box-shadow:0 2px 8px #667eea4d;position:relative;overflow:hidden}.add-button[data-v-f8e0fc57]:before{content:"";position:absolute;top:0;left:0;right:0;bottom:0;background:linear-gradient(135deg,#fff3 0,#ffffff1a);border-radius:12px;opacity:0;transition:opacity .3s ease}.add-button[data-v-f8e0fc57]:hover{transform:translateY(-2px) scale(1.05);box-shadow:0 4px 16px #667eea66}.add-button[data-v-f8e0fc57]:hover:before{opacity:1}.add-button.active[data-v-f8e0fc57]{background:linear-gradient(135deg,#f093fb 0,#f5576c);transform:translateY(-2px) scale(1.05) rotate(45deg);box-shadow:0 4px 16px #f5576c66}.add-button[data-v-f8e0fc57]:active{transform:translateY(0) scale(.98);transition:all .1s ease}.action-menu[data-v-f8e0fc57]{position:absolute;bottom:100%;left:0;margin-bottom:12px;background:#fffffffa;border:none;border-radius:16px;box-shadow:0 8px 32px #0000001f,0 2px 8px #00000014;backdrop-filter:blur(20px);-webkit-backdrop-filter:blur(20px);overflow:hidden;z-index:1000;min-width:140px;transform:translateY(8px);opacity:0;animation:menuSlideIn-f8e0fc57 .3s cubic-bezier(.4,0,.2,1) forwards}.action-menu-item[data-v-f8e0fc57]{display:flex;align-items:center;gap:12px;padding:14px 18px;background:0 0;border:none;color:#333;font-size:14px;font-weight:500;cursor:pointer;transition:all .2s cubic-bezier(.4,0,.2,1);width:100%;text-align:left;position:relative}.action-menu-item[data-v-f8e0fc57]:before{content:"";position:absolute;left:0;top:0;bottom:0;width:3px;background:linear-gradient(135deg,#667eea 0,#764ba2);transform:scaleY(0);transition:transform .2s cubic-bezier(.4,0,.2,1)}.action-menu-item[data-v-f8e0fc57]:hover{background:linear-gradient(90deg,#667eea14 0,#764ba214);color:#667eea;transform:translate(4px)}.action-menu-item[data-v-f8e0fc57]:hover:before{transform:scaleY(1)}.action-menu-item svg[data-v-f8e0fc57]{flex-shrink:0}@media (prefers-color-scheme:dark){.action-menu[data-v-f8e0fc57]{background:#1e1e1efa;box-shadow:0 8px 32px #0000004d,0 2px 8px #0003}.action-menu-item[data-v-f8e0fc57]{color:#fff}.action-menu-item[data-v-f8e0fc57]:before{background:linear-gradient(135deg,#4facfe 0,#00f2fe)}.action-menu-item[data-v-f8e0fc57]:hover{background:linear-gradient(90deg,#4facfe1f 0,#00f2fe1f);color:#4facfe}.add-button[data-v-f8e0fc57]{background:linear-gradient(135deg,#4facfe 0,#00f2fe);box-shadow:0 2px 8px #4facfe4d}.add-button[data-v-f8e0fc57]:hover{box-shadow:0 4px 16px #4facfe66}.add-button.active[data-v-f8e0fc57]{background:linear-gradient(135deg,#fa709a 0,#fee140);box-shadow:0 4px 16px #fa709a66}.image-preview-item[data-v-f8e0fc57]{border-color:#64b5f680;background:#ffffff0d}}@media (max-width:768px){.image-preview-container[data-v-f8e0fc57]{padding:6px 0;margin-bottom:6px}.image-preview-item[data-v-f8e0fc57]{width:50px;height:50px}.add-button[data-v-f8e0fc57]{width:32px;height:32px;border-radius:10px}.action-menu[data-v-f8e0fc57]{min-width:100px}.action-menu-item[data-v-f8e0fc57]{padding:10px 12px;font-size:13px}.remove-image-btn[data-v-f8e0fc57]{width:18px;height:18px;top:-5px;right:-5px}}.message-history[data-v-f8e0fc57]::-webkit-scrollbar{width:6px}.message-history[data-v-f8e0fc57]::-webkit-scrollbar-track{background:0 0}.message-history[data-v-f8e0fc57]::-webkit-scrollbar-thumb{background-color:#9093994d;border-radius:4px;transition:background-color .3s}.message-history[data-v-f8e0fc57]::-webkit-scrollbar-thumb:hover{background-color:#90939980}.bot-items[data-v-f8e0fc57]::-webkit-scrollbar,.channel-items[data-v-f8e0fc57]::-webkit-scrollbar{width:8px}.bot-items[data-v-f8e0fc57]::-webkit-scrollbar-track,.channel-items[data-v-f8e0fc57]::-webkit-scrollbar-track{background:#0000000d;border-radius:4px}.bot-items[data-v-f8e0fc57]::-webkit-scrollbar-thumb,.channel-items[data-v-f8e0fc57]::-webkit-scrollbar-thumb{background-color:#90939966;border-radius:4px;transition:all .3s ease;border:1px solid rgba(255,255,255,.1);background-clip:padding-box}.bot-items[data-v-f8e0fc57]::-webkit-scrollbar-thumb:hover,.channel-items[data-v-f8e0fc57]::-webkit-scrollbar-thumb:hover{background-color:#90939999;transform:scaleX(1.2)}.bot-items[data-v-f8e0fc57]::-webkit-scrollbar-thumb:active,.channel-items[data-v-f8e0fc57]::-webkit-scrollbar-thumb:active{background-color:#909399cc}.forward-message-expanded[data-v-f8e0fc57]::-webkit-scrollbar{width:6px}.forward-message-expanded[data-v-f8e0fc57]::-webkit-scrollbar-track{background:0 0}.forward-message-expanded[data-v-f8e0fc57]::-webkit-scrollbar-thumb{background-color:#9093994d;border-radius:4px;transition:background-color .3s}.forward-message-expanded[data-v-f8e0fc57]::-webkit-scrollbar-thumb:hover{background-color:#90939980}.json-raw-content[data-v-f8e0fc57]::-webkit-scrollbar{width:6px}.json-raw-content[data-v-f8e0fc57]::-webkit-scrollbar-track{background:0 0}.json-raw-content[data-v-f8e0fc57]::-webkit-scrollbar-thumb{background-color:#9093994d;border-radius:4px;transition:background-color .3s}.json-raw-content[data-v-f8e0fc57]::-webkit-scrollbar-thumb:hover{background-color:#90939980}@media (prefers-color-scheme:dark){.bot-items[data-v-f8e0fc57]::-webkit-scrollbar-track,.channel-items[data-v-f8e0fc57]::-webkit-scrollbar-track{background:#ffffff0d}.bot-items[data-v-f8e0fc57]::-webkit-scrollbar-thumb,.channel-items[data-v-f8e0fc57]::-webkit-scrollbar-thumb,.forward-message-expanded[data-v-f8e0fc57]::-webkit-scrollbar-thumb,.json-raw-content[data-v-f8e0fc57]::-webkit-scrollbar-thumb,.message-history[data-v-f8e0fc57]::-webkit-scrollbar-thumb{background-color:#ffffff4d;border-color:#0000001a}.bot-items[data-v-f8e0fc57]::-webkit-scrollbar-thumb:hover,.channel-items[data-v-f8e0fc57]::-webkit-scrollbar-thumb:hover,.forward-message-expanded[data-v-f8e0fc57]::-webkit-scrollbar-thumb:hover,.json-raw-content[data-v-f8e0fc57]::-webkit-scrollbar-thumb:hover,.message-history[data-v-f8e0fc57]::-webkit-scrollbar-thumb:hover{background-color:#ffffff80}.bot-items[data-v-f8e0fc57]::-webkit-scrollbar-thumb:active,.channel-items[data-v-f8e0fc57]::-webkit-scrollbar-thumb:active{background-color:#ffffffb3}}.loading-more-indicator[data-v-f8e0fc57]{display:flex;align-items:center;justify-content:center;padding:12px;color:#6c757d;font-size:14px}.loading-spinner[data-v-f8e0fc57]{width:20px;height:20px;border:2px solid #f3f3f3;border-top:2px solid #2196f3;border-radius:50%;animation:spin-f8e0fc57 1s linear infinite;margin-right:8px}@keyframes spin-f8e0fc57{0%{transform:rotate(0)}to{transform:rotate(360deg)}}.layout-iframe{width:100%;height:100%;border:none}
|
package/lib/index.d.ts
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { Console } from '@koishijs/console';
|
|
2
|
+
import { Context } from 'koishi';
|
|
3
|
+
import { Config } from './config';
|
|
4
|
+
export declare const name = "chat-patch";
|
|
5
|
+
export declare const reusable = false;
|
|
6
|
+
export declare const filter = true;
|
|
7
|
+
export declare const inject: {
|
|
8
|
+
required: string[];
|
|
9
|
+
};
|
|
10
|
+
declare module 'koishi' {
|
|
11
|
+
interface Context {
|
|
12
|
+
console: Console;
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
export declare const usage = "\n\n---\n\n\u5F00\u542F\u540E\uFF0C\u5373\u53EF\u5728koishi\u63A7\u5236\u53F0\u64CD\u4F5C\u673A\u5668\u4EBA\u6536\u53D1\u6D88\u606F\u5566\n\n\u6682\u65F6\u53EA\u652F\u6301\u63A5\u53D7\u56FE\u6587\u6D88\u606F / \u53D1\u9001\u6587\u5B57\u6D88\u606F\n\n---\n";
|
|
16
|
+
export { Config } from './config';
|
|
17
|
+
export declare function apply(ctx: Context, config: Config): Promise<void>;
|
package/lib/index.js
CHANGED
|
@@ -538,16 +538,25 @@ var ApiHandlers = class {
|
|
|
538
538
|
try {
|
|
539
539
|
const data = this.fileManager.readChatDataFromFile();
|
|
540
540
|
const channelKey = `${requestData.selfId}:${requestData.channelId}`;
|
|
541
|
-
|
|
542
|
-
const sortedMessages = messages.sort((a, b) =>
|
|
543
|
-
|
|
541
|
+
let messages = data.messages[channelKey] || [];
|
|
542
|
+
const sortedMessages = messages.sort((a, b) => b.timestamp - a.timestamp);
|
|
543
|
+
if (requestData.limit !== void 0) {
|
|
544
|
+
const limit = requestData.limit;
|
|
545
|
+
const offset = requestData.offset || 0;
|
|
546
|
+
messages = sortedMessages.slice(offset, offset + limit);
|
|
547
|
+
messages = messages.sort((a, b) => a.timestamp - b.timestamp);
|
|
548
|
+
} else {
|
|
549
|
+
messages = sortedMessages.sort((a, b) => a.timestamp - b.timestamp);
|
|
550
|
+
}
|
|
551
|
+
this.logInfo("获取历史消息:", channelKey, "共", messages.length, "条消息");
|
|
544
552
|
return {
|
|
545
553
|
success: true,
|
|
546
|
-
messages
|
|
554
|
+
messages,
|
|
555
|
+
total: sortedMessages.length
|
|
547
556
|
};
|
|
548
557
|
} catch (error) {
|
|
549
558
|
this.logger.error("获取历史消息失败:", error);
|
|
550
|
-
return { success: false, error: error?.message || String(error), messages: [] };
|
|
559
|
+
return { success: false, error: error?.message || String(error), messages: [], total: 0 };
|
|
551
560
|
}
|
|
552
561
|
});
|
|
553
562
|
this.ctx.console.addListener("get-all-channel-message-counts", async () => {
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "koishi-plugin-chat-patch",
|
|
3
3
|
"description": "[<ruby>chat-patch<rp>(</rp><rt>点我预览效果</rt><rp>)</rp></ruby>](https://i0.hdslb.com/bfs/openplatform/71074dfc9e5256fc3333d8bd8478bec1af874046.png) 视奸小插件((bushi( (低性能警告)。手机端适配。灵感来自 chat 插件。",
|
|
4
|
-
"version": "1.0
|
|
4
|
+
"version": "1.1.0",
|
|
5
5
|
"main": "lib/index.js",
|
|
6
6
|
"typings": "lib/index.d.ts",
|
|
7
7
|
"files": [
|
|
@@ -37,4 +37,4 @@
|
|
|
37
37
|
"@koishijs/client": "^5.11.0",
|
|
38
38
|
"koishi": "^4.16.0"
|
|
39
39
|
}
|
|
40
|
-
}
|
|
40
|
+
}
|
package/src/api-handlers.ts
CHANGED
|
@@ -2,7 +2,7 @@ import { FileManager } from './file-manager'
|
|
|
2
2
|
import { MessageHandler } from './message-handler'
|
|
3
3
|
import { Context, h, Logger } from 'koishi'
|
|
4
4
|
import { Config } from './config'
|
|
5
|
-
|
|
5
|
+
import { } from '@koishijs/plugin-console'
|
|
6
6
|
import { URL, pathToFileURL } from 'node:url'
|
|
7
7
|
|
|
8
8
|
export class ApiHandlers {
|
|
@@ -50,23 +50,39 @@ export class ApiHandlers {
|
|
|
50
50
|
this.ctx.console.addListener('get-history-messages' as any, async (requestData: {
|
|
51
51
|
selfId: string
|
|
52
52
|
channelId: string
|
|
53
|
+
limit?: number
|
|
54
|
+
offset?: number
|
|
53
55
|
}) => {
|
|
54
56
|
try {
|
|
55
57
|
const data = this.fileManager.readChatDataFromFile()
|
|
56
58
|
const channelKey = `${requestData.selfId}:${requestData.channelId}`
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
59
|
+
let messages = data.messages[channelKey] || []
|
|
60
|
+
|
|
61
|
+
// 按时间戳降序排列(最新的消息在前面)
|
|
62
|
+
const sortedMessages = messages.sort((a, b) => b.timestamp - a.timestamp)
|
|
63
|
+
|
|
64
|
+
// 如果提供了分页参数,则进行分页处理
|
|
65
|
+
if (requestData.limit !== undefined) {
|
|
66
|
+
const limit = requestData.limit
|
|
67
|
+
const offset = requestData.offset || 0
|
|
68
|
+
messages = sortedMessages.slice(offset, offset + limit)
|
|
69
|
+
// 重新按时间戳升序排列,以保持消息显示顺序
|
|
70
|
+
messages = messages.sort((a, b) => a.timestamp - b.timestamp)
|
|
71
|
+
} else {
|
|
72
|
+
// 默认返回所有消息,按时间戳升序排列
|
|
73
|
+
messages = sortedMessages.sort((a, b) => a.timestamp - b.timestamp)
|
|
74
|
+
}
|
|
60
75
|
|
|
61
|
-
this.logInfo('获取历史消息:', channelKey, '共',
|
|
76
|
+
this.logInfo('获取历史消息:', channelKey, '共', messages.length, '条消息')
|
|
62
77
|
|
|
63
78
|
return {
|
|
64
79
|
success: true,
|
|
65
|
-
messages:
|
|
80
|
+
messages: messages,
|
|
81
|
+
total: sortedMessages.length
|
|
66
82
|
}
|
|
67
83
|
} catch (error: any) {
|
|
68
84
|
this.logger.error('获取历史消息失败:', error)
|
|
69
|
-
return { success: false, error: error?.message || String(error), messages: [] }
|
|
85
|
+
return { success: false, error: error?.message || String(error), messages: [], total: 0 }
|
|
70
86
|
}
|
|
71
87
|
})
|
|
72
88
|
|
package/src/index.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
|
|
2
|
-
import { } from '@koishijs/
|
|
2
|
+
import { Console } from '@koishijs/console'
|
|
3
3
|
import { Context } from 'koishi'
|
|
4
4
|
import path from 'node:path'
|
|
5
5
|
|
|
@@ -16,6 +16,12 @@ export const inject = {
|
|
|
16
16
|
required: ['console']
|
|
17
17
|
}
|
|
18
18
|
|
|
19
|
+
declare module 'koishi' {
|
|
20
|
+
interface Context {
|
|
21
|
+
console: Console
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
19
25
|
export const usage = `
|
|
20
26
|
|
|
21
27
|
---
|
package/src/message-handler.ts
CHANGED
|
@@ -3,6 +3,7 @@ import { Context, Session, h, Logger } from 'koishi'
|
|
|
3
3
|
import { FileManager } from './file-manager'
|
|
4
4
|
import { Config } from './config'
|
|
5
5
|
import { Utils } from './utils'
|
|
6
|
+
import { } from '@koishijs/plugin-console'
|
|
6
7
|
|
|
7
8
|
export class MessageHandler {
|
|
8
9
|
private logger: Logger
|