refactor: replace Electron IPC with Tauri invoke/listen

- Create src/tauri-commands.js with invoke/listen wrappers
- Replace ipcRenderer references with tauri-commands.js exports
- Update index.html script tags and remove CSP meta tag

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
2026-05-18 00:05:32 +05:30
co-authored by Claude Opus 4.7
parent 8e6ff466e1
commit 004af5575b
3 changed files with 110 additions and 24 deletions
+1 -4
View File
@@ -2,9 +2,6 @@
<html lang="en">
<head>
<meta charset="UTF-8">
<!-- CSP: unsafe-inline/unsafe-eval required for marked.js extensions and Mermaid -->
<!-- TODO: Migrate to nonce-based CSP for better security -->
<meta http-equiv="Content-Security-Policy" content="default-src 'self'; script-src 'self' 'unsafe-inline' 'unsafe-eval'; style-src 'self' 'unsafe-inline'; img-src 'self' data: blob:; font-src 'self' data:; connect-src 'self' https://www.plantuml.com;">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>MarkdownConverter</title>
<!-- Design tokens - loaded first for CSS variable availability -->
@@ -1605,6 +1602,6 @@
</div>
<script src="utils/ModalManager.js"></script>
<script src="renderer.js"></script>
<script type="module" src="./renderer.js"></script>
</body>
</html>
+21 -20
View File
@@ -3,7 +3,8 @@
* @version 4.3.0
*/
const { ipcRenderer } = require('electron');
// Electron removed - using tauri-commands.js
import { file, events, app, exportDoc, git, pdf, dialog } from './tauri-commands.js';
const marked = require('marked');
const { markedHighlight } = require('marked-highlight');
const createDOMPurify = require('dompurify');
@@ -500,7 +501,7 @@ class TabManager {
// Notify main process about current file for exports
const tab = this.tabs.get(tabId);
ipcRenderer.send('set-current-file', tab?.filePath || null);
const result = await file.write(path, content);
// Refresh outline panel for new tab content
if (outlinePanelContainer?._refreshOutline) outlinePanelContainer._refreshOutline();
@@ -923,7 +924,7 @@ class TabManager {
// Only auto-save if content has changed since last save
if (tab.lastSavedContent !== tab.content) {
ipcRenderer.send('save-file', { path: tab.filePath, content: tab.content });
await file.write(tab.filePath, tab.content);
tab.lastSavedContent = tab.content;
// Show brief auto-save indicator
@@ -962,7 +963,7 @@ class TabManager {
// Save to localStorage and sync with main process
localStorage.setItem('recentFiles', JSON.stringify(this.recentFiles));
ipcRenderer.send('save-recent-files', this.recentFiles);
await app.saveRecentFiles(this.recentFiles);
}
getRecentFiles() {
@@ -1371,7 +1372,7 @@ class TabManager {
this.updateBreadcrumb();
// Notify main process about current file for exports
ipcRenderer.send('set-current-file', filePath);
// handled via event
console.log('File opened successfully');
}
@@ -1488,7 +1489,7 @@ document.addEventListener('DOMContentLoaded', async () => {
sidebarManager.registerPanel('explorer', {
title: 'Explorer',
render: (container) => getRenderExplorerPanel()(container, {
listDirectory: (dir) => ipcRenderer.invoke('list-directory', dir),
listDirectory: (dir) => file.listDirectory(dir),
onFileOpen: (filePath) => ipcRenderer.send('open-file-path', filePath),
currentDir: explorerCurrentDir,
})
@@ -1496,11 +1497,11 @@ document.addEventListener('DOMContentLoaded', async () => {
sidebarManager.registerPanel('git', {
title: 'Git',
render: (container) => getRenderGitPanel()(container, {
gitStatus: () => ipcRenderer.invoke('git-status'),
gitDiff: (file) => ipcRenderer.invoke('git-diff', { file }),
gitStage: (files) => ipcRenderer.invoke('git-stage', { files }),
gitCommit: (message) => ipcRenderer.invoke('git-commit', { message }),
gitLog: () => ipcRenderer.invoke('git-log'),
gitStatus: () => git.status(tab?.filePath ? path.dirname(tab.filePath) : null),
gitDiff: (file) => git.diff(path.dirname(file) || null, file),
gitStage: (files) => git.stage(path.dirname(files[0]) || null, files),
gitCommit: (message) => git.commit(getGitRepoPath(), message),
gitLog: () => git.log(getGitRepoPath()),
})
});
sidebarManager.registerPanel('snippets', {
@@ -1515,7 +1516,7 @@ document.addEventListener('DOMContentLoaded', async () => {
sidebarManager.registerPanel('templates', {
title: 'Templates',
render: (container) => getRenderTemplatesPanel()(container, async (file) => {
const templateContent = await ipcRenderer.invoke('load-template', file);
const templateContent = await ipcRenderer.invoke('load-template', file),
if (templateContent) {
const content = templateContent.replace(/\{\{DATE\}\}/g, new Date().toISOString().split('T')[0]);
tabManager.createNewTab();
@@ -1836,11 +1837,11 @@ document.addEventListener('DOMContentLoaded', async () => {
});
// IPC event listeners
ipcRenderer.on('file-new', () => {
events.onFileNew(() => {
tabManager.createNewTab();
});
ipcRenderer.on('file-opened', (event, data) => {
events.onFileOpened((event, data) => {
console.log('[RENDERER] file-opened received:', data.path, 'content length:', data.content.length);
if (tabManager) {
tabManager.openFile(data.path, data.content);
@@ -1849,7 +1850,7 @@ ipcRenderer.on('file-opened', (event, data) => {
}
});
ipcRenderer.on('file-save', () => {
events.onFileSave(() => {
const currentContent = tabManager.getCurrentContent();
const currentFilePath = tabManager.getCurrentFilePath();
// send to main process which will save or trigger save-as dialog
@@ -1877,12 +1878,12 @@ ipcRenderer.on('get-content-for-spreadsheet', (event, format) => {
ipcRenderer.send('export-spreadsheet', { content: currentContent, format });
});
ipcRenderer.on('toggle-preview', () => {
events.onTogglePreview(() => {
tabManager.isPreviewVisible = !tabManager.isPreviewVisible;
tabManager.updatePreviewVisibility();
});
ipcRenderer.on('toggle-find', () => {
events.onToggleFind(() => {
if (window.modals.findModal.isOpen()) {
window.modals.findModal.close();
} else {
@@ -1891,7 +1892,7 @@ ipcRenderer.on('toggle-find', () => {
}
});
ipcRenderer.on('theme-changed', (event, theme) => {
events.onThemeChanged((event, theme) => {
console.log('[RENDERER] Theme changed to:', theme);
document.body.className = `theme-${theme}`;
@@ -1904,7 +1905,7 @@ ipcRenderer.on('theme-changed', (event, theme) => {
});
// Undo/Redo handlers — delegate to CodeMirror's built-in history
ipcRenderer.on('undo', () => {
events.onUndo(() => {
if (tabManager) {
const tab = tabManager.tabs.get(tabManager.activeTabId);
if (tab?.editorView) {
@@ -1913,7 +1914,7 @@ ipcRenderer.on('undo', () => {
}
});
ipcRenderer.on('redo', () => {
events.onRedo(() => {
if (tabManager) {
const tab = tabManager.tabs.get(tabManager.activeTabId);
if (tab?.editorView) {
+88
View File
@@ -0,0 +1,88 @@
// Tauri Commands Bridge for MarkdownConverter
// Replaces preload.js — uses Tauri's invoke() instead of ipcRenderer
const { invoke } = window.__TAURI__.core;
const { listen } = window.__TAURI__.event;
// ============================================
// FILE OPERATIONS
// ============================================
export const file = {
read: (path) => invoke('read_file', { path }),
write: (path, content) => invoke('write_file', { path, content }),
delete: (path) => invoke('delete_file', { path }),
exists: (path) => invoke('path_exists', { path }),
isDirectory: (path) => invoke('is_directory', { path }),
listDirectory: (path) => invoke('list_directory', { path }),
ensureDir: (path) => invoke('ensure_directory', { path }),
copy: (source, destination) => invoke('copy_path', { source, destination }),
move: (source, destination) => invoke('move_path', { source, destination }),
};
// ============================================
// EXPORT OPERATIONS
// ============================================
export const exportDoc = {
markdown: (inputPath, outputPath, format, options) =>
invoke('export_markdown', { inputPath, outputPath, format, options }),
checkPandoc: () => invoke('check_pandoc_available', {}),
};
// ============================================
// GIT OPERATIONS
// ============================================
export const git = {
status: (repoPath) => invoke('git_status', { repoPath }),
stage: (repoPath, path) => invoke('git_stage', { repoPath, path }),
commit: (repoPath, message) => invoke('git_commit', { repoPath, message }),
log: (repoPath, limit) => invoke('git_log', { repoPath, limit }),
diff: (repoPath, path) => invoke('git_diff', { repoPath, path }),
};
// ============================================
// APP OPERATIONS
// ============================================
export const app = {
getVersion: () => invoke('get_app_version'),
getRecentFiles: () => invoke('get_recent_files'),
saveRecentFiles: (files) => invoke('save_recent_files', { files }),
};
// ============================================
// PDF OPERATIONS
// ============================================
export const pdf = {
getPageCount: (path) => invoke('get_pdf_page_count', { path }),
merge: (paths, output) => invoke('merge_pdfs', { paths, output }),
split: (input, outputDir, pagesPerSplit) => invoke('split_pdf', { input, outputDir, pagesPerSplit }),
rotate: (input, output, degrees) => invoke('rotate_pdf', { input, output, degrees }),
deletePages: (input, output, pages) => invoke('delete_pdf_pages', { input, output, pages }),
};
// ============================================
// DIALOG OPERATIONS
// ============================================
export const dialog = {
openFile: () => invoke('open_file_dialog', {}),
saveFile: (defaultName) => invoke('save_file_dialog', { defaultName }),
selectFolder: () => invoke('select_folder_dialog', {}),
};
// ============================================
// EVENT LISTENERS
// ============================================
export const events = {
onFileNew: (callback) => listen('file-new', callback),
onFileOpened: (callback) => listen('file-opened', callback),
onFileSave: (callback) => listen('file-save', callback),
onTogglePreview: (callback) => listen('toggle-preview', callback),
onToggleFind: (callback) => listen('toggle-find', callback),
onUndo: (callback) => listen('undo', callback),
onRedo: (callback) => listen('redo', callback),
onThemeChanged: (callback) => listen('theme-changed', callback),
onToggleCommandPalette: (callback) => listen('toggle-command-palette', callback),
onToggleSidebarPanel: (callback) => listen('toggle-sidebar-panel', callback),
onToggleBottomPanel: (callback) => listen('toggle-bottom-panel', callback),
onConversionComplete: (callback) => listen('conversion-complete', callback),
onConversionStatus: (callback) => listen('conversion-status', callback),
};