diff --git a/app/page.tsx b/app/page.tsx index fd52e7b..18ff523 100644 --- a/app/page.tsx +++ b/app/page.tsx @@ -31,6 +31,8 @@ import enMessages from '@/lib/locales/en.json'; import {NextIntlClientProvider, useTranslations} from "next-intl"; import {LangContext} from "@/context/LangContext"; import {AIUsageContext} from "@/context/AIUsageContext"; +import OfflineProvider from "@/context/OfflineProvider"; +import OfflineContext from "@/context/OfflineContext"; const messagesMap = { fr: frMessages, @@ -41,6 +43,7 @@ function ScribeContent() { const t = useTranslations(); const {lang: locale} = useContext(LangContext); const {errorMessage} = useContext(AlertContext); + const {initializeDatabase} = useContext(OfflineContext); const editor: Editor | null = useEditor({ extensions: [ StarterKit, @@ -216,6 +219,19 @@ function ScribeContent() { }); setCurrentCredits(user.creditsBalance) setAmountSpent(user.aiUsage) + + // Initialiser la DB locale en Electron + if (window.electron && user.id) { + try { + const dbInitialized = await initializeDatabase(user.id); + if (dbInitialized) { + console.log('Database initialized successfully'); + // TODO: Sync initial des données du serveur vers la DB locale + } + } catch (error) { + console.error('Failed to initialize database:', error); + } + } } catch (e: unknown) { if (e instanceof Error) { errorMessage(e.message); @@ -359,9 +375,11 @@ export default function Scribe() { return ( - - - + + + + + ); diff --git a/components/ScribeTopBar.tsx b/components/ScribeTopBar.tsx index 6aadf97..e6e8b83 100644 --- a/components/ScribeTopBar.tsx +++ b/components/ScribeTopBar.tsx @@ -2,6 +2,7 @@ import {useContext} from "react"; import {BookContext, BookContextProps} from "@/context/BookContext"; import {useTranslations} from "next-intl"; +import OfflineToggle from "@/components/offline/OfflineToggle"; export default function ScribeTopBar() { const book: BookContextProps = useContext(BookContext); @@ -33,6 +34,7 @@ export default function ScribeTopBar() { )}
+
) diff --git a/components/book/BookList.tsx b/components/book/BookList.tsx index 063f95f..b306ce7 100644 --- a/components/book/BookList.tsx +++ b/components/book/BookList.tsx @@ -244,8 +244,8 @@ export default function BookList() {

- - {category} + + {category}

{books.length} {t("bookList.works")} diff --git a/electron.d.ts b/electron.d.ts index 6f46db4..fe7c946 100644 --- a/electron.d.ts +++ b/electron.d.ts @@ -5,6 +5,23 @@ export interface IElectronAPI { removeToken: () => Promise; loginSuccess: (token: string) => void; logout: () => void; + + // Database operations + generateEncryptionKey: (userId: string) => Promise<{ success: boolean; key?: string; error?: string }>; + getUserEncryptionKey: (userId: string) => Promise; + setUserEncryptionKey: (userId: string, encryptionKey: string) => Promise; + dbInitialize: (userId: string, encryptionKey: string) => Promise<{ success: boolean; error?: string }>; + dbGetBooks: () => Promise<{ success: boolean; data?: any[]; error?: string }>; + dbGetBook: (bookId: string) => Promise<{ success: boolean; data?: any; error?: string }>; + dbSaveBook: (book: any, authorId?: string) => Promise<{ success: boolean; error?: string }>; + dbDeleteBook: (bookId: string) => Promise<{ success: boolean; error?: string }>; + dbSaveChapter: (chapter: any, bookId: string, contentId?: string) => Promise<{ success: boolean; error?: string }>; + dbGetCharacters: (bookId: string) => Promise<{ success: boolean; data?: any[]; error?: string }>; + dbSaveCharacter: (character: any, bookId: string) => Promise<{ success: boolean; error?: string }>; + dbGetConversations: (bookId: string) => Promise<{ success: boolean; data?: any[]; error?: string }>; + dbSaveConversation: (conversation: any, bookId: string) => Promise<{ success: boolean; error?: string }>; + dbGetSyncStatus: () => Promise<{ success: boolean; data?: any[]; error?: string }>; + dbGetPendingChanges: (limit?: number) => Promise<{ success: boolean; data?: any[]; error?: string }>; } declare global { diff --git a/electron/main.ts b/electron/main.ts index ba0461b..38771bc 100644 --- a/electron/main.ts +++ b/electron/main.ts @@ -4,6 +4,7 @@ import * as url from 'url'; import { fileURLToPath } from 'url'; import Store from 'electron-store'; import * as fs from 'fs'; +import { getDatabaseService } from './database/database.service.js'; // Fix pour __dirname en ES modules const __filename = fileURLToPath(import.meta.url); @@ -29,10 +30,8 @@ if (!isDev) { // Définir le nom de l'application app.setName('ERitors Scribe'); -// En dev, __dirname pointe vers electron/, en prod vers dist/electron/ -const preloadPath = isDev - ? path.join(__dirname, '../dist/electron/preload.js') - : path.join(__dirname, 'preload.js'); +// En dev et prod, __dirname pointe vers dist/electron/ +const preloadPath = path.join(__dirname, 'preload.js'); // Icône de l'application const iconPath = isDev @@ -143,6 +142,10 @@ ipcMain.on('login-success', (_event, token: string) => { ipcMain.on('logout', () => { store.delete('authToken'); + // Close database connection + const db = getDatabaseService(); + db.close(); + if (mainWindow) { mainWindow.close(); } @@ -150,6 +153,246 @@ ipcMain.on('logout', () => { createLoginWindow(); }); +// ========== DATABASE IPC HANDLERS ========== + +/** + * Generate user encryption key + */ +ipcMain.handle('generate-encryption-key', async (_event, userId: string) => { + try { + // Import encryption module dynamically + const { generateUserEncryptionKey } = await import('./database/encryption.js'); + const key = generateUserEncryptionKey(userId); + return { success: true, key }; + } catch (error) { + console.error('Failed to generate encryption key:', error); + return { + success: false, + error: error instanceof Error ? error.message : 'Unknown error' + }; + } +}); + +/** + * Get or generate user encryption key + */ +ipcMain.handle('get-user-encryption-key', (_event, userId: string) => { + const key = store.get(`encryptionKey-${userId}`, null); + return key; +}); + +/** + * Store user encryption key + */ +ipcMain.handle('set-user-encryption-key', (_event, userId: string, encryptionKey: string) => { + store.set(`encryptionKey-${userId}`, encryptionKey); + return true; +}); + +/** + * Initialize database for user + */ +ipcMain.handle('db-initialize', (_event, userId: string, encryptionKey: string) => { + try { + const db = getDatabaseService(); + db.initialize(userId, encryptionKey); + return { success: true }; + } catch (error) { + console.error('Failed to initialize database:', error); + return { + success: false, + error: error instanceof Error ? error.message : 'Unknown error' + }; + } +}); + +/** + * Get all books + */ +ipcMain.handle('db-get-books', () => { + try { + const db = getDatabaseService(); + const books = db.getBooks(); + return { success: true, data: books }; + } catch (error) { + console.error('Failed to get books:', error); + return { + success: false, + error: error instanceof Error ? error.message : 'Unknown error' + }; + } +}); + +/** + * Get single book with all data + */ +ipcMain.handle('db-get-book', (_event, bookId: string) => { + try { + const db = getDatabaseService(); + const book = db.getBook(bookId); + return { success: true, data: book }; + } catch (error) { + console.error('Failed to get book:', error); + return { + success: false, + error: error instanceof Error ? error.message : 'Unknown error' + }; + } +}); + +/** + * Save book + */ +ipcMain.handle('db-save-book', (_event, book: any, authorId?: string) => { + try { + const db = getDatabaseService(); + db.saveBook(book, authorId); + return { success: true }; + } catch (error) { + console.error('Failed to save book:', error); + return { + success: false, + error: error instanceof Error ? error.message : 'Unknown error' + }; + } +}); + +/** + * Delete book + */ +ipcMain.handle('db-delete-book', (_event, bookId: string) => { + try { + const db = getDatabaseService(); + db.deleteBook(bookId); + return { success: true }; + } catch (error) { + console.error('Failed to delete book:', error); + return { + success: false, + error: error instanceof Error ? error.message : 'Unknown error' + }; + } +}); + +/** + * Save chapter + */ +ipcMain.handle('db-save-chapter', (_event, chapter: any, bookId: string, contentId?: string) => { + try { + const db = getDatabaseService(); + db.saveChapter(chapter, bookId, contentId); + return { success: true }; + } catch (error) { + console.error('Failed to save chapter:', error); + return { + success: false, + error: error instanceof Error ? error.message : 'Unknown error' + }; + } +}); + +/** + * Get characters for a book + */ +ipcMain.handle('db-get-characters', (_event, bookId: string) => { + try { + const db = getDatabaseService(); + const characters = db.getCharacters(bookId); + return { success: true, data: characters }; + } catch (error) { + console.error('Failed to get characters:', error); + return { + success: false, + error: error instanceof Error ? error.message : 'Unknown error' + }; + } +}); + +/** + * Save character + */ +ipcMain.handle('db-save-character', (_event, character: any, bookId: string) => { + try { + const db = getDatabaseService(); + db.saveCharacter(character, bookId); + return { success: true }; + } catch (error) { + console.error('Failed to save character:', error); + return { + success: false, + error: error instanceof Error ? error.message : 'Unknown error' + }; + } +}); + +/** + * Get AI conversations for a book + */ +ipcMain.handle('db-get-conversations', (_event, bookId: string) => { + try { + const db = getDatabaseService(); + const conversations = db.getConversations(bookId); + return { success: true, data: conversations }; + } catch (error) { + console.error('Failed to get conversations:', error); + return { + success: false, + error: error instanceof Error ? error.message : 'Unknown error' + }; + } +}); + +/** + * Save AI conversation + */ +ipcMain.handle('db-save-conversation', (_event, conversation: any, bookId: string) => { + try { + const db = getDatabaseService(); + db.saveConversation(conversation, bookId); + return { success: true }; + } catch (error) { + console.error('Failed to save conversation:', error); + return { + success: false, + error: error instanceof Error ? error.message : 'Unknown error' + }; + } +}); + +/** + * Get sync status + */ +ipcMain.handle('db-get-sync-status', () => { + try { + const db = getDatabaseService(); + const status = db.getSyncStatus(); + return { success: true, data: status }; + } catch (error) { + console.error('Failed to get sync status:', error); + return { + success: false, + error: error instanceof Error ? error.message : 'Unknown error' + }; + } +}); + +/** + * Get pending changes + */ +ipcMain.handle('db-get-pending-changes', (_event, limit: number = 100) => { + try { + const db = getDatabaseService(); + const changes = db.getPendingChanges(limit); + return { success: true, data: changes }; + } catch (error) { + console.error('Failed to get pending changes:', error); + return { + success: false, + error: error instanceof Error ? error.message : 'Unknown error' + }; + } +}); + app.whenReady().then(() => { console.log('App ready, isDev:', isDev); console.log('resourcesPath:', process.resourcesPath); diff --git a/electron/preload.ts b/electron/preload.ts index db9dff3..50017b4 100644 --- a/electron/preload.ts +++ b/electron/preload.ts @@ -12,4 +12,21 @@ contextBridge.exposeInMainWorld('electron', { // Auth events loginSuccess: (token: string) => ipcRenderer.send('login-success', token), logout: () => ipcRenderer.send('logout'), + + // Database operations + generateEncryptionKey: (userId: string) => ipcRenderer.invoke('generate-encryption-key', userId), + getUserEncryptionKey: (userId: string) => ipcRenderer.invoke('get-user-encryption-key', userId), + setUserEncryptionKey: (userId: string, encryptionKey: string) => ipcRenderer.invoke('set-user-encryption-key', userId, encryptionKey), + dbInitialize: (userId: string, encryptionKey: string) => ipcRenderer.invoke('db-initialize', userId, encryptionKey), + dbGetBooks: () => ipcRenderer.invoke('db-get-books'), + dbGetBook: (bookId: string) => ipcRenderer.invoke('db-get-book', bookId), + dbSaveBook: (book: any, authorId?: string) => ipcRenderer.invoke('db-save-book', book, authorId), + dbDeleteBook: (bookId: string) => ipcRenderer.invoke('db-delete-book', bookId), + dbSaveChapter: (chapter: any, bookId: string, contentId?: string) => ipcRenderer.invoke('db-save-chapter', chapter, bookId, contentId), + dbGetCharacters: (bookId: string) => ipcRenderer.invoke('db-get-characters', bookId), + dbSaveCharacter: (character: any, bookId: string) => ipcRenderer.invoke('db-save-character', character, bookId), + dbGetConversations: (bookId: string) => ipcRenderer.invoke('db-get-conversations', bookId), + dbSaveConversation: (conversation: any, bookId: string) => ipcRenderer.invoke('db-save-conversation', conversation, bookId), + dbGetSyncStatus: () => ipcRenderer.invoke('db-get-sync-status'), + dbGetPendingChanges: (limit?: number) => ipcRenderer.invoke('db-get-pending-changes', limit), }); diff --git a/package-lock.json b/package-lock.json index 7f4ffce..337afca 100644 --- a/package-lock.json +++ b/package-lock.json @@ -32,6 +32,7 @@ "next": "^16.0.3", "next-export-i18n": "^2.4.3", "next-intl": "^4.5.3", + "node-sqlite3-wasm": "^0.8.51", "postcss": "^8.5.6", "react": "^19.2.0", "react-dom": "^19.2.0", @@ -51,6 +52,8 @@ "concurrently": "^9.2.1", "electron": "^39.2.1", "electron-builder": "^26.0.12", + "electron-rebuild": "^3.2.9", + "electronmon": "^2.0.4", "tsx": "^4.20.6", "typescript": "^5.9.3", "vite": "^7.2.2", @@ -4760,6 +4763,28 @@ "node": ">= 10.0.0" } }, + "node_modules/aproba": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/aproba/-/aproba-2.1.0.tgz", + "integrity": "sha512-tLIEcj5GuR2RSTnxNKdkK0dJ/GrC7P38sUkiDmDuHfsHmbagTFAxDVIBltoklXEVIQ/f14IL8IMJ5pn9Hez1Ew==", + "dev": true, + "license": "ISC" + }, + "node_modules/are-we-there-yet": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-3.0.1.tgz", + "integrity": "sha512-QZW4EDmGwlYur0Yyf/b2uGucHQMa8aFUP7eu9ddR73vvhFyt4V0Vl3QHPcTNJ8l6qYOBdxgXdnBXQrHilfRQBg==", + "deprecated": "This package is no longer supported.", + "dev": true, + "license": "ISC", + "dependencies": { + "delegates": "^1.0.0", + "readable-stream": "^3.6.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, "node_modules/argparse": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", @@ -4963,6 +4988,19 @@ "concat-map": "0.0.1" } }, + "node_modules/braces": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "dev": true, + "license": "MIT", + "dependencies": { + "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/browserslist": { "version": "4.28.0", "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.0.tgz", @@ -5445,6 +5483,16 @@ "dev": true, "license": "MIT" }, + "node_modules/color-support": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-support/-/color-support-1.1.3.tgz", + "integrity": "sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==", + "dev": true, + "license": "ISC", + "bin": { + "color-support": "bin.js" + } + }, "node_modules/combined-stream": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", @@ -5668,6 +5716,13 @@ "node": ">=16 || 14 >=14.17" } }, + "node_modules/console-control-strings": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", + "integrity": "sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ==", + "dev": true, + "license": "ISC" + }, "node_modules/convert-source-map": { "version": "1.9.0", "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz", @@ -5915,6 +5970,13 @@ "node": ">=0.4.0" } }, + "node_modules/delegates": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", + "integrity": "sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ==", + "dev": true, + "license": "MIT" + }, "node_modules/detect-libc": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", @@ -6286,6 +6348,97 @@ "node": ">= 10.0.0" } }, + "node_modules/electron-rebuild": { + "version": "3.2.9", + "resolved": "https://registry.npmjs.org/electron-rebuild/-/electron-rebuild-3.2.9.tgz", + "integrity": "sha512-FkEZNFViUem3P0RLYbZkUjC8LUFIK+wKq09GHoOITSJjfDAVQv964hwaNseTTWt58sITQX3/5fHNYcTefqaCWw==", + "deprecated": "Please use @electron/rebuild moving forward. There is no API change, just a package name change", + "dev": true, + "license": "MIT", + "dependencies": { + "@malept/cross-spawn-promise": "^2.0.0", + "chalk": "^4.0.0", + "debug": "^4.1.1", + "detect-libc": "^2.0.1", + "fs-extra": "^10.0.0", + "got": "^11.7.0", + "lzma-native": "^8.0.5", + "node-abi": "^3.0.0", + "node-api-version": "^0.1.4", + "node-gyp": "^9.0.0", + "ora": "^5.1.0", + "semver": "^7.3.5", + "tar": "^6.0.5", + "yargs": "^17.0.1" + }, + "bin": { + "electron-rebuild": "lib/src/cli.js" + }, + "engines": { + "node": ">=12.13.0" + } + }, + "node_modules/electron-rebuild/node_modules/fs-extra": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz", + "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/electron-rebuild/node_modules/jsonfile": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.2.0.tgz", + "integrity": "sha512-FGuPw30AdOIUTRMC2OMRtQV+jkVj2cfPqSeWXv1NEAJ1qZ5zb1X6z1mFhbfOB/iy3ssJCD+3KuZ8r8C3uVFlAg==", + "dev": true, + "license": "MIT", + "dependencies": { + "universalify": "^2.0.0" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/electron-rebuild/node_modules/node-api-version": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/node-api-version/-/node-api-version-0.1.4.tgz", + "integrity": "sha512-KGXihXdUChwJAOHO53bv9/vXcLmdUsZ6jIptbvYvkpKfth+r7jw44JkVxQFA3kX5nQjzjmGu1uAu/xNNLNlI5g==", + "dev": true, + "license": "MIT", + "dependencies": { + "semver": "^7.3.5" + } + }, + "node_modules/electron-rebuild/node_modules/semver": { + "version": "7.7.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz", + "integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/electron-rebuild/node_modules/universalify": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", + "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 10.0.0" + } + }, "node_modules/electron-store": { "version": "11.0.2", "resolved": "https://registry.npmjs.org/electron-store/-/electron-store-11.0.2.tgz", @@ -6378,6 +6531,39 @@ "dev": true, "license": "MIT" }, + "node_modules/electronmon": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/electronmon/-/electronmon-2.0.4.tgz", + "integrity": "sha512-u6eDrvUbqa+wsnMrhG2vHmo5neL1owLg2e5i1avGWcOb4rHsUf9lSfbs0FvfPsBNpLxxlPO98nrMhAGV+zw/fQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "chalk": "^3.0.0", + "import-from": "^3.0.0", + "runtime-required": "^1.1.0", + "watchboy": "^0.4.3" + }, + "bin": { + "electronmon": "bin/cli.js" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/electronmon/node_modules/chalk": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz", + "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/emoji-regex": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", @@ -6702,6 +6888,19 @@ "node": ">=10" } }, + "node_modules/fill-range": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "dev": true, + "license": "MIT", + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/find-root": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/find-root/-/find-root-1.1.0.tgz", @@ -6846,6 +7045,27 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/gauge": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/gauge/-/gauge-4.0.4.tgz", + "integrity": "sha512-f9m+BEN5jkg6a0fZjleidjN51VE1X+mPFQ2DJ0uv1V39oCLCbsGe6yjbBnp7eK7z/+GAon99a3nHuqbuuthyPg==", + "deprecated": "This package is no longer supported.", + "dev": true, + "license": "ISC", + "dependencies": { + "aproba": "^1.0.3 || ^2.0.0", + "color-support": "^1.1.3", + "console-control-strings": "^1.1.0", + "has-unicode": "^2.0.1", + "signal-exit": "^3.0.7", + "string-width": "^4.2.3", + "strip-ansi": "^6.0.1", + "wide-align": "^1.1.5" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, "node_modules/gensync": { "version": "1.0.0-beta.2", "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", @@ -7113,6 +7333,13 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/has-unicode": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", + "integrity": "sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ==", + "dev": true, + "license": "ISC" + }, "node_modules/hasown": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", @@ -7305,6 +7532,29 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/import-from": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/import-from/-/import-from-3.0.0.tgz", + "integrity": "sha512-CiuXOFFSzkU5x/CR0+z7T91Iht4CXgfCxVOFRhh2Zyhg5wOpWvvDLQUsWl+gcN+QscYBjez8hDCt85O7RLDttQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "resolve-from": "^5.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/import-from/node_modules/resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, "node_modules/imurmurhash": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", @@ -7434,6 +7684,16 @@ "dev": true, "license": "MIT" }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.12.0" + } + }, "node_modules/is-unicode-supported": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", @@ -7935,6 +8195,13 @@ "integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==", "license": "MIT" }, + "node_modules/lodash.difference": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.difference/-/lodash.difference-4.5.0.tgz", + "integrity": "sha512-dS2j+W26TQ7taQBGN8Lbbq04ssV3emRw4NY58WErlTO29pIqS0HmoT5aJ9+TUQ1N3G+JOZSji4eugsWwGp9yPA==", + "dev": true, + "license": "MIT" + }, "node_modules/log-symbols": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", @@ -7975,6 +8242,32 @@ "node": ">=10" } }, + "node_modules/lzma-native": { + "version": "8.0.6", + "resolved": "https://registry.npmjs.org/lzma-native/-/lzma-native-8.0.6.tgz", + "integrity": "sha512-09xfg67mkL2Lz20PrrDeNYZxzeW7ADtpYFbwSQh9U8+76RIzx5QsJBMy8qikv3hbUPfpy6hqwxt6FcGK81g9AA==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "dependencies": { + "node-addon-api": "^3.1.0", + "node-gyp-build": "^4.2.1", + "readable-stream": "^3.6.0" + }, + "bin": { + "lzmajs": "bin/lzmajs" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/lzma-native/node_modules/node-addon-api": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-3.2.1.tgz", + "integrity": "sha512-mmcei9JghVNDYydghQmeDX8KoAm0FAiYyIcUt/N4nhyAipB17pllZQDOJD2fotxABnt4Mdz+dKTO7eftLg4d0A==", + "dev": true, + "license": "MIT" + }, "node_modules/magic-string": { "version": "0.30.21", "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz", @@ -8110,6 +8403,33 @@ "integrity": "sha512-Lf+9+2r+Tdp5wXDXC4PcIBjTDtq4UKjCPMQhKIuzpJNW0b96kVqSwW0bT7FhRSfmAiFYgP+SCRvdrDozfh0U5w==", "license": "MIT" }, + "node_modules/micromatch": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", + "dev": true, + "license": "MIT", + "dependencies": { + "braces": "^3.0.3", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/micromatch/node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, "node_modules/mime": { "version": "2.6.0", "resolved": "https://registry.npmjs.org/mime/-/mime-2.6.0.tgz", @@ -8592,12 +8912,69 @@ "node": ">=10" } }, + "node_modules/node-gyp": { + "version": "9.4.1", + "resolved": "https://registry.npmjs.org/node-gyp/-/node-gyp-9.4.1.tgz", + "integrity": "sha512-OQkWKbjQKbGkMf/xqI1jjy3oCTgMKJac58G2+bjZb3fza6gW2YrCSdMQYaoTb70crvE//Gngr4f0AgVHmqHvBQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "env-paths": "^2.2.0", + "exponential-backoff": "^3.1.1", + "glob": "^7.1.4", + "graceful-fs": "^4.2.6", + "make-fetch-happen": "^10.0.3", + "nopt": "^6.0.0", + "npmlog": "^6.0.0", + "rimraf": "^3.0.2", + "semver": "^7.3.5", + "tar": "^6.1.2", + "which": "^2.0.2" + }, + "bin": { + "node-gyp": "bin/node-gyp.js" + }, + "engines": { + "node": "^12.13 || ^14.13 || >=16" + } + }, + "node_modules/node-gyp-build": { + "version": "4.8.4", + "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.8.4.tgz", + "integrity": "sha512-LA4ZjwlnUblHVgq0oBF3Jl/6h/Nvs5fzBLwdEF4nuxnFdsfajde4WfxtJr3CaiH+F6ewcIB/q4jQ4UzPyid+CQ==", + "dev": true, + "license": "MIT", + "bin": { + "node-gyp-build": "bin.js", + "node-gyp-build-optional": "optional.js", + "node-gyp-build-test": "build-test.js" + } + }, + "node_modules/node-gyp/node_modules/semver": { + "version": "7.7.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz", + "integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/node-releases": { "version": "2.0.27", "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.27.tgz", "integrity": "sha512-nmh3lCkYZ3grZvqcCH+fjmQ7X+H0OeZgP40OierEaAptX4XofMh5kwNbWh7lBduUzCcV/8kZ+NDLCwm2iorIlA==", "license": "MIT" }, + "node_modules/node-sqlite3-wasm": { + "version": "0.8.51", + "resolved": "https://registry.npmjs.org/node-sqlite3-wasm/-/node-sqlite3-wasm-0.8.51.tgz", + "integrity": "sha512-tbUWEZLrGJwfi6eE6Xw+6GYRIQne4/C7uyh76hDI1tD88JfnLQCqPED8IvW0FVfrMNYuhYkO5a10XjI1du1kLg==", + "license": "MIT" + }, "node_modules/nopt": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/nopt/-/nopt-6.0.0.tgz", @@ -8614,6 +8991,19 @@ "node": "^12.13.0 || ^14.15.0 || >=16.0.0" } }, + "node_modules/normalize-path": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz", + "integrity": "sha512-3pKJwH184Xo/lnH6oyP1q2pMd7HcypqqmRs91/6/i2CGtWwIKGCkOOMTm/zXbgTEWHw1uNpNi/igc3ePOYHb6w==", + "dev": true, + "license": "MIT", + "dependencies": { + "remove-trailing-separator": "^1.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/normalize-range": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/normalize-range/-/normalize-range-0.1.2.tgz", @@ -8636,6 +9026,23 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/npmlog": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-6.0.2.tgz", + "integrity": "sha512-/vBvz5Jfr9dT/aFWd0FIRf+T/Q2WBsLENygUaFUqstqsycmZAP/t5BvFJTK0viFmSUxiUKTUplWy5vt+rvKIxg==", + "deprecated": "This package is no longer supported.", + "dev": true, + "license": "ISC", + "dependencies": { + "are-we-there-yet": "^3.0.0", + "console-control-strings": "^1.1.0", + "gauge": "^4.0.3", + "set-blocking": "^2.0.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, "node_modules/object-keys": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", @@ -8892,6 +9299,16 @@ "url": "https://github.com/sponsors/jonschlinkert" } }, + "node_modules/pify": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", + "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, "node_modules/plist": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/plist/-/plist-3.1.0.tgz", @@ -10008,6 +10425,13 @@ "node": ">= 6" } }, + "node_modules/remove-trailing-separator": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz", + "integrity": "sha512-/hS+Y0u3aOfIETiaiirUFwDBDzmXPvO+jAfKTitUngIPzdKc6Z0LoFjM/CK5PL4C+eKwHohlHAb6H0VFfmmUsw==", + "dev": true, + "license": "ISC" + }, "node_modules/require-directory": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", @@ -10218,6 +10642,16 @@ "integrity": "sha512-UT5EDe2cu2E/6O4igUr5PSFs23nvvukicWHx6GnOPlHAiiYbzNuCRQCuiUdHJQcqKalLKlrYJnjY0ySGsXNQXQ==", "license": "MIT" }, + "node_modules/runtime-required": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/runtime-required/-/runtime-required-1.1.0.tgz", + "integrity": "sha512-yX97f5E0WfNpcQnfVjap6vzQcvErkYYCx6eTK4siqGEdC8lglwypUFgZVTX7ShvIlgfkC4XGFl9O1KTYcff0pw==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=4.0.0" + } + }, "node_modules/rxjs": { "version": "7.8.2", "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.2.tgz", @@ -10323,6 +10757,13 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/set-blocking": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", + "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==", + "dev": true, + "license": "ISC" + }, "node_modules/set-cookie-parser": { "version": "2.7.2", "resolved": "https://registry.npmjs.org/set-cookie-parser/-/set-cookie-parser-2.7.2.tgz", @@ -10961,6 +11402,19 @@ "tmp": "^0.2.0" } }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, "node_modules/toggle-selection": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/toggle-selection/-/toggle-selection-1.0.6.tgz", @@ -11102,6 +11556,19 @@ "node": ">= 4.0.0" } }, + "node_modules/unixify": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unixify/-/unixify-1.0.0.tgz", + "integrity": "sha512-6bc58dPYhCMHHuwxldQxO3RRNZ4eCogZ/st++0+fcC1nr0jiGUtAdBJ2qzmLQWSxbtz42pWt4QQMiZ9HvZf5cg==", + "dev": true, + "license": "MIT", + "dependencies": { + "normalize-path": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/update-browserslist-db": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.4.tgz", @@ -11312,6 +11779,22 @@ "node": ">=20.0.0" } }, + "node_modules/watchboy": { + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/watchboy/-/watchboy-0.4.3.tgz", + "integrity": "sha512-GHs1HxwvxSMBsqd/WfTOZhj5gBdMqf5HQpfgtKxDfZRxrlYPDdVLRB61LCeRzJaWANmvSIMlfmRVDwVmJFgAKA==", + "dev": true, + "license": "ISC", + "dependencies": { + "lodash.difference": "^4.5.0", + "micromatch": "^4.0.2", + "pify": "^4.0.1", + "unixify": "^1.0.0" + }, + "engines": { + "node": ">=8.0.0" + } + }, "node_modules/wcwidth": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/wcwidth/-/wcwidth-1.0.1.tgz", @@ -11344,6 +11827,16 @@ "node": ">= 8" } }, + "node_modules/wide-align": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.5.tgz", + "integrity": "sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg==", + "dev": true, + "license": "ISC", + "dependencies": { + "string-width": "^1.0.2 || 2 || 3 || 4" + } + }, "node_modules/wrap-ansi": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", diff --git a/package.json b/package.json index e606f3d..4829a4a 100644 --- a/package.json +++ b/package.json @@ -5,7 +5,7 @@ "type": "module", "main": "dist/electron/main.js", "scripts": { - "dev": "concurrently \"next dev -p 4000\" \"wait-on http://localhost:4000 && electron -r tsx/cjs electron/main.ts\"", + "dev": "tsc -p tsconfig.electron.json && tsc -p tsconfig.preload.json && concurrently \"tsc -p tsconfig.electron.json -w\" \"tsc -p tsconfig.preload.json -w\" \"next dev -p 4000\" \"wait-on http://localhost:4000 && wait-on dist/electron/main.js && electron dist/electron/main.js\"", "build:mac": "next build && tsc --project tsconfig.electron.json && tsc --project tsconfig.preload.json && electron-builder build --mac", "build:win": "next build && tsc --project tsconfig.electron.json && tsc --project tsconfig.preload.json && electron-builder build --win", "build:linux": "next build && tsc --project tsconfig.electron.json && tsc --project tsconfig.preload.json && electron-builder build --linux", @@ -26,6 +26,8 @@ "concurrently": "^9.2.1", "electron": "^39.2.1", "electron-builder": "^26.0.12", + "electron-rebuild": "^3.2.9", + "electronmon": "^2.0.4", "tsx": "^4.20.6", "typescript": "^5.9.3", "vite": "^7.2.2", @@ -56,6 +58,7 @@ "next": "^16.0.3", "next-export-i18n": "^2.4.3", "next-intl": "^4.5.3", + "node-sqlite3-wasm": "^0.8.51", "postcss": "^8.5.6", "react": "^19.2.0", "react-dom": "^19.2.0", diff --git a/tsconfig.electron.json b/tsconfig.electron.json index e324aa9..f58d065 100644 --- a/tsconfig.electron.json +++ b/tsconfig.electron.json @@ -20,6 +20,11 @@ "dist", "src", ".next", - "out" + "out", + "lib", + "components", + "app", + "context", + "electron/preload.ts" ] } diff --git a/tsconfig.preload.json b/tsconfig.preload.json index 9e0cff6..49f9906 100644 --- a/tsconfig.preload.json +++ b/tsconfig.preload.json @@ -1,11 +1,20 @@ { - "extends": "./tsconfig.electron.json", "compilerOptions": { "module": "CommonJS", "moduleResolution": "node", - "outDir": "dist/electron" + "target": "ES2022", + "outDir": "dist/electron", + "lib": ["ES2022"], + "esModuleInterop": true, + "skipLibCheck": true, + "strict": true, + "resolveJsonModule": true, + "noEmit": false }, "include": [ "electron/preload.ts" + ], + "exclude": [ + "node_modules" ] }