koishi-plugin-chat-patch 1.0.8 → 1.0.9
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/index.vue +397 -57
- package/dist/index.js +8 -8
- package/dist/style.css +1 -1
- package/package.json +1 -1
- package/lib/api-handlers.d.ts +0 -17
- package/lib/config.d.ts +0 -13
- package/lib/file-manager.d.ts +0 -18
- package/lib/index.d.ts +0 -11
- package/lib/message-handler.d.ts +0 -16
- package/lib/types.d.ts +0 -53
- package/lib/utils.d.ts +0 -9
package/client/vue/index.vue
CHANGED
|
@@ -222,7 +222,6 @@ function isFileUrl(url: string): boolean {
|
|
|
222
222
|
}
|
|
223
223
|
}
|
|
224
224
|
|
|
225
|
-
|
|
226
225
|
// 头像组件
|
|
227
226
|
const AvatarComponent = defineComponent({
|
|
228
227
|
props: {
|
|
@@ -899,13 +898,35 @@ interface ImageCacheItem {
|
|
|
899
898
|
|
|
900
899
|
// 内存中的URL缓存
|
|
901
900
|
const imageBlobUrls = ref<Record<string, string>>({})
|
|
902
|
-
const maxImagesPerChannel = 200 // 每个频道最大缓存图片数量
|
|
903
901
|
|
|
904
902
|
// 内存管理配置
|
|
905
903
|
const MAX_MEMORY_USAGE = 100 * 1024 * 1024 // 100MB 最大内存使用量
|
|
906
904
|
const MAX_BLOB_COUNT = 50 // 最大blob URL数量
|
|
907
905
|
let currentMemoryUsage = 0 // 当前内存使用量估算
|
|
908
906
|
|
|
907
|
+
// IndexedDB 配置和限制
|
|
908
|
+
let imageDB: IDBDatabase | null = null
|
|
909
|
+
const DB_NAME = 'ChatImageCache'
|
|
910
|
+
const DB_VERSION = 2 // 版本号
|
|
911
|
+
const STORE_NAME = 'images'
|
|
912
|
+
|
|
913
|
+
// 严格的存储限制
|
|
914
|
+
const MAX_DB_SIZE = 50 * 1024 * 1024 // 50MB 最大数据库大小
|
|
915
|
+
const MAX_IMAGES_PER_CHANNEL = 100 // 每个频道最多缓存100张图片
|
|
916
|
+
const MAX_TOTAL_IMAGES = 500 // 总共最多缓存500张图片
|
|
917
|
+
const MAX_IMAGE_SIZE = 2 * 1024 * 1024 // 单张图片最大2MB
|
|
918
|
+
const CLEANUP_THRESHOLD = 0.8 // 当达到80%限制时开始清理
|
|
919
|
+
const DB_HEALTH_CHECK_INTERVAL = 60 * 1000 // 每分钟检查一次数据库健康状态
|
|
920
|
+
|
|
921
|
+
// 数据库状态跟踪
|
|
922
|
+
let currentDbSize = 0
|
|
923
|
+
let currentImageCount = 0
|
|
924
|
+
let lastHealthCheck = 0
|
|
925
|
+
|
|
926
|
+
const selectedBot = ref<string>('')
|
|
927
|
+
const selectedChannel = ref<string>('')
|
|
928
|
+
const inputMessage = ref<string>('')
|
|
929
|
+
|
|
909
930
|
// 内存管理函数
|
|
910
931
|
function estimateBlobSize(blob: Blob): number {
|
|
911
932
|
return blob.size || 0
|
|
@@ -954,16 +975,6 @@ function checkAndCleanupMemory() {
|
|
|
954
975
|
}
|
|
955
976
|
}
|
|
956
977
|
|
|
957
|
-
// IndexedDB
|
|
958
|
-
let imageDB: IDBDatabase | null = null
|
|
959
|
-
const DB_NAME = 'ChatImageCache'
|
|
960
|
-
const DB_VERSION = 1
|
|
961
|
-
const STORE_NAME = 'images'
|
|
962
|
-
|
|
963
|
-
const selectedBot = ref<string>('')
|
|
964
|
-
const selectedChannel = ref<string>('')
|
|
965
|
-
const inputMessage = ref<string>('')
|
|
966
|
-
|
|
967
978
|
// 图片上传相关状态
|
|
968
979
|
const uploadedImages = ref<Array<{
|
|
969
980
|
tempId: string
|
|
@@ -971,6 +982,7 @@ const uploadedImages = ref<Array<{
|
|
|
971
982
|
preview: string
|
|
972
983
|
size: number
|
|
973
984
|
}>>([])
|
|
985
|
+
|
|
974
986
|
const showActionMenu = ref<boolean>(false)
|
|
975
987
|
const fileInput = ref<HTMLInputElement>()
|
|
976
988
|
|
|
@@ -1771,7 +1783,6 @@ function resetDragState() {
|
|
|
1771
1783
|
removeThresholdCircle()
|
|
1772
1784
|
}
|
|
1773
1785
|
|
|
1774
|
-
// getDragStyle 不再直接用于拖拽中的元素,而是用于原始元素隐藏
|
|
1775
1786
|
function getDragStyle(channelId: string) {
|
|
1776
1787
|
if (draggingChannel.value === channelId && isDragReady.value) {
|
|
1777
1788
|
// 当拖拽开始且准备就绪时,隐藏原始气泡
|
|
@@ -1951,30 +1962,319 @@ function removeThresholdCircle() {
|
|
|
1951
1962
|
}
|
|
1952
1963
|
}
|
|
1953
1964
|
|
|
1954
|
-
//
|
|
1955
|
-
async function
|
|
1956
|
-
|
|
1957
|
-
|
|
1965
|
+
// 数据库健康检查
|
|
1966
|
+
async function checkDatabaseHealth(): Promise<boolean> {
|
|
1967
|
+
try {
|
|
1968
|
+
if (!imageDB) return false
|
|
1969
|
+
|
|
1970
|
+
const now = Date.now()
|
|
1971
|
+
if (now - lastHealthCheck < DB_HEALTH_CHECK_INTERVAL) {
|
|
1972
|
+
return true // 跳过频繁检查
|
|
1973
|
+
}
|
|
1974
|
+
|
|
1975
|
+
lastHealthCheck = now
|
|
1976
|
+
|
|
1977
|
+
// 获取数据库统计信息
|
|
1978
|
+
const stats = await getDatabaseStats()
|
|
1979
|
+
currentDbSize = stats.totalSize
|
|
1980
|
+
currentImageCount = stats.totalImages
|
|
1981
|
+
|
|
1982
|
+
console.log('数据库健康检查:', {
|
|
1983
|
+
大小: `${(currentDbSize / 1024 / 1024).toFixed(2)}MB / ${(MAX_DB_SIZE / 1024 / 1024).toFixed(2)}MB`,
|
|
1984
|
+
图片数量: `${currentImageCount} / ${MAX_TOTAL_IMAGES}`,
|
|
1985
|
+
使用率: `${(currentDbSize / MAX_DB_SIZE * 100).toFixed(1)}%`
|
|
1986
|
+
})
|
|
1987
|
+
|
|
1988
|
+
// 检查是否需要清理
|
|
1989
|
+
const sizeRatio = currentDbSize / MAX_DB_SIZE
|
|
1990
|
+
const countRatio = currentImageCount / MAX_TOTAL_IMAGES
|
|
1991
|
+
|
|
1992
|
+
if (sizeRatio > CLEANUP_THRESHOLD || countRatio > CLEANUP_THRESHOLD) {
|
|
1993
|
+
console.warn('数据库使用率过高,开始自动清理')
|
|
1994
|
+
await performAutomaticCleanup()
|
|
1995
|
+
}
|
|
1996
|
+
|
|
1997
|
+
// 检查是否超过硬限制
|
|
1998
|
+
if (sizeRatio > 0.95 || countRatio > 0.95) {
|
|
1999
|
+
console.error('数据库接近极限,执行紧急清理')
|
|
2000
|
+
await performEmergencyCleanup()
|
|
2001
|
+
}
|
|
2002
|
+
|
|
2003
|
+
return true
|
|
2004
|
+
} catch (error) {
|
|
2005
|
+
console.error('数据库健康检查失败:', error)
|
|
2006
|
+
return false
|
|
2007
|
+
}
|
|
2008
|
+
}
|
|
2009
|
+
|
|
2010
|
+
// 获取数据库统计信息
|
|
2011
|
+
async function getDatabaseStats(): Promise<{ totalSize: number, totalImages: number, channelStats: Record<string, number> }> {
|
|
2012
|
+
if (!imageDB) return { totalSize: 0, totalImages: 0, channelStats: {} }
|
|
2013
|
+
|
|
2014
|
+
return new Promise((resolve) => {
|
|
2015
|
+
const transaction = imageDB!.transaction([STORE_NAME], 'readonly')
|
|
2016
|
+
const store = transaction.objectStore(STORE_NAME)
|
|
2017
|
+
const request = store.getAll()
|
|
2018
|
+
|
|
2019
|
+
request.onsuccess = () => {
|
|
2020
|
+
const items: ImageCacheItem[] = request.result || []
|
|
2021
|
+
let totalSize = 0
|
|
2022
|
+
const channelStats: Record<string, number> = {}
|
|
2023
|
+
|
|
2024
|
+
items.forEach(item => {
|
|
2025
|
+
totalSize += item.size || 0
|
|
2026
|
+
channelStats[item.channelKey] = (channelStats[item.channelKey] || 0) + 1
|
|
2027
|
+
})
|
|
2028
|
+
|
|
2029
|
+
resolve({
|
|
2030
|
+
totalSize,
|
|
2031
|
+
totalImages: items.length,
|
|
2032
|
+
channelStats
|
|
2033
|
+
})
|
|
2034
|
+
}
|
|
1958
2035
|
|
|
1959
2036
|
request.onerror = () => {
|
|
1960
|
-
console.error('
|
|
1961
|
-
resolve(
|
|
2037
|
+
console.error('获取数据库统计失败:', request.error)
|
|
2038
|
+
resolve({ totalSize: 0, totalImages: 0, channelStats: {} })
|
|
2039
|
+
}
|
|
2040
|
+
})
|
|
2041
|
+
}
|
|
2042
|
+
|
|
2043
|
+
// 自动清理
|
|
2044
|
+
async function performAutomaticCleanup() {
|
|
2045
|
+
try {
|
|
2046
|
+
console.log('开始自动清理...')
|
|
2047
|
+
|
|
2048
|
+
// 获取所有图片,按时间排序
|
|
2049
|
+
const allImages = await getAllImagesFromDB()
|
|
2050
|
+
if (allImages.length === 0) return
|
|
2051
|
+
|
|
2052
|
+
// 按频道分组
|
|
2053
|
+
const channelGroups: Record<string, ImageCacheItem[]> = {}
|
|
2054
|
+
allImages.forEach(item => {
|
|
2055
|
+
if (!channelGroups[item.channelKey]) {
|
|
2056
|
+
channelGroups[item.channelKey] = []
|
|
2057
|
+
}
|
|
2058
|
+
channelGroups[item.channelKey].push(item)
|
|
2059
|
+
})
|
|
2060
|
+
|
|
2061
|
+
let cleanedCount = 0
|
|
2062
|
+
let freedSize = 0
|
|
2063
|
+
|
|
2064
|
+
// 清理每个频道超出限制的图片
|
|
2065
|
+
for (const [channelKey, images] of Object.entries(channelGroups)) {
|
|
2066
|
+
if (images.length > MAX_IMAGES_PER_CHANNEL) {
|
|
2067
|
+
// 按时间排序,删除最旧的
|
|
2068
|
+
images.sort((a, b) => a.timestamp - b.timestamp)
|
|
2069
|
+
const toDelete = images.slice(0, images.length - MAX_IMAGES_PER_CHANNEL)
|
|
2070
|
+
|
|
2071
|
+
for (const item of toDelete) {
|
|
2072
|
+
await deleteImageFromDB(item.url)
|
|
2073
|
+
cleanedCount++
|
|
2074
|
+
freedSize += item.size || 0
|
|
2075
|
+
|
|
2076
|
+
// 清理内存中的blob URL
|
|
2077
|
+
if (imageBlobUrls.value[item.url]) {
|
|
2078
|
+
URL.revokeObjectURL(imageBlobUrls.value[item.url])
|
|
2079
|
+
delete imageBlobUrls.value[item.url]
|
|
2080
|
+
}
|
|
2081
|
+
}
|
|
2082
|
+
}
|
|
2083
|
+
}
|
|
2084
|
+
|
|
2085
|
+
console.log(`自动清理完成: 清理了 ${cleanedCount} 张图片,释放了 ${(freedSize / 1024 / 1024).toFixed(2)}MB`)
|
|
2086
|
+
|
|
2087
|
+
// 更新统计
|
|
2088
|
+
currentImageCount -= cleanedCount
|
|
2089
|
+
currentDbSize -= freedSize
|
|
2090
|
+
|
|
2091
|
+
} catch (error) {
|
|
2092
|
+
console.error('自动清理失败:', error)
|
|
2093
|
+
}
|
|
2094
|
+
}
|
|
2095
|
+
|
|
2096
|
+
// 紧急清理 // 激进
|
|
2097
|
+
async function performEmergencyCleanup() {
|
|
2098
|
+
try {
|
|
2099
|
+
console.log('开始紧急清理...')
|
|
2100
|
+
|
|
2101
|
+
// 获取所有图片
|
|
2102
|
+
const allImages = await getAllImagesFromDB()
|
|
2103
|
+
if (allImages.length === 0) return
|
|
2104
|
+
|
|
2105
|
+
// 按时间排序,只保留最新的一部分
|
|
2106
|
+
allImages.sort((a, b) => b.timestamp - a.timestamp)
|
|
2107
|
+
const keepCount = Math.floor(MAX_TOTAL_IMAGES * 0.3) // 只保留30%
|
|
2108
|
+
const toDelete = allImages.slice(keepCount)
|
|
2109
|
+
|
|
2110
|
+
let cleanedCount = 0
|
|
2111
|
+
let freedSize = 0
|
|
2112
|
+
|
|
2113
|
+
for (const item of toDelete) {
|
|
2114
|
+
await deleteImageFromDB(item.url)
|
|
2115
|
+
cleanedCount++
|
|
2116
|
+
freedSize += item.size || 0
|
|
2117
|
+
|
|
2118
|
+
// 清理内存中的blob URL
|
|
2119
|
+
if (imageBlobUrls.value[item.url]) {
|
|
2120
|
+
URL.revokeObjectURL(imageBlobUrls.value[item.url])
|
|
2121
|
+
delete imageBlobUrls.value[item.url]
|
|
2122
|
+
}
|
|
1962
2123
|
}
|
|
1963
2124
|
|
|
2125
|
+
console.log(`紧急清理完成: 清理了 ${cleanedCount} 张图片,释放了 ${(freedSize / 1024 / 1024).toFixed(2)}MB`)
|
|
2126
|
+
|
|
2127
|
+
// 更新统计
|
|
2128
|
+
currentImageCount = keepCount
|
|
2129
|
+
currentDbSize -= freedSize
|
|
2130
|
+
|
|
2131
|
+
} catch (error) {
|
|
2132
|
+
console.error('紧急清理失败:', error)
|
|
2133
|
+
}
|
|
2134
|
+
}
|
|
2135
|
+
|
|
2136
|
+
// 获取所有图片
|
|
2137
|
+
async function getAllImagesFromDB(): Promise<ImageCacheItem[]> {
|
|
2138
|
+
if (!imageDB) return []
|
|
2139
|
+
|
|
2140
|
+
return new Promise((resolve) => {
|
|
2141
|
+
const transaction = imageDB!.transaction([STORE_NAME], 'readonly')
|
|
2142
|
+
const store = transaction.objectStore(STORE_NAME)
|
|
2143
|
+
const request = store.getAll()
|
|
2144
|
+
|
|
1964
2145
|
request.onsuccess = () => {
|
|
1965
|
-
|
|
1966
|
-
|
|
2146
|
+
resolve(request.result || [])
|
|
2147
|
+
}
|
|
2148
|
+
|
|
2149
|
+
request.onerror = () => {
|
|
2150
|
+
console.error('获取所有图片失败:', request.error)
|
|
2151
|
+
resolve([])
|
|
2152
|
+
}
|
|
2153
|
+
})
|
|
2154
|
+
}
|
|
2155
|
+
|
|
2156
|
+
// 清理所有IndexedDB数据 //紧急情况使用
|
|
2157
|
+
async function clearAllIndexedDBData(): Promise<boolean> {
|
|
2158
|
+
return new Promise((resolve) => {
|
|
2159
|
+
try {
|
|
2160
|
+
// 先关闭现有连接
|
|
2161
|
+
if (imageDB) {
|
|
2162
|
+
imageDB.close()
|
|
2163
|
+
imageDB = null
|
|
2164
|
+
}
|
|
2165
|
+
|
|
2166
|
+
// 删除整个数据库
|
|
2167
|
+
const deleteRequest = indexedDB.deleteDatabase(DB_NAME)
|
|
2168
|
+
|
|
2169
|
+
deleteRequest.onsuccess = () => {
|
|
2170
|
+
console.log('IndexedDB数据库已完全清理')
|
|
2171
|
+
currentDbSize = 0
|
|
2172
|
+
currentImageCount = 0
|
|
2173
|
+
resolve(true)
|
|
2174
|
+
}
|
|
2175
|
+
|
|
2176
|
+
deleteRequest.onerror = () => {
|
|
2177
|
+
console.error('清理IndexedDB数据库失败:', deleteRequest.error)
|
|
2178
|
+
resolve(false)
|
|
2179
|
+
}
|
|
2180
|
+
|
|
2181
|
+
deleteRequest.onblocked = () => {
|
|
2182
|
+
console.warn('IndexedDB数据库删除被阻塞,可能有其他连接正在使用')
|
|
2183
|
+
// 等待一段时间后重试
|
|
2184
|
+
setTimeout(() => {
|
|
2185
|
+
resolve(false)
|
|
2186
|
+
}, 5000)
|
|
2187
|
+
}
|
|
2188
|
+
} catch (error) {
|
|
2189
|
+
console.error('清理数据库时出错:', error)
|
|
2190
|
+
resolve(false)
|
|
1967
2191
|
}
|
|
2192
|
+
})
|
|
2193
|
+
}
|
|
2194
|
+
|
|
2195
|
+
// IndexedDB初始化
|
|
2196
|
+
async function initImageDB(): Promise<boolean> {
|
|
2197
|
+
try {
|
|
2198
|
+
// 首先尝试打开数据库
|
|
2199
|
+
const success = await openDatabase()
|
|
2200
|
+
if (!success) {
|
|
2201
|
+
console.warn('数据库打开失败,尝试清理后重新初始化')
|
|
2202
|
+
await clearAllIndexedDBData()
|
|
2203
|
+
return await openDatabase()
|
|
2204
|
+
}
|
|
2205
|
+
|
|
2206
|
+
// 数据库打开成功,进行初始健康检查
|
|
2207
|
+
setTimeout(async () => {
|
|
2208
|
+
const stats = await getDatabaseStats()
|
|
2209
|
+
console.log('数据库初始状态:', {
|
|
2210
|
+
大小: `${(stats.totalSize / 1024 / 1024).toFixed(2)}MB`,
|
|
2211
|
+
图片数量: stats.totalImages,
|
|
2212
|
+
频道分布: stats.channelStats
|
|
2213
|
+
})
|
|
2214
|
+
|
|
2215
|
+
// 如果初始状态就超过限制,执行清理
|
|
2216
|
+
if (stats.totalSize > MAX_DB_SIZE * 0.9 || stats.totalImages > MAX_TOTAL_IMAGES * 0.9) {
|
|
2217
|
+
console.warn('数据库初始状态接近限制,执行清理')
|
|
2218
|
+
await performAutomaticCleanup()
|
|
2219
|
+
}
|
|
2220
|
+
}, 1000)
|
|
2221
|
+
|
|
2222
|
+
return true
|
|
2223
|
+
} catch (error) {
|
|
2224
|
+
console.error('IndexedDB初始化出错:', error)
|
|
2225
|
+
return false
|
|
2226
|
+
}
|
|
2227
|
+
}
|
|
2228
|
+
|
|
2229
|
+
// 打开数据库的内部函数
|
|
2230
|
+
async function openDatabase(): Promise<boolean> {
|
|
2231
|
+
return new Promise((resolve) => {
|
|
2232
|
+
try {
|
|
2233
|
+
const request = indexedDB.open(DB_NAME, DB_VERSION)
|
|
1968
2234
|
|
|
1969
|
-
|
|
1970
|
-
|
|
2235
|
+
request.onerror = () => {
|
|
2236
|
+
console.error('IndexedDB打开失败:', request.error)
|
|
2237
|
+
resolve(false)
|
|
2238
|
+
}
|
|
2239
|
+
|
|
2240
|
+
request.onsuccess = () => {
|
|
2241
|
+
imageDB = request.result
|
|
2242
|
+
|
|
2243
|
+
// 添加错误处理
|
|
2244
|
+
imageDB.onerror = (event) => {
|
|
2245
|
+
console.error('IndexedDB运行时错误:', event)
|
|
2246
|
+
}
|
|
2247
|
+
|
|
2248
|
+
// 添加版本变更处理
|
|
2249
|
+
imageDB.onversionchange = () => {
|
|
2250
|
+
console.warn('IndexedDB版本变更,关闭连接')
|
|
2251
|
+
imageDB?.close()
|
|
2252
|
+
imageDB = null
|
|
2253
|
+
}
|
|
2254
|
+
|
|
2255
|
+
resolve(true)
|
|
2256
|
+
}
|
|
2257
|
+
|
|
2258
|
+
request.onupgradeneeded = (event) => {
|
|
2259
|
+
const db = (event.target as IDBOpenDBRequest).result
|
|
2260
|
+
|
|
2261
|
+
// 创建对象存储
|
|
2262
|
+
if (!db.objectStoreNames.contains(STORE_NAME)) {
|
|
2263
|
+
const store = db.createObjectStore(STORE_NAME, { keyPath: 'url' })
|
|
2264
|
+
store.createIndex('channelKey', 'channelKey', { unique: false })
|
|
2265
|
+
store.createIndex('timestamp', 'timestamp', { unique: false })
|
|
2266
|
+
store.createIndex('size', 'size', { unique: false })
|
|
2267
|
+
console.log('IndexedDB对象存储创建完成')
|
|
2268
|
+
}
|
|
2269
|
+
}
|
|
1971
2270
|
|
|
1972
|
-
|
|
1973
|
-
|
|
1974
|
-
|
|
1975
|
-
store.createIndex('channelKey', 'channelKey', { unique: false })
|
|
1976
|
-
store.createIndex('timestamp', 'timestamp', { unique: false })
|
|
2271
|
+
request.onblocked = () => {
|
|
2272
|
+
console.warn('IndexedDB打开被阻塞')
|
|
2273
|
+
resolve(false)
|
|
1977
2274
|
}
|
|
2275
|
+
} catch (error) {
|
|
2276
|
+
console.error('打开数据库时出错:', error)
|
|
2277
|
+
resolve(false)
|
|
1978
2278
|
}
|
|
1979
2279
|
})
|
|
1980
2280
|
}
|
|
@@ -2003,20 +2303,60 @@ async function getImageFromDB(url: string): Promise<ImageCacheItem | null> {
|
|
|
2003
2303
|
async function saveImageToDB(item: ImageCacheItem): Promise<boolean> {
|
|
2004
2304
|
if (!imageDB) return false
|
|
2005
2305
|
|
|
2006
|
-
|
|
2007
|
-
|
|
2008
|
-
|
|
2009
|
-
|
|
2306
|
+
try {
|
|
2307
|
+
// 检查单张图片大小
|
|
2308
|
+
if (item.size > MAX_IMAGE_SIZE) {
|
|
2309
|
+
console.warn(`图片过大,跳过缓存: ${(item.size / 1024 / 1024).toFixed(2)}MB > ${(MAX_IMAGE_SIZE / 1024 / 1024).toFixed(2)}MB`)
|
|
2310
|
+
return false
|
|
2311
|
+
}
|
|
2010
2312
|
|
|
2011
|
-
|
|
2012
|
-
|
|
2313
|
+
// 执行健康检查
|
|
2314
|
+
await checkDatabaseHealth()
|
|
2315
|
+
|
|
2316
|
+
// 检查是否会超过限制
|
|
2317
|
+
if (currentDbSize + item.size > MAX_DB_SIZE) {
|
|
2318
|
+
console.warn('添加图片会超过数据库大小限制,执行清理')
|
|
2319
|
+
await performAutomaticCleanup()
|
|
2320
|
+
|
|
2321
|
+
// 清理后再次检查
|
|
2322
|
+
if (currentDbSize + item.size > MAX_DB_SIZE) {
|
|
2323
|
+
console.warn('清理后仍会超过限制,跳过此图片')
|
|
2324
|
+
return false
|
|
2325
|
+
}
|
|
2013
2326
|
}
|
|
2014
2327
|
|
|
2015
|
-
|
|
2016
|
-
console.
|
|
2017
|
-
|
|
2328
|
+
if (currentImageCount >= MAX_TOTAL_IMAGES) {
|
|
2329
|
+
console.warn('图片数量已达上限,执行清理')
|
|
2330
|
+
await performAutomaticCleanup()
|
|
2331
|
+
|
|
2332
|
+
// 清理后再次检查
|
|
2333
|
+
if (currentImageCount >= MAX_TOTAL_IMAGES) {
|
|
2334
|
+
console.warn('清理后仍达上限,跳过此图片')
|
|
2335
|
+
return false
|
|
2336
|
+
}
|
|
2018
2337
|
}
|
|
2019
|
-
|
|
2338
|
+
|
|
2339
|
+
return new Promise((resolve) => {
|
|
2340
|
+
const transaction = imageDB!.transaction([STORE_NAME], 'readwrite')
|
|
2341
|
+
const store = transaction.objectStore(STORE_NAME)
|
|
2342
|
+
const request = store.put(item)
|
|
2343
|
+
|
|
2344
|
+
request.onsuccess = () => {
|
|
2345
|
+
// 更新统计
|
|
2346
|
+
currentDbSize += item.size
|
|
2347
|
+
currentImageCount += 1
|
|
2348
|
+
resolve(true)
|
|
2349
|
+
}
|
|
2350
|
+
|
|
2351
|
+
request.onerror = () => {
|
|
2352
|
+
console.error('保存图片到IndexedDB失败:', request.error)
|
|
2353
|
+
resolve(false)
|
|
2354
|
+
}
|
|
2355
|
+
})
|
|
2356
|
+
} catch (error) {
|
|
2357
|
+
console.error('保存图片时出错:', error)
|
|
2358
|
+
return false
|
|
2359
|
+
}
|
|
2020
2360
|
}
|
|
2021
2361
|
|
|
2022
2362
|
// 从IndexedDB删除图片
|
|
@@ -2118,22 +2458,10 @@ async function cacheImage(channelKey: string, originalUrl: string): Promise<stri
|
|
|
2118
2458
|
const byteArray = new Uint8Array(byteNumbers)
|
|
2119
2459
|
const blob = new Blob([byteArray], { type: contentType })
|
|
2120
2460
|
|
|
2121
|
-
//
|
|
2122
|
-
|
|
2123
|
-
|
|
2124
|
-
|
|
2125
|
-
// 清理最旧的图片缓存
|
|
2126
|
-
const sortedImages = channelImages.sort((a, b) => a.timestamp - b.timestamp)
|
|
2127
|
-
const toDelete = sortedImages.slice(0, channelImages.length - maxImagesPerChannel + 1)
|
|
2128
|
-
|
|
2129
|
-
for (const item of toDelete) {
|
|
2130
|
-
await deleteImageFromDB(item.url)
|
|
2131
|
-
// 清理内存中的blob URL
|
|
2132
|
-
if (imageBlobUrls.value[item.url]) {
|
|
2133
|
-
URL.revokeObjectURL(imageBlobUrls.value[item.url])
|
|
2134
|
-
delete imageBlobUrls.value[item.url]
|
|
2135
|
-
}
|
|
2136
|
-
}
|
|
2461
|
+
// 检查blob大小
|
|
2462
|
+
if (blob.size > MAX_IMAGE_SIZE) {
|
|
2463
|
+
console.warn(`图片过大,跳过缓存: ${(blob.size / 1024 / 1024).toFixed(2)}MB`)
|
|
2464
|
+
return null
|
|
2137
2465
|
}
|
|
2138
2466
|
|
|
2139
2467
|
// 创建缓存项
|
|
@@ -2369,7 +2697,7 @@ function handleMessageEvent(messageEvent: any) {
|
|
|
2369
2697
|
chatData.value = { ...chatData.value }
|
|
2370
2698
|
}
|
|
2371
2699
|
|
|
2372
|
-
//
|
|
2700
|
+
// 处理机器人发送消息成功事件
|
|
2373
2701
|
function handleBotMessageSentEvent(sentEvent: any) {
|
|
2374
2702
|
const channelKey = `${sentEvent.selfId}:${sentEvent.channelId}`
|
|
2375
2703
|
if (!chatData.value.messages[channelKey]) {
|
|
@@ -2436,7 +2764,7 @@ function handleBotMessageSentEvent(sentEvent: any) {
|
|
|
2436
2764
|
chatData.value = { ...chatData.value }
|
|
2437
2765
|
}
|
|
2438
2766
|
|
|
2439
|
-
//
|
|
2767
|
+
// 处理机器人消息事件
|
|
2440
2768
|
function handleBotMessageEvent(botMessageEvent: any) {
|
|
2441
2769
|
// 更新机器人信息
|
|
2442
2770
|
if (!chatData.value.bots[botMessageEvent.selfId]) {
|
|
@@ -2562,7 +2890,7 @@ function handleBotMessageEvent(botMessageEvent: any) {
|
|
|
2562
2890
|
chatData.value = { ...chatData.value }
|
|
2563
2891
|
}
|
|
2564
2892
|
|
|
2565
|
-
//
|
|
2893
|
+
// 监听消息变化
|
|
2566
2894
|
watch(currentMessages, (newMessages, oldMessages) => {
|
|
2567
2895
|
// 只有在切换频道时(消息数组完全不同)才自动滚动
|
|
2568
2896
|
if (oldMessages.length === 0 && newMessages.length > 0) {
|
|
@@ -2830,6 +3158,18 @@ onMounted(async () => {
|
|
|
2830
3158
|
const dbInitialized = await initImageDB()
|
|
2831
3159
|
if (!dbInitialized) {
|
|
2832
3160
|
console.warn('IndexedDB初始化失败,图片缓存功能将不可用')
|
|
3161
|
+
} else {
|
|
3162
|
+
console.log('IndexedDB初始化成功')
|
|
3163
|
+
|
|
3164
|
+
// 启动时进行健康检查
|
|
3165
|
+
setTimeout(async () => {
|
|
3166
|
+
await checkDatabaseHealth()
|
|
3167
|
+
}, 2000) // 延迟2秒,避免影响页面加载
|
|
3168
|
+
|
|
3169
|
+
// 设置定期健康检查(每5分钟)
|
|
3170
|
+
setInterval(async () => {
|
|
3171
|
+
await checkDatabaseHealth()
|
|
3172
|
+
}, 5 * 60 * 1000)
|
|
2833
3173
|
}
|
|
2834
3174
|
|
|
2835
3175
|
// 首先加载插件配置
|
package/dist/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import{defineComponent as Q,ref as f,onMounted as Ne,h as d,computed as O,watch as nt,nextTick as re,onUnmounted as Gt,createElementBlock as p,openBlock as v,normalizeStyle as Oe,normalizeClass as F,createElementVNode as c,createCommentVNode as G,Fragment as P,renderList as oe,createBlock as he,unref as me,toDisplayString as x,withDirectives as st,createTextVNode as Pe,vShow as Qt,withModifiers as lt,withKeys as Zt,vModelText as Et,resolveComponent as ea}from"vue";import{receive as Re,send as _,icons as ta}from"@koishijs/client";const aa={class:"bot-list"},na={class:"bot-items"},sa=["onClick","onContextmenu"],la={class:"bot-avatar"},ra={key:1,class:"avatar-placeholder"},oa={class:"bot-info"},ca={class:"bot-name"},ia={class:"bot-platform"},ua={class:"channel-list"},da={key:0,class:"empty-state"},fa={key:1,class:"channel-items"},ha=["data-channel-id","onClick","onContextmenu"],ma={class:"channel-info"},va={class:"channel-name"},ga={class:"channel-type"},ya=["onMousedown","onTouchstart","title"],pa={class:"message-area"},wa={class:"panel-header"},Ia={key:0,class:"empty-state"},xa={key:1,class:"message-content"},Ca={class:"message-avatar"},ka={key:1,class:"avatar-placeholder"},ba={class:"message-content-wrapper"},_a={class:"message-header"},$a={class:"message-username"},Sa={class:"message-time"},Ba={key:0,class:"message-quote"},La={class:"quote-header"},Ta={class:"quote-avatar"},qa={key:1,class:"avatar-placeholder"},Da={class:"quote-username"},Ua={class:"quote-time"},ja={class:"quote-content"},Ka={class:"message-text"},Na={class:"message-input"},Oa={key:0,class:"image-preview-container"},Pa=["src","alt"],Ra=["onClick"],Aa={class:"input-row"},Ma={class:"input-actions"},za=["placeholder","disabled"],Xa=["disabled"],rt=200,Ha=100*1024*1024,Ae=50,Fa="ChatImageCache",Ya=1,R="images",A=80,Wa=Q({__name:"index",setup(Z){function Y(e){try{return new URL(e).protocol==="file:"}catch{return false}}const E=Q({props:{src:{type:String,required:true},alt:{type:String,default:"头像"},channelKey:{type:String,required:true}},setup(e){const a=f("loading"),t=f(e.src),n=f(""),l=async()=>{try{a.value="loading";const o=await je(e.channelKey,e.src);if(o){t.value=o,a.value="loaded";return}const r=new Image;r.crossOrigin="anonymous",r.referrerPolicy="no-referrer",r.draggable=false;const i=new Promise((g,y)=>{r.onload=()=>g(),r.onerror=()=>y(new Error("Direct load failed")),r.src=e.src}),h=new Promise((g,y)=>{setTimeout(()=>y(new Error("Timeout")),3e3)});try{await Promise.race([i,h]),t.value=e.src,a.value="loaded",se(e.channelKey,e.src).catch(g=>{console.warn("异步缓存头像失败:",g)})}catch{await s()}}catch(o){console.error("头像加载失败:",o),a.value="error",n.value="头像加载失败"}},s=async()=>{try{a.value="caching";const o=await se(e.channelKey,e.src);if(o)t.value=o,a.value="loaded";else throw new Error("缓存系统加载失败")}catch(o){console.error("缓存系统加载头像失败:",o),a.value="error",n.value=(o==null?void 0:o.message)||"缓存加载失败"}};return Ne(()=>{l()}),()=>{switch(a.value){case"loading":case"caching":return d("div",{class:"avatar-placeholder"},e.alt.charAt(0).toUpperCase());case"loaded":return d("img",{src:t.value,alt:e.alt,draggable:false,style:{width:"100%",height:"100%","object-fit":"cover"}});case"error":return d("div",{class:"avatar-placeholder"},e.alt.charAt(0).toUpperCase());default:return d("div",{class:"avatar-placeholder"},e.alt.charAt(0).toUpperCase())}}}}),ce=Q({props:{src:{type:String,required:true},alt:{type:String,default:"图片"},filename:{type:String,default:""},channelKey:{type:String,required:true}},setup(e){const a=f("loading"),t=f(e.src),n=f(""),l=f(null),s=async()=>{try{a.value="loading";const r=await je(e.channelKey,e.src);if(r){t.value=r,a.value="loaded";return}if(Y(e.src)){console.log("ImageComponent: 检测到本地文件,使用代理请求:",e.src),await o();return}const i=new Image;i.crossOrigin="anonymous",i.referrerPolicy="no-referrer",i.draggable=false;const h=new Promise((y,q)=>{i.onload=()=>y(),i.onerror=()=>q(new Error("Direct load failed")),i.src=e.src}),g=new Promise((y,q)=>{setTimeout(()=>q(new Error("Timeout")),3e3)});try{await Promise.race([h,g]),t.value=e.src,a.value="loaded",se(e.channelKey,e.src).catch(y=>{console.warn("异步缓存图片失败:",y)})}catch{await o()}}catch(r){console.error("图片加载失败:",r),a.value="error",n.value="图片加载失败"}},o=async()=>{try{a.value="caching";const r=await se(e.channelKey,e.src);if(r)t.value=r,a.value="loaded";else throw new Error("缓存系统加载失败")}catch(r){console.error("缓存系统加载图片失败:",r),a.value="error",n.value=(r==null?void 0:r.message)||"缓存加载失败"}};return Ne(()=>{s()}),()=>{switch(a.value){case"loading":return d("div",{class:"message-image-loading"},"加载中...");case"caching":return d("div",{class:"message-image-loading"},"[图片加载缓存中...]");case"loaded":return d("img",{src:t.value,alt:e.alt,class:"message-image",loading:"lazy",ref:l,draggable:false,style:{"max-width":"400px","max-height":"200px",width:"auto",height:"auto","object-fit":"contain"}});case"error":return d("div",{class:"message-image-error"},["图片加载失败",d("br"),d("small",e.filename||e.alt||"未知图片"),d("br"),d("small",{style:"color: #ff9800;"},n.value)]);default:return d("div",{class:"message-image-error"},"未知状态")}}}}),be=Q({props:{data:{type:String,required:true},channelKey:{type:String,required:true}},setup(e){const t=(()=>{try{const l=JSON.parse(e.data);if(l.meta&&l.meta.detail_1){const s=l.meta.detail_1;return{type:"share_card",title:s.title||l.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:l}}catch(l){return console.error("解析JSON数据失败:",l),{type:"error",error:"无法解析的JSON数据"}}})(),n=()=>{t.type==="share_card"&&t.url&&window.open(t.url,"_blank","noopener,noreferrer")};return()=>t.type==="share_card"&&t.preview?d("img",{src:t.preview,alt:t.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:t.url?"pointer":"default"},title:t.url?`点击打开: ${t.title||"链接"}`:t.title,onError:l=>{const o=l.target.parentElement;o&&(o.style.display="none")}}):t.type==="error"?d("div",{class:"message-json-error"},[d("span",{class:"json-error-text"},t.error),d("details",{class:"json-raw-data"},[d("summary","查看原始数据"),d("pre",{class:"json-raw-content"},e.data)])]):d("div",{class:"message-json-raw"},[d("div",{class:"json-label"},"[JSON数据]"),d("details",{class:"json-raw-data"},[d("summary","查看详情"),d("pre",{class:"json-raw-content"},JSON.stringify(t.data,null,2))])])}}),ct=Q({props:{element:{type:Object,required:true},channelKey:{type:String,required:true}},setup(e){const a=f(false),t=()=>{a.value=!a.value},n=()=>{if(!e.element.children||e.element.children.length===0)return{previews:[],messageCount:0};const s=e.element.children.filter(i=>i.type==="message"),o=s.length;return{previews:s.slice(0,3).map(i=>{var y,q;const h=((y=i.attrs)==null?void 0:y.nickname)||"用户";let g="";if(i.children&&i.children.length>0){const B=i.children[0];B.type==="text"?(g=(((q=B.attrs)==null?void 0:q.content)||"").substring(0,20),g.length>15&&(g+="...")):B.type==="img"?g="[图片]":B.type==="video"?g="[视频]":g=`[${B.type}]`}return`${h}:${g}`}),messageCount:o}},l=(s,o)=>{var h,g,y;const r=((h=s.attrs)==null?void 0:h.nickname)||"用户",i=((g=s.attrs)==null?void 0:g.userId)||"unknown";return d("div",{key:o,class:"forwarded-message-item"},[d("div",{class:"forwarded-message-header"},[d("span",{class:"forwarded-message-nickname"},r),d("span",{class:"forwarded-message-userid"},`(${i})`)]),d("div",{class:"forwarded-message-content"},((y=s.children)==null?void 0:y.map((q,B)=>d(ve,{key:B,element:q,channelKey:e.channelKey})))||[])])};return()=>{var r;const{previews:s,messageCount:o}=n();return d("div",{class:"forward-message-container"},[d("div",{class:"forward-message-preview",onClick:t},[d("div",{class:"forward-message-title"},"聊天记录"),...s.map((i,h)=>d("div",{key:h,class:"forward-message-preview-item"},i)),d("div",{class:"forward-message-footer"},[d("span",{class:"forward-message-count"},`查看${o}条转发消息`),d("span",{class:"forward-message-toggle"},a.value?"▲":"▼")])]),a.value&&d("div",{class:"forward-message-expanded"},((r=e.element.children)==null?void 0:r.filter(i=>i.type==="message").map((i,h)=>l(i,h)))||[])])}}}),ve=Q({props:{element:{type:Object,required:true},channelKey:{type:String,required:true}},setup(e){const a=t=>{var n,l,s,o,r,i;switch(t.type){case"text":return d("span",{class:"message-text-content"},t.attrs.content||"");case"forward":return d("span",{class:"message-text-content"},`[转发消息 ${t.attrs.id}]`||"[转发消息]");case"img":case"image":const h=t.attrs.src||t.attrs.url||t.attrs.file;return h&&Y(h)?console.log("检测到本地文件路径:",h):h!=null&&h.startsWith("http")?console.log("检测到网络图片:",h):console.log("检测到其他类型图片URL:",h),d("div",{class:"message-image-container"},[d(ce,{src:h,alt:t.attrs.summary||"图片",filename:t.attrs.filename||t.attrs.summary||"",channelKey:e.channelKey})]);case"mface":const g=t.attrs.src||t.attrs.url||t.attrs.file;return g&&Y(g)&&console.log("检测到本地表情文件路径:",g),d("div",{class:"message-image-container"},[d(ce,{src:g,alt:t.attrs.summary||"表情",filename:t.attrs.emojiId||t.attrs.summary||"",channelKey:e.channelKey})]);case"face":if((l=(n=t.children[0])==null?void 0:n.attrs)!=null&&l.src){const y=((o=(s=t.children[0])==null?void 0:s.attrs)==null?void 0:o.src)||((i=(r=t.children[0])==null?void 0:r.attrs)==null?void 0:i.url);return d("div",{class:"message-image-container"},[d(ce,{src:y,alt:t.attrs.name||t.attrs.id||"[表情]",filename:t.attrs.name||t.attrs.id||"[表情]",channelKey:e.channelKey})])}else return d("span",{class:"message-text-content"},`[${t.attrs.name||t.attrs.id}]`||"[表情]");case"at":return d("span",{class:"message-at",title:t.attrs.name},`${t.attrs.name||t.attrs.id}`);case"json":return d("div",{class:"message-image-container"},[d(be,{data:t.attrs.data||"",channelKey:e.channelKey})]);case"p":if(t.children&&t.children.length>0){const y=t.children.map((q,B)=>d(ve,{key:B,element:q,channelKey:e.channelKey}));return d("div",{class:"message-paragraph"},y)}else return d("div",{class:"message-paragraph"},"");case"figure":return d(ct,{element:t,channelKey:e.channelKey});default:return d("span",{class:"message-unknown",title:`未知消息类型: ${t.type}`},t.attrs.content||`[${t.type}]`)}};return()=>a(e.element)}}),u=f({bots:{},channels:{},messages:{}}),D=f({}),ie=f({maxMessagesPerChannel:1e3,keepMessagesOnClear:50,loggerinfo:false,blockedPlatforms:[],chatContainerHeight:80}),b=f({});let _e=0;function Me(e){return e.size||0}function ge(e){_e+=e,ie.value.loggerinfo&&console.log(`内存使用量变化: ${e>0?"+":""}${(e/1024/1024).toFixed(2)}MB, 总计: ${(_e/1024/1024).toFixed(2)}MB`)}function ze(e=10){const a=Object.entries(b.value);if(a.length<=e)return;const t=a.slice(0,a.length-e);let n=0;t.forEach(([l,s])=>{URL.revokeObjectURL(s),delete b.value[l],n+=500*1024,ie.value.loggerinfo&&console.log("清理旧blob URL:",l)}),ge(-n)}function $e(){Object.keys(b.value).length>Ae&&ze(Math.floor(Ae*.7)),_e>Ha&&ze(Math.floor(Ae*.5))}let S=null;const m=f(""),C=f(""),ue=f(""),U=f([]),J=f(false),Se=f(),L=f(false),W=f("bots"),M=f(null),ee=f(null),Be=f(false),V=f({show:false,text:""}),$=f(),de=f(),Le=f(false),ye=f(false),te=f(false),z=f(""),j=f({x:0,y:0}),T=f({x:0,y:0}),Xe=f({x:0,y:0}),Te=f({x:0,y:0}),qe=f(0),K=f(null),ae=f(false),w=f(null),I=f({show:false,x:0,y:0,type:null,targetId:"",isSecondClick:false}),X=f(new Set),H=f(new Set),it=O(()=>Object.values(u.value.bots).sort((a,t)=>{const n=X.value.has(a.selfId),l=X.value.has(t.selfId);return n&&!l?-1:!n&&l?1:0})),ut=O(()=>!m.value||!u.value.channels[m.value]?[]:Object.values(u.value.channels[m.value]).sort((a,t)=>{const n=H.value.has(`${m.value}:${a.id}`),l=H.value.has(`${m.value}:${t.id}`);return n&&!l?-1:!n&&l?1:0})),He=O(()=>{if(!m.value||!C.value)return[];const e=`${m.value}:${C.value}`,a=u.value.messages[e]||[];return a.filter(t=>t.quote),a}),dt=O(()=>{var a;if(!m.value||!C.value)return"";const e=u.value.channels[m.value];return((a=e==null?void 0:e[C.value])==null?void 0:a.name)||""}),pe=O(()=>!m.value||!C.value?"":`${m.value}:${C.value}`),Fe=O(()=>m.value&&C.value&&(ue.value.trim()||U.value.length>0)&&!te.value),ft=O(()=>m.value&&C.value&&!te.value),ht=O(()=>{if(!L.value)return"";switch(W.value){case"channels":return"show-channels";case"messages":return"show-messages";default:return""}}),mt=O(()=>L.value?"输入消息...(屏幕左滑返回)":"输入消息..."),vt=O(()=>{if(L.value){const e=ie.value.chatContainerHeight||80,a=100-e-19;return{height:`${e}vh`,marginTop:`${a}vh`}}return{}});function gt(e){m.value=e,C.value="",L.value&&(W.value="channels")}function yt(e,a){if(e.preventDefault(),e.stopPropagation(),I.value.show&&I.value.type==="bot"&&I.value.targetId===a){N();return}Ye(e,"bot",a)}function pt(e,a){if(e.preventDefault(),e.stopPropagation(),I.value.show&&I.value.type==="channel"&&I.value.targetId===a){N();return}Ye(e,"channel",a)}function Ye(e,a,t){let s=e.clientX,o=e.clientY;s+180>window.innerWidth&&(s=window.innerWidth-180-10),o+80>window.innerHeight&&(o=window.innerHeight-80-10),I.value={show:true,x:s,y:o,type:a,targetId:t,isSecondClick:false},document.addEventListener("click",N,{once:true}),document.addEventListener("keydown",We)}function N(){I.value.show=false,document.removeEventListener("click",N),document.removeEventListener("keydown",We)}function We(e){e.key==="Escape"&&I.value.show&&N()}async function wt(e){X.value.has(e)?X.value.delete(e):X.value.add(e),await _("set-pinned-bots",{pinnedBots:Array.from(X.value)}),N()}async function It(e){const a=`${m.value}:${e}`;H.value.has(a)?H.value.delete(a):H.value.add(a),await _("set-pinned-channels",{pinnedChannels:Array.from(H.value)}),N()}async function xt(e){try{const a=await _("delete-bot-data",{selfId:e});if(a.success){const t=Object.keys(u.value.messages).filter(n=>n.startsWith(`${e}:`));for(const n of t)delete u.value.messages[n],delete D.value[n],await ke(n);delete u.value.bots[e],delete u.value.channels[e],m.value===e&&(m.value="",C.value=""),k(a.message||"已删除该机器人的所有数据","success")}else throw new Error(a.error||"删除失败")}catch(a){console.error("删除机器人数据失败:",a),k("删除失败: "+((a==null?void 0:a.message)||String(a)),"error")}N()}async function Ct(e){try{const a=await _("delete-channel-data",{selfId:m.value,channelId:e});if(a.success){const t=`${m.value}:${e}`;delete u.value.messages[t],delete D.value[t],u.value.channels[m.value]&&delete u.value.channels[m.value][e],await ke(t),C.value===e&&(C.value=""),k(a.message||"已删除该频道的所有数据","success")}else throw new Error(a.error||"删除失败")}catch(a){console.error("删除频道数据失败:",a),k("删除失败: "+((a==null?void 0:a.message)||String(a)),"error")}N()}async function kt(e){C.value=e,ye.value=false,L.value&&(W.value="messages"),m.value&&await Xt(m.value,e),re(()=>{ne(),!L.value&&de.value&&de.value.focus()})}async function Je(){if(!Fe.value)return;const e=ue.value.trim();if(!e&&U.value.length===0)return;te.value=true;const a=[...U.value];try{const t=await _("send-message",{selfId:m.value,channelId:C.value,content:e,images:a.map(n=>({tempId:n.tempId,filename:n.filename}))});if(t.success){if(ue.value="",a.forEach(n=>{URL.revokeObjectURL(n.preview)}),U.value=[],J.value=false,t.tempImageIds&&t.tempImageIds.length>0)try{await _("cleanup-temp-images",{tempImageIds:t.tempImageIds}),console.log("临时图片清理完成:",t.tempImageIds)}catch(n){console.warn("清理临时图片失败:",n)}}else console.error("消息发送失败:",t.error),k("发送失败: "+t.error,"error")}catch(t){console.error("发送消息时出错:",t),k("发送失败: "+((t==null?void 0:t.message)||String(t)),"error")}finally{te.value=false}}function bt(){J.value=!J.value}function _t(){Se.value&&Se.value.click(),J.value=false}async function $t(e){const a=e.target,t=a.files;if(!(!t||t.length===0)){for(const n of Array.from(t))await Ve(n);a.value=""}}async function St(e){var t;const a=(t=e.clipboardData)==null?void 0:t.items;if(a){for(const n of Array.from(a))if(n.type.startsWith("image/")){e.preventDefault();const l=n.getAsFile();l&&await Ve(l)}}}async function Ve(e){try{if(e.size>10*1024*1024){k("图片文件过大,请选择小于10MB的图片","error");return}if(!e.type.startsWith("image/")){k("请选择图片文件","error");return}const a=await Lt(e),t=URL.createObjectURL(e),n=await _("upload-image",{file:a,filename:e.name,mimeType:e.type});n.success?U.value.push({tempId:n.tempId,filename:e.name,preview:t,size:e.size}):(URL.revokeObjectURL(t),k("图片上传失败: "+n.error,"error"))}catch(a){console.error("上传图片失败:",a),k("图片上传失败: "+((a==null?void 0:a.message)||String(a)),"error")}}async function Bt(e){try{const a=U.value.findIndex(t=>t.tempId===e);if(a!==-1){const t=U.value[a];URL.revokeObjectURL(t.preview),U.value.splice(a,1)}await _("delete-temp-image",{tempId:e})}catch(a){console.error("删除图片失败:",a)}}function Lt(e){return new Promise((a,t)=>{const n=new FileReader;n.onload=()=>a(n.result),n.onerror=t,n.readAsDataURL(e)})}function Ge(e){const a=e.target;a.closest(".input-actions")||(J.value=false),a.closest(".context-menu")||N()}function Qe(e){return new Date(e).toLocaleTimeString("zh-CN",{hour:"2-digit",minute:"2-digit"})}function Tt(e){if(typeof e=="number")switch(e){case 0:return"文本";case 1:return"私聊";default:return"未知"}return String(e)}function ne(){$.value&&($.value.scrollTop=$.value.scrollHeight,Le.value=false,ye.value=false)}function we(){if($.value){const{scrollTop:e,scrollHeight:a,clientHeight:t}=$.value,l=a-(e+t)<=50,s=!l;Le.value=s,l?ye.value=false:ye.value=true}}function De(){if(!$.value)return true;const{scrollTop:e,scrollHeight:a,clientHeight:t}=$.value;return a-(e+t)<=200}function Ue(e){var n;if(!m.value)return 0;const a=`${m.value}:${e}`,t=D.value[a];return t!==void 0?t:((n=u.value.messages[a])==null?void 0:n.length)||0}function Ze(e,a){e.preventDefault(),e.stopPropagation();const t="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:t,y:n},T.value={x:t,y:n},ae.value=false;const s=e.target.getBoundingClientRect();Xe.value={x:s.left,y:s.top},Te.value={x:t-s.left,y:n-s.top},K.value=window.setTimeout(()=>{if(qe.value>0){ae.value=true,z.value=a;const o=e.target,r=o.cloneNode(true);r.classList.add("dragging-clone"),r.style.position="fixed",r.style.zIndex="1000",r.style.pointerEvents="none";const i=o.getBoundingClientRect();r.style.left=`${i.left}px`,r.style.top=`${i.top}px`,r.style.width=`${i.width}px`,r.style.height=`${i.height}px`,document.body.appendChild(r),w.value=r,document.body.style.userSelect="none",document.body.style.cursor="grabbing",document.body.classList.add("dragging-bubble-global"),Ut(j.value.x,j.value.y)}},60),document.addEventListener("mousemove",Ie),document.addEventListener("mouseup",xe),document.addEventListener("touchmove",Ie),document.addEventListener("touchend",xe)}function Ie(e){if(!ae.value||!w.value)return;e.preventDefault();const a="touches"in e?e.touches[0].clientX:e.clientX,t="touches"in e?e.touches[0].clientY:e.clientY;T.value={x:a,y:t};const n=T.value.x-j.value.x,l=T.value.y-j.value.y,s=Math.sqrt(n*n+l*l),o=Math.max(.3,1-s/(A*2)),r=Math.max(.8,1-s/(A*3)),i=s>A,h=T.value.x-Te.value.x,g=T.value.y-Te.value.y;w.value.style.left=`${h}px`,w.value.style.top=`${g}px`,w.value.style.transform=`scale(${r})`,w.value.style.opacity=`${o}`,w.value.style.backgroundColor=i?"#f44336":"#2196f3",w.value.style.boxShadow=i?"0 4px 12px rgba(244, 67, 54, 0.4)":"0 4px 12px rgba(33, 150, 243, 0.4)",i?w.value.classList.add("will-delete"):w.value.classList.remove("will-delete")}function xe(e){if(K.value&&(clearTimeout(K.value),K.value=null),K.value&&(clearTimeout(K.value),K.value=null),!ae.value||!z.value){Ce();return}const a=z.value;if(Math.sqrt(Math.pow(T.value.x-j.value.x,2)+Math.pow(T.value.y-j.value.y,2))>A)Dt(a),Ce();else if(w.value){w.value.style.transition="all 0.3s cubic-bezier(0.68, -0.55, 0.265, 1.55)";const n=document.querySelector(`[data-channel-id="${a}"] .channel-message-count`);if(n){const l=n.getBoundingClientRect();w.value.style.left=`${l.left}px`,w.value.style.top=`${l.top}px`,w.value.style.transform="scale(1)",w.value.style.opacity="1",w.value.style.backgroundColor="#2196f3",w.value.style.boxShadow="0 4px 12px rgba(33, 150, 243, 0.4)"}setTimeout(()=>{Ce()},300)}else Ce()}function Ce(){K.value&&(clearTimeout(K.value),K.value=null),z.value="",j.value={x:0,y:0},T.value={x:0,y:0},Xe.value={x:0,y:0},qe.value=0,ae.value=false,document.removeEventListener("mousemove",Ie),document.removeEventListener("mouseup",xe),document.removeEventListener("touchmove",Ie),document.removeEventListener("touchend",xe),document.body.style.userSelect="",document.body.style.cursor="",document.body.classList.remove("dragging-bubble-global"),w.value&&w.value.parentNode&&(w.value.parentNode.removeChild(w.value),w.value=null),jt()}function qt(e){return z.value===e&&ae.value?{visibility:"hidden",pointerEvents:"none",transition:"none"}:{}}function Ee(e){if(z.value!==e)return 0;const a=T.value.x-j.value.x,t=T.value.y-j.value.y;return Math.sqrt(a*a+t*t)}async function Dt(e){if(m.value)try{const a=`${m.value}:${e}`,t=Ue(e),n=ie.value.keepMessagesOnClear;if(n>0&&t<=n){k("当前消息还很少诶~ 无需清理","success");return}const l=await _("clear-channel-history",{selfId:m.value,channelId:e});if(l.success)if(l.clearedCount&&l.clearedCount>0){if(u.value.messages[a]){const o=[...u.value.messages[a]].sort((r,i)=>r.timestamp-i.timestamp);u.value.messages[a]=o.slice(-l.keptCount)}D.value[a]=l.keptCount,await ke(a),k(`历史记录已清理,清理了 ${l.clearedCount} 条消息,保留最新 ${l.keptCount} 条`,"success")}else n===0?(u.value.messages[a]&&(u.value.messages[a]=[]),D.value[a]=0,await ke(a),k("历史记录已清理,所有消息已删除","success")):k("当前消息还很少诶~ 无需清理","success");else console.error("清理历史记录失败:",l.error),k("清理失败: "+l.error,"error")}catch(a){console.error("清理历史记录时出错:",a),k("清理失败: "+((a==null?void 0:a.message)||String(a)),"error")}}function k(e,a="success"){const t=document.createElement("div");t.className=`notification ${a}`,t.textContent=e;let n="#4caf50";switch(a){case"info":n="#2196f3";break;case"warn":n="#ff9800";break;case"error":n="#f44336";break;case"success":n="#4caf50";break}t.style.cssText=`
|
|
1
|
+
import{defineComponent as E,ref as f,onMounted as Pe,h as d,computed as O,watch as dt,nextTick as oe,onUnmounted as oa,createElementBlock as p,openBlock as v,normalizeStyle as Me,normalizeClass as W,createElementVNode as i,createCommentVNode as Q,Fragment as z,renderList as ce,createBlock as ve,unref as ge,toDisplayString as C,withDirectives as ft,createTextVNode as He,vShow as ca,withModifiers as ht,withKeys as ia,vModelText as ua,resolveComponent as da}from"vue";import{receive as Fe,send as S,icons as fa}from"@koishijs/client";const ha={class:"bot-list"},ma={class:"bot-items"},va=["onClick","onContextmenu"],ga={class:"bot-avatar"},ya={key:1,class:"avatar-placeholder"},pa={class:"bot-info"},wa={class:"bot-name"},Ia={class:"bot-platform"},xa={class:"channel-list"},Ca={key:0,class:"empty-state"},ka={key:1,class:"channel-items"},ba=["data-channel-id","onClick","onContextmenu"],_a={class:"channel-info"},$a={class:"channel-name"},Sa={class:"channel-type"},Ba=["onMousedown","onTouchstart","title"],Da={class:"message-area"},La={class:"panel-header"},Ta={key:0,class:"empty-state"},qa={key:1,class:"message-content"},Ka={class:"message-avatar"},Ua={key:1,class:"avatar-placeholder"},ja={class:"message-content-wrapper"},Aa={class:"message-header"},Na={class:"message-username"},Ra={class:"message-time"},Oa={key:0,class:"message-quote"},za={class:"quote-header"},Pa={class:"quote-avatar"},Ma={key:1,class:"avatar-placeholder"},Ha={class:"quote-username"},Fa={class:"quote-time"},Xa={class:"quote-content"},Ya={class:"message-text"},Wa={class:"message-input"},Ja={key:0,class:"image-preview-container"},Va=["src","alt"],Ga=["onClick"],Za={class:"input-row"},Qa={class:"input-actions"},Ea=["placeholder","disabled"],en=["disabled"],tn=100*1024*1024,Xe=50,mt="ChatImageCache",an=2,B="images",ie=50*1024*1024,vt=100,ue=500,Ye=2*1024*1024,gt=.8,nn=60*1e3,P=80,sn=E({__name:"index",setup(ee){function J(e){try{return new URL(e).protocol==="file:"}catch{return false}}const te=E({props:{src:{type:String,required:true},alt:{type:String,default:"头像"},channelKey:{type:String,required:true}},setup(e){const a=f("loading"),t=f(e.src),n=f(""),r=async()=>{try{a.value="loading";const o=await Oe(e.channelKey,e.src);if(o){t.value=o,a.value="loaded";return}const l=new Image;l.crossOrigin="anonymous",l.referrerPolicy="no-referrer",l.draggable=false;const c=new Promise((y,g)=>{l.onload=()=>y(),l.onerror=()=>g(new Error("Direct load failed")),l.src=e.src}),h=new Promise((y,g)=>{setTimeout(()=>g(new Error("Timeout")),3e3)});try{await Promise.race([c,h]),t.value=e.src,a.value="loaded",le(e.channelKey,e.src).catch(y=>{console.warn("异步缓存头像失败:",y)})}catch{await s()}}catch(o){console.error("头像加载失败:",o),a.value="error",n.value="头像加载失败"}},s=async()=>{try{a.value="caching";const o=await le(e.channelKey,e.src);if(o)t.value=o,a.value="loaded";else throw new Error("缓存系统加载失败")}catch(o){console.error("缓存系统加载头像失败:",o),a.value="error",n.value=(o==null?void 0:o.message)||"缓存加载失败"}};return Pe(()=>{r()}),()=>{switch(a.value){case"loading":case"caching":return d("div",{class:"avatar-placeholder"},e.alt.charAt(0).toUpperCase());case"loaded":return d("img",{src:t.value,alt:e.alt,draggable:false,style:{width:"100%",height:"100%","object-fit":"cover"}});case"error":return d("div",{class:"avatar-placeholder"},e.alt.charAt(0).toUpperCase());default:return d("div",{class:"avatar-placeholder"},e.alt.charAt(0).toUpperCase())}}}}),de=E({props:{src:{type:String,required:true},alt:{type:String,default:"图片"},filename:{type:String,default:""},channelKey:{type:String,required:true}},setup(e){const a=f("loading"),t=f(e.src),n=f(""),r=f(null),s=async()=>{try{a.value="loading";const l=await Oe(e.channelKey,e.src);if(l){t.value=l,a.value="loaded";return}if(J(e.src)){console.log("ImageComponent: 检测到本地文件,使用代理请求:",e.src),await o();return}const c=new Image;c.crossOrigin="anonymous",c.referrerPolicy="no-referrer",c.draggable=false;const h=new Promise((g,L)=>{c.onload=()=>g(),c.onerror=()=>L(new Error("Direct load failed")),c.src=e.src}),y=new Promise((g,L)=>{setTimeout(()=>L(new Error("Timeout")),3e3)});try{await Promise.race([h,y]),t.value=e.src,a.value="loaded",le(e.channelKey,e.src).catch(g=>{console.warn("异步缓存图片失败:",g)})}catch{await o()}}catch(l){console.error("图片加载失败:",l),a.value="error",n.value="图片加载失败"}},o=async()=>{try{a.value="caching";const l=await le(e.channelKey,e.src);if(l)t.value=l,a.value="loaded";else throw new Error("缓存系统加载失败")}catch(l){console.error("缓存系统加载图片失败:",l),a.value="error",n.value=(l==null?void 0:l.message)||"缓存加载失败"}};return Pe(()=>{s()}),()=>{switch(a.value){case"loading":return d("div",{class:"message-image-loading"},"加载中...");case"caching":return d("div",{class:"message-image-loading"},"[图片加载缓存中...]");case"loaded":return d("img",{src:t.value,alt:e.alt,class:"message-image",loading:"lazy",ref:r,draggable:false,style:{"max-width":"400px","max-height":"200px",width:"auto",height:"auto","object-fit":"contain"}});case"error":return d("div",{class:"message-image-error"},["图片加载失败",d("br"),d("small",e.filename||e.alt||"未知图片"),d("br"),d("small",{style:"color: #ff9800;"},n.value)]);default:return d("div",{class:"message-image-error"},"未知状态")}}}}),Se=E({props:{data:{type:String,required:true},channelKey:{type:String,required:true}},setup(e){const t=(()=>{try{const r=JSON.parse(e.data);if(r.meta&&r.meta.detail_1){const s=r.meta.detail_1;return{type:"share_card",title:s.title||r.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:r}}catch(r){return console.error("解析JSON数据失败:",r),{type:"error",error:"无法解析的JSON数据"}}})(),n=()=>{t.type==="share_card"&&t.url&&window.open(t.url,"_blank","noopener,noreferrer")};return()=>t.type==="share_card"&&t.preview?d("img",{src:t.preview,alt:t.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:t.url?"pointer":"default"},title:t.url?`点击打开: ${t.title||"链接"}`:t.title,onError:r=>{const o=r.target.parentElement;o&&(o.style.display="none")}}):t.type==="error"?d("div",{class:"message-json-error"},[d("span",{class:"json-error-text"},t.error),d("details",{class:"json-raw-data"},[d("summary","查看原始数据"),d("pre",{class:"json-raw-content"},e.data)])]):d("div",{class:"message-json-raw"},[d("div",{class:"json-label"},"[JSON数据]"),d("details",{class:"json-raw-data"},[d("summary","查看详情"),d("pre",{class:"json-raw-content"},JSON.stringify(t.data,null,2))])])}}),pt=E({props:{element:{type:Object,required:true},channelKey:{type:String,required:true}},setup(e){const a=f(false),t=()=>{a.value=!a.value},n=()=>{if(!e.element.children||e.element.children.length===0)return{previews:[],messageCount:0};const s=e.element.children.filter(c=>c.type==="message"),o=s.length;return{previews:s.slice(0,3).map(c=>{var g,L;const h=((g=c.attrs)==null?void 0:g.nickname)||"用户";let y="";if(c.children&&c.children.length>0){const $=c.children[0];$.type==="text"?(y=(((L=$.attrs)==null?void 0:L.content)||"").substring(0,20),y.length>15&&(y+="...")):$.type==="img"?y="[图片]":$.type==="video"?y="[视频]":y=`[${$.type}]`}return`${h}:${y}`}),messageCount:o}},r=(s,o)=>{var h,y,g;const l=((h=s.attrs)==null?void 0:h.nickname)||"用户",c=((y=s.attrs)==null?void 0:y.userId)||"unknown";return d("div",{key:o,class:"forwarded-message-item"},[d("div",{class:"forwarded-message-header"},[d("span",{class:"forwarded-message-nickname"},l),d("span",{class:"forwarded-message-userid"},`(${c})`)]),d("div",{class:"forwarded-message-content"},((g=s.children)==null?void 0:g.map((L,$)=>d(ye,{key:$,element:L,channelKey:e.channelKey})))||[])])};return()=>{var l;const{previews:s,messageCount:o}=n();return d("div",{class:"forward-message-container"},[d("div",{class:"forward-message-preview",onClick:t},[d("div",{class:"forward-message-title"},"聊天记录"),...s.map((c,h)=>d("div",{key:h,class:"forward-message-preview-item"},c)),d("div",{class:"forward-message-footer"},[d("span",{class:"forward-message-count"},`查看${o}条转发消息`),d("span",{class:"forward-message-toggle"},a.value?"▲":"▼")])]),a.value&&d("div",{class:"forward-message-expanded"},((l=e.element.children)==null?void 0:l.filter(c=>c.type==="message").map((c,h)=>r(c,h)))||[])])}}}),ye=E({props:{element:{type:Object,required:true},channelKey:{type:String,required:true}},setup(e){const a=t=>{var n,r,s,o,l,c;switch(t.type){case"text":return d("span",{class:"message-text-content"},t.attrs.content||"");case"forward":return d("span",{class:"message-text-content"},`[转发消息 ${t.attrs.id}]`||"[转发消息]");case"img":case"image":const h=t.attrs.src||t.attrs.url||t.attrs.file;return h&&J(h)?console.log("检测到本地文件路径:",h):h!=null&&h.startsWith("http")?console.log("检测到网络图片:",h):console.log("检测到其他类型图片URL:",h),d("div",{class:"message-image-container"},[d(de,{src:h,alt:t.attrs.summary||"图片",filename:t.attrs.filename||t.attrs.summary||"",channelKey:e.channelKey})]);case"mface":const y=t.attrs.src||t.attrs.url||t.attrs.file;return y&&J(y)&&console.log("检测到本地表情文件路径:",y),d("div",{class:"message-image-container"},[d(de,{src:y,alt:t.attrs.summary||"表情",filename:t.attrs.emojiId||t.attrs.summary||"",channelKey:e.channelKey})]);case"face":if((r=(n=t.children[0])==null?void 0:n.attrs)!=null&&r.src){const g=((o=(s=t.children[0])==null?void 0:s.attrs)==null?void 0:o.src)||((c=(l=t.children[0])==null?void 0:l.attrs)==null?void 0:c.url);return d("div",{class:"message-image-container"},[d(de,{src:g,alt:t.attrs.name||t.attrs.id||"[表情]",filename:t.attrs.name||t.attrs.id||"[表情]",channelKey:e.channelKey})])}else return d("span",{class:"message-text-content"},`[${t.attrs.name||t.attrs.id}]`||"[表情]");case"at":return d("span",{class:"message-at",title:t.attrs.name},`${t.attrs.name||t.attrs.id}`);case"json":return d("div",{class:"message-image-container"},[d(Se,{data:t.attrs.data||"",channelKey:e.channelKey})]);case"p":if(t.children&&t.children.length>0){const g=t.children.map((L,$)=>d(ye,{key:$,element:L,channelKey:e.channelKey}));return d("div",{class:"message-paragraph"},g)}else return d("div",{class:"message-paragraph"},"");case"figure":return d(pt,{element:t,channelKey:e.channelKey});default:return d("span",{class:"message-unknown",title:`未知消息类型: ${t.type}`},t.attrs.content||`[${t.type}]`)}};return()=>a(e.element)}}),u=f({bots:{},channels:{},messages:{}}),K=f({}),fe=f({maxMessagesPerChannel:1e3,keepMessagesOnClear:50,loggerinfo:false,blockedPlatforms:[],chatContainerHeight:80}),b=f({});let Be=0,w=null,U=0,M=0,We=0;const m=f(""),k=f(""),he=f("");function Je(e){return e.size||0}function pe(e){Be+=e,fe.value.loggerinfo&&console.log(`内存使用量变化: ${e>0?"+":""}${(e/1024/1024).toFixed(2)}MB, 总计: ${(Be/1024/1024).toFixed(2)}MB`)}function Ve(e=10){const a=Object.entries(b.value);if(a.length<=e)return;const t=a.slice(0,a.length-e);let n=0;t.forEach(([r,s])=>{URL.revokeObjectURL(s),delete b.value[r],n+=500*1024,fe.value.loggerinfo&&console.log("清理旧blob URL:",r)}),pe(-n)}function De(){Object.keys(b.value).length>Xe&&Ve(Math.floor(Xe*.7)),Be>tn&&Ve(Math.floor(Xe*.5))}const j=f([]),G=f(false),Le=f(),T=f(false),V=f("bots"),H=f(null),ae=f(null),Te=f(false),Z=f({show:false,text:""}),D=f(),me=f(),qe=f(false),we=f(false),ne=f(false),F=f(""),A=f({x:0,y:0}),q=f({x:0,y:0}),Ge=f({x:0,y:0}),Ke=f({x:0,y:0}),Ue=f(0),N=f(null),se=f(false),I=f(null),x=f({show:false,x:0,y:0,type:null,targetId:"",isSecondClick:false}),X=f(new Set),Y=f(new Set),wt=O(()=>Object.values(u.value.bots).sort((a,t)=>{const n=X.value.has(a.selfId),r=X.value.has(t.selfId);return n&&!r?-1:!n&&r?1:0})),It=O(()=>!m.value||!u.value.channels[m.value]?[]:Object.values(u.value.channels[m.value]).sort((a,t)=>{const n=Y.value.has(`${m.value}:${a.id}`),r=Y.value.has(`${m.value}:${t.id}`);return n&&!r?-1:!n&&r?1:0})),Ze=O(()=>{if(!m.value||!k.value)return[];const e=`${m.value}:${k.value}`,a=u.value.messages[e]||[];return a.filter(t=>t.quote),a}),xt=O(()=>{var a;if(!m.value||!k.value)return"";const e=u.value.channels[m.value];return((a=e==null?void 0:e[k.value])==null?void 0:a.name)||""}),Ie=O(()=>!m.value||!k.value?"":`${m.value}:${k.value}`),Qe=O(()=>m.value&&k.value&&(he.value.trim()||j.value.length>0)&&!ne.value),Ct=O(()=>m.value&&k.value&&!ne.value),kt=O(()=>{if(!T.value)return"";switch(V.value){case"channels":return"show-channels";case"messages":return"show-messages";default:return""}}),bt=O(()=>T.value?"输入消息...(屏幕左滑返回)":"输入消息..."),_t=O(()=>{if(T.value){const e=fe.value.chatContainerHeight||80,a=100-e-19;return{height:`${e}vh`,marginTop:`${a}vh`}}return{}});function $t(e){m.value=e,k.value="",T.value&&(V.value="channels")}function St(e,a){if(e.preventDefault(),e.stopPropagation(),x.value.show&&x.value.type==="bot"&&x.value.targetId===a){R();return}Ee(e,"bot",a)}function Bt(e,a){if(e.preventDefault(),e.stopPropagation(),x.value.show&&x.value.type==="channel"&&x.value.targetId===a){R();return}Ee(e,"channel",a)}function Ee(e,a,t){let s=e.clientX,o=e.clientY;s+180>window.innerWidth&&(s=window.innerWidth-180-10),o+80>window.innerHeight&&(o=window.innerHeight-80-10),x.value={show:true,x:s,y:o,type:a,targetId:t,isSecondClick:false},document.addEventListener("click",R,{once:true}),document.addEventListener("keydown",et)}function R(){x.value.show=false,document.removeEventListener("click",R),document.removeEventListener("keydown",et)}function et(e){e.key==="Escape"&&x.value.show&&R()}async function Dt(e){X.value.has(e)?X.value.delete(e):X.value.add(e),await S("set-pinned-bots",{pinnedBots:Array.from(X.value)}),R()}async function Lt(e){const a=`${m.value}:${e}`;Y.value.has(a)?Y.value.delete(a):Y.value.add(a),await S("set-pinned-channels",{pinnedChannels:Array.from(Y.value)}),R()}async function Tt(e){try{const a=await S("delete-bot-data",{selfId:e});if(a.success){const t=Object.keys(u.value.messages).filter(n=>n.startsWith(`${e}:`));for(const n of t)delete u.value.messages[n],delete K.value[n],await $e(n);delete u.value.bots[e],delete u.value.channels[e],m.value===e&&(m.value="",k.value=""),_(a.message||"已删除该机器人的所有数据","success")}else throw new Error(a.error||"删除失败")}catch(a){console.error("删除机器人数据失败:",a),_("删除失败: "+((a==null?void 0:a.message)||String(a)),"error")}R()}async function qt(e){try{const a=await S("delete-channel-data",{selfId:m.value,channelId:e});if(a.success){const t=`${m.value}:${e}`;delete u.value.messages[t],delete K.value[t],u.value.channels[m.value]&&delete u.value.channels[m.value][e],await $e(t),k.value===e&&(k.value=""),_(a.message||"已删除该频道的所有数据","success")}else throw new Error(a.error||"删除失败")}catch(a){console.error("删除频道数据失败:",a),_("删除失败: "+((a==null?void 0:a.message)||String(a)),"error")}R()}async function Kt(e){k.value=e,we.value=false,T.value&&(V.value="messages"),m.value&&await aa(m.value,e),oe(()=>{re(),!T.value&&me.value&&me.value.focus()})}async function tt(){if(!Qe.value)return;const e=he.value.trim();if(!e&&j.value.length===0)return;ne.value=true;const a=[...j.value];try{const t=await S("send-message",{selfId:m.value,channelId:k.value,content:e,images:a.map(n=>({tempId:n.tempId,filename:n.filename}))});if(t.success){if(he.value="",a.forEach(n=>{URL.revokeObjectURL(n.preview)}),j.value=[],G.value=false,t.tempImageIds&&t.tempImageIds.length>0)try{await S("cleanup-temp-images",{tempImageIds:t.tempImageIds}),console.log("临时图片清理完成:",t.tempImageIds)}catch(n){console.warn("清理临时图片失败:",n)}}else console.error("消息发送失败:",t.error),_("发送失败: "+t.error,"error")}catch(t){console.error("发送消息时出错:",t),_("发送失败: "+((t==null?void 0:t.message)||String(t)),"error")}finally{ne.value=false}}function Ut(){G.value=!G.value}function jt(){Le.value&&Le.value.click(),G.value=false}async function At(e){const a=e.target,t=a.files;if(!(!t||t.length===0)){for(const n of Array.from(t))await at(n);a.value=""}}async function Nt(e){var t;const a=(t=e.clipboardData)==null?void 0:t.items;if(a){for(const n of Array.from(a))if(n.type.startsWith("image/")){e.preventDefault();const r=n.getAsFile();r&&await at(r)}}}async function at(e){try{if(e.size>10*1024*1024){_("图片文件过大,请选择小于10MB的图片","error");return}if(!e.type.startsWith("image/")){_("请选择图片文件","error");return}const a=await Ot(e),t=URL.createObjectURL(e),n=await S("upload-image",{file:a,filename:e.name,mimeType:e.type});n.success?j.value.push({tempId:n.tempId,filename:e.name,preview:t,size:e.size}):(URL.revokeObjectURL(t),_("图片上传失败: "+n.error,"error"))}catch(a){console.error("上传图片失败:",a),_("图片上传失败: "+((a==null?void 0:a.message)||String(a)),"error")}}async function Rt(e){try{const a=j.value.findIndex(t=>t.tempId===e);if(a!==-1){const t=j.value[a];URL.revokeObjectURL(t.preview),j.value.splice(a,1)}await S("delete-temp-image",{tempId:e})}catch(a){console.error("删除图片失败:",a)}}function Ot(e){return new Promise((a,t)=>{const n=new FileReader;n.onload=()=>a(n.result),n.onerror=t,n.readAsDataURL(e)})}function nt(e){const a=e.target;a.closest(".input-actions")||(G.value=false),a.closest(".context-menu")||R()}function st(e){return new Date(e).toLocaleTimeString("zh-CN",{hour:"2-digit",minute:"2-digit"})}function zt(e){if(typeof e=="number")switch(e){case 0:return"文本";case 1:return"私聊";default:return"未知"}return String(e)}function re(){D.value&&(D.value.scrollTop=D.value.scrollHeight,qe.value=false,we.value=false)}function xe(){if(D.value){const{scrollTop:e,scrollHeight:a,clientHeight:t}=D.value,r=a-(e+t)<=50,s=!r;qe.value=s,r?we.value=false:we.value=true}}function je(){if(!D.value)return true;const{scrollTop:e,scrollHeight:a,clientHeight:t}=D.value;return a-(e+t)<=200}function Ae(e){var n;if(!m.value)return 0;const a=`${m.value}:${e}`,t=K.value[a];return t!==void 0?t:((n=u.value.messages[a])==null?void 0:n.length)||0}function rt(e,a){e.preventDefault(),e.stopPropagation();const t="touches"in e?e.touches[0].clientX:e.clientX,n="touches"in e?e.touches[0].clientY:e.clientY;Ue.value=Date.now(),A.value={x:t,y:n},q.value={x:t,y:n},se.value=false;const s=e.target.getBoundingClientRect();Ge.value={x:s.left,y:s.top},Ke.value={x:t-s.left,y:n-s.top},N.value=window.setTimeout(()=>{if(Ue.value>0){se.value=true,F.value=a;const o=e.target,l=o.cloneNode(true);l.classList.add("dragging-clone"),l.style.position="fixed",l.style.zIndex="1000",l.style.pointerEvents="none";const c=o.getBoundingClientRect();l.style.left=`${c.left}px`,l.style.top=`${c.top}px`,l.style.width=`${c.width}px`,l.style.height=`${c.height}px`,document.body.appendChild(l),I.value=l,document.body.style.userSelect="none",document.body.style.cursor="grabbing",document.body.classList.add("dragging-bubble-global"),Ht(A.value.x,A.value.y)}},60),document.addEventListener("mousemove",Ce),document.addEventListener("mouseup",ke),document.addEventListener("touchmove",Ce),document.addEventListener("touchend",ke)}function Ce(e){if(!se.value||!I.value)return;e.preventDefault();const a="touches"in e?e.touches[0].clientX:e.clientX,t="touches"in e?e.touches[0].clientY:e.clientY;q.value={x:a,y:t};const n=q.value.x-A.value.x,r=q.value.y-A.value.y,s=Math.sqrt(n*n+r*r),o=Math.max(.3,1-s/(P*2)),l=Math.max(.8,1-s/(P*3)),c=s>P,h=q.value.x-Ke.value.x,y=q.value.y-Ke.value.y;I.value.style.left=`${h}px`,I.value.style.top=`${y}px`,I.value.style.transform=`scale(${l})`,I.value.style.opacity=`${o}`,I.value.style.backgroundColor=c?"#f44336":"#2196f3",I.value.style.boxShadow=c?"0 4px 12px rgba(244, 67, 54, 0.4)":"0 4px 12px rgba(33, 150, 243, 0.4)",c?I.value.classList.add("will-delete"):I.value.classList.remove("will-delete")}function ke(e){if(N.value&&(clearTimeout(N.value),N.value=null),N.value&&(clearTimeout(N.value),N.value=null),!se.value||!F.value){be();return}const a=F.value;if(Math.sqrt(Math.pow(q.value.x-A.value.x,2)+Math.pow(q.value.y-A.value.y,2))>P)Mt(a),be();else if(I.value){I.value.style.transition="all 0.3s cubic-bezier(0.68, -0.55, 0.265, 1.55)";const n=document.querySelector(`[data-channel-id="${a}"] .channel-message-count`);if(n){const r=n.getBoundingClientRect();I.value.style.left=`${r.left}px`,I.value.style.top=`${r.top}px`,I.value.style.transform="scale(1)",I.value.style.opacity="1",I.value.style.backgroundColor="#2196f3",I.value.style.boxShadow="0 4px 12px rgba(33, 150, 243, 0.4)"}setTimeout(()=>{be()},300)}else be()}function be(){N.value&&(clearTimeout(N.value),N.value=null),F.value="",A.value={x:0,y:0},q.value={x:0,y:0},Ge.value={x:0,y:0},Ue.value=0,se.value=false,document.removeEventListener("mousemove",Ce),document.removeEventListener("mouseup",ke),document.removeEventListener("touchmove",Ce),document.removeEventListener("touchend",ke),document.body.style.userSelect="",document.body.style.cursor="",document.body.classList.remove("dragging-bubble-global"),I.value&&I.value.parentNode&&(I.value.parentNode.removeChild(I.value),I.value=null),Ft()}function Pt(e){return F.value===e&&se.value?{visibility:"hidden",pointerEvents:"none",transition:"none"}:{}}function lt(e){if(F.value!==e)return 0;const a=q.value.x-A.value.x,t=q.value.y-A.value.y;return Math.sqrt(a*a+t*t)}async function Mt(e){if(m.value)try{const a=`${m.value}:${e}`,t=Ae(e),n=fe.value.keepMessagesOnClear;if(n>0&&t<=n){_("当前消息还很少诶~ 无需清理","success");return}const r=await S("clear-channel-history",{selfId:m.value,channelId:e});if(r.success)if(r.clearedCount&&r.clearedCount>0){if(u.value.messages[a]){const o=[...u.value.messages[a]].sort((l,c)=>l.timestamp-c.timestamp);u.value.messages[a]=o.slice(-r.keptCount)}K.value[a]=r.keptCount,await $e(a),_(`历史记录已清理,清理了 ${r.clearedCount} 条消息,保留最新 ${r.keptCount} 条`,"success")}else n===0?(u.value.messages[a]&&(u.value.messages[a]=[]),K.value[a]=0,await $e(a),_("历史记录已清理,所有消息已删除","success")):_("当前消息还很少诶~ 无需清理","success");else console.error("清理历史记录失败:",r.error),_("清理失败: "+r.error,"error")}catch(a){console.error("清理历史记录时出错:",a),_("清理失败: "+((a==null?void 0:a.message)||String(a)),"error")}}function _(e,a="success"){const t=document.createElement("div");t.className=`notification ${a}`,t.textContent=e;let n="#4caf50";switch(a){case"info":n="#2196f3";break;case"warn":n="#ff9800";break;case"error":n="#f44336";break;case"success":n="#4caf50";break}t.style.cssText=`
|
|
2
2
|
position: fixed;
|
|
3
3
|
top: 20px;
|
|
4
4
|
right: 20px;
|
|
@@ -10,7 +10,7 @@ import{defineComponent as Q,ref as f,onMounted as Ne,h as d,computed as O,watch
|
|
|
10
10
|
animation: slideIn 0.3s ease-out;
|
|
11
11
|
background: ${n};
|
|
12
12
|
box-shadow: 0 4px 12px rgba(0,0,0,0.15);
|
|
13
|
-
`;const
|
|
13
|
+
`;const r=document.createElement("style");r.textContent=`
|
|
14
14
|
@keyframes slideIn {
|
|
15
15
|
from { transform: translateX(100%); opacity: 0; }
|
|
16
16
|
to { transform: translateX(0); opacity: 1; }
|
|
@@ -19,9 +19,9 @@ import{defineComponent as Q,ref as f,onMounted as Ne,h as d,computed as O,watch
|
|
|
19
19
|
from { transform: translateX(0); opacity: 1; }
|
|
20
20
|
to { transform: translateX(100%); opacity: 0; }
|
|
21
21
|
}
|
|
22
|
-
`,document.head.appendChild(
|
|
23
|
-
left: ${e-
|
|
24
|
-
top: ${a-
|
|
25
|
-
width: ${
|
|
26
|
-
height: ${
|
|
27
|
-
`,document.body.appendChild(t),window.dragThresholdCircle=t}function jt(){const e=window.dragThresholdCircle;e&&e.parentNode&&(e.parentNode.removeChild(e),window.dragThresholdCircle=null)}async function Kt(){return new Promise((e,a)=>{const t=indexedDB.open(Fa,Ya);t.onerror=()=>{console.error("IndexedDB初始化失败:",t.error),e(false)},t.onsuccess=()=>{S=t.result,e(true)},t.onupgradeneeded=n=>{const l=n.target.result;if(!l.objectStoreNames.contains(R)){const s=l.createObjectStore(R,{keyPath:"url"});s.createIndex("channelKey","channelKey",{unique:false}),s.createIndex("timestamp","timestamp",{unique:false})}}})}async function Nt(e){return S?new Promise((a,t)=>{const s=S.transaction([R],"readonly").objectStore(R).get(e);s.onsuccess=()=>{a(s.result||null)},s.onerror=()=>{console.error("从IndexedDB获取图片失败:",s.error),a(null)}}):null}async function et(e){return S?new Promise(a=>{const l=S.transaction([R],"readwrite").objectStore(R).put(e);l.onsuccess=()=>{a(true)},l.onerror=()=>{console.error("保存图片到IndexedDB失败:",l.error),a(false)}}):false}async function tt(e){return S?new Promise(a=>{const l=S.transaction([R],"readwrite").objectStore(R).delete(e);l.onsuccess=()=>{a(true)},l.onerror=()=>{console.error("从IndexedDB删除图片失败:",l.error),a(false)}}):false}async function at(e){return S?new Promise(a=>{const s=S.transaction([R],"readonly").objectStore(R).index("channelKey").getAll(e);s.onsuccess=()=>{a(s.result||[])},s.onerror=()=>{console.error("获取频道图片失败:",s.error),a([])}}):[]}async function je(e,a){const t=b.value[a];if(t)return t;const n=await Nt(a);if(!n)return null;$e();const l=URL.createObjectURL(n.blob);return b.value[a]=l,ge(Me(n.blob)),n.timestamp=Date.now(),await et(n),l}async function se(e,a){try{const t=await je(e,a);if(t)return t;const n=await _("fetch-image",{url:a});if(!n.success)return console.error("获取图片失败:",n.error),null;const l=n.base64,s=n.contentType||"image/jpeg",o=atob(l),r=new Array(o.length);for(let fe=0;fe<o.length;fe++)r[fe]=o.charCodeAt(fe);const i=new Uint8Array(r),h=new Blob([i],{type:s}),g=await at(e);if(g.length>=rt){const Jt=g.sort((le,Vt)=>le.timestamp-Vt.timestamp).slice(0,g.length-rt+1);for(const le of Jt)await tt(le.url),b.value[le.url]&&(URL.revokeObjectURL(b.value[le.url]),delete b.value[le.url])}const y={url:a,blob:h,timestamp:Date.now(),size:h.size,channelKey:e};if(!await et(y))return console.error("保存图片到IndexedDB失败"),null;$e();const B=URL.createObjectURL(h);return b.value[a]=B,ge(Me(h)),B}catch(t){return console.error("缓存图片失败:",t),null}}async function ke(e){try{const a=await at(e);let t=0;for(const n of a)await tt(n.url),b.value[n.url]&&(URL.revokeObjectURL(b.value[n.url]),delete b.value[n.url],t+=n.size||0);t>0&&ge(-t)}catch(a){console.error("清理频道图片缓存失败:",a)}}function Ot(e){var a,t,n,l;if(!u.value.bots[e.selfId])u.value.bots[e.selfId]={selfId:e.selfId,platform:e.platform,username:((a=e.bot)==null?void 0:a.name)||`Bot-${e.selfId}`,avatar:(t=e.bot)==null?void 0:t.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),(l=e.bot)!=null&&l.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.guildId?`${e.guildName||e.guildId} (${e.channelId})`:`私信 ${e.channelId}`;u.value.channels[e.selfId][e.channelId]={id:e.channelId,name:s,type:e.channelType||0,guildId:e.guildId,guildName:e.guildName||e.guildId||"私聊"}}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(r=>r.id===e.messageId)){const r={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},i=u.value.messages[s];let h=i.length;for(let y=i.length-1;y>=0;y--){if(i[y].timestamp<=e.timestamp){h=y+1;break}y===0&&(h=0)}i.splice(h,0,r),i.length>100&&(u.value.messages[s]=i.slice(-100)),D.value[s]=i.length;const g=De();re(()=>{setTimeout(()=>{g&&ne()},10)})}}if(e.elements&&e.elements.length>0){const s=`${e.selfId}:${e.channelId}`;e.elements.forEach(o=>{if((o.type==="img"||o.type==="image"||o.type==="mface")&&o.attrs){const r=o.attrs.src||o.attrs.url||o.attrs.file;r&&se(s,r).catch(i=>{console.warn("预缓存图片失败:",r,i)})}})}u.value={...u.value}}function Pt(e){const a=`${e.selfId}:${e.channelId}`;if(u.value.messages[a]||(u.value.messages[a]=[]),!u.value.messages[a].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},l=u.value.messages[a];let s=l.length;for(let r=l.length-1;r>=0;r--){if(l[r].timestamp<=e.timestamp){s=r+1;break}r===0&&(s=0)}l.splice(s,0,n),l.length>100&&(u.value.messages[a]=l.slice(-100)),D.value[a]=l.length;const o=De();re(()=>{setTimeout(()=>{o&&ne()},10)})}u.value={...u.value}}function Rt(e){var a,t,n,l;if(!u.value.bots[e.selfId])u.value.bots[e.selfId]={selfId:e.selfId,platform:e.platform,username:((a=e.bot)==null?void 0:a.name)||`Bot-${e.selfId}`,avatar:(t=e.bot)==null?void 0:t.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),(l=e.bot)!=null&&l.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.guildId?`${e.guildName||e.guildId} (${e.channelId})`:`私信 ${e.channelId}`;u.value.channels[e.selfId][e.channelId]={id:e.channelId,name:s,type:e.channelType||0,guildId:e.guildId,guildName:e.guildName||e.guildId||"私聊"}}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(r=>r.id===e.messageId)){const r={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},i=u.value.messages[s];let h=i.length;for(let y=i.length-1;y>=0;y--){if(i[y].timestamp<=e.timestamp){h=y+1;break}y===0&&(h=0)}i.splice(h,0,r),i.length>100&&(u.value.messages[s]=i.slice(-100)),D.value[s]=i.length;const g=De();re(()=>{setTimeout(()=>{g&&ne()},10)})}}if(e.elements&&e.elements.length>0){const s=`${e.selfId}:${e.channelId}`;e.elements.forEach(o=>{if((o.type==="img"||o.type==="image"||o.type==="mface")&&o.attrs){const r=o.attrs.src||o.attrs.url||o.attrs.file;r&&se(s,r).catch(i=>{console.warn("预缓存图片失败:",r,i)})}})}u.value={...u.value}}nt(He,(e,a)=>{a.length===0&&e.length>0&&re(()=>{ne()})});async function At(){try{const e=await _("get-chat-data");if(e.success&&e.data){const a={};X.value=new Set(e.data.pinnedBots||[]),H.value=new Set(e.data.pinnedChannels||[]);for(const[t,n]of Object.entries(e.data.messages||{})){const l=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}));l.sort((s,o)=>s.timestamp-o.timestamp),a[t]=l}return u.value={bots:e.data.bots||{},channels:e.data.channels||{},messages:a},await Mt(),true}else return console.warn("获取聊天数据失败:",e.error),false}catch(e){return console.error("获取聊天数据时出错:",e),false}}async function Mt(){try{const e=await _("get-all-channel-message-counts");if(e.success&&e.counts){const a={};for(const[t,n]of Object.entries(e.counts))a[t]=n;D.value=a}else console.warn("获取频道消息数量失败:",e.error)}catch(e){console.error("获取频道消息数量时出错:",e)}}async function zt(){try{const e=await _("get-plugin-config");e.success&&e.config?ie.value=e.config:console.warn("获取插件配置失败:",e.error)}catch(e){console.error("获取插件配置时出错:",e)}}async function Xt(e,a){try{const t=await _("get-history-messages",{selfId:e,channelId:a});if(t.success&&t.messages){const n=`${e}:${a}`,l=t.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 l.sort((s,o)=>s.timestamp-o.timestamp),u.value.messages[n]=l,D.value[n]=l.length,u.value={...u.value},true}else return console.warn("获取历史消息失败:",t.error),false}catch(t){return console.error("获取历史消息时出错:",t),false}}function Ht(e){if(!L.value||e.touches.length!==1)return;const a=e.touches[0];M.value={x:a.clientX,y:a.clientY,time:Date.now()},ee.value={x:a.clientX,y:a.clientY},Be.value=false}function Ft(e){if(!L.value||!M.value||e.touches.length!==1)return;const a=e.touches[0];ee.value={x:a.clientX,y:a.clientY};const t=a.clientX-M.value.x,n=a.clientY-M.value.y;if(Math.abs(t)>Math.abs(n)&&Math.abs(t)>30){const l=t>0,s=W.value==="messages"||W.value==="channels";if(l&&s){Be.value=true;const o=Math.min(t,200),r=150;o>r?V.value={show:true,text:"松开返回"}:V.value={show:true,text:`滑动返回 ${Math.round(o/r*100)}%`},e.preventDefault()}else V.value={show:false,text:""}}else V.value={show:false,text:""}}function Yt(e){if(!L.value||!M.value)return;const t=Date.now()-M.value.time;if(ee.value){const n=ee.value.x-M.value.x,l=ee.value.y-M.value.y,s=n>150,o=Math.abs(n)>Math.abs(l),r=t<300&&n>80;(s&&o||r)&&Wt()}M.value=null,ee.value=null,Be.value=false,V.value={show:false,text:""}}function Wt(){switch(W.value){case"messages":W.value="channels";break;case"channels":W.value="bots",m.value="",C.value="";break}}function Ke(){L.value=window.innerWidth<=768}return Ne(async()=>{Ke(),window.addEventListener("resize",Ke),document.addEventListener("click",Ge),await Kt()||console.warn("IndexedDB初始化失败,图片缓存功能将不可用"),await zt(),await At();const a=Re("chat-message-event",Ot),t=Re("bot-message-sent-event",Pt),n=Re("chat-bot-message-event",Rt);nt(C,l=>{l&&re(()=>{$.value&&($.value.removeEventListener("scroll",we),$.value.addEventListener("scroll",we),we()),!L.value&&de.value&&de.value.focus()})},{immediate:true}),setInterval(()=>{$e()},2*60*1e3),Gt(()=>{window.removeEventListener("resize",Ke),document.removeEventListener("click",Ge),a&&typeof a=="function"&&a(),t&&typeof t=="function"&&t(),n&&typeof n=="function"&&n(),$.value&&$.value.removeEventListener("scroll",we),Object.values(b.value).forEach(l=>{URL.revokeObjectURL(l)}),b.value={},S&&(S.close(),S=null)})}),(e,a)=>(v(),p("div",{class:F(["chat-container",ht.value]),style:Oe(vt.value),onTouchstart:Ht,onTouchmove:Ft,onTouchend:Yt},[c("div",aa,[a[7]||(a[7]=c("div",{class:"panel-header"},[c("h3",null,"机器人")],-1)),c("div",na,[(v(true),p(P,null,oe(it.value,t=>(v(),p("div",{key:t.selfId,class:F(["bot-item",{active:m.value===t.selfId,pinned:X.value.has(t.selfId)}]),onClick:n=>gt(t.selfId),onContextmenu:n=>yt(n,t.selfId)},[c("div",la,[t.avatar?(v(),he(me(E),{key:0,src:t.avatar,alt:t.username,"channel-key":"bot-list"},null,8,["src","alt"])):(v(),p("div",ra,x(t.username.charAt(0).toUpperCase()),1))]),c("div",oa,[c("div",ca,x(t.username),1),c("div",ia,x(t.platform),1)]),c("div",{class:F(["bot-status",t.status])},null,2)],42,sa))),128))])]),c("div",ua,[a[8]||(a[8]=c("div",{class:"panel-header"},[c("h3",null,"频道")],-1)),m.value?(v(),p("div",fa,[(v(true),p(P,null,oe(ut.value,t=>(v(),p("div",{key:t.id,class:F(["channel-item",{active:C.value===t.id,pinned:H.value.has(`${m.value}:${t.id}`)}]),"data-channel-id":t.id,onClick:n=>kt(t.id),onContextmenu:n=>pt(n,t.id)},[c("div",ma,[c("div",va,x(t.name),1),c("div",ga,x(Tt(t.type)),1)]),Ue(t.id)>0?(v(),p("div",{key:0,class:F(["channel-message-count draggable-bubble",{dragging:z.value===t.id,"will-delete":z.value===t.id&&Ee(t.id)>A}]),onMousedown:n=>Ze(n,t.id),onTouchstart:n=>Ze(n,t.id),style:Oe(qt(t.id)),title:z.value===t.id?Ee(t.id)>A?"松开清理历史记录":"拖拽更远以清理历史记录":"拖拽清理历史记录"},x(Ue(t.id)),47,ya)):G("",true)],42,ha))),128))])):(v(),p("div",da," 请选择一个机器人 "))]),c("div",pa,[c("div",wa,[c("h3",null,x(dt.value||"选择频道"),1)]),!m.value||!C.value?(v(),p("div",Ia," 请选择机器人和频道 ")):(v(),p("div",xa,[c("div",{class:"message-history",ref_key:"messageHistory",ref:$},[(v(true),p(P,null,oe(He.value,t=>(v(),p("div",{key:t.id,class:F(["message-item",{"bot-message":t.isBot}])},[c("div",Ca,[t.avatar?(v(),he(me(E),{key:0,src:t.avatar,alt:t.username,"channel-key":pe.value},null,8,["src","alt","channel-key"])):(v(),p("div",ka,x(t.username.charAt(0).toUpperCase()),1))]),c("div",ba,[c("div",_a,[c("span",$a,x(t.username),1),c("span",Sa,x(Qe(t.timestamp)),1)]),t.quote?(v(),p("div",Ba,[c("div",La,[c("div",Ta,[t.quote.user.avatar?(v(),he(me(E),{key:0,src:t.quote.user.avatar,alt:t.quote.user.username,"channel-key":pe.value},null,8,["src","alt","channel-key"])):(v(),p("div",qa,x(t.quote.user.username.charAt(0).toUpperCase()),1))]),c("span",Da,x(t.quote.user.username),1),c("span",Ua,x(Qe(t.quote.timestamp)),1)]),c("div",ja,[t.quote.elements&&t.quote.elements.length>0?(v(true),p(P,{key:0},oe(t.quote.elements,(n,l)=>(v(),he(me(ve),{key:`quote-${l}`,element:n,"channel-key":pe.value},null,8,["element","channel-key"]))),128)):(v(),p(P,{key:1},[Pe(x(t.quote.content),1)],64))])])):G("",true),c("div",Ka,[t.elements&&t.elements.length>0?(v(true),p(P,{key:0},oe(t.elements,(n,l)=>(v(),he(me(ve),{key:l,element:n,"channel-key":pe.value},null,8,["element","channel-key"]))),128)):(v(),p(P,{key:1},[Pe(x(t.content),1)],64))])])],2))),128))],512),st(c("div",{class:"floating-scroll-button",onClick:ne},a[9]||(a[9]=[c("svg",{width:"20",height:"20",viewBox:"0 0 24 24",fill:"currentColor"},[c("path",{d:"M7 10l5 5 5-5z"})],-1)]),512),[[Qt,Le.value]]),c("div",Na,[U.value.length>0?(v(),p("div",Oa,[(v(true),p(P,null,oe(U.value,t=>(v(),p("div",{key:t.tempId,class:"image-preview-item"},[c("img",{src:t.preview,alt:t.filename,class:"preview-image"},null,8,Pa),c("button",{class:"remove-image-btn",onClick:n=>Bt(t.tempId),title:"删除图片"},a[10]||(a[10]=[c("svg",{width:"12",height:"12",viewBox:"0 0 24 24",fill:"currentColor"},[c("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,Ra)]))),128))])):G("",true),c("div",Aa,[c("div",Ma,[c("button",{class:F(["add-button",{active:J.value}]),onClick:bt,title:"更多操作"},a[11]||(a[11]=[c("svg",{width:"18",height:"18",viewBox:"0 0 24 24",fill:"none",stroke:"currentColor","stroke-width":"2.5","stroke-linecap":"round","stroke-linejoin":"round"},[c("line",{x1:"12",y1:"5",x2:"12",y2:"19"}),c("line",{x1:"5",y1:"12",x2:"19",y2:"12"})],-1)]),2),J.value?(v(),p("div",{key:0,class:"action-menu",onClick:a[0]||(a[0]=lt(()=>{},["stop"]))},[c("button",{class:"action-menu-item",onClick:_t},a[12]||(a[12]=[c("svg",{width:"18",height:"18",viewBox:"0 0 24 24",fill:"none",stroke:"currentColor","stroke-width":"2","stroke-linecap":"round","stroke-linejoin":"round"},[c("rect",{x:"3",y:"3",width:"18",height:"18",rx:"2",ry:"2"}),c("circle",{cx:"8.5",cy:"8.5",r:"1.5"}),c("polyline",{points:"21,15 16,10 5,21"})],-1),Pe(" 上传图片 ",-1)]))])):G("",true)]),st(c("input",{"onUpdate:modelValue":a[1]||(a[1]=t=>ue.value=t),type:"text",placeholder:mt.value,onKeyup:Zt(Je,["enter"]),disabled:!ft.value,ref_key:"messageInput",ref:de,onPaste:St},null,40,za),[[Et,ue.value]]),c("button",{onClick:Je,disabled:!Fe.value,class:F({"is-sending":te.value})},x(te.value?"发送中...":"发送"),11,Xa)]),c("input",{type:"file",ref_key:"fileInput",ref:Se,onChange:$t,accept:"image/*",multiple:"",style:{display:"none"}},null,544)])]))]),I.value.show?(v(),p("div",{key:0,class:"context-menu",style:Oe({left:I.value.x+"px",top:I.value.y+"px"}),onClick:a[6]||(a[6]=lt(()=>{},["stop"]))},[I.value.type==="bot"?(v(),p(P,{key:0},[c("div",{class:"context-menu-item",onClick:a[2]||(a[2]=t=>wt(I.value.targetId))},x(X.value.has(I.value.targetId)?"取消置顶":"置顶"),1),c("div",{class:"context-menu-item danger",onClick:a[3]||(a[3]=t=>xt(I.value.targetId))}," 彻底删除此机器人所有数据 ")],64)):G("",true),I.value.type==="channel"?(v(),p(P,{key:1},[c("div",{class:"context-menu-item",onClick:a[4]||(a[4]=t=>It(I.value.targetId))},x(H.value.has(`${m.value}:${I.value.targetId}`)?"取消置顶":"置顶"),1),c("div",{class:"context-menu-item danger",onClick:a[5]||(a[5]=t=>Ct(I.value.targetId))}," 彻底删除此频道所有数据 ")],64)):G("",true)],4)):G("",true),c("div",{class:F(["swipe-indicator",{show:V.value.show}])},x(V.value.text),3)],38))}}),ot=(Z,Y)=>{const E=Z.__vccOpts||Z;for(const[ce,be]of Y)E[ce]=be;return E},Ja=ot(Wa,[["__scopeId","data-v-fc970c70"]]),Va={},Ga={class:"k-icon",xmlns:"http://www.w3.org/2000/svg",viewBox:"0 0 24 24",fill:"none"};function Qa(Z,Y){return v(),p("svg",Ga,Y[0]||(Y[0]=[c("path",{d:"M8 10.5H16",stroke:"currentColor","stroke-width":"1.5","stroke-linecap":"round"},null,-1),c("path",{d:"M8 14H13.5",stroke:"currentColor","stroke-width":"1.5","stroke-linecap":"round"},null,-1),c("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 Za=ot(Va,[["render",Qa]]);ta.register("activity:chat",Za);const tn=Z=>{Z.page({name:"聊天室",path:"/chat-patch",desc:"",authority:4,icon:"activity:chat",component:Q({setup(){return()=>d(ea("k-layout"),{},{default:()=>d(Ja)})}})})};export{tn as default};
|
|
22
|
+
`,document.head.appendChild(r),document.body.appendChild(t),setTimeout(()=>{t.style.animation="slideOut 0.3s ease-in",setTimeout(()=>{t.parentNode&&t.parentNode.removeChild(t),r.parentNode&&r.parentNode.removeChild(r)},300)},3e3)}function Ht(e,a){const t=document.createElement("div");t.className="drag-threshold-circle",t.style.cssText=`
|
|
23
|
+
left: ${e-P}px;
|
|
24
|
+
top: ${a-P}px;
|
|
25
|
+
width: ${P*2}px;
|
|
26
|
+
height: ${P*2}px;
|
|
27
|
+
`,document.body.appendChild(t),window.dragThresholdCircle=t}function Ft(){const e=window.dragThresholdCircle;e&&e.parentNode&&(e.parentNode.removeChild(e),window.dragThresholdCircle=null)}async function Ne(){try{if(!w)return false;const e=Date.now();if(e-We<nn)return true;We=e;const a=await ot();U=a.totalSize,M=a.totalImages,console.log("数据库健康检查:",{大小:`${(U/1024/1024).toFixed(2)}MB / ${(ie/1024/1024).toFixed(2)}MB`,图片数量:`${M} / ${ue}`,使用率:`${(U/ie*100).toFixed(1)}%`});const t=U/ie,n=M/ue;return(t>gt||n>gt)&&(console.warn("数据库使用率过高,开始自动清理"),await _e()),(t>.95||n>.95)&&(console.error("数据库接近极限,执行紧急清理"),await Xt()),true}catch(e){return console.error("数据库健康检查失败:",e),false}}async function ot(){return w?new Promise(e=>{const n=w.transaction([B],"readonly").objectStore(B).getAll();n.onsuccess=()=>{const r=n.result||[];let s=0;const o={};r.forEach(l=>{s+=l.size||0,o[l.channelKey]=(o[l.channelKey]||0)+1}),e({totalSize:s,totalImages:r.length,channelStats:o})},n.onerror=()=>{console.error("获取数据库统计失败:",n.error),e({totalSize:0,totalImages:0,channelStats:{}})}}):{totalSize:0,totalImages:0,channelStats:{}}}async function _e(){try{console.log("开始自动清理...");const e=await ct();if(e.length===0)return;const a={};e.forEach(r=>{a[r.channelKey]||(a[r.channelKey]=[]),a[r.channelKey].push(r)});let t=0,n=0;for(const[r,s]of Object.entries(a))if(s.length>vt){s.sort((l,c)=>l.timestamp-c.timestamp);const o=s.slice(0,s.length-vt);for(const l of o)await Re(l.url),t++,n+=l.size||0,b.value[l.url]&&(URL.revokeObjectURL(b.value[l.url]),delete b.value[l.url])}console.log(`自动清理完成: 清理了 ${t} 张图片,释放了 ${(n/1024/1024).toFixed(2)}MB`),M-=t,U-=n}catch(e){console.error("自动清理失败:",e)}}async function Xt(){try{console.log("开始紧急清理...");const e=await ct();if(e.length===0)return;e.sort((s,o)=>o.timestamp-s.timestamp);const a=Math.floor(ue*.3),t=e.slice(a);let n=0,r=0;for(const s of t)await Re(s.url),n++,r+=s.size||0,b.value[s.url]&&(URL.revokeObjectURL(b.value[s.url]),delete b.value[s.url]);console.log(`紧急清理完成: 清理了 ${n} 张图片,释放了 ${(r/1024/1024).toFixed(2)}MB`),M=a,U-=r}catch(e){console.error("紧急清理失败:",e)}}async function ct(){return w?new Promise(e=>{const n=w.transaction([B],"readonly").objectStore(B).getAll();n.onsuccess=()=>{e(n.result||[])},n.onerror=()=>{console.error("获取所有图片失败:",n.error),e([])}}):[]}async function Yt(){return new Promise(e=>{try{w&&(w.close(),w=null);const a=indexedDB.deleteDatabase(mt);a.onsuccess=()=>{console.log("IndexedDB数据库已完全清理"),U=0,M=0,e(true)},a.onerror=()=>{console.error("清理IndexedDB数据库失败:",a.error),e(false)},a.onblocked=()=>{console.warn("IndexedDB数据库删除被阻塞,可能有其他连接正在使用"),setTimeout(()=>{e(false)},5e3)}}catch(a){console.error("清理数据库时出错:",a),e(false)}})}async function Wt(){try{return await it()?(setTimeout(async()=>{const a=await ot();console.log("数据库初始状态:",{大小:`${(a.totalSize/1024/1024).toFixed(2)}MB`,图片数量:a.totalImages,频道分布:a.channelStats}),(a.totalSize>ie*.9||a.totalImages>ue*.9)&&(console.warn("数据库初始状态接近限制,执行清理"),await _e())},1e3),true):(console.warn("数据库打开失败,尝试清理后重新初始化"),await Yt(),await it())}catch(e){return console.error("IndexedDB初始化出错:",e),false}}async function it(){return new Promise(e=>{try{const a=indexedDB.open(mt,an);a.onerror=()=>{console.error("IndexedDB打开失败:",a.error),e(false)},a.onsuccess=()=>{w=a.result,w.onerror=t=>{console.error("IndexedDB运行时错误:",t)},w.onversionchange=()=>{console.warn("IndexedDB版本变更,关闭连接"),w==null||w.close(),w=null},e(true)},a.onupgradeneeded=t=>{const n=t.target.result;if(!n.objectStoreNames.contains(B)){const r=n.createObjectStore(B,{keyPath:"url"});r.createIndex("channelKey","channelKey",{unique:false}),r.createIndex("timestamp","timestamp",{unique:false}),r.createIndex("size","size",{unique:false}),console.log("IndexedDB对象存储创建完成")}},a.onblocked=()=>{console.warn("IndexedDB打开被阻塞"),e(false)}}catch(a){console.error("打开数据库时出错:",a),e(false)}})}async function Jt(e){return w?new Promise((a,t)=>{const s=w.transaction([B],"readonly").objectStore(B).get(e);s.onsuccess=()=>{a(s.result||null)},s.onerror=()=>{console.error("从IndexedDB获取图片失败:",s.error),a(null)}}):null}async function ut(e){if(!w)return false;try{return e.size>Ye?(console.warn(`图片过大,跳过缓存: ${(e.size/1024/1024).toFixed(2)}MB > ${(Ye/1024/1024).toFixed(2)}MB`),false):(await Ne(),U+e.size>ie&&(console.warn("添加图片会超过数据库大小限制,执行清理"),await _e(),U+e.size>ie)?(console.warn("清理后仍会超过限制,跳过此图片"),false):M>=ue&&(console.warn("图片数量已达上限,执行清理"),await _e(),M>=ue)?(console.warn("清理后仍达上限,跳过此图片"),false):new Promise(a=>{const r=w.transaction([B],"readwrite").objectStore(B).put(e);r.onsuccess=()=>{U+=e.size,M+=1,a(true)},r.onerror=()=>{console.error("保存图片到IndexedDB失败:",r.error),a(false)}}))}catch(a){return console.error("保存图片时出错:",a),false}}async function Re(e){return w?new Promise(a=>{const r=w.transaction([B],"readwrite").objectStore(B).delete(e);r.onsuccess=()=>{a(true)},r.onerror=()=>{console.error("从IndexedDB删除图片失败:",r.error),a(false)}}):false}async function Vt(e){return w?new Promise(a=>{const s=w.transaction([B],"readonly").objectStore(B).index("channelKey").getAll(e);s.onsuccess=()=>{a(s.result||[])},s.onerror=()=>{console.error("获取频道图片失败:",s.error),a([])}}):[]}async function Oe(e,a){const t=b.value[a];if(t)return t;const n=await Jt(a);if(!n)return null;De();const r=URL.createObjectURL(n.blob);return b.value[a]=r,pe(Je(n.blob)),n.timestamp=Date.now(),await ut(n),r}async function le(e,a){try{const t=await Oe(e,a);if(t)return t;const n=await S("fetch-image",{url:a});if(!n.success)return console.error("获取图片失败:",n.error),null;const r=n.base64,s=n.contentType||"image/jpeg",o=atob(r),l=new Array(o.length);for(let $=0;$<o.length;$++)l[$]=o.charCodeAt($);const c=new Uint8Array(l),h=new Blob([c],{type:s});if(h.size>Ye)return console.warn(`图片过大,跳过缓存: ${(h.size/1024/1024).toFixed(2)}MB`),null;const y={url:a,blob:h,timestamp:Date.now(),size:h.size,channelKey:e};if(!await ut(y))return console.error("保存图片到IndexedDB失败"),null;De();const L=URL.createObjectURL(h);return b.value[a]=L,pe(Je(h)),L}catch(t){return console.error("缓存图片失败:",t),null}}async function $e(e){try{const a=await Vt(e);let t=0;for(const n of a)await Re(n.url),b.value[n.url]&&(URL.revokeObjectURL(b.value[n.url]),delete b.value[n.url],t+=n.size||0);t>0&&pe(-t)}catch(a){console.error("清理频道图片缓存失败:",a)}}function Gt(e){var a,t,n,r;if(!u.value.bots[e.selfId])u.value.bots[e.selfId]={selfId:e.selfId,platform:e.platform,username:((a=e.bot)==null?void 0:a.name)||`Bot-${e.selfId}`,avatar:(t=e.bot)==null?void 0:t.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),(r=e.bot)!=null&&r.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.guildId?`${e.guildName||e.guildId} (${e.channelId})`:`私信 ${e.channelId}`;u.value.channels[e.selfId][e.channelId]={id:e.channelId,name:s,type:e.channelType||0,guildId:e.guildId,guildName:e.guildName||e.guildId||"私聊"}}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},c=u.value.messages[s];let h=c.length;for(let g=c.length-1;g>=0;g--){if(c[g].timestamp<=e.timestamp){h=g+1;break}g===0&&(h=0)}c.splice(h,0,l),c.length>100&&(u.value.messages[s]=c.slice(-100)),K.value[s]=c.length;const y=je();oe(()=>{setTimeout(()=>{y&&re()},10)})}}if(e.elements&&e.elements.length>0){const s=`${e.selfId}:${e.channelId}`;e.elements.forEach(o=>{if((o.type==="img"||o.type==="image"||o.type==="mface")&&o.attrs){const l=o.attrs.src||o.attrs.url||o.attrs.file;l&&le(s,l).catch(c=>{console.warn("预缓存图片失败:",l,c)})}})}u.value={...u.value}}function Zt(e){const a=`${e.selfId}:${e.channelId}`;if(u.value.messages[a]||(u.value.messages[a]=[]),!u.value.messages[a].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},r=u.value.messages[a];let s=r.length;for(let l=r.length-1;l>=0;l--){if(r[l].timestamp<=e.timestamp){s=l+1;break}l===0&&(s=0)}r.splice(s,0,n),r.length>100&&(u.value.messages[a]=r.slice(-100)),K.value[a]=r.length;const o=je();oe(()=>{setTimeout(()=>{o&&re()},10)})}u.value={...u.value}}function Qt(e){var a,t,n,r;if(!u.value.bots[e.selfId])u.value.bots[e.selfId]={selfId:e.selfId,platform:e.platform,username:((a=e.bot)==null?void 0:a.name)||`Bot-${e.selfId}`,avatar:(t=e.bot)==null?void 0:t.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),(r=e.bot)!=null&&r.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.guildId?`${e.guildName||e.guildId} (${e.channelId})`:`私信 ${e.channelId}`;u.value.channels[e.selfId][e.channelId]={id:e.channelId,name:s,type:e.channelType||0,guildId:e.guildId,guildName:e.guildName||e.guildId||"私聊"}}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},c=u.value.messages[s];let h=c.length;for(let g=c.length-1;g>=0;g--){if(c[g].timestamp<=e.timestamp){h=g+1;break}g===0&&(h=0)}c.splice(h,0,l),c.length>100&&(u.value.messages[s]=c.slice(-100)),K.value[s]=c.length;const y=je();oe(()=>{setTimeout(()=>{y&&re()},10)})}}if(e.elements&&e.elements.length>0){const s=`${e.selfId}:${e.channelId}`;e.elements.forEach(o=>{if((o.type==="img"||o.type==="image"||o.type==="mface")&&o.attrs){const l=o.attrs.src||o.attrs.url||o.attrs.file;l&&le(s,l).catch(c=>{console.warn("预缓存图片失败:",l,c)})}})}u.value={...u.value}}dt(Ze,(e,a)=>{a.length===0&&e.length>0&&oe(()=>{re()})});async function Et(){try{const e=await S("get-chat-data");if(e.success&&e.data){const a={};X.value=new Set(e.data.pinnedBots||[]),Y.value=new Set(e.data.pinnedChannels||[]);for(const[t,n]of Object.entries(e.data.messages||{})){const r=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}));r.sort((s,o)=>s.timestamp-o.timestamp),a[t]=r}return u.value={bots:e.data.bots||{},channels:e.data.channels||{},messages:a},await ea(),true}else return console.warn("获取聊天数据失败:",e.error),false}catch(e){return console.error("获取聊天数据时出错:",e),false}}async function ea(){try{const e=await S("get-all-channel-message-counts");if(e.success&&e.counts){const a={};for(const[t,n]of Object.entries(e.counts))a[t]=n;K.value=a}else console.warn("获取频道消息数量失败:",e.error)}catch(e){console.error("获取频道消息数量时出错:",e)}}async function ta(){try{const e=await S("get-plugin-config");e.success&&e.config?fe.value=e.config:console.warn("获取插件配置失败:",e.error)}catch(e){console.error("获取插件配置时出错:",e)}}async function aa(e,a){try{const t=await S("get-history-messages",{selfId:e,channelId:a});if(t.success&&t.messages){const n=`${e}:${a}`,r=t.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 r.sort((s,o)=>s.timestamp-o.timestamp),u.value.messages[n]=r,K.value[n]=r.length,u.value={...u.value},true}else return console.warn("获取历史消息失败:",t.error),false}catch(t){return console.error("获取历史消息时出错:",t),false}}function na(e){if(!T.value||e.touches.length!==1)return;const a=e.touches[0];H.value={x:a.clientX,y:a.clientY,time:Date.now()},ae.value={x:a.clientX,y:a.clientY},Te.value=false}function sa(e){if(!T.value||!H.value||e.touches.length!==1)return;const a=e.touches[0];ae.value={x:a.clientX,y:a.clientY};const t=a.clientX-H.value.x,n=a.clientY-H.value.y;if(Math.abs(t)>Math.abs(n)&&Math.abs(t)>30){const r=t>0,s=V.value==="messages"||V.value==="channels";if(r&&s){Te.value=true;const o=Math.min(t,200),l=150;o>l?Z.value={show:true,text:"松开返回"}:Z.value={show:true,text:`滑动返回 ${Math.round(o/l*100)}%`},e.preventDefault()}else Z.value={show:false,text:""}}else Z.value={show:false,text:""}}function ra(e){if(!T.value||!H.value)return;const t=Date.now()-H.value.time;if(ae.value){const n=ae.value.x-H.value.x,r=ae.value.y-H.value.y,s=n>150,o=Math.abs(n)>Math.abs(r),l=t<300&&n>80;(s&&o||l)&&la()}H.value=null,ae.value=null,Te.value=false,Z.value={show:false,text:""}}function la(){switch(V.value){case"messages":V.value="channels";break;case"channels":V.value="bots",m.value="",k.value="";break}}function ze(){T.value=window.innerWidth<=768}return Pe(async()=>{ze(),window.addEventListener("resize",ze),document.addEventListener("click",nt),await Wt()?(console.log("IndexedDB初始化成功"),setTimeout(async()=>{await Ne()},2e3),setInterval(async()=>{await Ne()},5*60*1e3)):console.warn("IndexedDB初始化失败,图片缓存功能将不可用"),await ta(),await Et();const a=Fe("chat-message-event",Gt),t=Fe("bot-message-sent-event",Zt),n=Fe("chat-bot-message-event",Qt);dt(k,r=>{r&&oe(()=>{D.value&&(D.value.removeEventListener("scroll",xe),D.value.addEventListener("scroll",xe),xe()),!T.value&&me.value&&me.value.focus()})},{immediate:true}),setInterval(()=>{De()},2*60*1e3),oa(()=>{window.removeEventListener("resize",ze),document.removeEventListener("click",nt),a&&typeof a=="function"&&a(),t&&typeof t=="function"&&t(),n&&typeof n=="function"&&n(),D.value&&D.value.removeEventListener("scroll",xe),Object.values(b.value).forEach(r=>{URL.revokeObjectURL(r)}),b.value={},w&&(w.close(),w=null)})}),(e,a)=>(v(),p("div",{class:W(["chat-container",kt.value]),style:Me(_t.value),onTouchstart:na,onTouchmove:sa,onTouchend:ra},[i("div",ha,[a[7]||(a[7]=i("div",{class:"panel-header"},[i("h3",null,"机器人")],-1)),i("div",ma,[(v(true),p(z,null,ce(wt.value,t=>(v(),p("div",{key:t.selfId,class:W(["bot-item",{active:m.value===t.selfId,pinned:X.value.has(t.selfId)}]),onClick:n=>$t(t.selfId),onContextmenu:n=>St(n,t.selfId)},[i("div",ga,[t.avatar?(v(),ve(ge(te),{key:0,src:t.avatar,alt:t.username,"channel-key":"bot-list"},null,8,["src","alt"])):(v(),p("div",ya,C(t.username.charAt(0).toUpperCase()),1))]),i("div",pa,[i("div",wa,C(t.username),1),i("div",Ia,C(t.platform),1)]),i("div",{class:W(["bot-status",t.status])},null,2)],42,va))),128))])]),i("div",xa,[a[8]||(a[8]=i("div",{class:"panel-header"},[i("h3",null,"频道")],-1)),m.value?(v(),p("div",ka,[(v(true),p(z,null,ce(It.value,t=>(v(),p("div",{key:t.id,class:W(["channel-item",{active:k.value===t.id,pinned:Y.value.has(`${m.value}:${t.id}`)}]),"data-channel-id":t.id,onClick:n=>Kt(t.id),onContextmenu:n=>Bt(n,t.id)},[i("div",_a,[i("div",$a,C(t.name),1),i("div",Sa,C(zt(t.type)),1)]),Ae(t.id)>0?(v(),p("div",{key:0,class:W(["channel-message-count draggable-bubble",{dragging:F.value===t.id,"will-delete":F.value===t.id&<(t.id)>P}]),onMousedown:n=>rt(n,t.id),onTouchstart:n=>rt(n,t.id),style:Me(Pt(t.id)),title:F.value===t.id?lt(t.id)>P?"松开清理历史记录":"拖拽更远以清理历史记录":"拖拽清理历史记录"},C(Ae(t.id)),47,Ba)):Q("",true)],42,ba))),128))])):(v(),p("div",Ca," 请选择一个机器人 "))]),i("div",Da,[i("div",La,[i("h3",null,C(xt.value||"选择频道"),1)]),!m.value||!k.value?(v(),p("div",Ta," 请选择机器人和频道 ")):(v(),p("div",qa,[i("div",{class:"message-history",ref_key:"messageHistory",ref:D},[(v(true),p(z,null,ce(Ze.value,t=>(v(),p("div",{key:t.id,class:W(["message-item",{"bot-message":t.isBot}])},[i("div",Ka,[t.avatar?(v(),ve(ge(te),{key:0,src:t.avatar,alt:t.username,"channel-key":Ie.value},null,8,["src","alt","channel-key"])):(v(),p("div",Ua,C(t.username.charAt(0).toUpperCase()),1))]),i("div",ja,[i("div",Aa,[i("span",Na,C(t.username),1),i("span",Ra,C(st(t.timestamp)),1)]),t.quote?(v(),p("div",Oa,[i("div",za,[i("div",Pa,[t.quote.user.avatar?(v(),ve(ge(te),{key:0,src:t.quote.user.avatar,alt:t.quote.user.username,"channel-key":Ie.value},null,8,["src","alt","channel-key"])):(v(),p("div",Ma,C(t.quote.user.username.charAt(0).toUpperCase()),1))]),i("span",Ha,C(t.quote.user.username),1),i("span",Fa,C(st(t.quote.timestamp)),1)]),i("div",Xa,[t.quote.elements&&t.quote.elements.length>0?(v(true),p(z,{key:0},ce(t.quote.elements,(n,r)=>(v(),ve(ge(ye),{key:`quote-${r}`,element:n,"channel-key":Ie.value},null,8,["element","channel-key"]))),128)):(v(),p(z,{key:1},[He(C(t.quote.content),1)],64))])])):Q("",true),i("div",Ya,[t.elements&&t.elements.length>0?(v(true),p(z,{key:0},ce(t.elements,(n,r)=>(v(),ve(ge(ye),{key:r,element:n,"channel-key":Ie.value},null,8,["element","channel-key"]))),128)):(v(),p(z,{key:1},[He(C(t.content),1)],64))])])],2))),128))],512),ft(i("div",{class:"floating-scroll-button",onClick:re},a[9]||(a[9]=[i("svg",{width:"20",height:"20",viewBox:"0 0 24 24",fill:"currentColor"},[i("path",{d:"M7 10l5 5 5-5z"})],-1)]),512),[[ca,qe.value]]),i("div",Wa,[j.value.length>0?(v(),p("div",Ja,[(v(true),p(z,null,ce(j.value,t=>(v(),p("div",{key:t.tempId,class:"image-preview-item"},[i("img",{src:t.preview,alt:t.filename,class:"preview-image"},null,8,Va),i("button",{class:"remove-image-btn",onClick:n=>Rt(t.tempId),title:"删除图片"},a[10]||(a[10]=[i("svg",{width:"12",height:"12",viewBox:"0 0 24 24",fill:"currentColor"},[i("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,Ga)]))),128))])):Q("",true),i("div",Za,[i("div",Qa,[i("button",{class:W(["add-button",{active:G.value}]),onClick:Ut,title:"更多操作"},a[11]||(a[11]=[i("svg",{width:"18",height:"18",viewBox:"0 0 24 24",fill:"none",stroke:"currentColor","stroke-width":"2.5","stroke-linecap":"round","stroke-linejoin":"round"},[i("line",{x1:"12",y1:"5",x2:"12",y2:"19"}),i("line",{x1:"5",y1:"12",x2:"19",y2:"12"})],-1)]),2),G.value?(v(),p("div",{key:0,class:"action-menu",onClick:a[0]||(a[0]=ht(()=>{},["stop"]))},[i("button",{class:"action-menu-item",onClick:jt},a[12]||(a[12]=[i("svg",{width:"18",height:"18",viewBox:"0 0 24 24",fill:"none",stroke:"currentColor","stroke-width":"2","stroke-linecap":"round","stroke-linejoin":"round"},[i("rect",{x:"3",y:"3",width:"18",height:"18",rx:"2",ry:"2"}),i("circle",{cx:"8.5",cy:"8.5",r:"1.5"}),i("polyline",{points:"21,15 16,10 5,21"})],-1),He(" 上传图片 ",-1)]))])):Q("",true)]),ft(i("input",{"onUpdate:modelValue":a[1]||(a[1]=t=>he.value=t),type:"text",placeholder:bt.value,onKeyup:ia(tt,["enter"]),disabled:!Ct.value,ref_key:"messageInput",ref:me,onPaste:Nt},null,40,Ea),[[ua,he.value]]),i("button",{onClick:tt,disabled:!Qe.value,class:W({"is-sending":ne.value})},C(ne.value?"发送中...":"发送"),11,en)]),i("input",{type:"file",ref_key:"fileInput",ref:Le,onChange:At,accept:"image/*",multiple:"",style:{display:"none"}},null,544)])]))]),x.value.show?(v(),p("div",{key:0,class:"context-menu",style:Me({left:x.value.x+"px",top:x.value.y+"px"}),onClick:a[6]||(a[6]=ht(()=>{},["stop"]))},[x.value.type==="bot"?(v(),p(z,{key:0},[i("div",{class:"context-menu-item",onClick:a[2]||(a[2]=t=>Dt(x.value.targetId))},C(X.value.has(x.value.targetId)?"取消置顶":"置顶"),1),i("div",{class:"context-menu-item danger",onClick:a[3]||(a[3]=t=>Tt(x.value.targetId))}," 彻底删除此机器人所有数据 ")],64)):Q("",true),x.value.type==="channel"?(v(),p(z,{key:1},[i("div",{class:"context-menu-item",onClick:a[4]||(a[4]=t=>Lt(x.value.targetId))},C(Y.value.has(`${m.value}:${x.value.targetId}`)?"取消置顶":"置顶"),1),i("div",{class:"context-menu-item danger",onClick:a[5]||(a[5]=t=>qt(x.value.targetId))}," 彻底删除此频道所有数据 ")],64)):Q("",true)],4)):Q("",true),i("div",{class:W(["swipe-indicator",{show:Z.value.show}])},C(Z.value.text),3)],38))}}),yt=(ee,J)=>{const te=ee.__vccOpts||ee;for(const[de,Se]of J)te[de]=Se;return te},rn=yt(sn,[["__scopeId","data-v-fbeb5b9c"]]),ln={},on={class:"k-icon",xmlns:"http://www.w3.org/2000/svg",viewBox:"0 0 24 24",fill:"none"};function cn(ee,J){return v(),p("svg",on,J[0]||(J[0]=[i("path",{d:"M8 10.5H16",stroke:"currentColor","stroke-width":"1.5","stroke-linecap":"round"},null,-1),i("path",{d:"M8 14H13.5",stroke:"currentColor","stroke-width":"1.5","stroke-linecap":"round"},null,-1),i("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 un=yt(ln,[["render",cn]]);fa.register("activity:chat",un);const hn=ee=>{ee.page({name:"聊天室",path:"/chat-patch",desc:"",authority:4,icon:"activity:chat",component:E({setup(){return()=>d(da("k-layout"),{},{default:()=>d(rn)})}})})};export{hn as default};
|
package/dist/style.css
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
.message-quote[data-v-fc970c70]{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-fc970c70]{font-size:.75em;color:#007bff;font-weight:600;margin-bottom:4px;text-transform:uppercase}.quote-header[data-v-fc970c70]{display:flex;align-items:center;margin-bottom:4px;gap:6px}.quote-avatar[data-v-fc970c70]{width:20px;height:20px;border-radius:50%;overflow:hidden;flex-shrink:0}.quote-avatar img[data-v-fc970c70]{width:100%;height:100%;object-fit:cover}.quote-avatar .avatar-placeholder[data-v-fc970c70]{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-fc970c70]{font-weight:600;color:#007bff;font-size:.85em}.quote-time[data-v-fc970c70]{color:#6c757d;font-size:.75em;margin-left:auto}.quote-content[data-v-fc970c70]{color:#7276b7;line-height:1.3;word-wrap:break-word}.quote-content .message-image[data-v-fc970c70]{max-width:200px;max-height:100px}.quote-content .message-image-container[data-v-fc970c70]{margin:2px 0}@media (prefers-color-scheme: dark){.message-quote[data-v-fc970c70]{background-color:#ffffff14;border-left-color:#0d6efd}.quote-content[data-v-fc970c70]{color:#adb5bd}.quote-username[data-v-fc970c70]{color:#0d6efd}}.chat-container[data-v-fc970c70]{display:flex;height:100vh;background:transparent;margin-left:0%;width:100%;overflow:hidden;color:inherit;position:relative}.bot-list[data-v-fc970c70],.channel-list[data-v-fc970c70],.message-area[data-v-fc970c70]{display:flex;flex-direction:column;background:transparent;border-right:1px solid rgba(128,128,128,.2)}.bot-list[data-v-fc970c70]{width:250px;min-width:250px;max-width:250px;flex-shrink:0}.channel-list[data-v-fc970c70]{width:300px;min-width:300px;max-width:300px;flex-shrink:0}.message-area[data-v-fc970c70]{flex:1;border-right:none}.panel-header[data-v-fc970c70]{padding:16px;border-bottom:1px solid rgba(128,128,128,.2);background:transparent;display:flex;align-items:center;justify-content:space-between}.panel-header h3[data-v-fc970c70]{margin:0;font-size:16px;font-weight:600;color:currentColor}.back-button[data-v-fc970c70]{display:none;background:none;border:none;color:currentColor;font-size:18px;cursor:pointer;padding:4px 8px;border-radius:4px;transition:background-color .2s}.back-button[data-v-fc970c70]:hover{background:#8080801a}.bot-items[data-v-fc970c70],.channel-items[data-v-fc970c70]{flex:1;overflow-y:auto;max-height:calc(100vh - 60px)}.bot-item[data-v-fc970c70],.channel-item[data-v-fc970c70]{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-fc970c70]:hover,.channel-item[data-v-fc970c70]:hover{background:#8080801a}.bot-item.active[data-v-fc970c70],.channel-item.active[data-v-fc970c70]{background:#2196f31a;border-right:3px solid #2196f3}.bot-avatar[data-v-fc970c70]{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-fc970c70]{width:100%;height:100%;object-fit:cover}.avatar-placeholder[data-v-fc970c70]{font-size:16px;font-weight:700;color:currentColor;opacity:.7}.bot-info[data-v-fc970c70]{flex:1}.bot-name[data-v-fc970c70]{font-weight:500;color:currentColor;margin-bottom:2px}.bot-platform[data-v-fc970c70]{font-size:12px;color:currentColor;opacity:.7}.bot-status[data-v-fc970c70]{width:8px;height:8px;border-radius:50%;margin-left:8px}.bot-status.online[data-v-fc970c70]{background:#4caf50}.bot-status.offline[data-v-fc970c70]{background:#f44336}.channel-info[data-v-fc970c70]{flex:1}.channel-name[data-v-fc970c70]{font-weight:500;color:currentColor;margin-bottom:2px}.channel-type[data-v-fc970c70]{font-size:12px;color:currentColor;opacity:.7}.channel-message-count[data-v-fc970c70]{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-fc970c70]{cursor:grab;user-select:none;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none}.channel-message-count.draggable-bubble[data-v-fc970c70]:hover{background:#1976d2;transform:scale(1.1);box-shadow:0 2px 8px #2196f34d}.channel-message-count.draggable-bubble[data-v-fc970c70]:active,.channel-message-count.draggable-bubble.dragging[data-v-fc970c70]{cursor:grabbing;background:#1565c0;box-shadow:0 4px 12px #2196f366}.dragging-clone[data-v-fc970c70]{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-fc970c70]{background:#f44336!important;animation:shake-fc970c70 .3s ease-in-out infinite}@keyframes pulseThreshold-fc970c70{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-fc970c70{0%{transform:translateY(8px);opacity:0}to{transform:translateY(0);opacity:1}}.message-content[data-v-fc970c70]{display:flex;flex-direction:column;height:calc(100vh - 60px);overflow:hidden;position:relative}.message-history[data-v-fc970c70]{flex:1;overflow-y:auto;padding:16px 16px 8px;max-height:calc(100vh - 200px)}.message-item[data-v-fc970c70]{display:flex;margin-bottom:16px;padding:8px;border-radius:8px;transition:background-color .3s ease}.message-item[data-v-fc970c70]:hover{background-color:#8080801a}.message-item.new-message[data-v-fc970c70]{background-color:#4caf5033;animation:highlight-fc970c70 2s ease-out}.message-item.bot-message[data-v-fc970c70]{flex-direction:row-reverse;justify-content:flex-start}.message-item.bot-message .message-avatar[data-v-fc970c70]{margin-right:0;margin-left:12px}.message-item.bot-message .message-content-wrapper[data-v-fc970c70]{display:flex;flex-direction:column;align-items:flex-end}.message-item.bot-message .message-header[data-v-fc970c70]{display:flex;align-items:center;justify-content:flex-end;margin-bottom:4px}.message-item.bot-message .message-username[data-v-fc970c70]{margin-right:0;margin-left:8px;order:2}.message-item.bot-message .message-time[data-v-fc970c70]{order:1}.message-item.bot-message .message-text[data-v-fc970c70]{background-color:#2196f31a;padding:8px 12px;border-radius:12px 12px 4px;display:inline-block;max-width:80%;text-align:left}@keyframes highlight-fc970c70{0%{background-color:#4caf504d}to{background-color:transparent}}.message-avatar[data-v-fc970c70]{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-fc970c70]{width:100%;height:100%;object-fit:cover}.message-avatar .avatar-placeholder[data-v-fc970c70]{font-size:12px;font-weight:700;color:currentColor;opacity:.7}.message-content-wrapper[data-v-fc970c70]{flex:1}.message-header[data-v-fc970c70]{display:flex;align-items:center;margin-bottom:4px}.message-username[data-v-fc970c70]{font-weight:500;color:currentColor;margin-right:8px}.message-time[data-v-fc970c70]{font-size:12px;color:currentColor;opacity:.5}.message-text[data-v-fc970c70]{color:currentColor;line-height:1.4;word-wrap:break-word;word-break:break-word;white-space:pre-wrap;max-width:100%;overflow-wrap:break-word}.message-item:not(.bot-message) .message-text[data-v-fc970c70]{background-color:#8080801a;padding:8px 12px;border-radius:12px 12px 12px 4px;display:inline-block;max-width:80%}.message-input[data-v-fc970c70]{display:flex;flex-direction:column;padding:12px 16px 16px;border-top:1px solid rgba(128,128,128,.2);background:transparent;flex-shrink:0;position:sticky;bottom:0;margin-top:-10px}.input-row[data-v-fc970c70]{display:flex;gap:8px;align-items:center}.floating-scroll-button[data-v-fc970c70]{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-fc970c70]:hover{background:#1976d2;transform:scale(1.1);box-shadow:0 6px 16px #2196f366}.floating-scroll-button[data-v-fc970c70]:active{transform:scale(.95)}.input-row button[data-v-fc970c70]{padding:8px 16px;background:#2196f3;color:#fff;border:none;border-radius:4px;cursor:pointer;transition:background-color .2s}.message-image-container[data-v-fc970c70]{margin:8px 0;max-width:100%;display:block}.message-image[data-v-fc970c70]{border-radius:8px;object-fit:contain;display:block;cursor:pointer;transition:transform .2s ease}.message-image-loading[data-v-fc970c70]{display:inline-block;padding:8px 12px;background:#8080801a;border-radius:4px;color:currentColor;opacity:.7;font-size:12px}.message-image-error[data-v-fc970c70]{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-fc970c70]{display:block;margin-bottom:4px}.message-paragraph[data-v-fc970c70]:last-child{margin-bottom:0}.forward-message-container[data-v-fc970c70]{border:1px solid #ffffff;border-radius:8px;margin:4px 0;overflow:hidden;background:#f9f9f900}.forward-message-preview[data-v-fc970c70]{padding:12px;cursor:pointer;transition:background-color .2s}.forward-message-preview[data-v-fc970c70]:hover{background:#f0f0f0}.forward-message-title[data-v-fc970c70]{font-weight:600;color:#333;margin-bottom:8px;font-size:14px}.forward-message-preview-item[data-v-fc970c70]{color:#666;font-size:13px;margin:2px 0;line-height:1.4}.forward-message-footer[data-v-fc970c70]{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-fc970c70]{color:#2196f3;font-size:12px}.forward-message-toggle[data-v-fc970c70]{color:#666;font-size:12px}.forward-message-expanded[data-v-fc970c70]{border-top:1px solid #e0e0e0;background:#fff;max-height:400px;overflow-y:auto}.forwarded-message-item[data-v-fc970c70]{padding:12px;border-bottom:1px solid #f0f0f0}.forwarded-message-item[data-v-fc970c70]:last-child{border-bottom:none}.forwarded-message-header[data-v-fc970c70]{display:flex;align-items:center;margin-bottom:6px}.forwarded-message-nickname[data-v-fc970c70]{font-weight:600;color:#333;font-size:13px}.forwarded-message-userid[data-v-fc970c70]{color:#999;font-size:11px;margin-left:6px}.forwarded-message-content[data-v-fc970c70]{color:#555;font-size:13px;line-height:1.4}.message-at[data-v-fc970c70]{background:#2196f31a;color:#2196f3;padding:2px 4px;border-radius:3px;font-weight:500;cursor:pointer}.message-unknown[data-v-fc970c70]{background:#ff98001a;color:#ff9800;padding:2px 4px;border-radius:3px;font-style:italic;cursor:help}.input-row input[data-v-fc970c70]{flex:1;min-width:0;padding:8px 12px;border:1px solid rgba(128,128,128,.3);border-radius:4px;outline:none;background:transparent;color:currentColor}.input-row input[data-v-fc970c70]:focus{border-color:#2196f3;box-shadow:0 0 0 2px #2196f333}.input-row input[data-v-fc970c70]::placeholder{color:currentColor;opacity:.5}.input-row button[data-v-fc970c70]{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-fc970c70]:hover:not(:disabled){background:#1976d2}.input-row button[data-v-fc970c70]:disabled{background:#8080804d;cursor:not-allowed}.input-row button.is-sending[data-v-fc970c70]{background:#ffc107!important;color:#333!important;cursor:default!important}.message-input .input-row button.is-sending[data-v-fc970c70]{background:#ffc107!important;color:#333!important;cursor:default!important;border-color:#ffc107!important}.empty-state[data-v-fc970c70]{display:flex;align-items:center;justify-content:center;height:100%;color:currentColor;opacity:.5;font-size:14px}.message-text-content[data-v-fc970c70]{display:inline}.message-image-container[data-v-fc970c70]{display:block;margin:4px 0}.message-image[data-v-fc970c70]{border-radius:8px;margin:4px 0;display:block;cursor:pointer;transition:transform .2s ease;object-fit:contain;background:#8080801a}.message-image[data-v-fc970c70]:hover{transform:scale(1.02)}.message-image-loading[data-v-fc970c70]{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-fc970c70]{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-fc970c70]{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-fc970c70]:hover{background:#2196f333}.message-unknown[data-v-fc970c70]{background:#ff98001a;color:#ff9800;padding:2px 6px;border-radius:4px;font-size:12px;display:inline-block;margin:0 2px;cursor:help}@keyframes pulse-fc970c70{0%,to{transform:translate(-50%,-50%) scale(1)}50%{transform:translate(-50%,-50%) scale(1.1)}}.channel-message-count.will-delete[data-v-fc970c70]{background:#f44336!important;animation:shake-fc970c70 .3s ease-in-out infinite}@keyframes shake-fc970c70{0%,to{transform:translate(0)}25%{transform:translate(-2px)}75%{transform:translate(2px)}}body.dragging-bubble-global[data-v-fc970c70]{user-select:none;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;cursor:grabbing}.drag-threshold-circle[data-v-fc970c70]{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-fc970c70 1.5s ease-in-out infinite}.context-menu[data-v-fc970c70]{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-fc970c70]{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-fc970c70]:last-child{border-bottom:none}.context-menu-item[data-v-fc970c70]:hover{background:#2196f31a;color:#1976d2;transform:translate(2px)}.context-menu-item.danger[data-v-fc970c70]{color:#d32f2f}.context-menu-item.danger[data-v-fc970c70]:hover{background:#f443361a;color:#c62828}.bot-item.pinned[data-v-fc970c70],.channel-item.pinned[data-v-fc970c70]{background:#ffc1071a;border-left:3px solid #ffc107}.bot-item.pinned[data-v-fc970c70]:before,.channel-item.pinned[data-v-fc970c70]:before{content:"📌";position:absolute;left:4px;top:50%;transform:translateY(-50%);font-size:12px;opacity:.7}.bot-item.pinned[data-v-fc970c70],.channel-item.pinned[data-v-fc970c70]{position:relative;padding-left:24px}@media (prefers-color-scheme: dark){.context-menu[data-v-fc970c70]{background:#ffffff26;border-color:#fff3;box-shadow:0 8px 32px #0006}.context-menu-item[data-v-fc970c70]{color:#fff;border-bottom-color:#ffffff1a}.context-menu-item[data-v-fc970c70]:hover{background:#2196f333;color:#64b5f6}.context-menu-item.danger[data-v-fc970c70]{color:#ff8a80}.context-menu-item.danger[data-v-fc970c70]:hover{background:#f4433633;color:#ffab91}.bot-item.pinned[data-v-fc970c70],.channel-item.pinned[data-v-fc970c70]{background:#ffc10726}}.message-share-card[data-v-fc970c70]{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-fc970c70]:hover{transform:translateY(-2px);box-shadow:0 8px 24px #00000026;border-color:#2196f34d}.share-card-preview[data-v-fc970c70]{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-fc970c70]{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-fc970c70]{transform:scale(1.05)}.share-card-content[data-v-fc970c70]{padding:8px}.share-card-header[data-v-fc970c70]{display:flex;align-items:center;margin-bottom:12px;gap:12px}.share-card-icon[data-v-fc970c70]{width:32px;height:32px;border-radius:8px;object-fit:cover;flex-shrink:0}.share-card-app-info[data-v-fc970c70]{flex:1;min-width:0}.share-card-app-name[data-v-fc970c70]{font-size:12px;color:currentColor;opacity:.7;margin-bottom:2px;font-weight:500}.share-card-title[data-v-fc970c70]{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-fc970c70]{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-fc970c70]{font-size:12px;color:#2196f3;font-weight:500;display:flex;align-items:center;gap:4px}.share-card-link-hint[data-v-fc970c70]:after{content:"→";transition:transform .2s ease}.message-share-card:hover .share-card-link-hint[data-v-fc970c70]:after{transform:translate(2px)}.message-json-error[data-v-fc970c70],.message-json-raw[data-v-fc970c70]{display:inline-block;margin:8px 0;max-width:400px}.json-error-text[data-v-fc970c70]{color:#f44336;font-size:14px;font-weight:500}.json-label[data-v-fc970c70]{color:currentColor;opacity:.7;font-size:12px;font-weight:500;margin-bottom:8px}.json-raw-data[data-v-fc970c70]{margin-top:8px}.json-raw-data summary[data-v-fc970c70]{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-fc970c70]:hover{background:#2196f333}.json-raw-content[data-v-fc970c70]{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;max-height:200px;overflow-y:auto;border:1px solid rgba(128,128,128,.2)}@media (prefers-color-scheme: dark){.message-share-card[data-v-fc970c70]{background:#ffffff14;border-color:#ffffff26}.message-share-card[data-v-fc970c70]:hover{border-color:#2196f380;box-shadow:0 8px 24px #0000004d}.json-raw-content[data-v-fc970c70]{background:#ffffff0d;border-color:#ffffff1a}}@media (max-width: 768px){.chat-container[data-v-fc970c70]{position:relative}.bot-list[data-v-fc970c70],.channel-list[data-v-fc970c70],.message-area[data-v-fc970c70]{position:absolute;top:0;left:0;width:100%;height:100%;transition:transform .3s ease;z-index:1}.bot-list[data-v-fc970c70]{transform:translate(0);z-index:3}.channel-list[data-v-fc970c70]{transform:translate(100%);z-index:2}.message-area[data-v-fc970c70]{transform:translate(200%);z-index:1}.chat-container.show-channels .bot-list[data-v-fc970c70]{transform:translate(-100%)}.chat-container.show-channels .channel-list[data-v-fc970c70]{transform:translate(0);z-index:3}.chat-container.show-channels .message-area[data-v-fc970c70]{transform:translate(100%)}.chat-container.show-messages .bot-list[data-v-fc970c70]{transform:translate(-200%)}.chat-container.show-messages .channel-list[data-v-fc970c70]{transform:translate(-100%)}.chat-container.show-messages .message-area[data-v-fc970c70]{transform:translate(0);z-index:3}.back-button[data-v-fc970c70]{display:none!important}.panel-header[data-v-fc970c70]{flex-direction:row-reverse;position:sticky;top:0;z-index:5;background:inherit}.panel-header h3[data-v-fc970c70]{text-align:right}.floating-scroll-button[data-v-fc970c70]{right:16px;bottom:70px;width:40px;height:40px}.message-input[data-v-fc970c70]{padding:8px 12px 12px;position:fixed;bottom:0;left:0;right:0;background:inherit;border-top:1px solid rgba(128,128,128,.2);z-index:10}.input-row input[data-v-fc970c70]{font-size:16px;padding:10px 12px}.input-row button[data-v-fc970c70]{padding:10px 16px;font-size:16px}.message-history[data-v-fc970c70]{padding:12px 12px 80px;max-height:calc(100vh - 140px)}.message-item[data-v-fc970c70]{margin-bottom:12px;padding:6px}.message-avatar[data-v-fc970c70]{width:28px;height:28px;margin-right:8px}.message-item.bot-message .message-avatar[data-v-fc970c70]{margin-left:8px}.message-image[data-v-fc970c70]{max-width:280px;max-height:160px}.quote-content .message-image[data-v-fc970c70]{max-width:150px;max-height:80px}.context-menu[data-v-fc970c70]{min-width:160px;backdrop-filter:blur(12px);-webkit-backdrop-filter:blur(12px)}.context-menu-item[data-v-fc970c70]{padding:14px 16px;font-size:16px}.bot-item[data-v-fc970c70],.channel-item[data-v-fc970c70]{padding:14px 16px}.bot-avatar[data-v-fc970c70]{width:36px;height:36px;margin-right:10px}.bot-name[data-v-fc970c70],.channel-name[data-v-fc970c70]{font-size:15px}.bot-platform[data-v-fc970c70],.channel-type[data-v-fc970c70]{font-size:13px}}@media (min-width: 769px) and (max-width: 1024px){.bot-list[data-v-fc970c70]{width:220px;min-width:220px;max-width:220px}.channel-list[data-v-fc970c70]{width:280px;min-width:280px;max-width:280px}.message-image[data-v-fc970c70]{max-width:350px;max-height:180px}}.chat-container.swiping[data-v-fc970c70]{overflow:hidden}.bot-list.swiping[data-v-fc970c70],.channel-list.swiping[data-v-fc970c70],.message-area.swiping[data-v-fc970c70]{transition:none!important}.swipe-indicator[data-v-fc970c70]{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-fc970c70]{opacity:1}.message-area .message-content[data-v-fc970c70]{padding-bottom:80px}.image-preview-container[data-v-fc970c70]{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-fc970c70]{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-fc970c70]{width:100%;height:100%;object-fit:cover;display:block}.remove-image-btn[data-v-fc970c70]{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-fc970c70]:hover{background:#d32f2f;transform:scale(1.1)}.input-actions[data-v-fc970c70]{position:relative;margin-right:8px}.add-button[data-v-fc970c70]{width:36px;height:36px;border-radius:12px;background:linear-gradient(135deg,#667eea,#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-fc970c70]:before{content:"";position:absolute;top:0;left:0;right:0;bottom:0;background:linear-gradient(135deg,#fff3,#ffffff1a);border-radius:12px;opacity:0;transition:opacity .3s ease}.add-button[data-v-fc970c70]:hover{transform:translateY(-2px) scale(1.05);box-shadow:0 4px 16px #667eea66}.add-button[data-v-fc970c70]:hover:before{opacity:1}.add-button.active[data-v-fc970c70]{background:linear-gradient(135deg,#f093fb,#f5576c);transform:translateY(-2px) scale(1.05) rotate(45deg);box-shadow:0 4px 16px #f5576c66}.add-button[data-v-fc970c70]:active{transform:translateY(0) scale(.98);transition:all .1s ease}.action-menu[data-v-fc970c70]{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-fc970c70 .3s cubic-bezier(.4,0,.2,1) forwards}.action-menu-item[data-v-fc970c70]{display:flex;align-items:center;gap:12px;padding:14px 18px;background:none;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-fc970c70]:before{content:"";position:absolute;left:0;top:0;bottom:0;width:3px;background:linear-gradient(135deg,#667eea,#764ba2);transform:scaleY(0);transition:transform .2s cubic-bezier(.4,0,.2,1)}.action-menu-item[data-v-fc970c70]:hover{background:linear-gradient(90deg,#667eea14,#764ba214);color:#667eea;transform:translate(4px)}.action-menu-item[data-v-fc970c70]:hover:before{transform:scaleY(1)}.action-menu-item svg[data-v-fc970c70]{flex-shrink:0}@media (prefers-color-scheme: dark){.action-menu[data-v-fc970c70]{background:#1e1e1efa;box-shadow:0 8px 32px #0000004d,0 2px 8px #0003}.action-menu-item[data-v-fc970c70]{color:#fff}.action-menu-item[data-v-fc970c70]:before{background:linear-gradient(135deg,#4facfe,#00f2fe)}.action-menu-item[data-v-fc970c70]:hover{background:linear-gradient(90deg,#4facfe1f,#00f2fe1f);color:#4facfe}.add-button[data-v-fc970c70]{background:linear-gradient(135deg,#4facfe,#00f2fe);box-shadow:0 2px 8px #4facfe4d}.add-button[data-v-fc970c70]:hover{box-shadow:0 4px 16px #4facfe66}.add-button.active[data-v-fc970c70]{background:linear-gradient(135deg,#fa709a,#fee140);box-shadow:0 4px 16px #fa709a66}.image-preview-item[data-v-fc970c70]{border-color:#64b5f680;background:#ffffff0d}}@media (max-width: 768px){.image-preview-container[data-v-fc970c70]{padding:6px 0;margin-bottom:6px}.image-preview-item[data-v-fc970c70]{width:50px;height:50px}.add-button[data-v-fc970c70]{width:32px;height:32px;border-radius:10px}.action-menu[data-v-fc970c70]{min-width:100px}.action-menu-item[data-v-fc970c70]{padding:10px 12px;font-size:13px}.remove-image-btn[data-v-fc970c70]{width:18px;height:18px;top:-5px;right:-5px}}.message-history[data-v-fc970c70]::-webkit-scrollbar{width:6px}.message-history[data-v-fc970c70]::-webkit-scrollbar-track{background:transparent}.message-history[data-v-fc970c70]::-webkit-scrollbar-thumb{background-color:#9093994d;border-radius:4px;transition:background-color .3s}.message-history[data-v-fc970c70]::-webkit-scrollbar-thumb:hover{background-color:#90939980}.bot-items[data-v-fc970c70]::-webkit-scrollbar{width:6px}.bot-items[data-v-fc970c70]::-webkit-scrollbar-track{background:transparent}.bot-items[data-v-fc970c70]::-webkit-scrollbar-thumb{background-color:#9093994d;border-radius:4px;transition:background-color .3s}.bot-items[data-v-fc970c70]::-webkit-scrollbar-thumb:hover{background-color:#90939980}.channel-items[data-v-fc970c70]::-webkit-scrollbar{width:6px}.channel-items[data-v-fc970c70]::-webkit-scrollbar-track{background:transparent}.channel-items[data-v-fc970c70]::-webkit-scrollbar-thumb{background-color:#9093994d;border-radius:4px;transition:background-color .3s}.channel-items[data-v-fc970c70]::-webkit-scrollbar-thumb:hover{background-color:#90939980}.forward-message-expanded[data-v-fc970c70]::-webkit-scrollbar{width:6px}.forward-message-expanded[data-v-fc970c70]::-webkit-scrollbar-track{background:transparent}.forward-message-expanded[data-v-fc970c70]::-webkit-scrollbar-thumb{background-color:#9093994d;border-radius:4px;transition:background-color .3s}.forward-message-expanded[data-v-fc970c70]::-webkit-scrollbar-thumb:hover{background-color:#90939980}.json-raw-content[data-v-fc970c70]::-webkit-scrollbar{width:6px}.json-raw-content[data-v-fc970c70]::-webkit-scrollbar-track{background:transparent}.json-raw-content[data-v-fc970c70]::-webkit-scrollbar-thumb{background-color:#9093994d;border-radius:4px;transition:background-color .3s}.json-raw-content[data-v-fc970c70]::-webkit-scrollbar-thumb:hover{background-color:#90939980}@media (prefers-color-scheme: dark){.message-history[data-v-fc970c70]::-webkit-scrollbar-thumb,.bot-items[data-v-fc970c70]::-webkit-scrollbar-thumb,.channel-items[data-v-fc970c70]::-webkit-scrollbar-thumb,.forward-message-expanded[data-v-fc970c70]::-webkit-scrollbar-thumb,.json-raw-content[data-v-fc970c70]::-webkit-scrollbar-thumb{background-color:#fff3}.message-history[data-v-fc970c70]::-webkit-scrollbar-thumb:hover,.bot-items[data-v-fc970c70]::-webkit-scrollbar-thumb:hover,.channel-items[data-v-fc970c70]::-webkit-scrollbar-thumb:hover,.forward-message-expanded[data-v-fc970c70]::-webkit-scrollbar-thumb:hover,.json-raw-content[data-v-fc970c70]::-webkit-scrollbar-thumb:hover{background-color:#fff6}}.layout-iframe{width:100%;height:100%;border:none}
|
|
1
|
+
.message-quote[data-v-fbeb5b9c]{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-fbeb5b9c]{font-size:.75em;color:#007bff;font-weight:600;margin-bottom:4px;text-transform:uppercase}.quote-header[data-v-fbeb5b9c]{display:flex;align-items:center;margin-bottom:4px;gap:6px}.quote-avatar[data-v-fbeb5b9c]{width:20px;height:20px;border-radius:50%;overflow:hidden;flex-shrink:0}.quote-avatar img[data-v-fbeb5b9c]{width:100%;height:100%;object-fit:cover}.quote-avatar .avatar-placeholder[data-v-fbeb5b9c]{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-fbeb5b9c]{font-weight:600;color:#007bff;font-size:.85em}.quote-time[data-v-fbeb5b9c]{color:#6c757d;font-size:.75em;margin-left:auto}.quote-content[data-v-fbeb5b9c]{color:#7276b7;line-height:1.3;word-wrap:break-word}.quote-content .message-image[data-v-fbeb5b9c]{max-width:200px;max-height:100px}.quote-content .message-image-container[data-v-fbeb5b9c]{margin:2px 0}@media (prefers-color-scheme: dark){.message-quote[data-v-fbeb5b9c]{background-color:#ffffff14;border-left-color:#0d6efd}.quote-content[data-v-fbeb5b9c]{color:#adb5bd}.quote-username[data-v-fbeb5b9c]{color:#0d6efd}}.chat-container[data-v-fbeb5b9c]{display:flex;height:100vh;background:transparent;margin-left:0%;width:100%;overflow:hidden;color:inherit;position:relative}.bot-list[data-v-fbeb5b9c],.channel-list[data-v-fbeb5b9c],.message-area[data-v-fbeb5b9c]{display:flex;flex-direction:column;background:transparent;border-right:1px solid rgba(128,128,128,.2)}.bot-list[data-v-fbeb5b9c]{width:250px;min-width:250px;max-width:250px;flex-shrink:0}.channel-list[data-v-fbeb5b9c]{width:300px;min-width:300px;max-width:300px;flex-shrink:0}.message-area[data-v-fbeb5b9c]{flex:1;border-right:none}.panel-header[data-v-fbeb5b9c]{padding:16px;border-bottom:1px solid rgba(128,128,128,.2);background:transparent;display:flex;align-items:center;justify-content:space-between}.panel-header h3[data-v-fbeb5b9c]{margin:0;font-size:16px;font-weight:600;color:currentColor}.back-button[data-v-fbeb5b9c]{display:none;background:none;border:none;color:currentColor;font-size:18px;cursor:pointer;padding:4px 8px;border-radius:4px;transition:background-color .2s}.back-button[data-v-fbeb5b9c]:hover{background:#8080801a}.bot-items[data-v-fbeb5b9c],.channel-items[data-v-fbeb5b9c]{flex:1;overflow-y:auto;max-height:calc(100vh - 60px)}.bot-item[data-v-fbeb5b9c],.channel-item[data-v-fbeb5b9c]{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-fbeb5b9c]:hover,.channel-item[data-v-fbeb5b9c]:hover{background:#8080801a}.bot-item.active[data-v-fbeb5b9c],.channel-item.active[data-v-fbeb5b9c]{background:#2196f31a;border-right:3px solid #2196f3}.bot-avatar[data-v-fbeb5b9c]{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-fbeb5b9c]{width:100%;height:100%;object-fit:cover}.avatar-placeholder[data-v-fbeb5b9c]{font-size:16px;font-weight:700;color:currentColor;opacity:.7}.bot-info[data-v-fbeb5b9c]{flex:1}.bot-name[data-v-fbeb5b9c]{font-weight:500;color:currentColor;margin-bottom:2px}.bot-platform[data-v-fbeb5b9c]{font-size:12px;color:currentColor;opacity:.7}.bot-status[data-v-fbeb5b9c]{width:8px;height:8px;border-radius:50%;margin-left:8px}.bot-status.online[data-v-fbeb5b9c]{background:#4caf50}.bot-status.offline[data-v-fbeb5b9c]{background:#f44336}.channel-info[data-v-fbeb5b9c]{flex:1}.channel-name[data-v-fbeb5b9c]{font-weight:500;color:currentColor;margin-bottom:2px}.channel-type[data-v-fbeb5b9c]{font-size:12px;color:currentColor;opacity:.7}.channel-message-count[data-v-fbeb5b9c]{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-fbeb5b9c]{cursor:grab;user-select:none;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none}.channel-message-count.draggable-bubble[data-v-fbeb5b9c]:hover{background:#1976d2;transform:scale(1.1);box-shadow:0 2px 8px #2196f34d}.channel-message-count.draggable-bubble[data-v-fbeb5b9c]:active,.channel-message-count.draggable-bubble.dragging[data-v-fbeb5b9c]{cursor:grabbing;background:#1565c0;box-shadow:0 4px 12px #2196f366}.dragging-clone[data-v-fbeb5b9c]{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-fbeb5b9c]{background:#f44336!important;animation:shake-fbeb5b9c .3s ease-in-out infinite}@keyframes pulseThreshold-fbeb5b9c{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-fbeb5b9c{0%{transform:translateY(8px);opacity:0}to{transform:translateY(0);opacity:1}}.message-content[data-v-fbeb5b9c]{display:flex;flex-direction:column;height:calc(100vh - 60px);overflow:hidden;position:relative}.message-history[data-v-fbeb5b9c]{flex:1;overflow-y:auto;padding:16px 16px 8px;max-height:calc(100vh - 200px)}.message-item[data-v-fbeb5b9c]{display:flex;margin-bottom:16px;padding:8px;border-radius:8px;transition:background-color .3s ease}.message-item[data-v-fbeb5b9c]:hover{background-color:#8080801a}.message-item.new-message[data-v-fbeb5b9c]{background-color:#4caf5033;animation:highlight-fbeb5b9c 2s ease-out}.message-item.bot-message[data-v-fbeb5b9c]{flex-direction:row-reverse;justify-content:flex-start}.message-item.bot-message .message-avatar[data-v-fbeb5b9c]{margin-right:0;margin-left:12px}.message-item.bot-message .message-content-wrapper[data-v-fbeb5b9c]{display:flex;flex-direction:column;align-items:flex-end}.message-item.bot-message .message-header[data-v-fbeb5b9c]{display:flex;align-items:center;justify-content:flex-end;margin-bottom:4px}.message-item.bot-message .message-username[data-v-fbeb5b9c]{margin-right:0;margin-left:8px;order:2}.message-item.bot-message .message-time[data-v-fbeb5b9c]{order:1}.message-item.bot-message .message-text[data-v-fbeb5b9c]{background-color:#2196f31a;padding:8px 12px;border-radius:12px 12px 4px;display:inline-block;max-width:80%;text-align:left}@keyframes highlight-fbeb5b9c{0%{background-color:#4caf504d}to{background-color:transparent}}.message-avatar[data-v-fbeb5b9c]{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-fbeb5b9c]{width:100%;height:100%;object-fit:cover}.message-avatar .avatar-placeholder[data-v-fbeb5b9c]{font-size:12px;font-weight:700;color:currentColor;opacity:.7}.message-content-wrapper[data-v-fbeb5b9c]{flex:1}.message-header[data-v-fbeb5b9c]{display:flex;align-items:center;margin-bottom:4px}.message-username[data-v-fbeb5b9c]{font-weight:500;color:currentColor;margin-right:8px}.message-time[data-v-fbeb5b9c]{font-size:12px;color:currentColor;opacity:.5}.message-text[data-v-fbeb5b9c]{color:currentColor;line-height:1.4;word-wrap:break-word;word-break:break-word;white-space:pre-wrap;max-width:100%;overflow-wrap:break-word}.message-item:not(.bot-message) .message-text[data-v-fbeb5b9c]{background-color:#8080801a;padding:8px 12px;border-radius:12px 12px 12px 4px;display:inline-block;max-width:80%}.message-input[data-v-fbeb5b9c]{display:flex;flex-direction:column;padding:12px 16px 16px;border-top:1px solid rgba(128,128,128,.2);background:transparent;flex-shrink:0;position:sticky;bottom:0;margin-top:-10px}.input-row[data-v-fbeb5b9c]{display:flex;gap:8px;align-items:center}.floating-scroll-button[data-v-fbeb5b9c]{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-fbeb5b9c]:hover{background:#1976d2;transform:scale(1.1);box-shadow:0 6px 16px #2196f366}.floating-scroll-button[data-v-fbeb5b9c]:active{transform:scale(.95)}.input-row button[data-v-fbeb5b9c]{padding:8px 16px;background:#2196f3;color:#fff;border:none;border-radius:4px;cursor:pointer;transition:background-color .2s}.message-image-container[data-v-fbeb5b9c]{margin:8px 0;max-width:100%;display:block}.message-image[data-v-fbeb5b9c]{border-radius:8px;object-fit:contain;display:block;cursor:pointer;transition:transform .2s ease}.message-image-loading[data-v-fbeb5b9c]{display:inline-block;padding:8px 12px;background:#8080801a;border-radius:4px;color:currentColor;opacity:.7;font-size:12px}.message-image-error[data-v-fbeb5b9c]{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-fbeb5b9c]{display:block;margin-bottom:4px}.message-paragraph[data-v-fbeb5b9c]:last-child{margin-bottom:0}.forward-message-container[data-v-fbeb5b9c]{border:1px solid #ffffff;border-radius:8px;margin:4px 0;overflow:hidden;background:#f9f9f900}.forward-message-preview[data-v-fbeb5b9c]{padding:12px;cursor:pointer;transition:background-color .2s}.forward-message-preview[data-v-fbeb5b9c]:hover{background:#f0f0f0}.forward-message-title[data-v-fbeb5b9c]{font-weight:600;color:#333;margin-bottom:8px;font-size:14px}.forward-message-preview-item[data-v-fbeb5b9c]{color:#666;font-size:13px;margin:2px 0;line-height:1.4}.forward-message-footer[data-v-fbeb5b9c]{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-fbeb5b9c]{color:#2196f3;font-size:12px}.forward-message-toggle[data-v-fbeb5b9c]{color:#666;font-size:12px}.forward-message-expanded[data-v-fbeb5b9c]{border-top:1px solid #e0e0e0;background:#fff;max-height:400px;overflow-y:auto}.forwarded-message-item[data-v-fbeb5b9c]{padding:12px;border-bottom:1px solid #f0f0f0}.forwarded-message-item[data-v-fbeb5b9c]:last-child{border-bottom:none}.forwarded-message-header[data-v-fbeb5b9c]{display:flex;align-items:center;margin-bottom:6px}.forwarded-message-nickname[data-v-fbeb5b9c]{font-weight:600;color:#333;font-size:13px}.forwarded-message-userid[data-v-fbeb5b9c]{color:#999;font-size:11px;margin-left:6px}.forwarded-message-content[data-v-fbeb5b9c]{color:#555;font-size:13px;line-height:1.4}.message-at[data-v-fbeb5b9c]{background:#2196f31a;color:#2196f3;padding:2px 4px;border-radius:3px;font-weight:500;cursor:pointer}.message-unknown[data-v-fbeb5b9c]{background:#ff98001a;color:#ff9800;padding:2px 4px;border-radius:3px;font-style:italic;cursor:help}.input-row input[data-v-fbeb5b9c]{flex:1;min-width:0;padding:8px 12px;border:1px solid rgba(128,128,128,.3);border-radius:4px;outline:none;background:transparent;color:currentColor}.input-row input[data-v-fbeb5b9c]:focus{border-color:#2196f3;box-shadow:0 0 0 2px #2196f333}.input-row input[data-v-fbeb5b9c]::placeholder{color:currentColor;opacity:.5}.input-row button[data-v-fbeb5b9c]{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-fbeb5b9c]:hover:not(:disabled){background:#1976d2}.input-row button[data-v-fbeb5b9c]:disabled{background:#8080804d;cursor:not-allowed}.input-row button.is-sending[data-v-fbeb5b9c]{background:#ffc107!important;color:#333!important;cursor:default!important}.message-input .input-row button.is-sending[data-v-fbeb5b9c]{background:#ffc107!important;color:#333!important;cursor:default!important;border-color:#ffc107!important}.empty-state[data-v-fbeb5b9c]{display:flex;align-items:center;justify-content:center;height:100%;color:currentColor;opacity:.5;font-size:14px}.message-text-content[data-v-fbeb5b9c]{display:inline}.message-image-container[data-v-fbeb5b9c]{display:block;margin:4px 0}.message-image[data-v-fbeb5b9c]{border-radius:8px;margin:4px 0;display:block;cursor:pointer;transition:transform .2s ease;object-fit:contain;background:#8080801a}.message-image[data-v-fbeb5b9c]:hover{transform:scale(1.02)}.message-image-loading[data-v-fbeb5b9c]{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-fbeb5b9c]{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-fbeb5b9c]{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-fbeb5b9c]:hover{background:#2196f333}.message-unknown[data-v-fbeb5b9c]{background:#ff98001a;color:#ff9800;padding:2px 6px;border-radius:4px;font-size:12px;display:inline-block;margin:0 2px;cursor:help}@keyframes pulse-fbeb5b9c{0%,to{transform:translate(-50%,-50%) scale(1)}50%{transform:translate(-50%,-50%) scale(1.1)}}.channel-message-count.will-delete[data-v-fbeb5b9c]{background:#f44336!important;animation:shake-fbeb5b9c .3s ease-in-out infinite}@keyframes shake-fbeb5b9c{0%,to{transform:translate(0)}25%{transform:translate(-2px)}75%{transform:translate(2px)}}body.dragging-bubble-global[data-v-fbeb5b9c]{user-select:none;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;cursor:grabbing}.drag-threshold-circle[data-v-fbeb5b9c]{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-fbeb5b9c 1.5s ease-in-out infinite}.context-menu[data-v-fbeb5b9c]{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-fbeb5b9c]{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-fbeb5b9c]:last-child{border-bottom:none}.context-menu-item[data-v-fbeb5b9c]:hover{background:#2196f31a;color:#1976d2;transform:translate(2px)}.context-menu-item.danger[data-v-fbeb5b9c]{color:#d32f2f}.context-menu-item.danger[data-v-fbeb5b9c]:hover{background:#f443361a;color:#c62828}.bot-item.pinned[data-v-fbeb5b9c],.channel-item.pinned[data-v-fbeb5b9c]{background:#ffc1071a;border-left:3px solid #ffc107}.bot-item.pinned[data-v-fbeb5b9c]:before,.channel-item.pinned[data-v-fbeb5b9c]:before{content:"📌";position:absolute;left:4px;top:50%;transform:translateY(-50%);font-size:12px;opacity:.7}.bot-item.pinned[data-v-fbeb5b9c],.channel-item.pinned[data-v-fbeb5b9c]{position:relative;padding-left:24px}@media (prefers-color-scheme: dark){.context-menu[data-v-fbeb5b9c]{background:#ffffff26;border-color:#fff3;box-shadow:0 8px 32px #0006}.context-menu-item[data-v-fbeb5b9c]{color:#fff;border-bottom-color:#ffffff1a}.context-menu-item[data-v-fbeb5b9c]:hover{background:#2196f333;color:#64b5f6}.context-menu-item.danger[data-v-fbeb5b9c]{color:#ff8a80}.context-menu-item.danger[data-v-fbeb5b9c]:hover{background:#f4433633;color:#ffab91}.bot-item.pinned[data-v-fbeb5b9c],.channel-item.pinned[data-v-fbeb5b9c]{background:#ffc10726}}.message-share-card[data-v-fbeb5b9c]{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-fbeb5b9c]:hover{transform:translateY(-2px);box-shadow:0 8px 24px #00000026;border-color:#2196f34d}.share-card-preview[data-v-fbeb5b9c]{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-fbeb5b9c]{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-fbeb5b9c]{transform:scale(1.05)}.share-card-content[data-v-fbeb5b9c]{padding:8px}.share-card-header[data-v-fbeb5b9c]{display:flex;align-items:center;margin-bottom:12px;gap:12px}.share-card-icon[data-v-fbeb5b9c]{width:32px;height:32px;border-radius:8px;object-fit:cover;flex-shrink:0}.share-card-app-info[data-v-fbeb5b9c]{flex:1;min-width:0}.share-card-app-name[data-v-fbeb5b9c]{font-size:12px;color:currentColor;opacity:.7;margin-bottom:2px;font-weight:500}.share-card-title[data-v-fbeb5b9c]{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-fbeb5b9c]{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-fbeb5b9c]{font-size:12px;color:#2196f3;font-weight:500;display:flex;align-items:center;gap:4px}.share-card-link-hint[data-v-fbeb5b9c]:after{content:"→";transition:transform .2s ease}.message-share-card:hover .share-card-link-hint[data-v-fbeb5b9c]:after{transform:translate(2px)}.message-json-error[data-v-fbeb5b9c],.message-json-raw[data-v-fbeb5b9c]{display:inline-block;margin:8px 0;max-width:400px}.json-error-text[data-v-fbeb5b9c]{color:#f44336;font-size:14px;font-weight:500}.json-label[data-v-fbeb5b9c]{color:currentColor;opacity:.7;font-size:12px;font-weight:500;margin-bottom:8px}.json-raw-data[data-v-fbeb5b9c]{margin-top:8px}.json-raw-data summary[data-v-fbeb5b9c]{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-fbeb5b9c]:hover{background:#2196f333}.json-raw-content[data-v-fbeb5b9c]{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;max-height:200px;overflow-y:auto;border:1px solid rgba(128,128,128,.2)}@media (prefers-color-scheme: dark){.message-share-card[data-v-fbeb5b9c]{background:#ffffff14;border-color:#ffffff26}.message-share-card[data-v-fbeb5b9c]:hover{border-color:#2196f380;box-shadow:0 8px 24px #0000004d}.json-raw-content[data-v-fbeb5b9c]{background:#ffffff0d;border-color:#ffffff1a}}@media (max-width: 768px){.chat-container[data-v-fbeb5b9c]{position:relative}.bot-list[data-v-fbeb5b9c],.channel-list[data-v-fbeb5b9c],.message-area[data-v-fbeb5b9c]{position:absolute;top:0;left:0;width:100%;height:100%;transition:transform .3s ease;z-index:1}.bot-list[data-v-fbeb5b9c]{transform:translate(0);z-index:3}.channel-list[data-v-fbeb5b9c]{transform:translate(100%);z-index:2}.message-area[data-v-fbeb5b9c]{transform:translate(200%);z-index:1}.chat-container.show-channels .bot-list[data-v-fbeb5b9c]{transform:translate(-100%)}.chat-container.show-channels .channel-list[data-v-fbeb5b9c]{transform:translate(0);z-index:3}.chat-container.show-channels .message-area[data-v-fbeb5b9c]{transform:translate(100%)}.chat-container.show-messages .bot-list[data-v-fbeb5b9c]{transform:translate(-200%)}.chat-container.show-messages .channel-list[data-v-fbeb5b9c]{transform:translate(-100%)}.chat-container.show-messages .message-area[data-v-fbeb5b9c]{transform:translate(0);z-index:3}.back-button[data-v-fbeb5b9c]{display:none!important}.panel-header[data-v-fbeb5b9c]{flex-direction:row-reverse;position:sticky;top:0;z-index:5;background:inherit}.panel-header h3[data-v-fbeb5b9c]{text-align:right}.floating-scroll-button[data-v-fbeb5b9c]{right:16px;bottom:70px;width:40px;height:40px}.message-input[data-v-fbeb5b9c]{padding:8px 12px 12px;position:fixed;bottom:0;left:0;right:0;background:inherit;border-top:1px solid rgba(128,128,128,.2);z-index:10}.input-row input[data-v-fbeb5b9c]{font-size:16px;padding:10px 12px}.input-row button[data-v-fbeb5b9c]{padding:10px 16px;font-size:16px}.message-history[data-v-fbeb5b9c]{padding:12px 12px 80px;max-height:calc(100vh - 140px)}.message-item[data-v-fbeb5b9c]{margin-bottom:12px;padding:6px}.message-avatar[data-v-fbeb5b9c]{width:28px;height:28px;margin-right:8px}.message-item.bot-message .message-avatar[data-v-fbeb5b9c]{margin-left:8px}.message-image[data-v-fbeb5b9c]{max-width:280px;max-height:160px}.quote-content .message-image[data-v-fbeb5b9c]{max-width:150px;max-height:80px}.context-menu[data-v-fbeb5b9c]{min-width:160px;backdrop-filter:blur(12px);-webkit-backdrop-filter:blur(12px)}.context-menu-item[data-v-fbeb5b9c]{padding:14px 16px;font-size:16px}.bot-item[data-v-fbeb5b9c],.channel-item[data-v-fbeb5b9c]{padding:14px 16px}.bot-avatar[data-v-fbeb5b9c]{width:36px;height:36px;margin-right:10px}.bot-name[data-v-fbeb5b9c],.channel-name[data-v-fbeb5b9c]{font-size:15px}.bot-platform[data-v-fbeb5b9c],.channel-type[data-v-fbeb5b9c]{font-size:13px}}@media (min-width: 769px) and (max-width: 1024px){.bot-list[data-v-fbeb5b9c]{width:220px;min-width:220px;max-width:220px}.channel-list[data-v-fbeb5b9c]{width:280px;min-width:280px;max-width:280px}.message-image[data-v-fbeb5b9c]{max-width:350px;max-height:180px}}.chat-container.swiping[data-v-fbeb5b9c]{overflow:hidden}.bot-list.swiping[data-v-fbeb5b9c],.channel-list.swiping[data-v-fbeb5b9c],.message-area.swiping[data-v-fbeb5b9c]{transition:none!important}.swipe-indicator[data-v-fbeb5b9c]{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-fbeb5b9c]{opacity:1}.message-area .message-content[data-v-fbeb5b9c]{padding-bottom:80px}.image-preview-container[data-v-fbeb5b9c]{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-fbeb5b9c]{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-fbeb5b9c]{width:100%;height:100%;object-fit:cover;display:block}.remove-image-btn[data-v-fbeb5b9c]{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-fbeb5b9c]:hover{background:#d32f2f;transform:scale(1.1)}.input-actions[data-v-fbeb5b9c]{position:relative;margin-right:8px}.add-button[data-v-fbeb5b9c]{width:36px;height:36px;border-radius:12px;background:linear-gradient(135deg,#667eea,#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-fbeb5b9c]:before{content:"";position:absolute;top:0;left:0;right:0;bottom:0;background:linear-gradient(135deg,#fff3,#ffffff1a);border-radius:12px;opacity:0;transition:opacity .3s ease}.add-button[data-v-fbeb5b9c]:hover{transform:translateY(-2px) scale(1.05);box-shadow:0 4px 16px #667eea66}.add-button[data-v-fbeb5b9c]:hover:before{opacity:1}.add-button.active[data-v-fbeb5b9c]{background:linear-gradient(135deg,#f093fb,#f5576c);transform:translateY(-2px) scale(1.05) rotate(45deg);box-shadow:0 4px 16px #f5576c66}.add-button[data-v-fbeb5b9c]:active{transform:translateY(0) scale(.98);transition:all .1s ease}.action-menu[data-v-fbeb5b9c]{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-fbeb5b9c .3s cubic-bezier(.4,0,.2,1) forwards}.action-menu-item[data-v-fbeb5b9c]{display:flex;align-items:center;gap:12px;padding:14px 18px;background:none;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-fbeb5b9c]:before{content:"";position:absolute;left:0;top:0;bottom:0;width:3px;background:linear-gradient(135deg,#667eea,#764ba2);transform:scaleY(0);transition:transform .2s cubic-bezier(.4,0,.2,1)}.action-menu-item[data-v-fbeb5b9c]:hover{background:linear-gradient(90deg,#667eea14,#764ba214);color:#667eea;transform:translate(4px)}.action-menu-item[data-v-fbeb5b9c]:hover:before{transform:scaleY(1)}.action-menu-item svg[data-v-fbeb5b9c]{flex-shrink:0}@media (prefers-color-scheme: dark){.action-menu[data-v-fbeb5b9c]{background:#1e1e1efa;box-shadow:0 8px 32px #0000004d,0 2px 8px #0003}.action-menu-item[data-v-fbeb5b9c]{color:#fff}.action-menu-item[data-v-fbeb5b9c]:before{background:linear-gradient(135deg,#4facfe,#00f2fe)}.action-menu-item[data-v-fbeb5b9c]:hover{background:linear-gradient(90deg,#4facfe1f,#00f2fe1f);color:#4facfe}.add-button[data-v-fbeb5b9c]{background:linear-gradient(135deg,#4facfe,#00f2fe);box-shadow:0 2px 8px #4facfe4d}.add-button[data-v-fbeb5b9c]:hover{box-shadow:0 4px 16px #4facfe66}.add-button.active[data-v-fbeb5b9c]{background:linear-gradient(135deg,#fa709a,#fee140);box-shadow:0 4px 16px #fa709a66}.image-preview-item[data-v-fbeb5b9c]{border-color:#64b5f680;background:#ffffff0d}}@media (max-width: 768px){.image-preview-container[data-v-fbeb5b9c]{padding:6px 0;margin-bottom:6px}.image-preview-item[data-v-fbeb5b9c]{width:50px;height:50px}.add-button[data-v-fbeb5b9c]{width:32px;height:32px;border-radius:10px}.action-menu[data-v-fbeb5b9c]{min-width:100px}.action-menu-item[data-v-fbeb5b9c]{padding:10px 12px;font-size:13px}.remove-image-btn[data-v-fbeb5b9c]{width:18px;height:18px;top:-5px;right:-5px}}.message-history[data-v-fbeb5b9c]::-webkit-scrollbar{width:6px}.message-history[data-v-fbeb5b9c]::-webkit-scrollbar-track{background:transparent}.message-history[data-v-fbeb5b9c]::-webkit-scrollbar-thumb{background-color:#9093994d;border-radius:4px;transition:background-color .3s}.message-history[data-v-fbeb5b9c]::-webkit-scrollbar-thumb:hover{background-color:#90939980}.bot-items[data-v-fbeb5b9c]::-webkit-scrollbar{width:6px}.bot-items[data-v-fbeb5b9c]::-webkit-scrollbar-track{background:transparent}.bot-items[data-v-fbeb5b9c]::-webkit-scrollbar-thumb{background-color:#9093994d;border-radius:4px;transition:background-color .3s}.bot-items[data-v-fbeb5b9c]::-webkit-scrollbar-thumb:hover{background-color:#90939980}.channel-items[data-v-fbeb5b9c]::-webkit-scrollbar{width:6px}.channel-items[data-v-fbeb5b9c]::-webkit-scrollbar-track{background:transparent}.channel-items[data-v-fbeb5b9c]::-webkit-scrollbar-thumb{background-color:#9093994d;border-radius:4px;transition:background-color .3s}.channel-items[data-v-fbeb5b9c]::-webkit-scrollbar-thumb:hover{background-color:#90939980}.forward-message-expanded[data-v-fbeb5b9c]::-webkit-scrollbar{width:6px}.forward-message-expanded[data-v-fbeb5b9c]::-webkit-scrollbar-track{background:transparent}.forward-message-expanded[data-v-fbeb5b9c]::-webkit-scrollbar-thumb{background-color:#9093994d;border-radius:4px;transition:background-color .3s}.forward-message-expanded[data-v-fbeb5b9c]::-webkit-scrollbar-thumb:hover{background-color:#90939980}.json-raw-content[data-v-fbeb5b9c]::-webkit-scrollbar{width:6px}.json-raw-content[data-v-fbeb5b9c]::-webkit-scrollbar-track{background:transparent}.json-raw-content[data-v-fbeb5b9c]::-webkit-scrollbar-thumb{background-color:#9093994d;border-radius:4px;transition:background-color .3s}.json-raw-content[data-v-fbeb5b9c]::-webkit-scrollbar-thumb:hover{background-color:#90939980}@media (prefers-color-scheme: dark){.message-history[data-v-fbeb5b9c]::-webkit-scrollbar-thumb,.bot-items[data-v-fbeb5b9c]::-webkit-scrollbar-thumb,.channel-items[data-v-fbeb5b9c]::-webkit-scrollbar-thumb,.forward-message-expanded[data-v-fbeb5b9c]::-webkit-scrollbar-thumb,.json-raw-content[data-v-fbeb5b9c]::-webkit-scrollbar-thumb{background-color:#fff3}.message-history[data-v-fbeb5b9c]::-webkit-scrollbar-thumb:hover,.bot-items[data-v-fbeb5b9c]::-webkit-scrollbar-thumb:hover,.channel-items[data-v-fbeb5b9c]::-webkit-scrollbar-thumb:hover,.forward-message-expanded[data-v-fbeb5b9c]::-webkit-scrollbar-thumb:hover,.json-raw-content[data-v-fbeb5b9c]::-webkit-scrollbar-thumb:hover{background-color:#fff6}}.layout-iframe{width:100%;height:100%;border:none}
|
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.0.9",
|
|
5
5
|
"main": "lib/index.js",
|
|
6
6
|
"typings": "lib/index.d.ts",
|
|
7
7
|
"files": [
|
package/lib/api-handlers.d.ts
DELETED
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
import { Context } from 'koishi';
|
|
2
|
-
import { Config } from './config';
|
|
3
|
-
import { FileManager } from './file-manager';
|
|
4
|
-
export declare class ApiHandlers {
|
|
5
|
-
private ctx;
|
|
6
|
-
private config;
|
|
7
|
-
private fileManager;
|
|
8
|
-
private logger;
|
|
9
|
-
constructor(ctx: Context, config: Config, fileManager: FileManager);
|
|
10
|
-
registerApiHandlers(): void;
|
|
11
|
-
private isFileUrl;
|
|
12
|
-
private createFileUrl;
|
|
13
|
-
private handleLocalFileRequest;
|
|
14
|
-
private setupTempFileCleanup;
|
|
15
|
-
private cleanupTempImagesByCount;
|
|
16
|
-
private logInfo;
|
|
17
|
-
}
|
package/lib/config.d.ts
DELETED
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
import { Schema } from 'koishi';
|
|
2
|
-
export interface Config {
|
|
3
|
-
loggerinfo: boolean;
|
|
4
|
-
maxMessagesPerChannel: number;
|
|
5
|
-
keepMessagesOnClear: number;
|
|
6
|
-
keepTempImages: number;
|
|
7
|
-
blockedPlatforms: Array<{
|
|
8
|
-
platformName: string;
|
|
9
|
-
exactMatch: boolean;
|
|
10
|
-
}>;
|
|
11
|
-
chatContainerHeight: number;
|
|
12
|
-
}
|
|
13
|
-
export declare const Config: Schema<Config>;
|
package/lib/file-manager.d.ts
DELETED
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
import { Context } from 'koishi';
|
|
2
|
-
import { ChatData, MessageInfo } from './types';
|
|
3
|
-
import { Config } from './config';
|
|
4
|
-
export declare class FileManager {
|
|
5
|
-
private ctx;
|
|
6
|
-
private config;
|
|
7
|
-
private dataFilePath;
|
|
8
|
-
private fileOperationLock;
|
|
9
|
-
private logger;
|
|
10
|
-
private utils;
|
|
11
|
-
constructor(ctx: Context, config: Config);
|
|
12
|
-
private ensureDataDir;
|
|
13
|
-
readChatDataFromFile(): ChatData;
|
|
14
|
-
writeChatDataToFile(data: ChatData): void;
|
|
15
|
-
cleanExcessMessages(data: ChatData): ChatData;
|
|
16
|
-
addMessageToFile(messageInfo: MessageInfo): Promise<void>;
|
|
17
|
-
private logInfo;
|
|
18
|
-
}
|
package/lib/index.d.ts
DELETED
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
import { Context } from 'koishi';
|
|
2
|
-
import { Config } from './config';
|
|
3
|
-
export declare const name = "chat-patch";
|
|
4
|
-
export declare const reusable = false;
|
|
5
|
-
export declare const filter = false;
|
|
6
|
-
export declare const inject: {
|
|
7
|
-
required: string[];
|
|
8
|
-
};
|
|
9
|
-
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";
|
|
10
|
-
export { Config } from './config';
|
|
11
|
-
export declare function apply(ctx: Context, config: Config): Promise<void>;
|
package/lib/message-handler.d.ts
DELETED
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
import { Context, Session } from 'koishi';
|
|
2
|
-
import { Config } from './config';
|
|
3
|
-
import { FileManager } from './file-manager';
|
|
4
|
-
export declare class MessageHandler {
|
|
5
|
-
private ctx;
|
|
6
|
-
private config;
|
|
7
|
-
private fileManager;
|
|
8
|
-
private logger;
|
|
9
|
-
private utils;
|
|
10
|
-
constructor(ctx: Context, config: Config, fileManager: FileManager);
|
|
11
|
-
updateBotInfoToFile(session: Session): Promise<void>;
|
|
12
|
-
updateChannelInfoToFile(session: Session): Promise<string>;
|
|
13
|
-
broadcastMessageEvent(session: Session): Promise<void>;
|
|
14
|
-
broadcastBotMessageEvent(session: Session): Promise<void>;
|
|
15
|
-
private logInfo;
|
|
16
|
-
}
|
package/lib/types.d.ts
DELETED
|
@@ -1,53 +0,0 @@
|
|
|
1
|
-
import { h } from 'koishi';
|
|
2
|
-
export interface BotInfo {
|
|
3
|
-
selfId: string;
|
|
4
|
-
platform: string;
|
|
5
|
-
username: string;
|
|
6
|
-
avatar?: string;
|
|
7
|
-
status: 'online' | 'offline';
|
|
8
|
-
}
|
|
9
|
-
export interface ChannelInfo {
|
|
10
|
-
id: string;
|
|
11
|
-
name: string;
|
|
12
|
-
type: number | string;
|
|
13
|
-
guildId?: string;
|
|
14
|
-
guildName?: string;
|
|
15
|
-
}
|
|
16
|
-
export interface QuoteInfo {
|
|
17
|
-
messageId: string;
|
|
18
|
-
id: string;
|
|
19
|
-
content: string;
|
|
20
|
-
elements?: h[];
|
|
21
|
-
user: {
|
|
22
|
-
id: string;
|
|
23
|
-
name: string;
|
|
24
|
-
userId: string;
|
|
25
|
-
avatar?: string;
|
|
26
|
-
username: string;
|
|
27
|
-
};
|
|
28
|
-
timestamp: number;
|
|
29
|
-
}
|
|
30
|
-
export interface MessageInfo {
|
|
31
|
-
id: string;
|
|
32
|
-
content: string;
|
|
33
|
-
userId: string;
|
|
34
|
-
username: string;
|
|
35
|
-
avatar?: string;
|
|
36
|
-
timestamp: number;
|
|
37
|
-
channelId: string;
|
|
38
|
-
selfId: string;
|
|
39
|
-
elements?: h[];
|
|
40
|
-
type: 'user' | 'bot';
|
|
41
|
-
guildId?: string;
|
|
42
|
-
guildName?: string;
|
|
43
|
-
platform: string;
|
|
44
|
-
quote?: QuoteInfo;
|
|
45
|
-
}
|
|
46
|
-
export interface ChatData {
|
|
47
|
-
bots: Record<string, BotInfo>;
|
|
48
|
-
channels: Record<string, Record<string, ChannelInfo>>;
|
|
49
|
-
messages: Record<string, MessageInfo[]>;
|
|
50
|
-
pinnedBots: string[];
|
|
51
|
-
pinnedChannels: string[];
|
|
52
|
-
lastSaveTime?: number;
|
|
53
|
-
}
|
package/lib/utils.d.ts
DELETED
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
import { Config } from './config';
|
|
2
|
-
export declare class Utils {
|
|
3
|
-
private config;
|
|
4
|
-
constructor(config: Config);
|
|
5
|
-
isPlatformBlocked(platform: string): boolean;
|
|
6
|
-
extractTextContent(elements: any[]): string;
|
|
7
|
-
private isBase64;
|
|
8
|
-
cleanBase64Content(obj: any): any;
|
|
9
|
-
}
|