const { ipcMain, BrowserWindow } = require('electron'); const path = require('path'); const fs = require('fs'); let winax; try { winax = require('winax'); console.log('✅ Winax loaded successfully'); } catch (error) { console.warn('❌ Winax not available:', error.message); } class WPSHelper { constructor() { this.wpsApp = null; this.activeDocument = null; this.isConnected = false; this.documents = new Map(); this.checkInterval = null; this.lastParagraphContent = ''; this.startMonitoring(); console.log('🔄 WPSHelper initialized'); } connectToWPS() { if (!winax) { console.warn('⚠️ Winax not available, using fallback mode'); this.setupFallbackMode(); return false; } try { this.wpsApp = new winax.Object('KWPS.Application'); this.wpsApp.Visible = true; this.isConnected = true; console.log('✅ Connected to WPS'); this.setupEventListeners(); this.startMonitoring(); this.sendWPSStatus(); return true; } catch (error) { console.error('❌ Failed to connect to WPS:', error); this.isConnected = false; this.setupFallbackMode(); return false; } } setupEventListeners() { this.lastActiveDocument = null; this.lastDocumentCount = 0; } startMonitoring() { if (this.checkInterval) { clearInterval(this.checkInterval); } this.checkInterval = setInterval(() => { this.checkWPSStatus(); this.updateDocumentsList(); }, 2000); } checkWPSStatus() { try { if (this.wpsApp && this.isConnected) { try { const name = this.wpsApp.Name; this.isConnected = true; } catch (error) { this.isConnected = false; this.wpsApp = null; } } else { this.isConnected = false; } } catch (error) { this.isConnected = false; this.wpsApp = null; } } updateDocumentsList() { if (!this.isConnected || !this.wpsApp) { if (!this.isConnected) { this.simulateDocumentsList(); } return; } try { const newDocs = new Map(); let documents; try { documents = this.wpsApp.Documents; } catch (error) { return; } for (let i = 1; i <= documents.Count; i++) { try { const doc = documents.Item(i); const fullName = doc.FullName; const name = doc.Name; newDocs.set(fullName, { name: name, path: fullName, document: doc, isActive: doc === this.activeDocument }); } catch (error) { console.warn('Error accessing document:', error); } } this.documents = newDocs; this.sendDocumentsList(); } catch (error) { console.error('Error updating documents list:', error); } } getFullParagraphContent() { if (!this.isConnected || !this.wpsApp || !this.activeDocument) { return this.getFallbackParagraphContent(); } try { const selection = this.wpsApp.Selection; if (!selection) { return this.getFallbackParagraphContent(); } const originalStart = selection.Start; const originalEnd = selection.End; try { selection.Expand(5); // wdParagraph const text = selection.Text; const revisions = this.getRevisionsInSelection(selection); const comments = this.getCommentsInSelection(selection); selection.SetRange(originalStart, originalEnd); return { text: text.trim(), html: text.trim(), revisions: revisions, comments: comments, hasRevisions: revisions.length > 0, hasComments: comments.length > 0 }; } catch (error) { selection.SetRange(originalStart, originalEnd); return this.getFallbackParagraphContent(); } } catch (error) { return this.getFallbackParagraphContent(); } } getRevisionsInSelection(selection) { const revisions = []; if (!selection || !selection.Revisions) return revisions; try { const revisionsCount = selection.Revisions.Count; for (let i = 1; i <= revisionsCount; i++) { try { const revision = selection.Revisions.Item(i); revisions.push({ type: 'insert', author: revision.Author || '未知作者', date: revision.Date || new Date(), text: revision.Range.Text || '', index: i }); } catch (error) { console.warn('Error getting revision:', error); } } } catch (error) { console.warn('Could not access revisions:', error.message); } return revisions; } getCommentsInSelection(selection) { const comments = []; if (!selection || !selection.Comments) return comments; try { const commentsCount = selection.Comments.Count; for (let i = 1; i <= commentsCount; i++) { try { const comment = selection.Comments.Item(i); comments.push({ author: comment.Author || '未知作者', date: comment.Date || new Date(), text: comment.Range.Text || '', index: i }); } catch (error) { console.warn('Error getting comment:', error); } } } catch (error) { console.warn('Could not access comments:', error.message); } return comments; } getFallbackParagraphContent() { return { text: this.lastParagraphContent || '', html: this.lastParagraphContent || '', revisions: [], comments: [], hasRevisions: false, hasComments: false }; } updateParagraphWithRevisions(newContent) { if (!this.isConnected || !this.wpsApp || !this.activeDocument) { return { success: false, error: 'WPS not connected' }; } try { const selection = this.wpsApp.Selection; if (!selection) { return { success: false, error: 'No selection available' }; } const originalStart = selection.Start; const originalEnd = selection.End; try { selection.Expand(5); // wdParagraph const originalText = selection.Text.trim(); if (originalText === newContent.trim()) { return { success: true, unchanged: true }; } selection.Text = newContent; this.lastParagraphContent = newContent; return { success: true, originalContent: originalText, newContent: newContent, hasChanges: true }; } catch (error) { return { success: false, error: error.message }; } finally { try { selection.SetRange(originalStart, originalStart); } catch (e) {} } } catch (error) { return { success: false, error: error.message }; } } navigateParagraph(direction) { if (!this.isConnected || !this.wpsApp || !this.activeDocument) { return false; } try { const selection = this.wpsApp.Selection; if (!selection) { return false; } if (direction === 'prev') { selection.MoveUp(5, 1); // wdParagraph } else if (direction === 'next') { selection.MoveDown(5, 1); // wdParagraph } return true; } catch (error) { console.error('Error navigating paragraphs:', error); return false; } } openDocument(filePath) { if (!this.isConnected || !this.wpsApp) { console.log('WPS not connected, using fallback'); return false; } try { if (!fs.existsSync(filePath)) { throw new Error('File does not exist: ' + filePath); } const doc = this.wpsApp.Documents.Open(filePath); this.activeDocument = doc; this.documents.set(filePath, { name: doc.Name, path: filePath, document: doc, isActive: true }); this.sendDocumentsList(); this.sendActiveDocumentChange(); console.log('Document opened successfully:', path.basename(filePath)); return true; } catch (error) { console.error('Error opening document:', error); return false; } } switchToDocument(filePath) { if (!this.isConnected || !this.wpsApp) { return false; } const docInfo = this.documents.get(filePath); if (docInfo && docInfo.document) { try { docInfo.document.Activate(); this.activeDocument = docInfo.document; this.sendActiveDocumentChange(); return true; } catch (error) { console.error('Error switching document:', error); } } return false; } handleRevision(action, revisionIndex) { if (!this.isConnected || !this.wpsApp || !this.activeDocument) { return { success: false, error: 'WPS not connected' }; } try { if (revisionIndex !== null) { const revision = this.activeDocument.Revisions.Item(revisionIndex); if (action === 'accept') { revision.Accept(); } else { revision.Reject(); } } else { const revisions = this.activeDocument.Revisions; if (action === 'accept') { revisions.AcceptAll(); } else { revisions.RejectAll(); } } return { success: true }; } catch (error) { return { success: false, error: error.message }; } } addComment(commentText) { if (!this.isConnected || !this.wpsApp || !this.activeDocument) { return { success: false, error: 'WPS not connected' }; } try { const selection = this.wpsApp.Selection; if (!selection) { return { success: false, error: 'No selection available' }; } const comment = selection.Comments.Add(selection.Range); comment.Range.Text = commentText; return { success: true, comment: commentText }; } catch (error) { return { success: false, error: error.message }; } } setTrackRevisions(track) { if (!this.isConnected || !this.wpsApp) return false; try { this.wpsApp.ActiveDocument.TrackRevisions = track; return true; } catch (error) { console.warn('Could not set track revisions:', error.message); return false; } } setupFallbackMode() { console.log('Setting up fallback mode'); this.isConnected = false; this.startMonitoring(); setTimeout(() => { this.simulateDocumentsList(); }, 2000); } simulateDocumentsList() { const mockDocuments = [ { name: '示例文档.docx', path: 'C:\\Documents\\示例文档.docx', isActive: true } ]; this.sendDocumentsList(mockDocuments); const sampleParagraph = `这是一个示例段落内容。在回退模式下,您可以查看界面功能,但实际的WPS文档操作需要安装WPS并确保Winax正常工作。 当前功能包括: • 文档列表显示 • 段落内容显示 • 基本的导航操作 • 文件拖拽处理 要启用完整的WPS集成功能,请确保: 1. 已安装WPS Office 2. Winax模块正确安装 3. 应用程序有足够的权限访问COM组件`; this.sendFullParagraphContent({ text: sampleParagraph, html: sampleParagraph, revisions: [], comments: [], hasRevisions: false, hasComments: false }); } sendWPSStatus() { if (ipcMain) { const windows = BrowserWindow.getAllWindows(); windows.forEach(window => { if (window && !window.isDestroyed()) { try { window.webContents.send('wps-status-changed', { connected: this.isConnected, hasActiveDocument: !!this.activeDocument, documentCount: this.documents.size, timestamp: new Date().toISOString() }); } catch (error) { console.warn('Error sending WPS status:', error); } } }); } } sendDocumentsList(documents = null) { if (ipcMain) { const docs = documents || Array.from(this.documents.values()).map(doc => ({ name: doc.name, path: doc.path, isActive: doc.document === this.activeDocument })); const windows = BrowserWindow.getAllWindows(); windows.forEach(window => { if (window && !window.isDestroyed()) { try { window.webContents.send('documents-list-changed', docs); } catch (error) { console.warn('Error sending documents list:', error); } } }); } } sendActiveDocumentChange() { if (ipcMain && this.activeDocument) { const windows = BrowserWindow.getAllWindows(); windows.forEach(window => { if (window && !window.isDestroyed()) { try { window.webContents.send('active-document-changed', { name: this.activeDocument.Name, path: this.activeDocument.FullName }); } catch (error) { console.warn('Error sending active document change:', error); } } }); } } sendFullParagraphContent(content) { if (ipcMain) { const windows = BrowserWindow.getAllWindows(); windows.forEach(window => { if (window && !window.isDestroyed()) { try { window.webContents.send('full-paragraph-content-changed', content); } catch (error) { console.warn('Error sending full paragraph content:', error); } } }); } } getStatus() { if (!this.isConnected) this.connectToWPS(); return { connected: this.isConnected, activeDocument: this.activeDocument ? { name: this.activeDocument.Name, path: this.activeDocument.FullName } : null, documents: Array.from(this.documents.values()).map(doc => ({ name: doc.name, path: doc.path, isActive: doc.document === this.activeDocument })), currentParagraph: this.lastParagraphContent, timestamp: new Date().toISOString() }; } cleanup() { console.log('Cleaning up WPSHelper resources...'); if (this.checkInterval) { clearInterval(this.checkInterval); this.checkInterval = null; } if (this.wpsApp) { try { console.log('WPS application reference cleaned'); } catch (error) { console.warn('Error cleaning WPS application:', error); } this.wpsApp = null; } this.activeDocument = null; this.isConnected = false; this.documents.clear(); console.log('WPSHelper cleanup completed'); } } const wpsHelper = new WPSHelper(); module.exports = { wpsHelper };