Skip to content

Image generation support #16

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 27 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
eca85ab
Function: Add General Settings Page
ShenKSPZ Apr 29, 2025
c418683
Function: Implement auto startup
ShenKSPZ May 2, 2025
57e490a
Fix Bug: Cannot quit by the tray button
ShenKSPZ May 2, 2025
bd78401
Fix Linter Error
ShenKSPZ May 2, 2025
92a3025
Fix: Move Language Settings to General Settings
ShenKSPZ May 3, 2025
f20fcd8
Function: Adding MCP Server
ShenKSPZ May 3, 2025
17aa471
Fix: Fix Image Generation Button Logic
ShenKSPZ May 3, 2025
de0c700
Fix: Add image generation display to markdown message
ShenKSPZ May 4, 2025
c33b445
Fix: fix image generation MCP
ShenKSPZ May 4, 2025
f2f7b5a
Fix: tool call
ShenKSPZ May 4, 2025
b1b45c2
Fix: Add MCP Page Layout
ShenKSPZ May 4, 2025
4292cc2
Fix: Add explore more button
ShenKSPZ May 4, 2025
58758c6
Fix: Optimize message sending logic
ShenKSPZ May 6, 2025
ea68c3f
Function: Image Generation History
ShenKSPZ May 7, 2025
d0bc0ab
Fix: Image generation page layout
ShenKSPZ May 7, 2025
e51152e
Update TranslationPage.tsx
ShenKSPZ May 7, 2025
1c32b8f
Function: Change Image Generation Page Layout
ShenKSPZ May 7, 2025
451a701
Fix: image generation page loading history
ShenKSPZ May 14, 2025
1b1efa2
Function: Add settings page
ShenKSPZ May 14, 2025
cf88b55
Function: Add tensorBlock Image Generation Support
ShenKSPZ May 14, 2025
850b981
Fix: Add image generation UI hint & date storage
ShenKSPZ May 14, 2025
e4a0bdc
Fix: Custom Provider Support Image Generation
ShenKSPZ May 16, 2025
9c74b4a
Fix: Add custom image generation capabilities edit
ShenKSPZ May 16, 2025
01bb14a
Fix: Custom Provider Image Generation
ShenKSPZ May 16, 2025
b908b77
Fix: fix image generation method calling depth
ShenKSPZ May 17, 2025
c5a0495
Fix: Hide MCP Server for now
ShenKSPZ May 17, 2025
8b446b7
Fix lint error
ShenKSPZ May 17, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
234 changes: 228 additions & 6 deletions app/main.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/* eslint-disable @typescript-eslint/no-unused-vars */
import {app, BrowserWindow, ipcMain, shell, dialog} from 'electron';
import {app, BrowserWindow, ipcMain, shell, dialog, Tray, Menu, nativeImage} from 'electron';
import * as path from 'path';
import * as fs from 'fs';
import * as os from 'os';
Expand All @@ -8,11 +8,151 @@ import { execSync } from 'child_process';

// Main window reference
let win: BrowserWindow | null = null;
// Tray reference
let tray: Tray | null = null;

// Settings
let closeToTray = true;
let forceQuit = false; // Flag to indicate we're trying to actually quit

// Check if app is running in development mode
const args = process.argv.slice(1);
const serve = args.some(val => val === '--serve');

/**
* Create the system tray
*/
function createTray() {
if (tray) {
return;
}

// Get appropriate icon based on platform
const iconPath = path.join(__dirname, '..', 'dist', 'logos', 'favicon.256x256.png');
const icon = nativeImage.createFromPath(iconPath);

tray = new Tray(icon);
tray.setToolTip('TensorBlock Desktop');

const contextMenu = Menu.buildFromTemplate([
{ label: 'Open TensorBlock', click: () => {
win?.show();
win?.setSkipTaskbar(false); // Show in taskbar
}},
{ type: 'separator' },
{ label: 'Quit', click: () => {
forceQuit = true; // Set flag to bypass close-to-tray
app.quit();
}}
]);

tray.setContextMenu(contextMenu);

tray.on('click', () => {
if (win) {
if (win.isVisible()) {
win.hide();
win.setSkipTaskbar(true); // Hide from taskbar
} else {
win.show();
win.setSkipTaskbar(false); // Show in taskbar
}
}
});
}

