diff --git a/src/main/gameStorage.js b/src/main/gameStorage.js new file mode 100644 index 00000000..a52e53d0 --- /dev/null +++ b/src/main/gameStorage.js @@ -0,0 +1,81 @@ +import fs from 'fs' +import path from 'path' +import { app } from 'electron' + +export function clearAllGamePaths () { + try { + fs.writeFileSync(savedGamesPath, JSON.stringify({ games: [] }, null, 2), 'utf8') + return true + } catch (error) { + console.error('Error clearing all game paths:', error) + return false + } +} +const appDataPath = app.getPath('userData') +const savedGamesPath = path.join(appDataPath, 'savedGames.json') + +/** + * Load all saved game file paths + */ +export function loadSavedGamePaths () { + try { + if (fs.existsSync(savedGamesPath)) { + const data = fs.readFileSync(savedGamesPath, 'utf8') + return JSON.parse(data) + } + } catch (error) { + console.error('Error loading saved game paths:', error) + } + return { games: [] } +} +/** + * Save a game file path to the registry + */ +export function addGamePath (filePath) { + try { + const data = loadSavedGamePaths() + // Check if path already exists + if (!data.games.includes(filePath)) { + data.games.push(filePath) + fs.writeFileSync(savedGamesPath, JSON.stringify(data, null, 2), 'utf8') + } + return true + } catch (error) { + console.error('Error adding game path:', error) + return false + } +} +/** + * Remove a game file path from the registry + */ +export function removeGamePath (filePath) { + try { + const data = loadSavedGamePaths() + data.games = data.games.filter(p => p !== filePath) + fs.writeFileSync(savedGamesPath, JSON.stringify(data, null, 2), 'utf8') + return true + } catch (error) { + console.error('Error removing game path:', error) + return false + } +} +/** + * Get all saved game file paths + */ +export function getAllSavedGamePaths () { + const data = loadSavedGamePaths() + const existingPaths = [] + for (const filePath of data.games) { + try { + if (fs.existsSync(filePath)) { + existingPaths.push(filePath) + } else { + // File no longer exists, remove it from the list + removeGamePath(filePath) + } + } catch (error) { + console.error(`Error checking game file ${filePath}:`, error) + } + } + return existingPaths +} diff --git a/src/main/index.js b/src/main/index.js index 872ebb7d..af3c30e0 100644 --- a/src/main/index.js +++ b/src/main/index.js @@ -1,7 +1,17 @@ 'use strict' import { app, BrowserWindow, dialog, ipcMain, Menu } from 'electron' +import { addGamePath, removeGamePath, getAllSavedGamePaths, clearAllGamePaths } from './gameStorage' import { createSchema, insertEval, getEvals } from './evalCache' +// IPC handler to clear all saved game paths +ipcMain.handle('clear-all-game-paths', async () => { + try { + const success = clearAllGamePaths() + return { success } + } catch (err) { + return { success: false, error: err.message } + } +}) /** * Set `__static` path to static files in production @@ -90,6 +100,28 @@ ipcMain.handle('show-open-dialog', async (event, options) => { } }) +// IPC handler for save-as dialog +ipcMain.handle('show-save-dialog', async (event, options) => { + const browserWindow = mainWindow || null + try { + const res = await dialog.showSaveDialog(browserWindow, options || {}) + return res + } catch (err) { + return { canceled: true, filePath: '' } + } +}) + +// IPC handler for writing files +ipcMain.handle('write-file', async (event, filePath, content) => { + const fs = require('fs') + try { + fs.writeFileSync(filePath, content, 'utf8') + return { success: true } + } catch (err) { + return { success: false, error: err.message } + } +}) + // Build and show a context menu requested from renderer. The renderer // sends a simplified template (no functions) and we build native menu // items whose click handlers forward a message back to the renderer. @@ -120,6 +152,48 @@ ipcMain.handle('show-context-menu', async (event, template) => { return false } }) + +// IPC handler to load all saved game paths +ipcMain.handle('load-saved-games', async (event) => { + try { + const paths = getAllSavedGamePaths() + return { success: true, paths } + } catch (err) { + return { success: false, error: err.message } + } +}) + +// IPC handler to add a game path to the saved games list +ipcMain.handle('add-game-path', async (event, filePath) => { + try { + const success = addGamePath(filePath) + return { success } + } catch (err) { + return { success: false, error: err.message } + } +}) + +// IPC handler to remove a game path from the saved games list +ipcMain.handle('remove-game-path', async (event, filePath) => { + try { + const success = removeGamePath(filePath) + return { success } + } catch (err) { + return { success: false, error: err.message } + } +}) + +// IPC handler to read a PGN file content +ipcMain.handle('read-pgn-file', async (event, filePath) => { + const fs = require('fs') + try { + const content = fs.readFileSync(filePath, 'utf8') + return { success: true, content } + } catch (err) { + return { success: false, error: err.message } + } +}) + /** * Auto Updater * diff --git a/src/renderer/components/AddCommentModal.vue b/src/renderer/components/AddCommentModal.vue new file mode 100644 index 00000000..90bef748 --- /dev/null +++ b/src/renderer/components/AddCommentModal.vue @@ -0,0 +1,237 @@ +