feat: initial release v0.1.0

This commit is contained in:
2026-01-07 16:44:16 +09:00
commit fa05c56cf5
139 changed files with 95667 additions and 0 deletions

149
electron/main.js Normal file
View File

@@ -0,0 +1,149 @@
import { app, BrowserWindow, ipcMain, dialog } from 'electron';
import { fileURLToPath } from 'url';
import path from 'path';
import fs from 'fs';
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
const SETTINGS_FILE = path.join(app.getPath('userData'), 'settings.json');
let mainWindow;
function loadSettings() {
try {
if (fs.existsSync(SETTINGS_FILE)) {
return JSON.parse(fs.readFileSync(SETTINGS_FILE, 'utf-8'));
}
} catch (err) {
console.error('Failed to load settings:', err);
}
return {};
}
function saveSettings(settings) {
try {
const current = loadSettings();
const updated = { ...current, ...settings };
fs.writeFileSync(SETTINGS_FILE, JSON.stringify(updated, null, 2));
} catch (err) {
console.error('Failed to save settings:', err);
}
}
function createWindow() {
const settings = loadSettings();
const windowBounds = settings.windowBounds || { width: 1200, height: 800 };
mainWindow = new BrowserWindow({
x: windowBounds.x,
y: windowBounds.y,
width: windowBounds.width,
height: windowBounds.height,
minWidth: 1000,
minHeight: 600,
webPreferences: {
preload: path.join(__dirname, 'preload.js'),
contextIsolation: true,
nodeIntegration: false,
},
});
const isDev = process.env.NODE_ENV === 'development';
if (isDev) {
mainWindow.loadURL('http://localhost:5173');
mainWindow.webContents.openDevTools();
} else {
// In production, use app.getAppPath() to correctly resolve the ASAR root
const appPath = app.getAppPath();
mainWindow.loadFile(path.join(appPath, 'dist/index.html'));
}
// Persist window bounds on change
const saveBounds = () => {
const bounds = mainWindow.getBounds();
saveSettings({ windowBounds: bounds });
};
mainWindow.on('resize', saveBounds);
mainWindow.on('move', saveBounds);
}
app.whenReady().then(() => {
createWindow();
app.on('activate', () => {
if (BrowserWindow.getAllWindows().length === 0) {
createWindow();
}
});
});
app.on('window-all-closed', () => {
if (process.platform !== 'darwin') {
app.quit();
}
});
// Settings Handlers
ipcMain.handle('get-settings', async () => {
return loadSettings();
});
ipcMain.handle('update-settings', async (event, settings) => {
saveSettings(settings);
return { success: true };
});
// IPC Handler for Image Export
// IPC Handler for Image/PDF/HTML Export
ipcMain.handle('save-image', async (event, dataUrl, format) => {
const isPdf = format === 'pdf';
const isHtml = format === 'html';
let filters = [{ name: 'Images', extensions: [format] }];
if (isPdf) filters = [{ name: 'PDF Documents', extensions: ['pdf'] }];
if (isHtml) filters = [{ name: 'HTML Files', extensions: ['html'] }];
const { canceled, filePath } = await dialog.showSaveDialog(mainWindow, {
title: isPdf ? 'Save PDF' : (isHtml ? 'Save HTML' : 'Save Image'),
defaultPath: `export.${format}`,
filters: filters,
});
if (canceled || !filePath) {
return { success: false, message: 'Cancelled' };
}
// Remove header (e.g., "data:image/png;base64," or "data:application/pdf;base64," or "data:text/html;base64,")
const base64Data = dataUrl.replace(/^data:(image\/\w+|application\/pdf|text\/html);base64,/, '');
try {
fs.writeFileSync(filePath, base64Data, 'base64');
return { success: true, filePath };
} catch (error) {
console.error('Failed to save file:', error);
return { success: false, message: error.message };
}
});
// IPC Handler for HTML Import
ipcMain.handle('open-file', async () => {
const { canceled, filePaths } = await dialog.showOpenDialog(mainWindow, {
title: 'Open HTML File',
filters: [{ name: 'HTML Files', extensions: ['html'] }],
properties: ['openFile']
});
if (canceled || filePaths.length === 0) {
return { success: false, message: 'Cancelled' };
}
try {
const content = fs.readFileSync(filePaths[0], 'utf-8');
return { success: true, content, filePath: filePaths[0] };
} catch (error) {
console.error('Failed to open file:', error);
return { success: false, message: error.message };
}
});

8
electron/preload.js Normal file
View File

@@ -0,0 +1,8 @@
const { contextBridge, ipcRenderer } = require('electron');
contextBridge.exposeInMainWorld('electronAPI', {
saveImage: (dataUrl, format) => ipcRenderer.invoke('save-image', dataUrl, format),
openFile: () => ipcRenderer.invoke('open-file'),
getSettings: () => ipcRenderer.invoke('get-settings'),
updateSettings: (settings) => ipcRenderer.invoke('update-settings', settings),
});