/**
* Set or remove auto launch on system startup
*/
function setAutoLaunch(enable: boolean): boolean {
try {
if (process.platform === 'win32') {
const appPath = app.getPath('exe');
const regKey = 'HKCU\\Software\\Microsoft\\Windows\\CurrentVersion\\Run';
const appName = app.getName();

if (enable) {
// Add to registry to enable auto launch
execSync(`reg add ${regKey} /v ${appName} /t REG_SZ /d "${appPath}" /f`);
} else {
// Remove from registry to disable auto launch
execSync(`reg delete ${regKey} /v ${appName} /f`);
}
return true;
} else if (process.platform === 'darwin') {
const appPath = app.getPath('exe');
const loginItemSettings = app.getLoginItemSettings();

// Set login item settings for macOS
app.setLoginItemSettings({
openAtLogin: enable,
path: appPath
});

return app.getLoginItemSettings().openAtLogin === enable;
} else if (process.platform === 'linux') {
// For Linux, create or remove a .desktop file in autostart directory
const desktopFilePath = path.join(os.homedir(), '.config', 'autostart', `${app.getName()}.desktop`);

if (enable) {
// Create directory if it doesn't exist
const autoStartDir = path.dirname(desktopFilePath);
if (!fs.existsSync(autoStartDir)) {
fs.mkdirSync(autoStartDir, { recursive: true });
}

// Create .desktop file
const desktopFileContent = `
[Desktop Entry]
Type=Application
Exec=${app.getPath('exe')}
Hidden=false
NoDisplay=false
X-GNOME-Autostart-enabled=true
Name=${app.getName()}
Comment=${app.getName()} startup script
`;
fs.writeFileSync(desktopFilePath, desktopFileContent);
} else if (fs.existsSync(desktopFilePath)) {
// Remove .desktop file
fs.unlinkSync(desktopFilePath);
}

return true;
}

return false;
} catch (error) {
console.error('Error setting auto launch:', error);
return false;
}
}

/**
* Check if app is set to auto launch on system startup
*/
function getAutoLaunchEnabled(): boolean {
try {
if (process.platform === 'win32') {
const regKey = 'HKCU\\Software\\Microsoft\\Windows\\CurrentVersion\\Run';
const appName = app.getName();

const output = execSync(`reg query ${regKey} /v ${appName} 2>nul`).toString();
return output.includes(appName);
} else if (process.platform === 'darwin') {
return app.getLoginItemSettings().openAtLogin;
} else if (process.platform === 'linux') {
const desktopFilePath = path.join(os.homedir(), '.config', 'autostart', `${app.getName()}.desktop`);
return fs.existsSync(desktopFilePath);
}

return false;
} catch (error) {
// If command fails (e.g., key doesn't exist), auto launch is not enabled
return false;
}
}

