mirror of
https://github.com/LifeArchiveProject/WeChatDataAnalysis.git
synced 2026-06-18 15:54:08 +08:00
Compare commits
3 Commits
@@ -1359,6 +1359,34 @@ function getRendererConsoleLogPath() {
|
||||
}
|
||||
}
|
||||
|
||||
function getRendererDebugLogPath() {
|
||||
try {
|
||||
const dir = app.getPath("userData");
|
||||
fs.mkdirSync(dir, { recursive: true });
|
||||
return path.join(dir, "renderer-debug.log");
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
function appendRendererDebugLog(line) {
|
||||
const logPath = getRendererDebugLogPath();
|
||||
if (!logPath) return;
|
||||
try {
|
||||
fs.appendFileSync(logPath, line, { encoding: "utf8" });
|
||||
} catch {}
|
||||
}
|
||||
|
||||
function stringifyDebugDetails(details) {
|
||||
if (details == null) return "";
|
||||
if (typeof details === "string") return details;
|
||||
try {
|
||||
return JSON.stringify(details);
|
||||
} catch (err) {
|
||||
return `[unserializable:${err?.message || err}]`;
|
||||
}
|
||||
}
|
||||
|
||||
function setupRendererConsoleLogging(win) {
|
||||
if (!debugEnabled()) return;
|
||||
|
||||
@@ -1575,6 +1603,15 @@ function registerWindowIpc() {
|
||||
}
|
||||
});
|
||||
|
||||
ipcMain.on("debug:log", (event, payload) => {
|
||||
const scope = String(payload?.scope || "renderer").trim() || "renderer";
|
||||
const message = String(payload?.message || "").trim() || "(empty)";
|
||||
const url = String(payload?.url || event?.sender?.getURL?.() || "").trim();
|
||||
const details = stringifyDebugDetails(payload?.details);
|
||||
const suffix = details ? ` details=${details}` : "";
|
||||
appendRendererDebugLog(`[${nowIso()}] [${scope}] ${message} url=${url}${suffix}\n`);
|
||||
});
|
||||
|
||||
ipcMain.handle("app:setCloseBehavior", (_event, behavior) => {
|
||||
try {
|
||||
const next = setCloseBehavior(behavior);
|
||||
|
||||
@@ -1,5 +1,65 @@
|
||||
const { contextBridge, ipcRenderer } = require("electron");
|
||||
|
||||
function sendDebugLog(scope, message, details) {
|
||||
try {
|
||||
ipcRenderer.send("debug:log", {
|
||||
scope: String(scope || "renderer"),
|
||||
message: String(message || ""),
|
||||
details: details == null ? {} : details,
|
||||
url: typeof location !== "undefined" ? String(location.href || "") : "",
|
||||
});
|
||||
} catch {}
|
||||
}
|
||||
|
||||
sendDebugLog("preload", "script-start", {
|
||||
userAgent: typeof navigator !== "undefined" ? String(navigator.userAgent || "") : "",
|
||||
});
|
||||
|
||||
if (typeof document !== "undefined") {
|
||||
document.addEventListener("readystatechange", () => {
|
||||
sendDebugLog("preload", "document-readystate", {
|
||||
readyState: String(document.readyState || ""),
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
if (typeof window !== "undefined") {
|
||||
window.addEventListener("DOMContentLoaded", () => {
|
||||
sendDebugLog("preload", "dom-content-loaded");
|
||||
});
|
||||
|
||||
window.addEventListener("load", () => {
|
||||
sendDebugLog("preload", "window-load");
|
||||
});
|
||||
|
||||
window.addEventListener("error", (event) => {
|
||||
sendDebugLog("preload", "window-error", {
|
||||
message: String(event?.message || ""),
|
||||
filename: String(event?.filename || ""),
|
||||
lineno: Number(event?.lineno || 0),
|
||||
colno: Number(event?.colno || 0),
|
||||
});
|
||||
});
|
||||
|
||||
window.addEventListener("unhandledrejection", (event) => {
|
||||
const reason = event?.reason;
|
||||
sendDebugLog("preload", "window-unhandledrejection", {
|
||||
reason:
|
||||
reason instanceof Error
|
||||
? {
|
||||
name: String(reason.name || "Error"),
|
||||
message: String(reason.message || ""),
|
||||
stack: String(reason.stack || ""),
|
||||
}
|
||||
: String(reason || ""),
|
||||
});
|
||||
});
|
||||
|
||||
window.setTimeout(() => {
|
||||
sendDebugLog("preload", "set-timeout-0");
|
||||
}, 0);
|
||||
}
|
||||
|
||||
contextBridge.exposeInMainWorld("wechatDesktop", {
|
||||
// Marker used by the frontend to distinguish the Electron desktop shell from the pure web build.
|
||||
__brand: "WeChatDataAnalysisDesktop",
|
||||
@@ -8,6 +68,7 @@ contextBridge.exposeInMainWorld("wechatDesktop", {
|
||||
close: () => ipcRenderer.invoke("window:close"),
|
||||
isMaximized: () => ipcRenderer.invoke("window:isMaximized"),
|
||||
isDebugEnabled: () => ipcRenderer.invoke("app:isDebugEnabled"),
|
||||
logDebug: (scope, message, details = {}) => sendDebugLog(scope, message, details),
|
||||
|
||||
getAutoLaunch: () => ipcRenderer.invoke("app:getAutoLaunch"),
|
||||
setAutoLaunch: (enabled) => ipcRenderer.invoke("app:setAutoLaunch", !!enabled),
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<script>
|
||||
import { defineComponent, h, ref } from 'vue'
|
||||
import { defineComponent, h, ref, watch } from 'vue'
|
||||
import miniProgramIconUrl from '~/assets/images/wechat/mini-program.svg'
|
||||
|
||||
export default defineComponent({
|
||||
@@ -19,7 +19,15 @@ export default defineComponent({
|
||||
setup(props) {
|
||||
const fromAvatarImgOk = ref(false)
|
||||
const fromAvatarImgError = ref(false)
|
||||
const lastFromAvatarUrl = ref('')
|
||||
|
||||
watch(
|
||||
() => String(props.fromAvatar || '').trim(),
|
||||
() => {
|
||||
fromAvatarImgOk.value = false
|
||||
fromAvatarImgError.value = false
|
||||
},
|
||||
{ immediate: true }
|
||||
)
|
||||
|
||||
const getFromText = () => {
|
||||
const raw = String(props.from || '').trim()
|
||||
@@ -47,12 +55,6 @@ export default defineComponent({
|
||||
const isCoverVariant = !isMiniProgram && String(props.variant || '').trim() === 'cover'
|
||||
const Tag = canNavigate ? 'a' : 'div'
|
||||
|
||||
if (fromAvatarUrl !== lastFromAvatarUrl.value) {
|
||||
lastFromAvatarUrl.value = fromAvatarUrl
|
||||
fromAvatarImgOk.value = false
|
||||
fromAvatarImgError.value = false
|
||||
}
|
||||
|
||||
const showFromAvatarImg = Boolean(fromAvatarUrl) && !fromAvatarImgError.value
|
||||
const showFromAvatarText = (!fromAvatarUrl) || (!fromAvatarImgOk.value)
|
||||
const fromAvatarStyle = fromAvatarImgOk.value
|
||||
|
||||
@@ -36,6 +36,9 @@ export const useChatMessages = ({
|
||||
|
||||
const logMessagePhase = (phase, details = {}) => {
|
||||
if (!isDesktopRenderer()) return
|
||||
try {
|
||||
window.wechatDesktop?.logDebug?.('chat-messages', phase, details)
|
||||
} catch {}
|
||||
console.info(`[chat-messages] ${phase}`, {
|
||||
account: String(selectedAccount.value || '').trim(),
|
||||
selectedUsername: String(selectedContact.value?.username || '').trim(),
|
||||
@@ -44,12 +47,21 @@ export const useChatMessages = ({
|
||||
})
|
||||
}
|
||||
|
||||
const summarizeRenderTypes = (list) => {
|
||||
const counts = {}
|
||||
for (const item of Array.isArray(list) ? list : []) {
|
||||
const key = String(item?.renderType || 'unknown').trim() || 'unknown'
|
||||
counts[key] = Number(counts[key] || 0) + 1
|
||||
}
|
||||
return counts
|
||||
}
|
||||
|
||||
const previewImageUrl = ref(null)
|
||||
const previewVideoUrl = ref(null)
|
||||
const previewVideoPosterUrl = ref('')
|
||||
const previewVideoError = ref('')
|
||||
|
||||
const voiceRefs = ref({})
|
||||
const voiceRefs = new Map()
|
||||
const currentPlayingVoice = ref(null)
|
||||
const playingVoiceId = ref(null)
|
||||
|
||||
@@ -227,18 +239,16 @@ export const useChatMessages = ({
|
||||
const key = String(id || '').trim()
|
||||
if (!key) return
|
||||
if (element) {
|
||||
voiceRefs.value = { ...voiceRefs.value, [key]: element }
|
||||
} else if (voiceRefs.value[key]) {
|
||||
const next = { ...voiceRefs.value }
|
||||
delete next[key]
|
||||
voiceRefs.value = next
|
||||
voiceRefs.set(key, element)
|
||||
} else {
|
||||
voiceRefs.delete(key)
|
||||
}
|
||||
}
|
||||
|
||||
const playVoiceById = async (voiceId) => {
|
||||
const key = String(voiceId || '').trim()
|
||||
if (!key) return
|
||||
const audio = voiceRefs.value[key]
|
||||
const audio = voiceRefs.get(key)
|
||||
if (!audio) return
|
||||
|
||||
try {
|
||||
@@ -418,7 +428,8 @@ export const useChatMessages = ({
|
||||
const mapped = dedupeMessagesById(raw.map(normalizeMessage))
|
||||
logMessagePhase('loadMessages:normalize:end', {
|
||||
username,
|
||||
mappedCount: mapped.length
|
||||
mappedCount: mapped.length,
|
||||
renderTypeCounts: summarizeRenderTypes(mapped)
|
||||
})
|
||||
|
||||
if (activeMessagesFor.value !== username) {
|
||||
@@ -595,7 +606,18 @@ export const useChatMessages = ({
|
||||
} catch {}
|
||||
}
|
||||
|
||||
const clearVoicePlaybackState = () => {
|
||||
try {
|
||||
currentPlayingVoice.value?.pause?.()
|
||||
if (currentPlayingVoice.value) currentPlayingVoice.value.currentTime = 0
|
||||
} catch {}
|
||||
currentPlayingVoice.value = null
|
||||
playingVoiceId.value = null
|
||||
voiceRefs.clear()
|
||||
}
|
||||
|
||||
const resetMessageState = () => {
|
||||
clearVoicePlaybackState()
|
||||
allMessages.value = {}
|
||||
messagesMeta.value = {}
|
||||
messagesError.value = ''
|
||||
@@ -804,6 +826,7 @@ export const useChatMessages = ({
|
||||
if (highlightTimer) clearTimeout(highlightTimer)
|
||||
highlightTimer = null
|
||||
clearContactProfileHoverHideTimer()
|
||||
clearVoicePlaybackState()
|
||||
})
|
||||
|
||||
return {
|
||||
|
||||
@@ -94,6 +94,9 @@ const shouldLogChatBootstrap = () => isDesktopShell() || desktopDebugEnabled.val
|
||||
|
||||
const logChatBootstrap = (phase, details = {}) => {
|
||||
if (!shouldLogChatBootstrap()) return
|
||||
try {
|
||||
window.wechatDesktop?.logDebug?.('chat-bootstrap', phase, details)
|
||||
} catch {}
|
||||
console.info(`[chat-bootstrap] ${phase}`, {
|
||||
elapsedMs: chatBootstrapElapsedMs(),
|
||||
route: route.fullPath,
|
||||
|
||||
@@ -0,0 +1,83 @@
|
||||
const isDesktopShell = () => {
|
||||
if (typeof window === 'undefined') return false
|
||||
return !!window.wechatDesktop?.__brand
|
||||
}
|
||||
|
||||
const formatError = (error) => {
|
||||
if (!error) return ''
|
||||
if (error instanceof Error) {
|
||||
return {
|
||||
name: String(error.name || 'Error'),
|
||||
message: String(error.message || ''),
|
||||
stack: String(error.stack || '')
|
||||
}
|
||||
}
|
||||
if (typeof error === 'object') {
|
||||
try {
|
||||
return JSON.parse(JSON.stringify(error))
|
||||
} catch {}
|
||||
}
|
||||
return String(error)
|
||||
}
|
||||
|
||||
const logDesktopDebug = (phase, details = {}) => {
|
||||
if (!isDesktopShell()) return
|
||||
try {
|
||||
window.wechatDesktop?.logDebug?.('nuxt-bootstrap', phase, {
|
||||
href: String(window.location?.href || ''),
|
||||
...details
|
||||
})
|
||||
} catch {}
|
||||
try {
|
||||
console.info(`[nuxt-bootstrap] ${phase}`, details)
|
||||
} catch {}
|
||||
}
|
||||
|
||||
export default defineNuxtPlugin((nuxtApp) => {
|
||||
logDesktopDebug('plugin:setup')
|
||||
|
||||
if (typeof window !== 'undefined') {
|
||||
window.addEventListener('error', (event) => {
|
||||
logDesktopDebug('window:error', {
|
||||
message: String(event?.message || ''),
|
||||
filename: String(event?.filename || ''),
|
||||
lineno: Number(event?.lineno || 0),
|
||||
colno: Number(event?.colno || 0),
|
||||
error: formatError(event?.error)
|
||||
})
|
||||
})
|
||||
|
||||
window.addEventListener('unhandledrejection', (event) => {
|
||||
logDesktopDebug('window:unhandledrejection', {
|
||||
reason: formatError(event?.reason)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
nuxtApp.hook('app:created', () => {
|
||||
logDesktopDebug('app:created')
|
||||
})
|
||||
|
||||
nuxtApp.hook('app:beforeMount', () => {
|
||||
logDesktopDebug('app:beforeMount')
|
||||
})
|
||||
|
||||
nuxtApp.hook('app:mounted', () => {
|
||||
logDesktopDebug('app:mounted')
|
||||
})
|
||||
|
||||
nuxtApp.hook('page:start', () => {
|
||||
logDesktopDebug('page:start')
|
||||
})
|
||||
|
||||
nuxtApp.hook('page:finish', () => {
|
||||
logDesktopDebug('page:finish')
|
||||
})
|
||||
|
||||
nuxtApp.hook('vue:error', (error, _instance, info) => {
|
||||
logDesktopDebug('vue:error', {
|
||||
info: String(info || ''),
|
||||
error: formatError(error)
|
||||
})
|
||||
})
|
||||
})
|
||||
Reference in New Issue
Block a user