diff --git a/package-lock.json b/package-lock.json index db844737..e9581899 100644 --- a/package-lock.json +++ b/package-lock.json @@ -100,6 +100,7 @@ "npm-check-updates": "^17.1.13", "postcss": "^8.4.38", "prettier": "^3.2.5", + "rimraf": "^6.0.1", "tailwindcss": "^3.4.1", "typescript": "~5.2.2", "vite": "^5.2.0", @@ -2628,6 +2629,66 @@ "node": ">=18.0.0" } }, + "node_modules/@capacitor/cli/node_modules/glob": { + "version": "9.3.5", + "resolved": "https://registry.npmjs.org/glob/-/glob-9.3.5.tgz", + "integrity": "sha512-e1LleDykUz2Iu+MTYdkSsuWX8lvAjAcs0Xef0lNIu0S2wOAzuTxCJtcd9S3cijlwYF18EsU3rzb8jPVobxDh9Q==", + "license": "ISC", + "dependencies": { + "fs.realpath": "^1.0.0", + "minimatch": "^8.0.2", + "minipass": "^4.2.4", + "path-scurry": "^1.6.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@capacitor/cli/node_modules/minimatch": { + "version": "8.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-8.0.4.tgz", + "integrity": "sha512-W0Wvr9HyFXZRGIDgCicunpQ299OKXs9RgZfaukz4qAW/pJhcpUfupc9c+OObPOFueNy8VSrZgEmDtk6Kh4WzDA==", + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@capacitor/cli/node_modules/minipass": { + "version": "4.2.8", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-4.2.8.tgz", + "integrity": "sha512-fNzuVyifolSLFL4NzpF+wEF4qrgqaaKX0haXPQEdQ7NKAN+WecoKMHV09YcuL/DHxrUsYQOK3MiuDf7Ip2OXfQ==", + "license": "ISC", + "engines": { + "node": ">=8" + } + }, + "node_modules/@capacitor/cli/node_modules/rimraf": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-4.4.1.tgz", + "integrity": "sha512-Gk8NlF062+T9CqNGn6h4tls3k6T1+/nXdOcSZVikNVtlRdYpA7wRJJMoXmuvOnLW844rPjdQ7JgXCYM6PPC/og==", + "license": "ISC", + "dependencies": { + "glob": "^9.2.0" + }, + "bin": { + "rimraf": "dist/cjs/src/bin.js" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/@capacitor/core": { "version": "6.2.0", "resolved": "https://registry.npmjs.org/@capacitor/core/-/core-6.2.0.tgz", @@ -23165,63 +23226,106 @@ } }, "node_modules/rimraf": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-4.4.1.tgz", - "integrity": "sha512-Gk8NlF062+T9CqNGn6h4tls3k6T1+/nXdOcSZVikNVtlRdYpA7wRJJMoXmuvOnLW844rPjdQ7JgXCYM6PPC/og==", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-6.0.1.tgz", + "integrity": "sha512-9dkvaxAsk/xNXSJzMgFqqMCuFgt2+KsOFek3TMLfo8NCPfWpBmqwyNn5Y+NX56QUYfCtsyhF3ayiboEoUmJk/A==", + "dev": true, "license": "ISC", "dependencies": { - "glob": "^9.2.0" + "glob": "^11.0.0", + "package-json-from-dist": "^1.0.0" }, "bin": { - "rimraf": "dist/cjs/src/bin.js" + "rimraf": "dist/esm/bin.mjs" }, "engines": { - "node": ">=14" + "node": "20 || >=22" }, "funding": { "url": "https://github.com/sponsors/isaacs" } }, "node_modules/rimraf/node_modules/glob": { - "version": "9.3.5", - "resolved": "https://registry.npmjs.org/glob/-/glob-9.3.5.tgz", - "integrity": "sha512-e1LleDykUz2Iu+MTYdkSsuWX8lvAjAcs0Xef0lNIu0S2wOAzuTxCJtcd9S3cijlwYF18EsU3rzb8jPVobxDh9Q==", + "version": "11.0.1", + "resolved": "https://registry.npmjs.org/glob/-/glob-11.0.1.tgz", + "integrity": "sha512-zrQDm8XPnYEKawJScsnM0QzobJxlT/kHOOlRTio8IH/GrmxRE5fjllkzdaHclIuNjUQTJYH2xHNIGfdpJkDJUw==", + "dev": true, "license": "ISC", "dependencies": { - "fs.realpath": "^1.0.0", - "minimatch": "^8.0.2", - "minipass": "^4.2.4", - "path-scurry": "^1.6.1" + "foreground-child": "^3.1.0", + "jackspeak": "^4.0.1", + "minimatch": "^10.0.0", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^2.0.0" + }, + "bin": { + "glob": "dist/esm/bin.mjs" }, "engines": { - "node": ">=16 || 14 >=14.17" + "node": "20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/rimraf/node_modules/jackspeak": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-4.0.3.tgz", + "integrity": "sha512-oSwM7q8PTHQWuZAlp995iPpPJ4Vkl7qT0ZRD+9duL9j2oBy6KcTfyxc8mEuHJYC+z/kbps80aJLkaNzTOrf/kw==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "@isaacs/cliui": "^8.0.2" + }, + "engines": { + "node": "20 || >=22" }, "funding": { "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/rimraf/node_modules/lru-cache": { + "version": "11.0.2", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.0.2.tgz", + "integrity": "sha512-123qHRfJBmo2jXDbo/a5YOQrJoHF/GNQTLzQ5+IdK5pWpceK17yRc6ozlWd25FxvGKQbIUs91fDFkXmDHTKcyA==", + "dev": true, + "license": "ISC", + "engines": { + "node": "20 || >=22" + } + }, "node_modules/rimraf/node_modules/minimatch": { - "version": "8.0.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-8.0.4.tgz", - "integrity": "sha512-W0Wvr9HyFXZRGIDgCicunpQ299OKXs9RgZfaukz4qAW/pJhcpUfupc9c+OObPOFueNy8VSrZgEmDtk6Kh4WzDA==", + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.0.1.tgz", + "integrity": "sha512-ethXTt3SGGR+95gudmqJ1eNhRO7eGEGIgYA9vnPatK4/etz2MEVDno5GMCibdMTuBMyElzIlgxMna3K94XDIDQ==", + "dev": true, "license": "ISC", "dependencies": { "brace-expansion": "^2.0.1" }, "engines": { - "node": ">=16 || 14 >=14.17" + "node": "20 || >=22" }, "funding": { "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/rimraf/node_modules/minipass": { - "version": "4.2.8", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-4.2.8.tgz", - "integrity": "sha512-fNzuVyifolSLFL4NzpF+wEF4qrgqaaKX0haXPQEdQ7NKAN+WecoKMHV09YcuL/DHxrUsYQOK3MiuDf7Ip2OXfQ==", - "license": "ISC", + "node_modules/rimraf/node_modules/path-scurry": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-2.0.0.tgz", + "integrity": "sha512-ypGJsmGtdXUOeM5u93TyeIEfEhM6s+ljAhrk5vAvSx8uyY/02OvrZnA0YNGUrPXfpJMgI1ODd3nwz8Npx4O4cg==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "lru-cache": "^11.0.0", + "minipass": "^7.1.2" + }, "engines": { - "node": ">=8" + "node": "20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, "node_modules/ripemd160": { diff --git a/package.json b/package.json index e6d74a13..85145c34 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,10 @@ { "name": "TimeSafari", "version": "0.3.54-beta", + "description": "TimeSafari Desktop Application", + "author": { + "name": "TimeSafari Team" + }, "scripts": { "dev": "vite", "serve": "vite preview", @@ -9,7 +13,14 @@ "lint-fix": "eslint --ext .js,.ts,.vue --ignore-path .gitignore --fix src", "prebuild": "eslint --ext .js,.ts,.vue --ignore-path .gitignore src && node sw_combine.js", "test-local": "npx playwright test -c playwright.config-local.ts --trace on", - "test-all": "npm run build && npx playwright test -c playwright.config-local.ts --trace on" + "test-all": "npm run build && npx playwright test -c playwright.config-local.ts --trace on", + "clean:electron": "rimraf dist-electron dist-electron-build", + "build:electron": "npm run clean:electron && vite build --mode electron && node scripts/build-electron.js", + "build:capacitor": "vite build --mode capacitor", + "build:web": "vite build", + "electron:build:linux": "npm run build:electron && electron-builder --linux", + "electron:build:mac": "npm run build:electron && npx electron-builder --mac", + "electron:build:win": "npm run build:electron && npx electron-builder --win" }, "dependencies": { "@capacitor/android": "^6.2.0", @@ -104,30 +115,31 @@ "npm-check-updates": "^17.1.13", "postcss": "^8.4.38", "prettier": "^3.2.5", + "rimraf": "^6.0.1", "tailwindcss": "^3.4.1", "typescript": "~5.2.2", "vite": "^5.2.0", "vite-plugin-pwa": "^0.19.8" }, + "main": "main.js", "build": { - "appId": "com.example.app", + "appId": "app.timesafari.app", "productName": "TimeSafari", "directories": { "output": "dist-electron-build" }, "files": [ - "dist-electron/**", - "src/electron/**" + { + "from": "dist-electron", + "to": ".", + "filter": ["**/*"] + } ], - "mac": { - "target": "dmg" - }, - "win": { - "target": "nsis" - }, "linux": { - "target": "AppImage" + "target": ["AppImage"], + "category": "Utility", + "icon": "build/icons" }, - "asar": false + "asar": true } -} \ No newline at end of file +} diff --git a/scripts/build-electron.js b/scripts/build-electron.js new file mode 100644 index 00000000..432d55f6 --- /dev/null +++ b/scripts/build-electron.js @@ -0,0 +1,72 @@ +const fs = require('fs-extra'); +const path = require('path'); + +async function main() { + try { + console.log('Starting electron build process...'); + + // Clean directories + const distElectronDir = path.resolve(__dirname, '../dist-electron'); + const buildDir = path.resolve(__dirname, '../dist-electron-build'); + await fs.emptyDir(distElectronDir); + await fs.emptyDir(buildDir); + console.log('Cleaned directories'); + + // First build the web app if it doesn't exist + const webDist = path.resolve(__dirname, '../dist'); + if (!await fs.pathExists(webDist)) { + console.log('Web dist not found, building web app first...'); + throw new Error('Please run \'npm run build\' first to build the web app'); + } + + // Copy web files to www directory + const wwwDir = path.join(distElectronDir, 'www'); + await fs.copy(webDist, wwwDir); + console.log('Copied web files to:', wwwDir); + + // Copy and process main.js + const mainJsSrc = path.resolve(__dirname, '../src/electron/main.js'); + const mainJsDest = path.join(distElectronDir, 'main.js'); + await fs.copy(mainJsSrc, mainJsDest); + console.log('Copied main.js to:', mainJsDest); + + // Create the production package.json + const devPackageJson = require('../package.json'); + const prodPackageJson = { + name: devPackageJson.name, + version: devPackageJson.version, + description: devPackageJson.description, + author: devPackageJson.author, + main: 'main.js', + private: true + }; + + await fs.writeJson( + path.join(distElectronDir, 'package.json'), + prodPackageJson, + { spaces: 2 } + ); + + // Verify the structure + console.log('\nVerifying build structure:'); + const printDir = async (dir, prefix = '') => { + const items = await fs.readdir(dir); + for (const item of items) { + const fullPath = path.join(dir, item); + const stat = await fs.stat(fullPath); + console.log(`${prefix}${item}${stat.isDirectory() ? '/' : ''}`); + if (stat.isDirectory()) { + await printDir(fullPath, `${prefix} `); + } + } + }; + await printDir(distElectronDir); + + console.log('\nBuild completed successfully!'); + } catch (error) { + console.error('Build failed:', error); + process.exit(1); + } +} + +main().catch(console.error); \ No newline at end of file diff --git a/src/electron/main.js b/src/electron/main.js index 24f9dd2a..ac497e3a 100644 --- a/src/electron/main.js +++ b/src/electron/main.js @@ -1,46 +1,44 @@ const { app, BrowserWindow } = require("electron"); const path = require("path"); -let mainWindow; - -app.on("ready", () => { - mainWindow = new BrowserWindow({ - width: 800, - height: 600, +function createWindow() { + // Create the browser window. + const mainWindow = new BrowserWindow({ + width: 1200, + height: 800, webPreferences: { - //preload: path.join(__dirname, "preload.js"), - contextIsolation: true, // Security setting + nodeIntegration: true, + contextIsolation: false, }, }); - const indexPath = path.join( - __dirname, - "../../", - "dist-electron", - "index.html", - ); - - console.log("Loading Vue app from:", indexPath); - mainWindow.webContents.openDevTools(); - - mainWindow.webContents.on( - "did-fail-load", - (event, errorCode, errorDescription, validatedURL) => { - console.error( - "Failed to load:", - validatedURL, - "Error:", - errorDescription, - ); - }, - ); - - mainWindow.webContents.on("console-message", (event, level, message) => { - console.log(`[Renderer] ${message}`); - }); + // Log the paths we're trying to load from + const appPath = app.getAppPath(); + const indexPath = path.join(appPath, "www", "index.html"); + console.log("App path:", appPath); + console.log("Trying to load from:", indexPath); + // Load the index.html file mainWindow.loadFile(indexPath).catch((err) => { console.error("Failed to load index.html:", err); + // Try to list directory contents to debug + const fs = require("fs"); + console.log("Directory contents:", fs.readdirSync(appPath)); + }); + + // Open the DevTools in development + if (process.env.NODE_ENV === "development") { + mainWindow.webContents.openDevTools(); + } +} + +app.whenReady().then(() => { + createWindow(); + + app.on("activate", () => { + if (BrowserWindow.getAllWindows().length === 0) { + createWindow(); + } }); }); @@ -49,3 +47,8 @@ app.on("window-all-closed", () => { app.quit(); } }); + +// Handle any errors +process.on("uncaughtException", (error) => { + console.error("Uncaught Exception:", error); +}); diff --git a/vite.config.mjs b/vite.config.mjs index 6fc30afa..5a1ccb0a 100644 --- a/vite.config.mjs +++ b/vite.config.mjs @@ -3,6 +3,7 @@ import { VitePWA } from "vite-plugin-pwa"; import vue from "@vitejs/plugin-vue"; import dotenv from "dotenv"; import { loadAppConfig } from "./vite.config.utils"; +import path from "path"; // Load environment variables from .env file dotenv.config(); @@ -27,7 +28,20 @@ export default defineConfig(({ mode }) => { port: process.env.VITE_PORT || 8080, }, build: { - outDir, // Dynamically set output directory + outDir, + rollupOptions: { + ...(isElectron && { + input: { + main: path.resolve(__dirname, 'src/electron/main.js'), + index: path.resolve(__dirname, 'index.html') + }, + output: { + dir: outDir, + format: 'cjs', + entryFileNames: '[name].js' + } + }) + } }, plugins: [ vue(),