/**
* Creates the main application window
*/
Expand All @@ -29,8 +169,8 @@ function createWindow(): BrowserWindow {
frame: false,
fullscreenable: false,
autoHideMenuBar: true,
minWidth: 600,
minHeight: 600,
minWidth: 800,
minHeight: 700,
webPreferences: {
preload: path.join(__dirname, 'preload.js'),
contextIsolation: true,
Expand Down Expand Up @@ -180,14 +320,67 @@ function createWindow(): BrowserWindow {

// Close application
ipcMain.on('close-app', () => {
app.quit();
if (closeToTray && !forceQuit) {
win?.hide();
win?.setSkipTaskbar(true); // Hide from taskbar
} else {
forceQuit = true; // Ensure we're really quitting
app.quit();
}
});

// Open URL in default browser
ipcMain.on('open-url', (event, url) => {
shell.openExternal(url);
});

// Auto-startup handlers
ipcMain.handle('get-auto-launch', () => {
return getAutoLaunchEnabled();
});

ipcMain.handle('set-auto-launch', (event, enable) => {
return setAutoLaunch(enable);
});

// Tray handlers
ipcMain.handle('set-close-to-tray', (event, enable) => {
closeToTray = enable;
return true;
});

ipcMain.handle('get-close-to-tray', () => {
return closeToTray;
});

ipcMain.handle('set-startup-to-tray', (event, enable) => {
// Store this preference for the next app start
try {
const configPath = path.join(app.getPath('userData'), 'config.json');
let config = {};

if (fs.existsSync(configPath)) {
config = JSON.parse(fs.readFileSync(configPath, 'utf8'));
}

config = { ...config, startupToTray: enable };
fs.writeFileSync(configPath, JSON.stringify(config));
return true;
} catch (error) {
console.error('Error saving startup to tray setting:', error);
return false;
}
});

// Listen for window close event
win.on('close', (e) => {
if (closeToTray && !forceQuit) {
e.preventDefault();
win?.hide();
win?.setSkipTaskbar(true); // Hide from taskbar
}
});

// Disable page refresh in production
if (process.env.NODE_ENV === 'production') {
win.webContents.on('before-input-event', (event, input) => {
Expand Down Expand Up @@ -290,10 +483,36 @@ function getCPUName() {
try {
app.commandLine.appendSwitch('class', 'tensorblock-desktop');

// Set force quit flag when app is about to quit
app.on('before-quit', () => {
forceQuit = true;
});

// Initialize app when Electron is ready
// Added delay to fix black background issue with transparent windows
// See: https://github.com/electron/electron/issues/15947
app.on('ready', () => setTimeout(createWindow, 400));
app.on('ready', () => {
setTimeout(() => {
// Create window
win = createWindow();

// Create tray
createTray();

// Check if we should start minimized to tray
try {
const configPath = path.join(app.getPath('userData'), 'config.json');
if (fs.existsSync(configPath)) {
const config = JSON.parse(fs.readFileSync(configPath, 'utf8'));
if (config.startupToTray) {
win.hide();
}
}
} catch (error) {
console.error('Error reading config file:', error);
}
}, 400);
});

// Quit when all windows are closed
app.on('window-all-closed', () => {
Expand All @@ -303,7 +522,10 @@ try {
// Re-create window if activated and no windows exist
app.on('activate', () => {
if (win === null) {
createWindow();
win = createWindow();
} else {
win.show();
win.setSkipTaskbar(false); // Show in taskbar
}
});
} catch (_e) {
Expand Down
7 changes: 7 additions & 0 deletions app/preload.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,4 +29,11 @@ contextBridge.exposeInMainWorld('electron', {
saveFile: (fileBuffer: ArrayBuffer | string, fileName: string, fileType: string) =>
ipcRenderer.invoke('save-file', { fileBuffer, fileName, fileType }),
openFile: (filePath: string) => ipcRenderer.invoke('open-file', filePath),

// Auto-startup and tray functions
getAutoLaunch: () => ipcRenderer.invoke('get-auto-launch'),
setAutoLaunch: (enable: boolean) => ipcRenderer.invoke('set-auto-launch', enable),
setCloseToTray: (enable: boolean) => ipcRenderer.invoke('set-close-to-tray', enable),
getCloseToTray: () => ipcRenderer.invoke('get-close-to-tray'),
setStartupToTray: (enable: boolean) => ipcRenderer.invoke('set-startup-to-tray', enable),
});
2 changes: 2 additions & 0 deletions src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { ChatPage } from './components/pages/ChatPage';
import { ImageGenerationPage } from './components/pages/ImageGenerationPage';
import { TranslationPage } from './components/pages/TranslationPage';
import { FileManagementPage } from './components/pages/FileManagementPage';
import { MCPServerPage } from './components/pages/MCPServerPage';
import MainLayout from './components/layout/MainLayout';
import DatabaseInitializer from './components/core/DatabaseInitializer';

Expand Down Expand Up @@ -38,6 +39,7 @@ function App() {
{activePage === 'image' && <ImageGenerationPage />}
{activePage === 'translation' && <TranslationPage />}
{activePage === 'files' && <FileManagementPage />}
{activePage === 'mcpserver' && <MCPServerPage />}
</MainLayout>
</DatabaseInitializer>
);
Expand Down
Loading
Loading