@@ -18,25 +18,7 @@ import { app, BrowserWindow, ipcMain, dialog } from 'electron'
1818import { fileURLToPath } from 'url'
1919import path from 'path'
2020import fs from 'fs/promises'
21- import {
22- parseDocument ,
23- createMaskedDocx ,
24- createMaskedXlsx ,
25- createMaskedPdf
26- } from './services/document-parser.js'
27- import { extractEntities , detectPersonNames } from './services/detector.js'
28- import {
29- extractTextFromImage ,
30- extractTextFromImages ,
31- combineOCRResults ,
32- isValidImage
33- } from './services/ocr.js'
34- import {
35- computePerceptualHash ,
36- calculateSimilarity ,
37- createThumbnail ,
38- isSharpAvailable
39- } from './services/image-hash.js'
21+ // Light imports only - heavy modules are lazy-loaded
4022import { createApplicationMenu } from './menu.js'
4123import {
4224 getAllProfiles ,
@@ -58,6 +40,46 @@ import {
5840 validateThreshold
5941} from './services/security.js'
6042
43+ // ============================================================================
44+ // LAZY-LOADED MODULES
45+ // ============================================================================
46+ // Heavy modules are imported on-demand to speed up app startup.
47+ // Each module is cached after first import.
48+ // ============================================================================
49+
50+ let documentParserModule : typeof import ( './services/document-parser.js' ) | null = null
51+ let detectorModule : typeof import ( './services/detector.js' ) | null = null
52+ let ocrModule : typeof import ( './services/ocr.js' ) | null = null
53+ let imageHashModule : typeof import ( './services/image-hash.js' ) | null = null
54+
55+ async function getDocumentParser ( ) {
56+ if ( ! documentParserModule ) {
57+ documentParserModule = await import ( './services/document-parser.js' )
58+ }
59+ return documentParserModule
60+ }
61+
62+ async function getDetector ( ) {
63+ if ( ! detectorModule ) {
64+ detectorModule = await import ( './services/detector.js' )
65+ }
66+ return detectorModule
67+ }
68+
69+ async function getOCR ( ) {
70+ if ( ! ocrModule ) {
71+ ocrModule = await import ( './services/ocr.js' )
72+ }
73+ return ocrModule
74+ }
75+
76+ async function getImageHash ( ) {
77+ if ( ! imageHashModule ) {
78+ imageHashModule = await import ( './services/image-hash.js' )
79+ }
80+ return imageHashModule
81+ }
82+
6183/** Current directory path (ESM equivalent of __dirname) */
6284const __dirname = path . dirname ( fileURLToPath ( import . meta. url ) )
6385
@@ -320,6 +342,7 @@ ipcMain.handle('document:parse', async (_event, fileName: string, bufferBase64:
320342 }
321343
322344 const buffer = Buffer . from ( bufferBase64 , 'base64' )
345+ const { parseDocument } = await getDocumentParser ( )
323346 const parsed = await parseDocument ( fileName , buffer )
324347
325348 return {
@@ -384,6 +407,7 @@ ipcMain.handle('ner:extract', async (_event, text: string, customNames?: string[
384407 }
385408 }
386409
410+ const { extractEntities, detectPersonNames } = await getDetector ( )
387411 const entities = extractEntities ( text , customNames )
388412 const persons = detectPersonNames ( text )
389413
@@ -440,6 +464,7 @@ ipcMain.handle(
440464
441465 const originalBuffer = Buffer . from ( originalBufferBase64 , 'base64' )
442466 let resultBuffer : Buffer
467+ const { createMaskedDocx, createMaskedXlsx, createMaskedPdf } = await getDocumentParser ( )
443468
444469 switch ( format ) {
445470 case 'docx' :
@@ -506,6 +531,7 @@ ipcMain.handle('ocr:extractText', async (_event, imageBufferBase64: string, lang
506531 }
507532
508533 const imageBuffer = Buffer . from ( imageBufferBase64 , 'base64' )
534+ const { isValidImage, extractTextFromImage } = await getOCR ( )
509535
510536 // Validate image
511537 const valid = await isValidImage ( imageBuffer )
@@ -571,6 +597,7 @@ ipcMain.handle(
571597 }
572598
573599 const imageBuffers = imageBuffersBase64 . map ( ( b ) => Buffer . from ( b , 'base64' ) )
600+ const { extractTextFromImages, combineOCRResults } = await getOCR ( )
574601 const results = await extractTextFromImages ( imageBuffers , language || 'eng' )
575602 const combinedText = combineOCRResults ( results )
576603
@@ -670,7 +697,8 @@ ipcMain.handle('profiles:create', (_event, name: string, config: ConfigProfile['
670697 * Sharp is an optional dependency - logo detection gracefully degrades if missing.
671698 * @returns true if Sharp is available, false otherwise
672699 */
673- ipcMain . handle ( 'logo:isAvailable' , ( ) => {
700+ ipcMain . handle ( 'logo:isAvailable' , async ( ) => {
701+ const { isSharpAvailable } = await getImageHash ( )
674702 return isSharpAvailable ( )
675703} )
676704
@@ -696,6 +724,8 @@ ipcMain.handle('logo:computeHash', async (_event, imageBufferBase64: string) =>
696724 return { success : false , error : bufferValidation . error }
697725 }
698726
727+ const { isSharpAvailable, computePerceptualHash, createThumbnail } = await getImageHash ( )
728+
699729 if ( ! isSharpAvailable ( ) ) {
700730 return {
701731 success : false ,
@@ -789,6 +819,8 @@ ipcMain.handle(
789819 return { success : false , error : thresholdValidation . error }
790820 }
791821
822+ const { isSharpAvailable, computePerceptualHash, calculateSimilarity } = await getImageHash ( )
823+
792824 if ( ! isSharpAvailable ( ) ) {
793825 return {
794826 success : false ,
@@ -797,6 +829,7 @@ ipcMain.handle(
797829 }
798830
799831 const buffer = Buffer . from ( bufferBase64 , 'base64' )
832+ const { parseDocument } = await getDocumentParser ( )
800833 const parsed = await parseDocument ( fileName , buffer )
801834
802835 if ( ! parsed . images || parsed . images . length === 0 ) {
0 commit comments