From 0046282fb8d968c001a1c63864c17b39ce4d87c8 Mon Sep 17 00:00:00 2001 From: bubblepipe42 Date: Mon, 29 Sep 2025 13:01:07 +0800 Subject: [PATCH] stash --- electron/create-tray-icon.js | 60 +++++++++++++++++++++++++++++++ electron/entitlements.mac.plist | 18 ++++++++++ electron/main.js | 63 ++++++++++++++++++++++++++++++--- electron/package.json | 5 +++ 4 files changed, 142 insertions(+), 4 deletions(-) create mode 100644 electron/create-tray-icon.js create mode 100644 electron/entitlements.mac.plist diff --git a/electron/create-tray-icon.js b/electron/create-tray-icon.js new file mode 100644 index 000000000..517393b2e --- /dev/null +++ b/electron/create-tray-icon.js @@ -0,0 +1,60 @@ +// Create a simple tray icon for macOS +// Run: node create-tray-icon.js + +const fs = require('fs'); +const { createCanvas } = require('canvas'); + +function createTrayIcon() { + // For macOS, we'll use a Template image (black and white) + // Size should be 22x22 for Retina displays (@2x would be 44x44) + const canvas = createCanvas(22, 22); + const ctx = canvas.getContext('2d'); + + // Clear canvas + ctx.clearRect(0, 0, 22, 22); + + // Draw a simple "API" icon + ctx.fillStyle = '#000000'; + ctx.font = 'bold 10px system-ui'; + ctx.textAlign = 'center'; + ctx.textBaseline = 'middle'; + ctx.fillText('API', 11, 11); + + // Save as PNG + const buffer = canvas.toBuffer('image/png'); + fs.writeFileSync('tray-icon.png', buffer); + + // For Template images on macOS (will adapt to menu bar theme) + fs.writeFileSync('tray-iconTemplate.png', buffer); + fs.writeFileSync('tray-iconTemplate@2x.png', buffer); + + console.log('Tray icon created successfully!'); +} + +// Check if canvas is installed +try { + createTrayIcon(); +} catch (err) { + console.log('Canvas module not installed.'); + console.log('For now, creating a placeholder. Install canvas with: npm install canvas'); + + // Create a minimal 1x1 transparent PNG as placeholder + const minimalPNG = Buffer.from([ + 0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A, + 0x00, 0x00, 0x00, 0x0D, 0x49, 0x48, 0x44, 0x52, + 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, + 0x01, 0x03, 0x00, 0x00, 0x00, 0x25, 0xDB, 0x56, + 0xCA, 0x00, 0x00, 0x00, 0x03, 0x50, 0x4C, 0x54, + 0x45, 0x00, 0x00, 0x00, 0xA7, 0x7A, 0x3D, 0xDA, + 0x00, 0x00, 0x00, 0x01, 0x74, 0x52, 0x4E, 0x53, + 0x00, 0x40, 0xE6, 0xD8, 0x66, 0x00, 0x00, 0x00, + 0x0A, 0x49, 0x44, 0x41, 0x54, 0x08, 0x1D, 0x62, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, + 0x00, 0x01, 0x0A, 0x2D, 0xCB, 0x59, 0x00, 0x00, + 0x00, 0x00, 0x49, 0x45, 0x4E, 0x44, 0xAE, 0x42, + 0x60, 0x82 + ]); + + fs.writeFileSync('tray-icon.png', minimalPNG); + console.log('Created placeholder tray icon.'); +} \ No newline at end of file diff --git a/electron/entitlements.mac.plist b/electron/entitlements.mac.plist new file mode 100644 index 000000000..a00aebcd0 --- /dev/null +++ b/electron/entitlements.mac.plist @@ -0,0 +1,18 @@ + + + + + com.apple.security.cs.allow-unsigned-executable-memory + + com.apple.security.cs.allow-jit + + com.apple.security.cs.disable-library-validation + + com.apple.security.cs.allow-dyld-environment-variables + + com.apple.security.network.client + + com.apple.security.network.server + + + \ No newline at end of file diff --git a/electron/main.js b/electron/main.js index 9fb954b88..50307eac2 100644 --- a/electron/main.js +++ b/electron/main.js @@ -1,4 +1,4 @@ -const { app, BrowserWindow, dialog } = require('electron'); +const { app, BrowserWindow, dialog, Tray, Menu } = require('electron'); const { spawn } = require('child_process'); const path = require('path'); const http = require('http'); @@ -6,6 +6,7 @@ const fs = require('fs'); let mainWindow; let serverProcess; +let tray = null; const PORT = 3000; function getBinaryPath() { @@ -109,6 +110,49 @@ function waitForServer(resolve, reject, retries = 30) { req.end(); } +function createTray() { + tray = new Tray(path.join(__dirname, 'tray-icon.png')); + + const contextMenu = Menu.buildFromTemplate([ + { + label: 'Show New API', + click: () => { + if (mainWindow === null) { + createWindow(); + } else { + mainWindow.show(); + if (process.platform === 'darwin') { + app.dock.show(); + } + } + } + }, + { type: 'separator' }, + { + label: 'Quit', + click: () => { + app.isQuitting = true; + app.quit(); + } + } + ]); + + tray.setToolTip('New API'); + tray.setContextMenu(contextMenu); + + // On macOS, clicking the tray icon shows the menu + tray.on('click', () => { + if (mainWindow === null) { + createWindow(); + } else { + mainWindow.isVisible() ? mainWindow.hide() : mainWindow.show(); + if (mainWindow.isVisible() && process.platform === 'darwin') { + app.dock.show(); + } + } + }); +} + function createWindow() { mainWindow = new BrowserWindow({ width: 1400, @@ -128,6 +172,17 @@ function createWindow() { mainWindow.webContents.openDevTools(); } + // Close to tray instead of quitting + mainWindow.on('close', (event) => { + if (!app.isQuitting) { + event.preventDefault(); + mainWindow.hide(); + if (process.platform === 'darwin') { + app.dock.hide(); + } + } + }); + mainWindow.on('closed', () => { mainWindow = null; }); @@ -136,6 +191,7 @@ function createWindow() { app.whenReady().then(async () => { try { await startServer(); + createTray(); createWindow(); } catch (err) { console.error('Failed to start application:', err); @@ -145,9 +201,8 @@ app.whenReady().then(async () => { }); app.on('window-all-closed', () => { - if (process.platform !== 'darwin') { - app.quit(); - } + // Don't quit when window is closed, keep running in tray + // Only quit when explicitly choosing Quit from tray menu }); app.on('activate', () => { diff --git a/electron/package.json b/electron/package.json index d7fd66100..a480b5da0 100644 --- a/electron/package.json +++ b/electron/package.json @@ -58,6 +58,11 @@ "mac": { "category": "public.app-category.developer-tools", "icon": "icon.png", + "identity": null, + "hardenedRuntime": false, + "gatekeeperAssess": false, + "entitlements": "entitlements.mac.plist", + "entitlementsInherit": "entitlements.mac.plist", "target": [ "dmg", "zip"