koishi-plugin-chat-patch 3.0.2 → 5.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (194) hide show
  1. package/client/vue/index.vue +60 -634
  2. package/client/web/env.d.ts +56 -0
  3. package/client/web/index.html +21 -0
  4. package/client/web/package-lock.json +3419 -0
  5. package/client/web/package.json +62 -0
  6. package/client/web/public/bcui/css/color-dark.css +35 -0
  7. package/client/web/public/bcui/css/color-light.css +35 -0
  8. package/client/web/public/bcui/css/style.css +468 -0
  9. package/client/web/public/bcui/js/main.js +193 -0
  10. package/client/web/public/css/append-dark.css +13 -0
  11. package/client/web/public/css/append-light.css +13 -0
  12. package/client/web/public/css/append-napcat-dark.css +14 -0
  13. package/client/web/public/css/append-napcat-light.css +14 -0
  14. package/client/web/public/favicon.ico +0 -0
  15. package/client/web/public/img/icons/icon-for-fuck-apple.png +0 -0
  16. package/client/web/public/img/icons/icon-maskable.png +0 -0
  17. package/client/web/public/img/icons/icon.png +0 -0
  18. package/client/web/public/img/icons/icon.svg +10 -0
  19. package/client/web/public/robots.txt +2 -0
  20. package/client/web/public/sw.js +58 -0
  21. package/client/web/scripts/copy-bcui.mjs +17 -0
  22. package/client/web/src/App.vue +1127 -0
  23. package/client/web/src/assets/css/append/append_darwin.css +68 -0
  24. package/client/web/src/assets/css/append/append_full_vibrancy.css +290 -0
  25. package/client/web/src/assets/css/append/append_linux.css +125 -0
  26. package/client/web/src/assets/css/append/append_linux_vibrancy.css +22 -0
  27. package/client/web/src/assets/css/append/append_napcat.css +35 -0
  28. package/client/web/src/assets/css/append/append_new.css +843 -0
  29. package/client/web/src/assets/css/append/append_vibrancy.css +113 -0
  30. package/client/web/src/assets/css/append/append_win32.css +109 -0
  31. package/client/web/src/assets/css/append/mobile/append_mobile_horizontal.css +349 -0
  32. package/client/web/src/assets/css/append/mobile/append_mobile_vertical.css +96 -0
  33. package/client/web/src/assets/css/chat.css +2104 -0
  34. package/client/web/src/assets/css/msg.css +941 -0
  35. package/client/web/src/assets/css/options.css +811 -0
  36. package/client/web/src/assets/css/sys_notice.css +62 -0
  37. package/client/web/src/assets/css/view.css +2105 -0
  38. package/client/web/src/assets/font//346/226/271/346/255/243/345/226/265/345/221/234/344/275/223.ttf +0 -0
  39. package/client/web/src/assets/img/Vue.png +0 -0
  40. package/client/web/src/assets/img/cakes.svg +189 -0
  41. package/client/web/src/assets/img/icons/AppIcon.png +0 -0
  42. package/client/web/src/assets/img/icons/AppIconDark.png +0 -0
  43. package/client/web/src/assets/img/icons/CapacitorAppIcon.png +0 -0
  44. package/client/web/src/assets/img/icons/CapacitorAppIconDark.png +0 -0
  45. package/client/web/src/assets/img/icons/NapcatAppIcon.png +0 -0
  46. package/client/web/src/assets/img/icons/NapcatAppIconDark.png +0 -0
  47. package/client/web/src/assets/img/icons/exit.png +0 -0
  48. package/client/web/src/assets/img/icons/pen.png +0 -0
  49. package/client/web/src/assets/img/stars.svg +89 -0
  50. package/client/web/src/assets/img//346/234/237/345/276/205.png +0 -0
  51. package/client/web/src/assets/l10n/_About I10n.md +35 -0
  52. package/client/web/src/assets/l10n/_l10nconfig.json +7 -0
  53. package/client/web/src/assets/l10n/zh-CN.po +1941 -0
  54. package/client/web/src/assets/logo.png +0 -0
  55. package/client/web/src/assets/pathMap/LLOneBot.yaml +2 -0
  56. package/client/web/src/assets/pathMap/Lagrange.OneBot.yaml +180 -0
  57. package/client/web/src/assets/pathMap/NapCat.Onebot.yaml +224 -0
  58. package/client/web/src/assets/pathMap/SnowLuma.yaml +80 -0
  59. package/client/web/src/assets/qed.txt +1 -0
  60. package/client/web/src/components/AboutPan.vue +197 -0
  61. package/client/web/src/components/BulletinBody.vue +129 -0
  62. package/client/web/src/components/DepPan.vue +310 -0
  63. package/client/web/src/components/EmojiFace.vue +32 -0
  64. package/client/web/src/components/FacePan.vue +461 -0
  65. package/client/web/src/components/FileBody.vue +129 -0
  66. package/client/web/src/components/FileManager.vue +532 -0
  67. package/client/web/src/components/FriendBody.vue +54 -0
  68. package/client/web/src/components/GlobalSessionSearchBar.vue +301 -0
  69. package/client/web/src/components/History.vue +49 -0
  70. package/client/web/src/components/LazyLottie.vue +82 -0
  71. package/client/web/src/components/MergePan.vue +127 -0
  72. package/client/web/src/components/MsgBody.vue +1478 -0
  73. package/client/web/src/components/MusicPlayer.vue +549 -0
  74. package/client/web/src/components/NoticeBody.vue +168 -0
  75. package/client/web/src/components/QzonePermissionPan.vue +150 -0
  76. package/client/web/src/components/RawMsgRenderPreviewPan.vue +190 -0
  77. package/client/web/src/components/ThemeColorPickerPan.vue +420 -0
  78. package/client/web/src/components/TinySessionBody.vue +51 -0
  79. package/client/web/src/components/UmamiInfoPan.vue +1549 -0
  80. package/client/web/src/components/UpdatePan.vue +263 -0
  81. package/client/web/src/components/ViewerCom.vue +1360 -0
  82. package/client/web/src/components/VoiceMsg.vue +626 -0
  83. package/client/web/src/components/WelPan.vue +894 -0
  84. package/client/web/src/components/msg-body/.gitkeep +0 -0
  85. package/client/web/src/components/msg-component/JsonSegComp.vue +138 -0
  86. package/client/web/src/components/msg-component/XmlSegComp.vue +132 -0
  87. package/client/web/src/components/msg-component/jsonComp/AutoReply.vue +97 -0
  88. package/client/web/src/components/msg-component/jsonComp/Contact.lua.vue +129 -0
  89. package/client/web/src/components/msg-component/jsonComp/Feed.lua.vue +94 -0
  90. package/client/web/src/components/msg-component/jsonComp/Forum.vue +84 -0
  91. package/client/web/src/components/msg-component/jsonComp/Mannounce.vue +111 -0
  92. package/client/web/src/components/msg-component/jsonComp/Map.vue +52 -0
  93. package/client/web/src/components/msg-component/jsonComp/Miniapp.lua.vue +53 -0
  94. package/client/web/src/components/msg-component/jsonComp/Miniapp.vue +69 -0
  95. package/client/web/src/components/msg-component/jsonComp/Music.lua.vue +135 -0
  96. package/client/web/src/components/msg-component/jsonComp/Tuwen.lua.vue +56 -0
  97. package/client/web/src/components/notice-component/.gitkeep +0 -0
  98. package/client/web/src/components/notice-component/MealHungryPan.vue +119 -0
  99. package/client/web/src/components/tooltip/CustomFaceTooltip.vue +22 -0
  100. package/client/web/src/components/tooltip/Tooltip.vue +50 -0
  101. package/client/web/src/components/tooltip/Tooltips.vue +14 -0
  102. package/client/web/src/components/tooltip/UserInfoTooltip.vue +69 -0
  103. package/client/web/src/function/base.ts +238 -0
  104. package/client/web/src/function/connect.ts +2042 -0
  105. package/client/web/src/function/debug.ts +10 -0
  106. package/client/web/src/function/elements/information.ts +141 -0
  107. package/client/web/src/function/elements/system.ts +101 -0
  108. package/client/web/src/function/elements/vueComp.ts +62 -0
  109. package/client/web/src/function/favicon.ts +76 -0
  110. package/client/web/src/function/input.ts +69 -0
  111. package/client/web/src/function/model/emoji.ts +127 -0
  112. package/client/web/src/function/model/img.ts +150 -0
  113. package/client/web/src/function/model/msg-body.ts +51 -0
  114. package/client/web/src/function/msg.ts +2674 -0
  115. package/client/web/src/function/notify.ts +225 -0
  116. package/client/web/src/function/option.ts +749 -0
  117. package/client/web/src/function/satori-model.ts +818 -0
  118. package/client/web/src/function/satori.ts +325 -0
  119. package/client/web/src/function/sender.ts +178 -0
  120. package/client/web/src/function/tooltip.ts +49 -0
  121. package/client/web/src/function/utils/appUtil.ts +2288 -0
  122. package/client/web/src/function/utils/avatarUtil.ts +31 -0
  123. package/client/web/src/function/utils/backgroundUtil.ts +110 -0
  124. package/client/web/src/function/utils/linkViewUtil.ts +101 -0
  125. package/client/web/src/function/utils/localHistoryUtil.ts +517 -0
  126. package/client/web/src/function/utils/msgUtil.ts +1242 -0
  127. package/client/web/src/function/utils/pinyin.ts +141 -0
  128. package/client/web/src/function/utils/recordUtil.ts +187 -0
  129. package/client/web/src/function/utils/sessionUtil.ts +114 -0
  130. package/client/web/src/function/utils/systemUtil.ts +664 -0
  131. package/client/web/src/main.ts +91 -0
  132. package/client/web/src/pages/Chat.vue +3453 -0
  133. package/client/web/src/pages/Friends.vue +636 -0
  134. package/client/web/src/pages/Info.vue +717 -0
  135. package/client/web/src/pages/Messages.vue +875 -0
  136. package/client/web/src/pages/Options.vue +82 -0
  137. package/client/web/src/pages/Qzone.vue +1178 -0
  138. package/client/web/src/pages/chat-view/ChatDanmaku.vue +759 -0
  139. package/client/web/src/pages/chat-view/ChatGlagame.vue +642 -0
  140. package/client/web/src/pages/chat-view/ChatTerminal.vue +1044 -0
  141. package/client/web/src/pages/chat-view/SystemNotice.vue +171 -0
  142. package/client/web/src/pages/options/OptAccount.vue +107 -0
  143. package/client/web/src/pages/options/OptAddon.vue +185 -0
  144. package/client/web/src/pages/options/OptDev.vue +704 -0
  145. package/client/web/src/pages/options/OptFunction.vue +403 -0
  146. package/client/web/src/pages/options/OptInfo.vue +165 -0
  147. package/client/web/src/pages/options/OptView.vue +846 -0
  148. package/client/web/src/runtime/backend.ts +310 -0
  149. package/client/web/src/state/auth.ts +14 -0
  150. package/client/web/src/state/chat.ts +36 -0
  151. package/client/web/src/state/connection.ts +20 -0
  152. package/client/web/src/state/contact.ts +37 -0
  153. package/client/web/src/state/qzone.ts +18 -0
  154. package/client/web/src/state/sessionHistory.ts +25 -0
  155. package/client/web/src/state/settings.ts +18 -0
  156. package/client/web/src/state/sticker.ts +10 -0
  157. package/client/web/src/state/ui.ts +56 -0
  158. package/client/web/tsconfig.json +27 -0
  159. package/client/web/tsconfig.tsbuildinfo +1 -0
  160. package/client/web/vite.config.ts +33 -0
  161. package/dist/index.js +1 -12
  162. package/dist/style.css +1 -1
  163. package/lib/index.js +2070 -2077
  164. package/package.json +15 -2
  165. package/readme.md +0 -22
  166. package/src/bootstrap.ts +71 -0
  167. package/src/cache.ts +319 -0
  168. package/src/config.ts +23 -35
  169. package/src/database.ts +650 -0
  170. package/src/index.ts +27 -59
  171. package/src/media.ts +302 -0
  172. package/src/recorder.ts +175 -0
  173. package/src/self-message.ts +456 -0
  174. package/src/server.d.ts +26 -0
  175. package/src/types.ts +108 -48
  176. package/src/web.ts +712 -0
  177. package/client/vue/chat-logic.ts +0 -683
  178. package/client/vue/composables/useChatActions.ts +0 -61
  179. package/client/vue/composables/useChatData.ts +0 -185
  180. package/client/vue/composables/useImageCache.ts +0 -281
  181. package/client/vue/composables/useVideoCache.ts +0 -125
  182. package/client/vue/types.ts +0 -72
  183. package/lib/api-handlers.d.ts +0 -25
  184. package/lib/config.d.ts +0 -14
  185. package/lib/file-manager.d.ts +0 -124
  186. package/lib/index.d.ts +0 -17
  187. package/lib/logger.d.ts +0 -9
  188. package/lib/message-handler.d.ts +0 -35
  189. package/lib/types.d.ts +0 -57
  190. package/lib/utils.d.ts +0 -18
  191. package/src/api-handlers.ts +0 -646
  192. package/src/file-manager.ts +0 -1436
  193. package/src/message-handler.ts +0 -507
  194. package/src/utils.ts +0 -189
