Initial TrueGrowth source import

This commit is contained in:
2026-07-07 09:36:36 +08:00
commit 3b6781d695
2283 changed files with 691996 additions and 0 deletions

360
apps/electron/main.mjs Normal file
View File

@@ -0,0 +1,360 @@
import {
app,
BrowserWindow,
ipcMain,
Menu,
nativeTheme,
shell,
utilityProcess,
} from 'electron';
import fs from 'node:fs';
import path from 'node:path';
import { fileURLToPath, pathToFileURL } from 'node:url';
const __dirname = path.dirname(fileURLToPath(import.meta.url));
const brandName = 'TrueGrowth';
const rendererUrl = process.env.VITE_DEV_SERVER_URL;
const localApiPort = Number(process.env.TRUEGROWTH_LOCAL_API_PORT || 48177);
const userDataPath =
process.env.TRUEGROWTH_ELECTRON_USER_DATA ||
path.join(app.getPath('appData'), brandName);
const isDev = Boolean(rendererUrl);
const appRoot = app.isPackaged
? app.getAppPath()
: path.resolve(__dirname, '../..');
const brandIconPath = resolveAppPath(
'apps/electron/assets/truegrowth-dock-icon.png'
);
let mainWindow = null;
let localApi = null;
function readAppVersion() {
try {
const packageInfo = JSON.parse(
fs.readFileSync(resolveAppPath('package.json'), 'utf8')
);
return packageInfo.version || app.getVersion();
} catch {
return app.getVersion();
}
}
const appVersion = readAppVersion();
function resolveAppPath(...segments) {
return path.join(appRoot, ...segments);
}
function createDesktopLocalRendererEntry(indexHtmlPath) {
const html = fs.readFileSync(indexHtmlPath, 'utf8');
const appAssetPattern =
/https:\/\/cdn\.jsdelivr\.net\/npm\/aitu-app@[^/"'>\s]+\/([^"'<>\s]+)/g;
return html.replace(appAssetPattern, (_url, assetPath) => `./${assetPath}`);
}
process.title = brandName;
app.setName(brandName);
app.setAppUserModelId('ai.truegrowth.desktop');
app.setPath('userData', userDataPath);
process.env.TRUEGROWTH_ELECTRON_USER_DATA = userDataPath;
process.env.TRUEGROWTH_RUNTIME_STATE_DIR =
process.env.TRUEGROWTH_RUNTIME_STATE_DIR ||
path.join(userDataPath, 'runtime');
if (!isDev) {
process.env.TRUEGROWTH_DESKTOP_PACKAGED =
process.env.TRUEGROWTH_DESKTOP_PACKAGED || '1';
}
process.env.TRUEGROWTH_DESKTOP_APP_ROOT =
process.env.TRUEGROWTH_DESKTOP_APP_ROOT || appRoot;
process.env.TRUEGROWTH_DESKTOP_RESOURCE_DIR =
process.env.TRUEGROWTH_DESKTOP_RESOURCE_DIR ||
(app.isPackaged ? process.resourcesPath : appRoot);
const hasSingleInstanceLock = app.requestSingleInstanceLock({
rendererUrl,
userDataPath,
});
if (!hasSingleInstanceLock) {
app.quit();
process.exit(0);
}
app.on('second-instance', () => {
if (!mainWindow) {
return;
}
if (mainWindow.isMinimized()) {
mainWindow.restore();
}
mainWindow.focus();
mainWindow.show();
});
function getWindowState(window) {
return {
isFullScreen: window?.isFullScreen?.() ?? false,
isMaximized: window?.isMaximized?.() ?? false,
platform: process.platform,
};
}
function getSystemThemeState() {
const theme = nativeTheme.shouldUseDarkColors ? 'dark' : 'light';
return {
theme,
shouldUseDarkColors: theme === 'dark',
source: 'system',
};
}
function sendWindowState(window) {
if (!window || window.isDestroyed()) {
return;
}
window.webContents.send('truegrowth-window:state', getWindowState(window));
}
function sendSystemThemeState(window) {
if (!window || window.isDestroyed()) {
return;
}
const systemTheme = getSystemThemeState();
window.setBackgroundColor(
systemTheme.theme === 'dark' ? '#060606' : '#f7f7f5'
);
window.webContents.send('truegrowth-theme:system', systemTheme);
}
function getWindowFromIpcEvent(event) {
const window = BrowserWindow.fromWebContents(event.sender);
return window && !window.isDestroyed() ? window : null;
}
function installWindowControlsIpc() {
ipcMain.handle('truegrowth-window:minimize', (event) => {
const window = getWindowFromIpcEvent(event);
window?.minimize();
return window ? getWindowState(window) : getWindowState(mainWindow);
});
ipcMain.handle('truegrowth-window:toggle-maximize', (event) => {
const window = getWindowFromIpcEvent(event);
if (!window) {
return getWindowState(mainWindow);
}
if (window.isFullScreen()) {
window.setFullScreen(false);
} else if (window.isMaximized()) {
window.unmaximize();
} else {
window.maximize();
}
return getWindowState(window);
});
ipcMain.handle('truegrowth-window:close', (event) => {
const window = getWindowFromIpcEvent(event);
window?.close();
return { closed: Boolean(window) };
});
ipcMain.handle('truegrowth-window:is-maximized', (event) => {
const window = getWindowFromIpcEvent(event);
return getWindowState(window || mainWindow);
});
}
function installThemeIpc() {
ipcMain.handle('truegrowth-theme:get-system', () => getSystemThemeState());
nativeTheme.on('updated', () => {
BrowserWindow.getAllWindows().forEach(sendSystemThemeState);
});
}
function installApplicationMenu() {
const editMenu = [
{ role: 'undo' },
{ role: 'redo' },
{ type: 'separator' },
{ role: 'cut' },
{ role: 'copy' },
{ role: 'paste' },
{ role: 'selectAll' },
];
const viewMenu = [
{ role: 'reload' },
{ role: 'forceReload' },
{ role: 'toggleDevTools' },
{ type: 'separator' },
{ role: 'resetZoom' },
{ role: 'zoomIn' },
{ role: 'zoomOut' },
{ type: 'separator' },
{ role: 'togglefullscreen' },
];
const windowMenu = [
{ role: 'minimize' },
{ role: 'zoom' },
...(process.platform === 'darwin'
? [{ type: 'separator' }, { role: 'front' }]
: [{ role: 'close' }]),
];
const template = [
...(process.platform === 'darwin'
? [
{
label: brandName,
submenu: [
{ role: 'about', label: `About ${brandName}` },
{ type: 'separator' },
{ role: 'services' },
{ type: 'separator' },
{ role: 'hide', label: `Hide ${brandName}` },
{ role: 'hideOthers' },
{ role: 'unhide' },
{ type: 'separator' },
{ role: 'quit', label: `Quit ${brandName}` },
],
},
]
: []),
{
label: 'File',
submenu: [
process.platform === 'darwin' ? { role: 'close' } : { role: 'quit' },
],
},
{ label: 'Edit', submenu: editMenu },
{ label: 'View', submenu: viewMenu },
{ label: 'Window', submenu: windowMenu },
{ label: 'Help', submenu: [] },
];
Menu.setApplicationMenu(Menu.buildFromTemplate(template));
}
function createWindow() {
const initialSystemTheme = getSystemThemeState().theme;
mainWindow = new BrowserWindow({
width: 1440,
height: 920,
minWidth: 1180,
minHeight: 760,
title: brandName,
icon: brandIconPath,
frame: false,
autoHideMenuBar: true,
backgroundColor: initialSystemTheme === 'dark' ? '#060606' : '#f7f7f5',
webPreferences: {
preload: resolveAppPath('apps/electron/preload.cjs'),
contextIsolation: true,
nodeIntegration: false,
sandbox: false,
},
});
if (process.platform !== 'darwin') {
mainWindow.setMenuBarVisibility(false);
}
mainWindow.on('closed', () => {
mainWindow = null;
});
[
'enter-full-screen',
'leave-full-screen',
'maximize',
'resize',
'restore',
'unmaximize',
].forEach((eventName) => {
mainWindow.on(eventName, () => sendWindowState(mainWindow));
});
mainWindow.webContents.once('did-finish-load', () => {
sendWindowState(mainWindow);
sendSystemThemeState(mainWindow);
});
mainWindow.webContents.setWindowOpenHandler(({ url }) => {
void shell.openExternal(url);
return { action: 'deny' };
});
if (isDev && rendererUrl) {
void mainWindow.loadURL(rendererUrl);
return;
}
const indexHtmlPath = resolveAppPath('dist/apps/web/index.html');
const entryHtml = createDesktopLocalRendererEntry(indexHtmlPath);
void mainWindow.loadURL(
`data:text/html;charset=utf-8,${encodeURIComponent(entryHtml)}`,
{
baseURLForDataURL: `${pathToFileURL(
path.dirname(indexHtmlPath)
).toString()}/`,
}
);
}
async function startLocalApi() {
const { createLocalApiServer } = await import(
pathToFileURL(resolveAppPath('scripts/truegrowth-local-api.mjs')).toString()
);
localApi = createLocalApiServer({ utilityProcess });
localApi.on('error', (error) => {
if (error?.code === 'EADDRINUSE') {
console.log(
`TrueGrowth local API already available at http://127.0.0.1:${localApiPort}`
);
return;
}
throw error;
});
localApi.listen(localApiPort, '127.0.0.1', () => {
console.log(
`TrueGrowth local API listening at http://127.0.0.1:${localApiPort}`
);
});
}
app.whenReady().then(async () => {
installApplicationMenu();
installWindowControlsIpc();
installThemeIpc();
if (process.platform === 'darwin' && app.dock) {
app.dock.setIcon(brandIconPath);
}
app.setAboutPanelOptions({
applicationName: brandName,
applicationVersion: appVersion,
iconPath: brandIconPath,
copyright: `Copyright © ${new Date().getFullYear()} TrueGrowth`,
});
await startLocalApi();
createWindow();
app.on('activate', () => {
if (BrowserWindow.getAllWindows().length === 0) {
createWindow();
}
});
});
app.on('window-all-closed', () => {
if (process.platform !== 'darwin') {
app.quit();
}
});
app.on('before-quit', () => {
localApi?.close();
});