mirror of
https://github.com/amitwh/markdown-converter.git
synced 2026-08-03 02:11:07 +05:30
style: run prettier formatter over src and tests
This commit is contained in:
+62
-43
@@ -4,11 +4,11 @@ const { PDFDocument, rgb, degrees, StandardFonts } = require('pdf-lib');
|
||||
|
||||
function parsePageRanges(rangeString, totalPages) {
|
||||
const pages = [];
|
||||
const ranges = rangeString.split(',').map(r => r.trim());
|
||||
const ranges = rangeString.split(',').map((r) => r.trim());
|
||||
|
||||
for (const range of ranges) {
|
||||
if (range.includes('-')) {
|
||||
const [start, end] = range.split('-').map(n => parseInt(n.trim()));
|
||||
const [start, end] = range.split('-').map((n) => parseInt(n.trim()));
|
||||
for (let i = start; i <= end && i <= totalPages; i++) {
|
||||
if (i > 0 && !pages.includes(i - 1)) {
|
||||
pages.push(i - 1);
|
||||
@@ -27,11 +27,13 @@ function parsePageRanges(rangeString, totalPages) {
|
||||
|
||||
function hexToRgb(hex) {
|
||||
const result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
|
||||
return result ? {
|
||||
r: parseInt(result[1], 16) / 255,
|
||||
g: parseInt(result[2], 16) / 255,
|
||||
b: parseInt(result[3], 16) / 255
|
||||
} : { r: 0, g: 0, b: 0 };
|
||||
return result
|
||||
? {
|
||||
r: parseInt(result[1], 16) / 255,
|
||||
g: parseInt(result[2], 16) / 255,
|
||||
b: parseInt(result[3], 16) / 255,
|
||||
}
|
||||
: { r: 0, g: 0, b: 0 };
|
||||
}
|
||||
|
||||
async function pdfMerge(data) {
|
||||
@@ -42,7 +44,7 @@ async function pdfMerge(data) {
|
||||
const pdfBytes = fs.readFileSync(filePath);
|
||||
const pdf = await PDFDocument.load(pdfBytes);
|
||||
const copiedPages = await mergedPdf.copyPages(pdf, pdf.getPageIndices());
|
||||
copiedPages.forEach(page => mergedPdf.addPage(page));
|
||||
copiedPages.forEach((page) => mergedPdf.addPage(page));
|
||||
}
|
||||
|
||||
const pdfBytes = await mergedPdf.save();
|
||||
@@ -63,13 +65,13 @@ async function pdfSplit(data) {
|
||||
const splits = [];
|
||||
|
||||
if (data.splitMode === 'pages') {
|
||||
const ranges = data.pageRanges.split(',').map(r => r.trim());
|
||||
const ranges = data.pageRanges.split(',').map((r) => r.trim());
|
||||
for (let i = 0; i < ranges.length; i++) {
|
||||
const range = ranges[i];
|
||||
const pages = [];
|
||||
|
||||
if (range.includes('-')) {
|
||||
const [start, end] = range.split('-').map(n => parseInt(n.trim()));
|
||||
const [start, end] = range.split('-').map((n) => parseInt(n.trim()));
|
||||
for (let p = start; p <= end && p <= totalPages; p++) {
|
||||
pages.push(p - 1);
|
||||
}
|
||||
@@ -108,7 +110,7 @@ async function pdfSplit(data) {
|
||||
for (const split of splits) {
|
||||
const newPdf = await PDFDocument.create();
|
||||
const copiedPages = await newPdf.copyPages(pdf, split.pages);
|
||||
copiedPages.forEach(page => newPdf.addPage(page));
|
||||
copiedPages.forEach((page) => newPdf.addPage(page));
|
||||
|
||||
const outputPath = path.join(data.outputFolder, `${baseName}_${split.name}.pdf`);
|
||||
const newPdfBytes = await newPdf.save();
|
||||
@@ -129,18 +131,18 @@ async function pdfCompress(data) {
|
||||
const compressedPdfBytes = await pdf.save({
|
||||
useObjectStreams: true,
|
||||
addDefaultPage: false,
|
||||
objectsPerTick: 50
|
||||
objectsPerTick: 50,
|
||||
});
|
||||
|
||||
fs.writeFileSync(data.outputPath, compressedPdfBytes);
|
||||
|
||||
const originalSize = fs.statSync(data.inputPath).size;
|
||||
const compressedSize = fs.statSync(data.outputPath).size;
|
||||
const savings = ((originalSize - compressedSize) / originalSize * 100).toFixed(1);
|
||||
const savings = (((originalSize - compressedSize) / originalSize) * 100).toFixed(1);
|
||||
|
||||
return {
|
||||
success: true,
|
||||
message: `PDF compressed. Size reduced by ${savings}% (${(originalSize / 1024).toFixed(1)}KB → ${(compressedSize / 1024).toFixed(1)}KB)`
|
||||
message: `PDF compressed. Size reduced by ${savings}% (${(originalSize / 1024).toFixed(1)}KB → ${(compressedSize / 1024).toFixed(1)}KB)`,
|
||||
};
|
||||
} catch (error) {
|
||||
return { success: false, error: error.message };
|
||||
@@ -160,7 +162,7 @@ async function pdfRotate(data) {
|
||||
pagesToRotate = Array.from({ length: totalPages }, (_, i) => i);
|
||||
}
|
||||
|
||||
pagesToRotate.forEach(pageIndex => {
|
||||
pagesToRotate.forEach((pageIndex) => {
|
||||
const page = pdf.getPage(pageIndex);
|
||||
page.setRotation(degrees(data.angle));
|
||||
});
|
||||
@@ -170,7 +172,7 @@ async function pdfRotate(data) {
|
||||
|
||||
return {
|
||||
success: true,
|
||||
message: `Successfully rotated ${pagesToRotate.length} page(s) by ${data.angle}\u00B0`
|
||||
message: `Successfully rotated ${pagesToRotate.length} page(s) by ${data.angle}\u00B0`,
|
||||
};
|
||||
} catch (error) {
|
||||
return { success: false, error: error.message };
|
||||
@@ -185,16 +187,18 @@ async function pdfDeletePages(data) {
|
||||
|
||||
const pagesToDelete = parsePageRanges(data.pages, totalPages);
|
||||
|
||||
pagesToDelete.sort((a, b) => b - a).forEach(pageIndex => {
|
||||
pdf.removePage(pageIndex);
|
||||
});
|
||||
pagesToDelete
|
||||
.sort((a, b) => b - a)
|
||||
.forEach((pageIndex) => {
|
||||
pdf.removePage(pageIndex);
|
||||
});
|
||||
|
||||
const newPdfBytes = await pdf.save();
|
||||
fs.writeFileSync(data.outputPath, newPdfBytes);
|
||||
|
||||
return {
|
||||
success: true,
|
||||
message: `Successfully deleted ${pagesToDelete.length} page(s). New PDF has ${totalPages - pagesToDelete.length} pages`
|
||||
message: `Successfully deleted ${pagesToDelete.length} page(s). New PDF has ${totalPages - pagesToDelete.length} pages`,
|
||||
};
|
||||
} catch (error) {
|
||||
return { success: false, error: error.message };
|
||||
@@ -207,7 +211,7 @@ async function pdfReorder(data) {
|
||||
const pdf = await PDFDocument.load(pdfBytes);
|
||||
const totalPages = pdf.getPageCount();
|
||||
|
||||
const newOrder = data.newOrder.split(',').map(n => parseInt(n.trim()) - 1);
|
||||
const newOrder = data.newOrder.split(',').map((n) => parseInt(n.trim()) - 1);
|
||||
|
||||
if (newOrder.length !== totalPages) {
|
||||
return { success: false, error: `New order must include all ${totalPages} pages` };
|
||||
@@ -215,7 +219,7 @@ async function pdfReorder(data) {
|
||||
|
||||
const newPdf = await PDFDocument.create();
|
||||
const copiedPages = await newPdf.copyPages(pdf, newOrder);
|
||||
copiedPages.forEach(page => newPdf.addPage(page));
|
||||
copiedPages.forEach((page) => newPdf.addPage(page));
|
||||
|
||||
const reorderedPdfBytes = await newPdf.save();
|
||||
fs.writeFileSync(data.outputPath, reorderedPdfBytes);
|
||||
@@ -246,7 +250,9 @@ async function pdfWatermark(data) {
|
||||
const page = pdf.getPage(pageIndex);
|
||||
const { width, height } = page.getSize();
|
||||
|
||||
let x, y, rotation = 0;
|
||||
let x,
|
||||
y,
|
||||
rotation = 0;
|
||||
|
||||
switch (data.position) {
|
||||
case 'center':
|
||||
@@ -294,7 +300,7 @@ async function pdfWatermark(data) {
|
||||
font,
|
||||
color: rgb(color.r, color.g, color.b),
|
||||
opacity: data.opacity,
|
||||
rotate: degrees(rotation)
|
||||
rotate: degrees(rotation),
|
||||
});
|
||||
}
|
||||
|
||||
@@ -303,7 +309,7 @@ async function pdfWatermark(data) {
|
||||
|
||||
return {
|
||||
success: true,
|
||||
message: `Successfully added watermark to ${pagesToWatermark.length} page(s)`
|
||||
message: `Successfully added watermark to ${pagesToWatermark.length} page(s)`,
|
||||
};
|
||||
} catch (error) {
|
||||
return { success: false, error: error.message };
|
||||
@@ -325,8 +331,8 @@ async function pdfEncrypt(data) {
|
||||
annotating: data.permissions.annotating,
|
||||
fillingForms: data.permissions.fillingForms,
|
||||
contentAccessibility: data.permissions.contentAccessibility,
|
||||
documentAssembly: data.permissions.documentAssembly
|
||||
}
|
||||
documentAssembly: data.permissions.documentAssembly,
|
||||
},
|
||||
});
|
||||
|
||||
fs.writeFileSync(data.outputPath, encryptedPdfBytes);
|
||||
@@ -336,7 +342,8 @@ async function pdfEncrypt(data) {
|
||||
if (error.message.includes('encrypt') || error.message.includes('password')) {
|
||||
return {
|
||||
success: false,
|
||||
error: 'PDF encryption requires pdf-lib with encryption support. This feature may not be available in the current version.'
|
||||
error:
|
||||
'PDF encryption requires pdf-lib with encryption support. This feature may not be available in the current version.',
|
||||
};
|
||||
}
|
||||
return { success: false, error: error.message };
|
||||
@@ -375,8 +382,8 @@ async function pdfSetPermissions(data) {
|
||||
annotating: data.permissions.annotating,
|
||||
fillingForms: data.permissions.fillingForms,
|
||||
contentAccessibility: data.permissions.contentAccessibility,
|
||||
documentAssembly: data.permissions.documentAssembly
|
||||
}
|
||||
documentAssembly: data.permissions.documentAssembly,
|
||||
},
|
||||
});
|
||||
|
||||
fs.writeFileSync(data.outputPath, newPdfBytes);
|
||||
@@ -386,7 +393,8 @@ async function pdfSetPermissions(data) {
|
||||
if (error.message.includes('encrypt') || error.message.includes('permission')) {
|
||||
return {
|
||||
success: false,
|
||||
error: 'PDF permissions require pdf-lib with encryption support. This feature may not be available in the current version.'
|
||||
error:
|
||||
'PDF permissions require pdf-lib with encryption support. This feature may not be available in the current version.',
|
||||
};
|
||||
}
|
||||
return { success: false, error: error.message };
|
||||
@@ -395,17 +403,28 @@ async function pdfSetPermissions(data) {
|
||||
|
||||
function executeOperation(operation, data) {
|
||||
switch (operation) {
|
||||
case 'merge': return pdfMerge(data);
|
||||
case 'split': return pdfSplit(data);
|
||||
case 'compress': return pdfCompress(data);
|
||||
case 'rotate': return pdfRotate(data);
|
||||
case 'delete': return pdfDeletePages(data);
|
||||
case 'reorder': return pdfReorder(data);
|
||||
case 'watermark': return pdfWatermark(data);
|
||||
case 'encrypt': return pdfEncrypt(data);
|
||||
case 'decrypt': return pdfDecrypt(data);
|
||||
case 'permissions': return pdfSetPermissions(data);
|
||||
default: return Promise.resolve({ success: false, error: `Unknown operation: ${operation}` });
|
||||
case 'merge':
|
||||
return pdfMerge(data);
|
||||
case 'split':
|
||||
return pdfSplit(data);
|
||||
case 'compress':
|
||||
return pdfCompress(data);
|
||||
case 'rotate':
|
||||
return pdfRotate(data);
|
||||
case 'delete':
|
||||
return pdfDeletePages(data);
|
||||
case 'reorder':
|
||||
return pdfReorder(data);
|
||||
case 'watermark':
|
||||
return pdfWatermark(data);
|
||||
case 'encrypt':
|
||||
return pdfEncrypt(data);
|
||||
case 'decrypt':
|
||||
return pdfDecrypt(data);
|
||||
case 'permissions':
|
||||
return pdfSetPermissions(data);
|
||||
default:
|
||||
return Promise.resolve({ success: false, error: `Unknown operation: ${operation}` });
|
||||
}
|
||||
}
|
||||
|
||||
@@ -429,5 +448,5 @@ module.exports = {
|
||||
pdfDecrypt,
|
||||
pdfSetPermissions,
|
||||
executeOperation,
|
||||
getPageCount
|
||||
getPageCount,
|
||||
};
|
||||
|
||||
@@ -10,4 +10,4 @@ function register() {
|
||||
});
|
||||
}
|
||||
|
||||
module.exports = { register };
|
||||
module.exports = { register };
|
||||
|
||||
+13
-5
@@ -5,24 +5,32 @@ const GitOperations = require('../GitOperations');
|
||||
|
||||
function register(currentFileRef) {
|
||||
ipcMain.handle('git-status', async () => {
|
||||
const dir = currentFileRef.current ? require('path').dirname(currentFileRef.current) : process.cwd();
|
||||
const dir = currentFileRef.current
|
||||
? require('path').dirname(currentFileRef.current)
|
||||
: process.cwd();
|
||||
return GitOperations.getStatus(dir);
|
||||
});
|
||||
|
||||
ipcMain.handle('git-stage', async (_event, { files }) => {
|
||||
const dir = currentFileRef.current ? require('path').dirname(currentFileRef.current) : process.cwd();
|
||||
const dir = currentFileRef.current
|
||||
? require('path').dirname(currentFileRef.current)
|
||||
: process.cwd();
|
||||
return GitOperations.stage(dir, files);
|
||||
});
|
||||
|
||||
ipcMain.handle('git-commit', async (_event, { message }) => {
|
||||
const dir = currentFileRef.current ? require('path').dirname(currentFileRef.current) : process.cwd();
|
||||
const dir = currentFileRef.current
|
||||
? require('path').dirname(currentFileRef.current)
|
||||
: process.cwd();
|
||||
return GitOperations.commit(dir, message);
|
||||
});
|
||||
|
||||
ipcMain.handle('git-log', async () => {
|
||||
const dir = currentFileRef.current ? require('path').dirname(currentFileRef.current) : process.cwd();
|
||||
const dir = currentFileRef.current
|
||||
? require('path').dirname(currentFileRef.current)
|
||||
: process.cwd();
|
||||
return GitOperations.log(dir);
|
||||
});
|
||||
}
|
||||
|
||||
module.exports = { register };
|
||||
module.exports = { register };
|
||||
|
||||
@@ -6,7 +6,13 @@ const path = require('path');
|
||||
const { register: registerGit } = require('./git');
|
||||
const { register: registerBinary } = require('./binary');
|
||||
|
||||
function register({ validatePath, resolveWritablePath, isPathAccessible, currentFileRef, mainWindow }) {
|
||||
function register({
|
||||
validatePath,
|
||||
resolveWritablePath,
|
||||
isPathAccessible,
|
||||
currentFileRef,
|
||||
mainWindow,
|
||||
}) {
|
||||
// pick-folder
|
||||
ipcMain.handle('pick-folder', async () => {
|
||||
const result = await dialog.showOpenDialog(mainWindow, {
|
||||
@@ -154,4 +160,4 @@ function register({ validatePath, resolveWritablePath, isPathAccessible, current
|
||||
registerBinary();
|
||||
}
|
||||
|
||||
module.exports = { register };
|
||||
module.exports = { register };
|
||||
|
||||
+701
-524
File diff suppressed because it is too large
Load Diff
@@ -10,7 +10,10 @@ function register({ crash, getMainWindow }) {
|
||||
});
|
||||
|
||||
ipcMain.handle('crash:delete', (_event, filename) => {
|
||||
if (typeof filename === 'string' && /^\d+(-\d+)?-(uncaughtException|unhandledRejection)\.json$/.test(filename)) {
|
||||
if (
|
||||
typeof filename === 'string' &&
|
||||
/^\d+(-\d+)?-(uncaughtException|unhandledRejection)\.json$/.test(filename)
|
||||
) {
|
||||
crash.delete(filename);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -10,7 +10,7 @@ const {
|
||||
convertItems,
|
||||
pdfEditorItems,
|
||||
toolsItems,
|
||||
helpItems
|
||||
helpItems,
|
||||
} = require('./items');
|
||||
|
||||
function buildMenu(mainWindow) {
|
||||
@@ -22,7 +22,7 @@ function buildMenu(mainWindow) {
|
||||
{ label: '&Convert', submenu: convertItems(mainWindow) },
|
||||
{ label: 'PDF Editor', submenu: pdfEditorItems(mainWindow) },
|
||||
{ label: '&Tools', submenu: toolsItems(mainWindow) },
|
||||
{ label: '&Help', submenu: helpItems(mainWindow) }
|
||||
{ label: '&Help', submenu: helpItems(mainWindow) },
|
||||
];
|
||||
return Menu.buildFromTemplate(template);
|
||||
}
|
||||
@@ -31,4 +31,4 @@ function register(mainWindow) {
|
||||
Menu.setApplicationMenu(buildMenu(mainWindow));
|
||||
}
|
||||
|
||||
module.exports = { register, buildMenu };
|
||||
module.exports = { register, buildMenu };
|
||||
|
||||
+404
-133
@@ -11,14 +11,14 @@ function buildRecentFilesMenu(mainWindow) {
|
||||
const recentFilesPath = path.join(app.getPath('userData'), 'recent-files.json');
|
||||
if (!fs.existsSync(recentFilesPath)) return [{ label: 'No Recent Files', enabled: false }];
|
||||
const recentFiles = JSON.parse(fs.readFileSync(recentFilesPath, 'utf-8'));
|
||||
const existing = recentFiles.filter(file => fs.existsSync(file));
|
||||
const existing = recentFiles.filter((file) => fs.existsSync(file));
|
||||
if (existing.length === 0) return [{ label: 'No Recent Files', enabled: false }];
|
||||
const items = existing.map(file => ({
|
||||
const items = existing.map((file) => ({
|
||||
label: path.basename(file),
|
||||
click: () => {
|
||||
const { openFileFromPath } = require('../index');
|
||||
openFileFromPath(file);
|
||||
}
|
||||
},
|
||||
}));
|
||||
items.push(
|
||||
{ type: 'separator' },
|
||||
@@ -28,7 +28,7 @@ function buildRecentFilesMenu(mainWindow) {
|
||||
if (mainWindow) {
|
||||
mainWindow.webContents.send('clear-recent-files');
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
);
|
||||
return items;
|
||||
@@ -42,7 +42,7 @@ function fileItems(mainWindow) {
|
||||
{
|
||||
label: 'New',
|
||||
accelerator: 'CmdOrCtrl+N',
|
||||
click: () => mainWindow.webContents.send('file-new')
|
||||
click: () => mainWindow.webContents.send('file-new'),
|
||||
},
|
||||
{
|
||||
label: 'Open',
|
||||
@@ -50,7 +50,7 @@ function fileItems(mainWindow) {
|
||||
click: () => {
|
||||
const { openFile } = require('../index');
|
||||
openFile();
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
label: 'Open PDF',
|
||||
@@ -58,12 +58,12 @@ function fileItems(mainWindow) {
|
||||
click: () => {
|
||||
const { openPdfFile } = require('../index');
|
||||
openPdfFile();
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
label: 'Save',
|
||||
accelerator: 'CmdOrCtrl+S',
|
||||
click: () => mainWindow.webContents.send('file-save')
|
||||
click: () => mainWindow.webContents.send('file-save'),
|
||||
},
|
||||
{
|
||||
label: 'Save As',
|
||||
@@ -71,30 +71,60 @@ function fileItems(mainWindow) {
|
||||
click: () => {
|
||||
const { saveAsFile } = require('../index');
|
||||
saveAsFile();
|
||||
}
|
||||
},
|
||||
},
|
||||
{ type: 'separator' },
|
||||
// NOTE: Print Preview submenu removed — handled by React <PrintPreview> overlay
|
||||
{ type: 'separator' },
|
||||
{
|
||||
label: 'Recent Files',
|
||||
submenu: buildRecentFilesMenu(mainWindow)
|
||||
submenu: buildRecentFilesMenu(mainWindow),
|
||||
},
|
||||
{ type: 'separator' },
|
||||
{
|
||||
label: 'New from Template',
|
||||
submenu: [
|
||||
{ label: 'Blog Post', click: () => mainWindow.webContents.send('load-template-menu', 'blog-post.md') },
|
||||
{ label: 'Meeting Notes', click: () => mainWindow.webContents.send('load-template-menu', 'meeting-notes.md') },
|
||||
{ label: 'Technical Spec', click: () => mainWindow.webContents.send('load-template-menu', 'technical-spec.md') },
|
||||
{ label: 'Changelog', click: () => mainWindow.webContents.send('load-template-menu', 'changelog.md') },
|
||||
{ label: 'README', click: () => mainWindow.webContents.send('load-template-menu', 'readme.md') },
|
||||
{ label: 'Project Plan', click: () => mainWindow.webContents.send('load-template-menu', 'project-plan.md') },
|
||||
{ label: 'API Documentation', click: () => mainWindow.webContents.send('load-template-menu', 'api-docs.md') },
|
||||
{ label: 'Tutorial', click: () => mainWindow.webContents.send('load-template-menu', 'tutorial.md') },
|
||||
{ label: 'Release Notes', click: () => mainWindow.webContents.send('load-template-menu', 'release-notes.md') },
|
||||
{ label: 'Comparison', click: () => mainWindow.webContents.send('load-template-menu', 'comparison.md') }
|
||||
]
|
||||
{
|
||||
label: 'Blog Post',
|
||||
click: () => mainWindow.webContents.send('load-template-menu', 'blog-post.md'),
|
||||
},
|
||||
{
|
||||
label: 'Meeting Notes',
|
||||
click: () => mainWindow.webContents.send('load-template-menu', 'meeting-notes.md'),
|
||||
},
|
||||
{
|
||||
label: 'Technical Spec',
|
||||
click: () => mainWindow.webContents.send('load-template-menu', 'technical-spec.md'),
|
||||
},
|
||||
{
|
||||
label: 'Changelog',
|
||||
click: () => mainWindow.webContents.send('load-template-menu', 'changelog.md'),
|
||||
},
|
||||
{
|
||||
label: 'README',
|
||||
click: () => mainWindow.webContents.send('load-template-menu', 'readme.md'),
|
||||
},
|
||||
{
|
||||
label: 'Project Plan',
|
||||
click: () => mainWindow.webContents.send('load-template-menu', 'project-plan.md'),
|
||||
},
|
||||
{
|
||||
label: 'API Documentation',
|
||||
click: () => mainWindow.webContents.send('load-template-menu', 'api-docs.md'),
|
||||
},
|
||||
{
|
||||
label: 'Tutorial',
|
||||
click: () => mainWindow.webContents.send('load-template-menu', 'tutorial.md'),
|
||||
},
|
||||
{
|
||||
label: 'Release Notes',
|
||||
click: () => mainWindow.webContents.send('load-template-menu', 'release-notes.md'),
|
||||
},
|
||||
{
|
||||
label: 'Comparison',
|
||||
click: () => mainWindow.webContents.send('load-template-menu', 'comparison.md'),
|
||||
},
|
||||
],
|
||||
},
|
||||
{ type: 'separator' },
|
||||
{
|
||||
@@ -103,58 +133,137 @@ function fileItems(mainWindow) {
|
||||
click: () => {
|
||||
const { importDocument } = require('../index');
|
||||
importDocument();
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
label: 'Export',
|
||||
submenu: [
|
||||
{
|
||||
label: 'HTML', click: () => {
|
||||
label: 'HTML',
|
||||
click: () => {
|
||||
const { exportFile } = require('../index');
|
||||
exportFile('html');
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
label: 'PDF', click: () => {
|
||||
label: 'PDF',
|
||||
click: () => {
|
||||
const { exportFile } = require('../index');
|
||||
exportFile('pdf');
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
label: 'PDF (Enhanced)', click: () => {
|
||||
label: 'PDF (Enhanced)',
|
||||
click: () => {
|
||||
const { exportPDFViaWordTemplate } = require('../index');
|
||||
exportPDFViaWordTemplate();
|
||||
}, accelerator: 'Ctrl+Shift+P'
|
||||
},
|
||||
accelerator: 'Ctrl+Shift+P',
|
||||
},
|
||||
{
|
||||
label: 'DOCX', click: () => {
|
||||
label: 'DOCX',
|
||||
click: () => {
|
||||
const { exportFile } = require('../index');
|
||||
exportFile('docx');
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
label: 'DOCX (Enhanced)', click: () => {
|
||||
label: 'DOCX (Enhanced)',
|
||||
click: () => {
|
||||
const { exportWordWithTemplate } = require('../index');
|
||||
exportWordWithTemplate();
|
||||
}, accelerator: 'Ctrl+Shift+W'
|
||||
},
|
||||
accelerator: 'Ctrl+Shift+W',
|
||||
},
|
||||
{
|
||||
label: 'LaTeX',
|
||||
click: () => {
|
||||
const { exportFile } = require('../index');
|
||||
exportFile('latex');
|
||||
},
|
||||
},
|
||||
{
|
||||
label: 'RTF',
|
||||
click: () => {
|
||||
const { exportFile } = require('../index');
|
||||
exportFile('rtf');
|
||||
},
|
||||
},
|
||||
{
|
||||
label: 'ODT',
|
||||
click: () => {
|
||||
const { exportFile } = require('../index');
|
||||
exportFile('odt');
|
||||
},
|
||||
},
|
||||
{
|
||||
label: 'EPUB',
|
||||
click: () => {
|
||||
const { exportFile } = require('../index');
|
||||
exportFile('epub');
|
||||
},
|
||||
},
|
||||
{ label: 'LaTeX', click: () => { const { exportFile } = require('../index'); exportFile('latex'); } },
|
||||
{ label: 'RTF', click: () => { const { exportFile } = require('../index'); exportFile('rtf'); } },
|
||||
{ label: 'ODT', click: () => { const { exportFile } = require('../index'); exportFile('odt'); } },
|
||||
{ label: 'EPUB', click: () => { const { exportFile } = require('../index'); exportFile('epub'); } },
|
||||
{ type: 'separator' },
|
||||
{ label: 'PowerPoint (PPTX)', click: () => { const { exportFile } = require('../index'); exportFile('pptx'); } },
|
||||
{ label: 'OpenDocument Presentation (ODP)', click: () => { const { exportFile } = require('../index'); exportFile('odp'); } },
|
||||
{
|
||||
label: 'PowerPoint (PPTX)',
|
||||
click: () => {
|
||||
const { exportFile } = require('../index');
|
||||
exportFile('pptx');
|
||||
},
|
||||
},
|
||||
{
|
||||
label: 'OpenDocument Presentation (ODP)',
|
||||
click: () => {
|
||||
const { exportFile } = require('../index');
|
||||
exportFile('odp');
|
||||
},
|
||||
},
|
||||
{ type: 'separator' },
|
||||
{ label: 'CSV (Tables)', click: () => { const { exportSpreadsheet } = require('../index'); exportSpreadsheet('csv'); } },
|
||||
{
|
||||
label: 'CSV (Tables)',
|
||||
click: () => {
|
||||
const { exportSpreadsheet } = require('../index');
|
||||
exportSpreadsheet('csv');
|
||||
},
|
||||
},
|
||||
{ type: 'separator' },
|
||||
{ label: 'JSON (.json)', click: () => { const { exportFile } = require('../index'); exportFile('json'); } },
|
||||
{ label: 'YAML (.yaml)', click: () => { const { exportFile } = require('../index'); exportFile('yaml'); } },
|
||||
{ label: 'XML (.xml)', click: () => { const { exportFile } = require('../index'); exportFile('xml'); } },
|
||||
{
|
||||
label: 'JSON (.json)',
|
||||
click: () => {
|
||||
const { exportFile } = require('../index');
|
||||
exportFile('json');
|
||||
},
|
||||
},
|
||||
{
|
||||
label: 'YAML (.yaml)',
|
||||
click: () => {
|
||||
const { exportFile } = require('../index');
|
||||
exportFile('yaml');
|
||||
},
|
||||
},
|
||||
{
|
||||
label: 'XML (.xml)',
|
||||
click: () => {
|
||||
const { exportFile } = require('../index');
|
||||
exportFile('xml');
|
||||
},
|
||||
},
|
||||
{ type: 'separator' },
|
||||
{ label: 'Confluence Wiki (.txt)', click: () => { const { exportFile } = require('../index'); exportFile('confluence'); } },
|
||||
{ label: 'MOBI E-book (.mobi)', click: () => { const { exportFile } = require('../index'); exportFile('mobi'); } }
|
||||
]
|
||||
{
|
||||
label: 'Confluence Wiki (.txt)',
|
||||
click: () => {
|
||||
const { exportFile } = require('../index');
|
||||
exportFile('confluence');
|
||||
},
|
||||
},
|
||||
{
|
||||
label: 'MOBI E-book (.mobi)',
|
||||
click: () => {
|
||||
const { exportFile } = require('../index');
|
||||
exportFile('mobi');
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
{ type: 'separator' },
|
||||
{
|
||||
@@ -162,14 +271,14 @@ function fileItems(mainWindow) {
|
||||
click: () => {
|
||||
const { selectWordTemplate } = require('../index');
|
||||
selectWordTemplate();
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
label: 'Template Settings...',
|
||||
click: () => {
|
||||
const { showTemplateSettings } = require('../index');
|
||||
showTemplateSettings();
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
label: 'Header & Footer Settings...',
|
||||
@@ -177,14 +286,14 @@ function fileItems(mainWindow) {
|
||||
if (mainWindow) {
|
||||
mainWindow.webContents.send('open-header-footer-dialog');
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
{ type: 'separator' },
|
||||
{
|
||||
label: 'Quit',
|
||||
accelerator: process.platform === 'darwin' ? 'Cmd+Q' : 'Ctrl+Q',
|
||||
click: () => app.quit()
|
||||
}
|
||||
click: () => app.quit(),
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
@@ -193,12 +302,12 @@ function editItems(mainWindow) {
|
||||
{
|
||||
label: 'Undo',
|
||||
accelerator: 'CmdOrCtrl+Z',
|
||||
click: () => mainWindow.webContents.send('undo')
|
||||
click: () => mainWindow.webContents.send('undo'),
|
||||
},
|
||||
{
|
||||
label: 'Redo',
|
||||
accelerator: 'CmdOrCtrl+Shift+Z',
|
||||
click: () => mainWindow.webContents.send('redo')
|
||||
click: () => mainWindow.webContents.send('redo'),
|
||||
},
|
||||
{ type: 'separator' },
|
||||
{ label: 'Cut', accelerator: 'CmdOrCtrl+X', role: 'cut' },
|
||||
@@ -209,8 +318,8 @@ function editItems(mainWindow) {
|
||||
{
|
||||
label: 'Find & Replace',
|
||||
accelerator: 'CmdOrCtrl+F',
|
||||
click: () => mainWindow.webContents.send('toggle-find')
|
||||
}
|
||||
click: () => mainWindow.webContents.send('toggle-find'),
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
@@ -219,60 +328,222 @@ function viewItems(mainWindow) {
|
||||
{
|
||||
label: 'Toggle Preview',
|
||||
accelerator: 'CmdOrCtrl+Shift+V',
|
||||
click: () => mainWindow.webContents.send('toggle-preview')
|
||||
click: () => mainWindow.webContents.send('toggle-preview'),
|
||||
},
|
||||
{
|
||||
label: 'Writing Analytics',
|
||||
accelerator: 'CmdOrCtrl+Shift+A',
|
||||
click: () => mainWindow.webContents.send('show-analytics-dialog')
|
||||
click: () => mainWindow.webContents.send('show-analytics-dialog'),
|
||||
},
|
||||
// NOTE: Command Palette removed — handled by useCommandStore
|
||||
{ type: 'separator' },
|
||||
{
|
||||
label: 'Sidebar',
|
||||
submenu: [
|
||||
{ label: 'Files', click: () => mainWindow.webContents.send('toggle-sidebar-panel', 'explorer') },
|
||||
{ label: 'Outline', click: () => mainWindow.webContents.send('toggle-sidebar-panel', 'outline') },
|
||||
{ label: 'Snippets', click: () => mainWindow.webContents.send('toggle-sidebar-panel', 'snippets') },
|
||||
{ label: 'Templates', click: () => mainWindow.webContents.send('toggle-sidebar-panel', 'templates') },
|
||||
{ label: 'Git', click: () => mainWindow.webContents.send('toggle-sidebar-panel', 'git') }
|
||||
]
|
||||
{
|
||||
label: 'Files',
|
||||
click: () => mainWindow.webContents.send('toggle-sidebar-panel', 'explorer'),
|
||||
},
|
||||
{
|
||||
label: 'Outline',
|
||||
click: () => mainWindow.webContents.send('toggle-sidebar-panel', 'outline'),
|
||||
},
|
||||
{
|
||||
label: 'Snippets',
|
||||
click: () => mainWindow.webContents.send('toggle-sidebar-panel', 'snippets'),
|
||||
},
|
||||
{
|
||||
label: 'Templates',
|
||||
click: () => mainWindow.webContents.send('toggle-sidebar-panel', 'templates'),
|
||||
},
|
||||
{ label: 'Git', click: () => mainWindow.webContents.send('toggle-sidebar-panel', 'git') },
|
||||
],
|
||||
},
|
||||
{
|
||||
label: 'Bottom Panel (REPL)',
|
||||
click: () => mainWindow.webContents.send('toggle-bottom-panel')
|
||||
click: () => mainWindow.webContents.send('toggle-bottom-panel'),
|
||||
},
|
||||
{ type: 'separator' },
|
||||
{
|
||||
label: 'Theme',
|
||||
submenu: [
|
||||
{ label: 'Atom One Light (Default)', click: () => { const { setTheme } = require('../index'); setTheme('atomonelight'); } },
|
||||
{ label: 'GitHub Light', click: () => { const { setTheme } = require('../index'); setTheme('github'); } },
|
||||
{ label: 'Light', click: () => { const { setTheme } = require('../index'); setTheme('light'); } },
|
||||
{ label: 'Solarized Light', click: () => { const { setTheme } = require('../index'); setTheme('solarized'); } },
|
||||
{ label: 'Gruvbox Light', click: () => { const { setTheme } = require('../index'); setTheme('gruvbox-light'); } },
|
||||
{ label: 'Ayu Light', click: () => { const { setTheme } = require('../index'); setTheme('ayu-light'); } },
|
||||
{ label: 'Sepia', click: () => { const { setTheme } = require('../index'); setTheme('sepia'); } },
|
||||
{ label: 'Paper', click: () => { const { setTheme } = require('../index'); setTheme('paper'); } },
|
||||
{ label: 'Rose Pine Dawn', click: () => { const { setTheme } = require('../index'); setTheme('rosepine-dawn'); } },
|
||||
{ label: 'Concrete Light', click: () => { const { setTheme } = require('../index'); setTheme('concrete-light'); } },
|
||||
{
|
||||
label: 'Atom One Light (Default)',
|
||||
click: () => {
|
||||
const { setTheme } = require('../index');
|
||||
setTheme('atomonelight');
|
||||
},
|
||||
},
|
||||
{
|
||||
label: 'GitHub Light',
|
||||
click: () => {
|
||||
const { setTheme } = require('../index');
|
||||
setTheme('github');
|
||||
},
|
||||
},
|
||||
{
|
||||
label: 'Light',
|
||||
click: () => {
|
||||
const { setTheme } = require('../index');
|
||||
setTheme('light');
|
||||
},
|
||||
},
|
||||
{
|
||||
label: 'Solarized Light',
|
||||
click: () => {
|
||||
const { setTheme } = require('../index');
|
||||
setTheme('solarized');
|
||||
},
|
||||
},
|
||||
{
|
||||
label: 'Gruvbox Light',
|
||||
click: () => {
|
||||
const { setTheme } = require('../index');
|
||||
setTheme('gruvbox-light');
|
||||
},
|
||||
},
|
||||
{
|
||||
label: 'Ayu Light',
|
||||
click: () => {
|
||||
const { setTheme } = require('../index');
|
||||
setTheme('ayu-light');
|
||||
},
|
||||
},
|
||||
{
|
||||
label: 'Sepia',
|
||||
click: () => {
|
||||
const { setTheme } = require('../index');
|
||||
setTheme('sepia');
|
||||
},
|
||||
},
|
||||
{
|
||||
label: 'Paper',
|
||||
click: () => {
|
||||
const { setTheme } = require('../index');
|
||||
setTheme('paper');
|
||||
},
|
||||
},
|
||||
{
|
||||
label: 'Rose Pine Dawn',
|
||||
click: () => {
|
||||
const { setTheme } = require('../index');
|
||||
setTheme('rosepine-dawn');
|
||||
},
|
||||
},
|
||||
{
|
||||
label: 'Concrete Light',
|
||||
click: () => {
|
||||
const { setTheme } = require('../index');
|
||||
setTheme('concrete-light');
|
||||
},
|
||||
},
|
||||
{ type: 'separator' },
|
||||
{ label: 'Dark', click: () => { const { setTheme } = require('../index'); setTheme('dark'); } },
|
||||
{ label: 'One Dark', click: () => { const { setTheme } = require('../index'); setTheme('onedark'); } },
|
||||
{ label: 'Dracula', click: () => { const { setTheme } = require('../index'); setTheme('dracula'); } },
|
||||
{ label: 'Nord', click: () => { const { setTheme } = require('../index'); setTheme('nord'); } },
|
||||
{ label: 'Monokai', click: () => { const { setTheme } = require('../index'); setTheme('monokai'); } },
|
||||
{ label: 'Material', click: () => { const { setTheme } = require('../index'); setTheme('material'); } },
|
||||
{ label: 'Gruvbox Dark', click: () => { const { setTheme } = require('../index'); setTheme('gruvbox-dark'); } },
|
||||
{ label: 'Tokyo Night', click: () => { const { setTheme } = require('../index'); setTheme('tokyonight'); } },
|
||||
{ label: 'Palenight', click: () => { const { setTheme } = require('../index'); setTheme('palenight'); } },
|
||||
{ label: 'Ayu Dark', click: () => { const { setTheme } = require('../index'); setTheme('ayu-dark'); } },
|
||||
{ label: 'Ayu Mirage', click: () => { const { setTheme } = require('../index'); setTheme('ayu-mirage'); } },
|
||||
{ label: 'Oceanic Next', click: () => { const { setTheme } = require('../index'); setTheme('oceanic-next'); } },
|
||||
{ label: 'Cobalt2', click: () => { const { setTheme } = require('../index'); setTheme('cobalt2'); } },
|
||||
{ label: 'Concrete Dark', click: () => { const { setTheme } = require('../index'); setTheme('concrete-dark'); } },
|
||||
{ label: 'Concrete Warm', click: () => { const { setTheme } = require('../index'); setTheme('concrete-warm'); } }
|
||||
]
|
||||
{
|
||||
label: 'Dark',
|
||||
click: () => {
|
||||
const { setTheme } = require('../index');
|
||||
setTheme('dark');
|
||||
},
|
||||
},
|
||||
{
|
||||
label: 'One Dark',
|
||||
click: () => {
|
||||
const { setTheme } = require('../index');
|
||||
setTheme('onedark');
|
||||
},
|
||||
},
|
||||
{
|
||||
label: 'Dracula',
|
||||
click: () => {
|
||||
const { setTheme } = require('../index');
|
||||
setTheme('dracula');
|
||||
},
|
||||
},
|
||||
{
|
||||
label: 'Nord',
|
||||
click: () => {
|
||||
const { setTheme } = require('../index');
|
||||
setTheme('nord');
|
||||
},
|
||||
},
|
||||
{
|
||||
label: 'Monokai',
|
||||
click: () => {
|
||||
const { setTheme } = require('../index');
|
||||
setTheme('monokai');
|
||||
},
|
||||
},
|
||||
{
|
||||
label: 'Material',
|
||||
click: () => {
|
||||
const { setTheme } = require('../index');
|
||||
setTheme('material');
|
||||
},
|
||||
},
|
||||
{
|
||||
label: 'Gruvbox Dark',
|
||||
click: () => {
|
||||
const { setTheme } = require('../index');
|
||||
setTheme('gruvbox-dark');
|
||||
},
|
||||
},
|
||||
{
|
||||
label: 'Tokyo Night',
|
||||
click: () => {
|
||||
const { setTheme } = require('../index');
|
||||
setTheme('tokyonight');
|
||||
},
|
||||
},
|
||||
{
|
||||
label: 'Palenight',
|
||||
click: () => {
|
||||
const { setTheme } = require('../index');
|
||||
setTheme('palenight');
|
||||
},
|
||||
},
|
||||
{
|
||||
label: 'Ayu Dark',
|
||||
click: () => {
|
||||
const { setTheme } = require('../index');
|
||||
setTheme('ayu-dark');
|
||||
},
|
||||
},
|
||||
{
|
||||
label: 'Ayu Mirage',
|
||||
click: () => {
|
||||
const { setTheme } = require('../index');
|
||||
setTheme('ayu-mirage');
|
||||
},
|
||||
},
|
||||
{
|
||||
label: 'Oceanic Next',
|
||||
click: () => {
|
||||
const { setTheme } = require('../index');
|
||||
setTheme('oceanic-next');
|
||||
},
|
||||
},
|
||||
{
|
||||
label: 'Cobalt2',
|
||||
click: () => {
|
||||
const { setTheme } = require('../index');
|
||||
setTheme('cobalt2');
|
||||
},
|
||||
},
|
||||
{
|
||||
label: 'Concrete Dark',
|
||||
click: () => {
|
||||
const { setTheme } = require('../index');
|
||||
setTheme('concrete-dark');
|
||||
},
|
||||
},
|
||||
{
|
||||
label: 'Concrete Warm',
|
||||
click: () => {
|
||||
const { setTheme } = require('../index');
|
||||
setTheme('concrete-warm');
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
{ type: 'separator' },
|
||||
{
|
||||
@@ -281,19 +552,19 @@ function viewItems(mainWindow) {
|
||||
{
|
||||
label: 'Increase Font Size',
|
||||
accelerator: 'CmdOrCtrl+Shift+Plus',
|
||||
click: () => mainWindow.webContents.send('adjust-font-size', 'increase')
|
||||
click: () => mainWindow.webContents.send('adjust-font-size', 'increase'),
|
||||
},
|
||||
{
|
||||
label: 'Decrease Font Size',
|
||||
accelerator: 'CmdOrCtrl+Shift+-',
|
||||
click: () => mainWindow.webContents.send('adjust-font-size', 'decrease')
|
||||
click: () => mainWindow.webContents.send('adjust-font-size', 'decrease'),
|
||||
},
|
||||
{
|
||||
label: 'Reset Font Size',
|
||||
accelerator: 'CmdOrCtrl+Shift+0',
|
||||
click: () => mainWindow.webContents.send('adjust-font-size', 'reset')
|
||||
}
|
||||
]
|
||||
click: () => mainWindow.webContents.send('adjust-font-size', 'reset'),
|
||||
},
|
||||
],
|
||||
},
|
||||
{ type: 'separator' },
|
||||
{
|
||||
@@ -302,7 +573,7 @@ function viewItems(mainWindow) {
|
||||
checked: true,
|
||||
click: (menuItem) => {
|
||||
mainWindow.webContents.session.setSpellCheckerEnabled(menuItem.checked);
|
||||
}
|
||||
},
|
||||
},
|
||||
{ type: 'separator' },
|
||||
{
|
||||
@@ -310,13 +581,13 @@ function viewItems(mainWindow) {
|
||||
submenu: [
|
||||
{
|
||||
label: 'Load Custom Preview CSS...',
|
||||
click: () => mainWindow.webContents.send('load-custom-css')
|
||||
click: () => mainWindow.webContents.send('load-custom-css'),
|
||||
},
|
||||
{
|
||||
label: 'Clear Custom Preview CSS',
|
||||
click: () => mainWindow.webContents.send('clear-custom-css')
|
||||
}
|
||||
]
|
||||
click: () => mainWindow.webContents.send('clear-custom-css'),
|
||||
},
|
||||
],
|
||||
},
|
||||
{ type: 'separator' },
|
||||
{ label: 'Reload', accelerator: 'CmdOrCtrl+R', role: 'reload' },
|
||||
@@ -324,7 +595,7 @@ function viewItems(mainWindow) {
|
||||
{ type: 'separator' },
|
||||
{ label: 'Zoom In', accelerator: 'CmdOrCtrl+Plus', role: 'zoomIn' },
|
||||
{ label: 'Zoom Out', accelerator: 'CmdOrCtrl+-', role: 'zoomOut' },
|
||||
{ label: 'Reset Zoom', accelerator: 'CmdOrCtrl+0', role: 'resetZoom' }
|
||||
{ label: 'Reset Zoom', accelerator: 'CmdOrCtrl+0', role: 'resetZoom' },
|
||||
];
|
||||
}
|
||||
|
||||
@@ -335,25 +606,25 @@ function batchItems(mainWindow) {
|
||||
click: () => {
|
||||
const { showBatchConversionDialog } = require('../index');
|
||||
showBatchConversionDialog();
|
||||
}
|
||||
},
|
||||
},
|
||||
{ type: 'separator' },
|
||||
{
|
||||
label: 'Batch Image Conversion...',
|
||||
click: () => mainWindow.webContents.send('show-batch-converter', 'image')
|
||||
click: () => mainWindow.webContents.send('show-batch-converter', 'image'),
|
||||
},
|
||||
{
|
||||
label: 'Batch Audio Conversion...',
|
||||
click: () => mainWindow.webContents.send('show-batch-converter', 'audio')
|
||||
click: () => mainWindow.webContents.send('show-batch-converter', 'audio'),
|
||||
},
|
||||
{
|
||||
label: 'Batch Video Conversion...',
|
||||
click: () => mainWindow.webContents.send('show-batch-converter', 'video')
|
||||
click: () => mainWindow.webContents.send('show-batch-converter', 'video'),
|
||||
},
|
||||
{
|
||||
label: 'Batch PDF Conversion...',
|
||||
click: () => mainWindow.webContents.send('show-batch-converter', 'pdf')
|
||||
}
|
||||
click: () => mainWindow.webContents.send('show-batch-converter', 'pdf'),
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
@@ -365,8 +636,8 @@ function convertItems(mainWindow) {
|
||||
click: () => {
|
||||
const { showUniversalConverterDialog } = require('../index');
|
||||
showUniversalConverterDialog();
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
@@ -378,7 +649,7 @@ function pdfEditorItems(mainWindow) {
|
||||
click: () => {
|
||||
const { openPdfFile } = require('../index');
|
||||
openPdfFile();
|
||||
}
|
||||
},
|
||||
},
|
||||
{ type: 'separator' },
|
||||
{
|
||||
@@ -386,21 +657,21 @@ function pdfEditorItems(mainWindow) {
|
||||
click: () => {
|
||||
const { showPDFEditorDialog } = require('../index');
|
||||
showPDFEditorDialog('merge');
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
label: 'Split PDF...',
|
||||
click: () => {
|
||||
const { showPDFEditorDialog } = require('../index');
|
||||
showPDFEditorDialog('split');
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
label: 'Compress PDF...',
|
||||
click: () => {
|
||||
const { showPDFEditorDialog } = require('../index');
|
||||
showPDFEditorDialog('compress');
|
||||
}
|
||||
},
|
||||
},
|
||||
{ type: 'separator' },
|
||||
{
|
||||
@@ -408,21 +679,21 @@ function pdfEditorItems(mainWindow) {
|
||||
click: () => {
|
||||
const { showPDFEditorDialog } = require('../index');
|
||||
showPDFEditorDialog('rotate');
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
label: 'Delete Pages...',
|
||||
click: () => {
|
||||
const { showPDFEditorDialog } = require('../index');
|
||||
showPDFEditorDialog('delete');
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
label: 'Reorder Pages...',
|
||||
click: () => {
|
||||
const { showPDFEditorDialog } = require('../index');
|
||||
showPDFEditorDialog('reorder');
|
||||
}
|
||||
},
|
||||
},
|
||||
{ type: 'separator' },
|
||||
{
|
||||
@@ -430,7 +701,7 @@ function pdfEditorItems(mainWindow) {
|
||||
click: () => {
|
||||
const { showPDFEditorDialog } = require('../index');
|
||||
showPDFEditorDialog('watermark');
|
||||
}
|
||||
},
|
||||
},
|
||||
{ type: 'separator' },
|
||||
{
|
||||
@@ -441,23 +712,23 @@ function pdfEditorItems(mainWindow) {
|
||||
click: () => {
|
||||
const { showPDFEditorDialog } = require('../index');
|
||||
showPDFEditorDialog('encrypt');
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
label: 'Remove Password...',
|
||||
click: () => {
|
||||
const { showPDFEditorDialog } = require('../index');
|
||||
showPDFEditorDialog('decrypt');
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
label: 'Set Permissions...',
|
||||
click: () => {
|
||||
const { showPDFEditorDialog } = require('../index');
|
||||
showPDFEditorDialog('permissions');
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
{ type: 'separator' },
|
||||
{
|
||||
@@ -465,8 +736,8 @@ function pdfEditorItems(mainWindow) {
|
||||
click: () => {
|
||||
const { showAboutDialog } = require('../index');
|
||||
showAboutDialog();
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
@@ -478,8 +749,8 @@ function toolsItems(mainWindow) {
|
||||
{ type: 'separator' },
|
||||
{
|
||||
label: 'Document Compare',
|
||||
click: () => mainWindow.webContents.send('show-document-compare')
|
||||
}
|
||||
click: () => mainWindow.webContents.send('show-document-compare'),
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
@@ -490,7 +761,7 @@ function helpItems(mainWindow) {
|
||||
click: () => {
|
||||
const { showAboutDialog } = require('../index');
|
||||
showAboutDialog();
|
||||
}
|
||||
},
|
||||
},
|
||||
{ type: 'separator' },
|
||||
{
|
||||
@@ -498,21 +769,21 @@ function helpItems(mainWindow) {
|
||||
click: () => {
|
||||
const { showDependenciesDialog } = require('../index');
|
||||
showDependenciesDialog();
|
||||
}
|
||||
},
|
||||
},
|
||||
{ type: 'separator' },
|
||||
{
|
||||
label: 'Documentation',
|
||||
click: () => shell.openExternal('https://github.com/amitwh/markdown-converter')
|
||||
click: () => shell.openExternal('https://github.com/amitwh/markdown-converter'),
|
||||
},
|
||||
{
|
||||
label: 'Report Issue',
|
||||
click: () => shell.openExternal('https://github.com/amitwh/markdown-converter/issues')
|
||||
click: () => shell.openExternal('https://github.com/amitwh/markdown-converter/issues'),
|
||||
},
|
||||
{
|
||||
label: 'Check for Updates',
|
||||
click: () => shell.openExternal('https://github.com/amitwh/markdown-converter/releases')
|
||||
}
|
||||
click: () => shell.openExternal('https://github.com/amitwh/markdown-converter/releases'),
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
@@ -524,5 +795,5 @@ module.exports = {
|
||||
convertItems,
|
||||
pdfEditorItems,
|
||||
toolsItems,
|
||||
helpItems
|
||||
};
|
||||
helpItems,
|
||||
};
|
||||
|
||||
+1
-1
@@ -25,7 +25,7 @@ const store = {
|
||||
} catch {}
|
||||
settings[key] = value;
|
||||
fs.writeFileSync(settingsPath, JSON.stringify(settings, null, 2));
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
module.exports = store;
|
||||
|
||||
@@ -29,13 +29,18 @@ class CrashWriter {
|
||||
const files = fs.readdirSync(this.dir).sort();
|
||||
while (files.length > MAX_DUMPS) {
|
||||
const oldest = files.shift();
|
||||
try { fs.unlinkSync(path.join(this.dir, oldest)); } catch (_) { /* ignore */ }
|
||||
try {
|
||||
fs.unlinkSync(path.join(this.dir, oldest));
|
||||
} catch (_) {
|
||||
/* ignore */
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
list() {
|
||||
if (!fs.existsSync(this.dir)) return [];
|
||||
return fs.readdirSync(this.dir)
|
||||
return fs
|
||||
.readdirSync(this.dir)
|
||||
.filter((f) => f.endsWith('.json'))
|
||||
.sort()
|
||||
.reverse()
|
||||
@@ -56,4 +61,4 @@ class CrashWriter {
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = { CrashWriter };
|
||||
module.exports = { CrashWriter };
|
||||
|
||||
@@ -2,42 +2,46 @@
|
||||
// Kept in sync manually; if the renderer transform changes, update this too.
|
||||
const { z } = require('zod');
|
||||
|
||||
const v4SettingsSchema = z.object({
|
||||
theme: z.enum(['light', 'dark', 'auto']).default('auto'),
|
||||
customCss: z.string().optional().nullable(),
|
||||
recentFiles: z.array(z.string()).default([]),
|
||||
editorFontSize: z.number().min(10).max(28).default(14),
|
||||
keyBindings: z.record(z.string(), z.string()).optional(),
|
||||
snippets: z.array(z.unknown()).default([]),
|
||||
}).passthrough();
|
||||
const v4SettingsSchema = z
|
||||
.object({
|
||||
theme: z.enum(['light', 'dark', 'auto']).default('auto'),
|
||||
customCss: z.string().optional().nullable(),
|
||||
recentFiles: z.array(z.string()).default([]),
|
||||
editorFontSize: z.number().min(10).max(28).default(14),
|
||||
keyBindings: z.record(z.string(), z.string()).optional(),
|
||||
snippets: z.array(z.unknown()).default([]),
|
||||
})
|
||||
.passthrough();
|
||||
|
||||
const v5SettingsSchema = z.object({
|
||||
fontSize: z.number().default(14),
|
||||
tabSize: z.number().default(4),
|
||||
lineNumbers: z.boolean().default(true),
|
||||
wordWrap: z.boolean().default(true),
|
||||
minimap: z.boolean().default(true),
|
||||
theme: z.enum(['light', 'dark', 'system']).default('system'),
|
||||
accentColor: z.string().default('brand'),
|
||||
fontFamily: z.string().default('system'),
|
||||
pdfFormat: z.string().default('a4'),
|
||||
pdfMargins: z.string().default('normal'),
|
||||
pdfEmbedFonts: z.boolean().default(true),
|
||||
docxTemplate: z.string().default('standard'),
|
||||
docxCustomTemplatePath: z.string().nullable().default(null),
|
||||
replOpen: z.boolean().default(false),
|
||||
breadcrumbSymbols: z.boolean().default(true),
|
||||
htmlHighlightStyle: z.string().default('github'),
|
||||
renderTablesAsAscii: z.boolean().default(false),
|
||||
welcomeDismissed: z.boolean().default(false),
|
||||
editorFontSize: z.number().default(14),
|
||||
customCssPath: z.string().nullable().default(null),
|
||||
userBindings: z.record(z.string(), z.string()).default({}),
|
||||
updateChannel: z.enum(['github', 'concreteinfo']).default('github'),
|
||||
autoCheckUpdates: z.boolean().default(true),
|
||||
firstRun: z.boolean().default(true),
|
||||
'migration.version': z.literal(5).optional(),
|
||||
}).passthrough();
|
||||
const v5SettingsSchema = z
|
||||
.object({
|
||||
fontSize: z.number().default(14),
|
||||
tabSize: z.number().default(4),
|
||||
lineNumbers: z.boolean().default(true),
|
||||
wordWrap: z.boolean().default(true),
|
||||
minimap: z.boolean().default(true),
|
||||
theme: z.enum(['light', 'dark', 'system']).default('system'),
|
||||
accentColor: z.string().default('brand'),
|
||||
fontFamily: z.string().default('system'),
|
||||
pdfFormat: z.string().default('a4'),
|
||||
pdfMargins: z.string().default('normal'),
|
||||
pdfEmbedFonts: z.boolean().default(true),
|
||||
docxTemplate: z.string().default('standard'),
|
||||
docxCustomTemplatePath: z.string().nullable().default(null),
|
||||
replOpen: z.boolean().default(false),
|
||||
breadcrumbSymbols: z.boolean().default(true),
|
||||
htmlHighlightStyle: z.string().default('github'),
|
||||
renderTablesAsAscii: z.boolean().default(false),
|
||||
welcomeDismissed: z.boolean().default(false),
|
||||
editorFontSize: z.number().default(14),
|
||||
customCssPath: z.string().nullable().default(null),
|
||||
userBindings: z.record(z.string(), z.string()).default({}),
|
||||
updateChannel: z.enum(['github', 'concreteinfo']).default('github'),
|
||||
autoCheckUpdates: z.boolean().default(true),
|
||||
firstRun: z.boolean().default(true),
|
||||
'migration.version': z.literal(5).optional(),
|
||||
})
|
||||
.passthrough();
|
||||
|
||||
const v5OnlyFields = ['updateChannel', 'autoCheckUpdates', 'firstRun'];
|
||||
const v5ThemeValues = ['light', 'dark', 'system'];
|
||||
|
||||
@@ -18,7 +18,8 @@ class UpdaterService extends EventEmitter {
|
||||
au.on('update-downloaded', (info) => this._emit({ state: 'ready', version: info.version }));
|
||||
au.on('update-not-available', () => this._emit({ state: 'idle' }));
|
||||
au.on('error', (err) => {
|
||||
const code = err && /ENOTFOUND|ETIMEDOUT|ECONNREFUSED/.test(err.message) ? 'NETWORK' : 'UNKNOWN';
|
||||
const code =
|
||||
err && /ENOTFOUND|ETIMEDOUT|ECONNREFUSED/.test(err.message) ? 'NETWORK' : 'UNKNOWN';
|
||||
this._emit({ state: 'error', code });
|
||||
});
|
||||
}
|
||||
@@ -39,4 +40,4 @@ class UpdaterService extends EventEmitter {
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = { UpdaterService };
|
||||
module.exports = { UpdaterService };
|
||||
|
||||
@@ -10,7 +10,7 @@ function getAllowedDirectories() {
|
||||
app.getPath('desktop'),
|
||||
app.getPath('downloads'),
|
||||
app.getPath('home'),
|
||||
process.cwd() // Current working directory
|
||||
process.cwd(), // Current working directory
|
||||
].filter(Boolean); // Remove any undefined paths
|
||||
return dirs;
|
||||
}
|
||||
@@ -88,9 +88,13 @@ function resolveWritablePath(filePath) {
|
||||
function isPathAccessible(resolvedPath) {
|
||||
// Block access to sensitive system directories
|
||||
const blockedPaths = [
|
||||
'/etc/passwd', '/etc/shadow', '/root',
|
||||
'C:\\Windows\\System32', 'C:\\Windows\\System',
|
||||
'/System', '/private/etc'
|
||||
'/etc/passwd',
|
||||
'/etc/shadow',
|
||||
'/root',
|
||||
'C:\\Windows\\System32',
|
||||
'C:\\Windows\\System',
|
||||
'/System',
|
||||
'/private/etc',
|
||||
];
|
||||
|
||||
const normalizedPath = resolvedPath.toLowerCase();
|
||||
|
||||
+16
-11
@@ -25,9 +25,9 @@ function createMainWindow() {
|
||||
// on load and the renderer never gets the IPC bridge.
|
||||
contextIsolation: true,
|
||||
nodeIntegration: false,
|
||||
spellcheck: true
|
||||
spellcheck: true,
|
||||
},
|
||||
icon: path.join(__dirname, '../../../assets/icon.png')
|
||||
icon: path.join(__dirname, '../../../assets/icon.png'),
|
||||
});
|
||||
|
||||
// Dev (Vite): load the running dev server so .tsx is transformed on the fly.
|
||||
@@ -54,7 +54,7 @@ function createMainWindow() {
|
||||
console.error(
|
||||
'[WINDOW] Renderer not found at',
|
||||
rendererIndex,
|
||||
'— did you run `npm run build:renderer` before packaging?',
|
||||
'— did you run `npm run build:renderer` before packaging?'
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -84,18 +84,23 @@ function createMainWindow() {
|
||||
// Add spell check suggestions
|
||||
if (params.misspelledWord) {
|
||||
for (const suggestion of params.dictionarySuggestions) {
|
||||
ctxMenu.append(new MenuItem({
|
||||
label: suggestion,
|
||||
click: () => win.webContents.replaceMisspelling(suggestion)
|
||||
}));
|
||||
ctxMenu.append(
|
||||
new MenuItem({
|
||||
label: suggestion,
|
||||
click: () => win.webContents.replaceMisspelling(suggestion),
|
||||
})
|
||||
);
|
||||
}
|
||||
if (params.dictionarySuggestions.length > 0) {
|
||||
ctxMenu.append(new MenuItem({ type: 'separator' }));
|
||||
}
|
||||
ctxMenu.append(new MenuItem({
|
||||
label: 'Add to Dictionary',
|
||||
click: () => win.webContents.session.addWordToSpellCheckerDictionary(params.misspelledWord)
|
||||
}));
|
||||
ctxMenu.append(
|
||||
new MenuItem({
|
||||
label: 'Add to Dictionary',
|
||||
click: () =>
|
||||
win.webContents.session.addWordToSpellCheckerDictionary(params.misspelledWord),
|
||||
})
|
||||
);
|
||||
ctxMenu.append(new MenuItem({ type: 'separator' }));
|
||||
}
|
||||
|
||||
|
||||
+611
-546
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user