@@ -0,0 +1,168 @@
1
+ <!--
2
+ * @FileDescription: 通知消息模板
3
+ * @Author: Stapxs
4
+ * @Date: 2022/12/04
5
+ * @Version: 1.0
6
+ -->
7
+
8
+ <template>
9
+ <div :id="'notice-' + id"
10
+ class="note">
11
+ <div v-if="data.notice_type && data.notice_type.indexOf('recall') >= 0" class="note-recall note-base">
12
+ <a>{{ info.name }}</a>
13
+ <span>{{ $t('撤回了一条消息') }}</span>
14
+ <div />
15
+ <a
16
+ v-if="data.originMsg"
17
+ @click="$emit('reedit', data.originMsg)">
18
+ {{ $t('重新编辑') }}
19
+ </a>
20
+ </div>
21
+ <div v-else-if="data.notice_type == 'group_ban'" class="note-ban note-base">
22
+ <template v-if="data.sub_type === 'ban'">
23
+ <template v-if="isMe(data.user_id)">
24
+ <span>{{ $t('成员类型_admin') }}</span>
25
+ <a>&nbsp;{{ getName(data.operator_id) }}&nbsp;</a>
26
+ <span>{{ $t('禁言了你') }}</span>
27
+ <span>&nbsp;{{ fTime(data.duration) }}</span>
28
+ </template>
29
+ <template v-else>
30
+ <span>{{ $t('管理员禁言了') }}</span>
31
+ <a>&nbsp;{{ getName(data.user_id) }}&nbsp;</a>
32
+ <span>{{ fTime(data.duration) }}</span>
33
+ </template>
34
+ </template>
35
+ <span v-else>{{
36
+ $t('管理员解除了 {name} 的禁言', { name: isMe(data.user_id) ? $t('你') : getName(data.user_id)})
37
+ }}</span>
38
+ </div>
39
+ <div v-else-if="data.sub_type === 'delete'" class="note-recall note-base">
40
+ <span>{{ $t('这条消息迷失在虚空里了') }}</span>
41
+ <div />
42
+ </div>
43
+ <div v-else-if="data.sub_type === 'poke'"
44
+ class="note-notify note-base"
45
+ v-html="data.str + '<div class=\'space\'</div>'" />
46
+ <div v-else-if="data.sub_type === 'time' && data.time != undefined"
47
+ class="note-time note-base">
48
+ <a>{{ Intl.DateTimeFormat(
49
+ trueLang,
50
+ getTimeConfig(new Date(data.time * 1000)),
51
+ ).format(new Date(data.time * 1000))
52
+ }}</a>
53
+ </div>
54
+ </div>
55
+ </template>
56
+
57
+ <script setup lang="ts">
58
+ import { ref, onMounted } from 'vue'
59
+ import { i18n } from '../main'
60
+ import { useAuthStore } from '../state/auth'
61
+ import { useChatStore } from '../state/chat'
62
+ import {
63
+ getTimeConfig,
64
+ getTrueLang,
65
+ } from '../function/utils/systemUtil'
66
+ import { pokeAnime } from '../function/utils/msgUtil'
67
+ import { backend } from '../runtime/backend'
68
+
69
+ const $t = i18n.global.t
70
+
71
+ const authStore = useAuthStore()
72
+ const chatStore = useChatStore()
73
+
74
+ defineOptions({ name: 'NoticeBody' })
75
+
76
+ const props = defineProps(['data', 'id'])
77
+ defineEmits(['reedit'])
78
+
79
+ const trueLang = getTrueLang()
80
+ const info = ref(props.data) as { [key: string]: any }
81
+
82
+ function isMe(id: number) {
83
+ return authStore.loginInfo.uin === id
84
+ }
85
+
86
+ function getName(id: number) {
87
+ const back = chatStore.chatInfo.info.group_members.filter(
88
+ (item) => {
89
+ return item.user_id === id
90
+ },
91
+ )
92
+ if (back.length === 1) {
93
+ return back[0].card === '' || back[0].card == null? back[0].nickname: back[0].card
94
+ }
95
+ return id
96
+ }
97
+
98
+ function fTime(time: number) {
99
+ // 将秒数转换为可阅读的时间,最大单位天
100
+ const day = Math.floor(time / 86400)
101
+ const hour = Math.floor((time % 86400) / 3600)
102
+ const minute = Math.floor((time % 3600) / 60)
103
+ const second = time % 60
104
+
105
+ let back = ''
106
+ if (day > 0) {
107
+ back += `${day} ${$t('天')} `
108
+ }
109
+ if (hour > 0) {
110
+ back += `${hour} ${$t('小时')} `
111
+ }
112
+ if (minute > 0) {
113
+ back += `${minute} ${$t('分钟')} `
114
+ }
115
+ if (second > 0) {
116
+ back += `${second} ${$t('秒')} `
117
+ }
118
+ return back
119
+ }
120
+
121
+ onMounted(async () => {
122
+ let windowInfo = null as {
123
+ x: number
124
+ y: number
125
+ width: number
126
+ height: number
127
+ } | null
128
+ windowInfo = await backend.call(undefined, 'win:getWindowInfo', true)
129
+ // 补全撤回者信息
130
+ if (
131
+ info.value.notice_type &&
132
+ info.value.notice_type.indexOf('recall') >= 0
133
+ ) {
134
+ if (chatStore.chatInfo.show.type === 'group') {
135
+ const id = info.value.operator_id
136
+ // 寻找群成员信息
137
+ if (chatStore.chatInfo.info.group_members !== undefined) {
138
+ const back =
139
+ chatStore.chatInfo.info.group_members.filter(
140
+ (item) => {
141
+ return item.user_id === Number(id)
142
+ },
143
+ )
144
+ if (back.length === 1) {
145
+ info.value.name =
146
+ back[0].card === '' || back[0].card == null? back[0].nickname: back[0].card
147
+ } else {
148
+ info.value.name = id
149
+ }
150
+ } else {
151
+ info.value.name = id
152
+ }
153
+ } else {
154
+ info.value.name = chatStore.chatInfo.show.name
155
+ }
156
+ }
157
+ // poke 通知创建对应的动画
158
+ // PS:只有最后一条 poke 通知会触发动画,避免反复触发动画
159
+ if (info.value.sub_type === 'poke' && info.value.pokeMe &&
160
+ info.value == chatStore.messageList[chatStore.messageList.length - 1]) {
161
+ let item = document.getElementById('app')
162
+ if (backend.isDesktop()) {
163
+ item = document.getElementById('notice-' + props.id)?.getElementsByClassName('space')[0] as HTMLElement
164
+ }
165
+ pokeAnime(item, windowInfo)
166
+ }
167
+ })
168
+ </script>
@@ -0,0 +1,150 @@
1
+ <template>
2
+ <div class="qzone-permission-pan">
3
+ <label :for="permissionModeInputId" class="sr-only">{{ $t('说说可见范围') }}</label>
4
+ <select :id="permissionModeInputId"
5
+ v-model="permission.mode"
6
+ class="qzone-permission-select">
7
+ <option value="public">
8
+ {{ $t('所有人可见') }}
9
+ </option>
10
+ <option value="friends">
11
+ {{ $t('好友可见') }}
12
+ </option>
13
+ <option value="allow_some">
14
+ {{ $t('部分好友可见') }}
15
+ </option>
16
+ <option value="private">
17
+ {{ $t('仅自己可见') }}
18
+ </option>
19
+ <option value="deny_some">
20
+ {{ $t('部分好友不可见') }}
21
+ </option>
22
+ </select>
23
+ <template v-if="showList">
24
+ <label for="qzone-permission-search" class="sr-only">{{ $t('搜索联系人') }}</label>
25
+ <input id="qzone-permission-search"
26
+ v-model="searchText"
27
+ class="ss-input qzone-permission-search"
28
+ :placeholder="$t('搜索 ……')">
29
+ <div class="qzone-permission-list">
30
+ <div v-for="session in showSessions"
31
+ :key="session.user_id"
32
+ class="qzone-permission-item"
33
+ @click="toggleSession(session)">
34
+ <TinySessionBody :session="session as any" :selected="isSelected(session)" />
35
+ </div>
36
+ </div>
37
+ </template>
38
+ </div>
39
+ </template>
40
+
41
+ <script setup lang="ts">
42
+ import { computed, reactive, ref } from 'vue'
43
+ import TinySessionBody from '../components/TinySessionBody.vue'
44
+ import { UserGroupElem } from '../function/elements/information'
45
+ import { matchPinyin } from '../function/utils/pinyin'
46
+ import { i18n } from '../main'
47
+
48
+ defineOptions({ name: 'QzonePermissionPan' })
49
+
50
+ const permission = defineModel<{
51
+ mode: 'public' | 'friends' | 'allow_some' | 'private' | 'deny_some'
52
+ userIds: number[]
53
+ }>({ required: true })
54
+
55
+ const { sessions } = defineProps<{
56
+ sessions: UserGroupElem[]
57
+ }>()
58
+
59
+ const $t = i18n.global.t
60
+ const searchText = ref('')
61
+ const selectedMap = reactive(new Set<number>())
62
+ const permissionModeInputId = `qzone-permission-mode-${Math.random().toString(36).slice(2, 9)}`
63
+ const showList = computed(() => {
64
+ return ['allow_some', 'deny_some'].includes(permission.value.mode)
65
+ })
66
+
67
+ for (const userId of permission.value.userIds) {
68
+ selectedMap.add(userId)
69
+ }
70
+
71
+ const showSessions = computed(() => {
72
+ const value = searchText.value.trim().toLowerCase()
73
+ const filteredSessions = !value ? sessions : sessions.filter((session) => {
74
+ const name = `${session.nickname}${session.remark ?? ''}`.toLowerCase()
75
+ if (name.includes(value)) return true
76
+ const id = Number(session.user_id)
77
+ if (String(id).includes(value)) return true
78
+ if (session.py_name && matchPinyin(session.py_name, value)) return true
79
+ return false
80
+ })
81
+
82
+ return [...filteredSessions].sort((a, b) => {
83
+ const aSelected = selectedMap.has(getSessionId(a))
84
+ const bSelected = selectedMap.has(getSessionId(b))
85
+ if (aSelected === bSelected) return 0
86
+ return aSelected ? -1 : 1
87
+ })
88
+ })
89
+
90
+ function getSessionId(session: UserGroupElem) {
91
+ return Number(session.user_id)
92
+ }
93
+
94
+ function isSelected(session: UserGroupElem) {
95
+ return selectedMap.has(getSessionId(session))
96
+ }
97
+
98
+ function toggleSession(session: UserGroupElem) {
99
+ const id = getSessionId(session)
100
+ if (selectedMap.has(id)) {
101
+ selectedMap.delete(id)
102
+ } else {
103
+ selectedMap.add(id)
104
+ }
105
+ permission.value.userIds = Array.from(selectedMap.values())
106
+ }
107
+ </script>
108
+
109
+ <style scoped>
110
+ .qzone-permission-pan {
111
+ display: flex;
112
+ flex-direction: column;
113
+ gap: 12px;
114
+ min-height: 220px;
115
+ }
116
+
117
+ .qzone-permission-search {
118
+ width: 100%;
119
+ box-sizing: border-box;
120
+ }
121
+
122
+ .qzone-permission-list {
123
+ overflow-y: scroll;
124
+ overflow-x: hidden;
125
+ min-height: 200px;
126
+ max-height: 30vh;
127
+ width: 40vw;
128
+ padding: 8px;
129
+ }
130
+
131
+ .qzone-permission-item {
132
+ width: 100%;
133
+ cursor: pointer;
134
+ border-radius: 8px;
135
+ }
136
+
137
+ .qzone-permission-item + .qzone-permission-item {
138
+ margin-top: 6px;
139
+ }
140
+
141
+ .qzone-permission-select {
142
+ background: var(--color-card);
143
+ height: 35px;
144
+ color: var(--color-font);
145
+ border: none;
146
+ border-radius: 8px;
147
+ padding: 10px 12px;
148
+ font-size: 0.9rem;
149
+ }
150
+ </style>
@@ -0,0 +1,190 @@
1
+ <template>
2
+ <div class="raw-render-preview-pan">
3
+ <div class="raw-render-preview-pane">
4
+ <span v-if="rawRenderPreviewLoading" class="raw-render-preview-tip">
5
+ {{ $t('正在处理原始消息……') }}
6
+ </span>
7
+ <span v-else-if="rawRenderPreviewError" class="raw-render-preview-error">
8
+ {{ rawRenderPreviewError }}
9
+ </span>
10
+ <span v-else-if="rawRenderPreviewList.length === 0" class="raw-render-preview-tip">
11
+ {{ $t('还没有可预览的消息') }}
12
+ </span>
13
+ <div v-else class="raw-render-preview-msg-list">
14
+ <MsgBody
15
+ v-for="(item, index) in rawRenderPreviewList"
16
+ :key="item.message_id ?? `raw-render-preview-${index}`"
17
+ :data="item"
18
+ :type="'body'" />
19
+ </div>
20
+ </div>
21
+ <div class="raw-render-preview-title">
22
+ <label for="raw-render-preview-textarea">{{ $t('原始消息') }}</label>
23
+ </div>
24
+ <textarea
25
+ id="raw-render-preview-textarea"
26
+ v-model="rawRenderPreviewText"
27
+ class="raw-render-preview-textarea ss-input"
28
+ @keydown.ctrl.enter.prevent="renderRawPreview" />
29
+ </div>
30
+ </template>
31
+
32
+ <script setup lang="ts">
33
+ import { ref } from 'vue'
34
+
35
+ import MsgBody from '../components/MsgBody.vue'
36
+ import { normalizeMessagesForPreview } from '../function/msg'
37
+ import { getMsgRawTxt } from '../function/utils/msgUtil'
38
+ import { useAuthStore } from '../state/auth'
39
+ import { i18n } from '../main'
40
+
41
+ defineOptions({ name: 'RawMsgRenderPreviewPan' })
42
+
43
+ const {
44
+ data,
45
+ } = defineProps<{
46
+ data?: {
47
+ text?: string
48
+ }
49
+ }>()
50
+
51
+ const authStore = useAuthStore()
52
+ const $t = i18n.global.t
53
+
54
+ const rawRenderPreviewText = ref(data?.text ?? '')
55
+ const rawRenderPreviewLoading = ref(false)
56
+ const rawRenderPreviewError = ref('')
57
+ const rawRenderPreviewList = ref<any[]>([])
58
+
59
+ function buildRawRenderFallbackMessage(msg: any, index: number) {
60
+ const previewMsg = { ...msg }
61
+ const senderId = Number(
62
+ previewMsg.sender?.user_id
63
+ ?? previewMsg.user_id
64
+ ?? authStore.loginInfo.uin
65
+ ?? 0,
66
+ )
67
+
68
+ previewMsg.message_id ??= `raw-render-preview-${index}`
69
+ previewMsg.time = Number(previewMsg.time ?? Math.floor(Date.now() / 1000))
70
+ previewMsg.post_type ??= 'message'
71
+ previewMsg.message = Array.isArray(previewMsg.message) ? previewMsg.message : []
72
+ previewMsg.sender = {
73
+ user_id: senderId,
74
+ nickname: previewMsg.sender?.nickname
75
+ ?? previewMsg.nickname
76
+ ?? authStore.loginInfo.nickname
77
+ ?? $t('预览消息'),
78
+ card: previewMsg.sender?.card ?? '',
79
+ ...previewMsg.sender,
80
+ }
81
+ previewMsg.raw_message ??= getMsgRawTxt(previewMsg)
82
+
83
+ return previewMsg
84
+ }
85
+
86
+ async function renderRawPreview() {
87
+ if (rawRenderPreviewText.value.trim() === '') {
88
+ rawRenderPreviewList.value = []
89
+ rawRenderPreviewError.value = ''
90
+ return
91
+ }
92
+
93
+ rawRenderPreviewLoading.value = true
94
+ rawRenderPreviewError.value = ''
95
+
96
+ try {
97
+ const raw = JSON.parse(rawRenderPreviewText.value)
98
+ const list = (await normalizeMessagesForPreview(raw))
99
+ .map((item, index) => buildRawRenderFallbackMessage(item, index))
100
+ .filter((item) => Array.isArray(item.message))
101
+
102
+ rawRenderPreviewList.value = list
103
+ if (list.length === 0) {
104
+ rawRenderPreviewError.value = $t('当前原始消息中没有可渲染的消息体')
105
+ }
106
+ } catch (error) {
107
+ rawRenderPreviewList.value = []
108
+ rawRenderPreviewError.value = error instanceof Error? error.message: $t('处理原始消息失败')
109
+ } finally {
110
+ rawRenderPreviewLoading.value = false
111
+ }
112
+ }
113
+
114
+ if (rawRenderPreviewText.value !== '') {
115
+ void renderRawPreview()
116
+ }
117
+ </script>
118
+
119
+ <style scoped>
120
+ .raw-render-preview-pan {
121
+ display: flex;
122
+ flex-direction: column;
123
+ gap: 14px;
124
+ width: calc(100% - 70px);
125
+ height: calc(100vh - 90px);
126
+ overflow-y: scroll;
127
+ overflow-x: hidden;
128
+ margin-top: 10px;
129
+ padding: 0 50px 20px 20px !important;
130
+ }
131
+
132
+ .raw-render-preview-title {
133
+ display: flex;
134
+ gap: 12px;
135
+ align-items: center;
136
+ justify-content: space-between;
137
+ font-size: 0.9rem;
138
+ font-weight: 700;
139
+ }
140
+
141
+ .raw-render-preview-title span:last-child {
142
+ color: var(--color-font-2);
143
+ font-size: 0.8rem;
144
+ font-weight: 500;
145
+ }
146
+
147
+ .raw-render-preview-textarea,
148
+ .raw-render-preview-pane {
149
+ border: unset;
150
+ border-radius: 7px;
151
+ }
152
+
153
+ .raw-render-preview-textarea {
154
+ width: 100%;
155
+ min-height: 260px;
156
+ padding: 16px;
157
+ background: var(--color-bg);
158
+ color: var(--color-font);
159
+ resize: vertical;
160
+ font-family: monospace;
161
+ font-size: 0.85rem;
162
+ line-height: 1.6;
163
+ }
164
+
165
+ .raw-render-preview-pane {
166
+ background: transparent;
167
+ }
168
+
169
+ .raw-render-preview-msg-list {
170
+ display: flex;
171
+ flex-direction: column;
172
+ gap: 14px;
173
+ }
174
+
175
+ .raw-render-preview-tip,
176
+ .raw-render-preview-error {
177
+ display: block;
178
+ font-size: 0.9rem;
179
+ line-height: 1.6;
180
+ }
181
+
182
+ .raw-render-preview-tip {
183
+ color: var(--color-font-2);
184
+ }
185
+
186
+ .raw-render-preview-error {
187
+ color: var(--color-red);
188
+ white-space: pre-wrap;
189
+ }
190
+ </style>