mirror of
https://github.com/hicccc77/WeFlow.git
synced 2026-03-24 23:06:51 +00:00
Merge branch 'dev' of https://github.com/hicccc77/WeFlow into dev
This commit is contained in:
15
.github/workflows/release.yml
vendored
15
.github/workflows/release.yml
vendored
@@ -8,20 +8,23 @@ on:
|
||||
permissions:
|
||||
contents: write
|
||||
|
||||
env:
|
||||
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: "true"
|
||||
|
||||
jobs:
|
||||
release-mac-arm64:
|
||||
runs-on: macos-14
|
||||
|
||||
steps:
|
||||
- name: Check out git repository
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v5
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Install Node.js
|
||||
uses: actions/setup-node@v4
|
||||
uses: actions/setup-node@v5
|
||||
with:
|
||||
node-version: 22.12
|
||||
node-version: 24
|
||||
cache: "npm"
|
||||
|
||||
- name: Install Dependencies
|
||||
@@ -66,14 +69,14 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Check out git repository
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v5
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Install Node.js
|
||||
uses: actions/setup-node@v4
|
||||
uses: actions/setup-node@v5
|
||||
with:
|
||||
node-version: 22.12
|
||||
node-version: 24
|
||||
cache: 'npm'
|
||||
|
||||
- name: Install Dependencies
|
||||
|
||||
@@ -988,6 +988,17 @@ function registerIpcHandlers() {
|
||||
}
|
||||
})
|
||||
|
||||
ipcMain.handle('log:clear', async () => {
|
||||
try {
|
||||
const logPath = join(app.getPath('userData'), 'logs', 'wcdb.log')
|
||||
await mkdir(dirname(logPath), { recursive: true })
|
||||
await writeFile(logPath, '', 'utf8')
|
||||
return { success: true }
|
||||
} catch (e) {
|
||||
return { success: false, error: String(e) }
|
||||
}
|
||||
})
|
||||
|
||||
ipcMain.handle('diagnostics:getExportCardLogs', async (_, options?: { limit?: number }) => {
|
||||
return exportCardDiagnosticsService.snapshot(options?.limit)
|
||||
})
|
||||
|
||||
@@ -70,6 +70,7 @@ contextBridge.exposeInMainWorld('electronAPI', {
|
||||
log: {
|
||||
getPath: () => ipcRenderer.invoke('log:getPath'),
|
||||
read: () => ipcRenderer.invoke('log:read'),
|
||||
clear: () => ipcRenderer.invoke('log:clear'),
|
||||
debug: (data: any) => ipcRenderer.send('log:debug', data)
|
||||
},
|
||||
|
||||
|
||||
@@ -359,16 +359,15 @@ class ChatService {
|
||||
// 这种方式更高效,且不占用 JS 线程,并能直接监听 session/message 目录变更
|
||||
wcdbService.setMonitor((type, json) => {
|
||||
this.handleSessionStatsMonitorChange(type, json)
|
||||
const windows = BrowserWindow.getAllWindows()
|
||||
// 广播给所有渲染进程窗口
|
||||
BrowserWindow.getAllWindows().forEach((win) => {
|
||||
windows.forEach((win) => {
|
||||
if (!win.isDestroyed()) {
|
||||
win.webContents.send('wcdb-change', { type, json })
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 预热 media 数据库列表缓存(后台异步执行)
|
||||
|
||||
@@ -173,7 +173,6 @@ export class WcdbCore {
|
||||
}
|
||||
} catch {}
|
||||
}
|
||||
|
||||
this.connectMonitorPipe(pipePath)
|
||||
return true
|
||||
} catch (e) {
|
||||
@@ -194,8 +193,14 @@ export class WcdbCore {
|
||||
|
||||
let buffer = ''
|
||||
this.monitorPipeClient.on('data', (data: Buffer) => {
|
||||
buffer += data.toString('utf8')
|
||||
const lines = buffer.split('\n')
|
||||
const rawChunk = data.toString('utf8')
|
||||
// macOS 侧可能使用 '\0' 或无换行分隔,统一归一化并兜底拆包
|
||||
const normalizedChunk = rawChunk
|
||||
.replace(/\u0000/g, '\n')
|
||||
.replace(/}\s*{/g, '}\n{')
|
||||
|
||||
buffer += normalizedChunk
|
||||
const lines = buffer.split(/\r?\n/)
|
||||
buffer = lines.pop() || ''
|
||||
for (const line of lines) {
|
||||
if (line.trim()) {
|
||||
@@ -207,9 +212,23 @@ export class WcdbCore {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 兜底:如果没有分隔符但已形成完整 JSON,则直接上报
|
||||
const tail = buffer.trim()
|
||||
if (tail.startsWith('{') && tail.endsWith('}')) {
|
||||
try {
|
||||
const parsed = JSON.parse(tail)
|
||||
this.monitorCallback?.(parsed.action || 'update', tail)
|
||||
buffer = ''
|
||||
} catch {
|
||||
// 不可解析则继续等待下一块数据
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
this.monitorPipeClient.on('error', () => {})
|
||||
this.monitorPipeClient.on('error', () => {
|
||||
// 保持静默,与现有错误处理策略一致
|
||||
})
|
||||
|
||||
this.monitorPipeClient.on('close', () => {
|
||||
this.monitorPipeClient = null
|
||||
|
||||
@@ -136,7 +136,7 @@ export class WcdbService {
|
||||
*/
|
||||
setMonitor(callback: (type: string, json: string) => void): void {
|
||||
this.monitorListener = callback;
|
||||
this.callWorker('setMonitor').catch(() => { });
|
||||
this.callWorker<{ success?: boolean }>('setMonitor').catch(() => { });
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -20,15 +20,17 @@ if (parentPort) {
|
||||
result = { success: true }
|
||||
break
|
||||
case 'setMonitor':
|
||||
core.setMonitor((type, json) => {
|
||||
{
|
||||
const monitorOk = core.setMonitor((type, json) => {
|
||||
parentPort!.postMessage({
|
||||
id: -1,
|
||||
type: 'monitor',
|
||||
payload: { type, json }
|
||||
})
|
||||
})
|
||||
result = { success: true }
|
||||
result = { success: monitorOk }
|
||||
break
|
||||
}
|
||||
case 'testConnection':
|
||||
result = await core.testConnection(payload.dbPath, payload.hexKey, payload.wxid)
|
||||
break
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
|
||||
.update-dialog {
|
||||
width: 680px;
|
||||
background: #f5f5f5;
|
||||
background: var(--bg-secondary, #f5f5f5);
|
||||
border-radius: 24px;
|
||||
box-shadow: 0 20px 40px rgba(0, 0, 0, 0.2);
|
||||
overflow: hidden;
|
||||
@@ -25,7 +25,7 @@
|
||||
|
||||
/* Top Section (White/Gradient) */
|
||||
.dialog-header {
|
||||
background: #ffffff;
|
||||
background: var(--bg-primary, #ffffff);
|
||||
padding: 40px 20px 30px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
@@ -41,14 +41,14 @@
|
||||
left: -50px;
|
||||
width: 200px;
|
||||
height: 200px;
|
||||
background: radial-gradient(circle, rgba(255, 235, 220, 0.4) 0%, rgba(255, 255, 255, 0) 70%);
|
||||
opacity: 0.8;
|
||||
background: radial-gradient(circle, rgba(255, 235, 220, 0.15) 0%, rgba(255, 255, 255, 0) 70%);
|
||||
opacity: 0.5;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.version-tag {
|
||||
background: #f0eee9;
|
||||
color: #8c7b6e;
|
||||
background: var(--bg-tertiary, #f0eee9);
|
||||
color: var(--text-tertiary, #8c7b6e);
|
||||
padding: 4px 16px;
|
||||
border-radius: 12px;
|
||||
font-size: 13px;
|
||||
@@ -60,21 +60,21 @@
|
||||
h2 {
|
||||
font-size: 32px;
|
||||
font-weight: 800;
|
||||
color: #333333;
|
||||
color: var(--text-primary, #333333);
|
||||
margin: 0 0 12px;
|
||||
letter-spacing: -0.5px;
|
||||
}
|
||||
|
||||
.subtitle {
|
||||
font-size: 15px;
|
||||
color: #999999;
|
||||
color: var(--text-secondary, #999999);
|
||||
font-weight: 400;
|
||||
}
|
||||
}
|
||||
|
||||
/* Content Section (Light Gray) */
|
||||
.dialog-content {
|
||||
background: #f2f2f2;
|
||||
background: var(--bg-tertiary, #f2f2f2);
|
||||
padding: 24px 40px 40px;
|
||||
flex: 1;
|
||||
display: flex;
|
||||
@@ -87,7 +87,7 @@
|
||||
margin-bottom: 30px;
|
||||
|
||||
.icon-box {
|
||||
background: #fbfbfb; // Beige-ish white
|
||||
background: var(--bg-primary, #fbfbfb);
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
border-radius: 16px;
|
||||
@@ -96,7 +96,7 @@
|
||||
justify-content: center;
|
||||
margin-right: 20px;
|
||||
flex-shrink: 0;
|
||||
color: #8c7b6e;
|
||||
color: var(--text-tertiary, #8c7b6e);
|
||||
box-shadow: 0 4px 10px rgba(0, 0, 0, 0.03);
|
||||
|
||||
svg {
|
||||
@@ -107,27 +107,38 @@
|
||||
.text-box {
|
||||
flex: 1;
|
||||
|
||||
h3 {
|
||||
font-size: 18px;
|
||||
h1, h2, h3, h4, h5, h6 {
|
||||
color: var(--text-primary, #333333);
|
||||
font-weight: 700;
|
||||
color: #333333;
|
||||
margin: 0 0 8px;
|
||||
margin: 16px 0 8px;
|
||||
|
||||
&:first-child {
|
||||
margin-top: 0;
|
||||
}
|
||||
}
|
||||
|
||||
h2 {
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
h3 {
|
||||
font-size: 15px;
|
||||
}
|
||||
|
||||
p {
|
||||
font-size: 14px;
|
||||
color: #666666;
|
||||
color: var(--text-secondary, #666666);
|
||||
line-height: 1.6;
|
||||
margin: 0;
|
||||
margin: 4px 0;
|
||||
}
|
||||
|
||||
ul {
|
||||
margin: 8px 0 0 18px;
|
||||
margin: 4px 0 0 18px;
|
||||
padding: 0;
|
||||
|
||||
li {
|
||||
font-size: 14px;
|
||||
color: #666666;
|
||||
color: var(--text-secondary, #666666);
|
||||
line-height: 1.6;
|
||||
}
|
||||
}
|
||||
@@ -142,19 +153,19 @@
|
||||
justify-content: space-between;
|
||||
margin-bottom: 8px;
|
||||
font-size: 12px;
|
||||
color: #888;
|
||||
color: var(--text-secondary, #888);
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.progress-bar-bg {
|
||||
height: 6px;
|
||||
background: #e0e0e0;
|
||||
background: var(--border-color, #e0e0e0);
|
||||
border-radius: 3px;
|
||||
overflow: hidden;
|
||||
|
||||
.progress-bar-fill {
|
||||
height: 100%;
|
||||
background: #000000;
|
||||
background: var(--text-primary, #000000);
|
||||
border-radius: 3px;
|
||||
transition: width 0.3s ease;
|
||||
}
|
||||
@@ -164,7 +175,7 @@
|
||||
text-align: center;
|
||||
margin-top: 12px;
|
||||
font-size: 13px;
|
||||
color: #666;
|
||||
color: var(--text-secondary, #666);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -175,8 +186,8 @@
|
||||
|
||||
.btn-ignore {
|
||||
background: transparent;
|
||||
color: #666666;
|
||||
border: 1px solid #d0d0d0;
|
||||
color: var(--text-secondary, #666666);
|
||||
border: 1px solid var(--border-color, #d0d0d0);
|
||||
padding: 16px 32px;
|
||||
border-radius: 20px;
|
||||
font-size: 16px;
|
||||
@@ -185,9 +196,9 @@
|
||||
transition: all 0.2s;
|
||||
|
||||
&:hover {
|
||||
background: #f5f5f5;
|
||||
border-color: #999999;
|
||||
color: #333333;
|
||||
background: var(--bg-hover, #f5f5f5);
|
||||
border-color: var(--text-secondary, #999999);
|
||||
color: var(--text-primary, #333333);
|
||||
}
|
||||
|
||||
&:active {
|
||||
@@ -196,11 +207,11 @@
|
||||
}
|
||||
|
||||
.btn-update {
|
||||
background: #000000;
|
||||
color: #ffffff;
|
||||
background: var(--text-primary, #000000);
|
||||
color: var(--bg-primary, #ffffff);
|
||||
border: none;
|
||||
padding: 16px 48px;
|
||||
border-radius: 20px; // Pill shape
|
||||
border-radius: 20px;
|
||||
font-size: 16px;
|
||||
font-weight: 600;
|
||||
cursor: pointer;
|
||||
@@ -231,7 +242,7 @@
|
||||
right: 16px;
|
||||
background: rgba(0, 0, 0, 0.05);
|
||||
border: none;
|
||||
color: #999;
|
||||
color: var(--text-secondary, #999);
|
||||
cursor: pointer;
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
@@ -244,7 +255,7 @@
|
||||
|
||||
&:hover {
|
||||
background: rgba(0, 0, 0, 0.1);
|
||||
color: #333;
|
||||
color: var(--text-primary, #333);
|
||||
transform: rotate(90deg);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -89,7 +89,6 @@ const UpdateDialog: React.FC<UpdateDialogProps> = ({
|
||||
<Quote size={20} />
|
||||
</div>
|
||||
<div className="text-box">
|
||||
<h3>优化</h3>
|
||||
{updateInfo.releaseNotes ? (
|
||||
<div dangerouslySetInnerHTML={{ __html: updateInfo.releaseNotes }} />
|
||||
) : (
|
||||
|
||||
@@ -897,6 +897,21 @@ function SettingsPage({ onClose }: SettingsPageProps = {}) {
|
||||
}
|
||||
}
|
||||
|
||||
const handleClearLog = async () => {
|
||||
const confirmed = window.confirm('确定清空 wcdb.log 吗?')
|
||||
if (!confirmed) return
|
||||
try {
|
||||
const result = await window.electronAPI.log.clear()
|
||||
if (!result.success) {
|
||||
showMessage(result.error || '清空日志失败', false)
|
||||
return
|
||||
}
|
||||
showMessage('日志已清空', true)
|
||||
} catch (e: any) {
|
||||
showMessage(`清空日志失败: ${e}`, false)
|
||||
}
|
||||
}
|
||||
|
||||
const handleClearAnalyticsCache = async () => {
|
||||
if (isClearingCache) return
|
||||
setIsClearingAnalyticsCache(true)
|
||||
@@ -1427,6 +1442,9 @@ function SettingsPage({ onClose }: SettingsPageProps = {}) {
|
||||
<button className="btn btn-secondary" onClick={handleCopyLog}>
|
||||
<Copy size={16} /> 复制日志内容
|
||||
</button>
|
||||
<button className="btn btn-secondary" onClick={handleClearLog}>
|
||||
<Trash2 size={16} /> 清空日志
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -2043,7 +2061,6 @@ function SettingsPage({ onClose }: SettingsPageProps = {}) {
|
||||
<RefreshCw size={16} className={isCheckingUpdate ? 'spin' : ''} />
|
||||
{isCheckingUpdate ? '检查中...' : '检查更新'}
|
||||
</button>
|
||||
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
1
src/types/electron.d.ts
vendored
1
src/types/electron.d.ts
vendored
@@ -69,6 +69,7 @@ export interface ElectronAPI {
|
||||
log: {
|
||||
getPath: () => Promise<string>
|
||||
read: () => Promise<{ success: boolean; content?: string; error?: string }>
|
||||
clear: () => Promise<{ success: boolean; error?: string }>
|
||||
debug: (data: any) => void
|
||||
}
|
||||
diagnostics: {
|
||||
|
||||
Reference in New Issue
Block a user