diff --git a/app/electron/main.ts b/app/electron/main.ts
index 678f7de933e..68beaba0e01 100644
--- a/app/electron/main.ts
+++ b/app/electron/main.ts
@@ -128,9 +128,6 @@ const defaultPort = 4466;
const useExternalServer = process.env.EXTERNAL_SERVER || false;
const shouldCheckForUpdates = process.env.HEADLAMP_CHECK_FOR_UPDATES !== 'false';
-const manifestDir = isDev ? path.resolve('./') : process.resourcesPath;
-const manifestFile = path.join(manifestDir, 'app-build-manifest.json');
-const buildManifest = fs.existsSync(manifestFile) ? require(manifestFile) : {};
// make it global so that it doesn't get garbage collected
let mainWindow: BrowserWindow | null;
@@ -585,6 +582,18 @@ async function startServer(flags: string[] = []): Promise = {};
+ try {
+ const manifestContent = await fsPromises.readFile(manifestFile, 'utf8');
+ buildManifest = JSON.parse(manifestContent);
+ } catch (err) {
+ // If the manifest doesn't exist or can't be read, fall back to empty object
+ buildManifest = {};
+ }
+
const proxyUrls = !!buildManifest && buildManifest['proxy-urls'];
if (!!proxyUrls && proxyUrls.length > 0) {
serverArgs = serverArgs.concat(['--proxy-urls', proxyUrls.join(',')]);
@@ -603,8 +612,16 @@ async function startServer(flags: string[] = []): Promise {
+ // Cheap platform check first to avoid reading /proc on non-Linux platforms.
+ if (platform() !== 'linux') {
+ return false;
+ }
+
try {
- const data = fs.readFileSync('/proc/version', {
+ const data = await fsPromises.readFile('/proc/version', {
encoding: 'utf8',
- flag: 'r',
});
- return data.indexOf('icrosoft') !== -1;
+ const lower = data.toLowerCase();
+ return lower.includes('microsoft') || lower.includes('wsl');
} catch {
return false;
}
@@ -1081,10 +1103,11 @@ function saveZoomFactor(factor: number) {
}
}
-function loadZoomFactor() {
+async function loadZoomFactor(): Promise {
try {
- const { zoomFactor = 1.0 } = JSON.parse(fs.readFileSync(ZOOM_FILE_PATH, 'utf-8'));
- return zoomFactor;
+ const content = await fsPromises.readFile(ZOOM_FILE_PATH, 'utf-8');
+ const { zoomFactor = 1.0 } = JSON.parse(content);
+ return typeof zoomFactor === 'number' ? zoomFactor : 1.0;
} catch (err) {
console.error('Failed to load zoom factor, defaulting to 1.0:', err);
return 1.0;
@@ -1119,7 +1142,7 @@ function startElecron() {
console.log('Check for updates: ', shouldCheckForUpdates);
- async function createWindow() {
+ async function startServerIfNeeded() {
if (!useExternalServer) {
serverProcess = await startServer();
attachServerEventHandlers(serverProcess);
@@ -1207,9 +1230,11 @@ function startElecron() {
}
});
}
+ }
+ async function createWindow() {
// WSL has a problem with full size window placement, so make it smaller.
- const withMargin = isWSL();
+ const withMargin = await isWSL();
const { width, height } = windowSize(screen.getPrimaryDisplay().workAreaSize, withMargin);
mainWindow = new BrowserWindow({
@@ -1250,9 +1275,11 @@ function startElecron() {
}
});
- mainWindow.webContents.on('did-finish-load', () => {
- const startZoom = loadZoomFactor();
- setZoom(startZoom);
+ mainWindow.webContents.on('did-finish-load', async () => {
+ const startZoom = await loadZoomFactor();
+ if (startZoom !== 1.0) {
+ setZoom(startZoom);
+ }
});
mainWindow.webContents.on('dom-ready', () => {
@@ -1419,10 +1446,12 @@ function startElecron() {
app.disableHardwareAcceleration();
}
- app.on('ready', createWindow);
- app.on('activate', function () {
+ app.on('ready', async () => {
+ await Promise.all([startServerIfNeeded(), createWindow()]);
+ });
+ app.on('activate', async function () {
if (mainWindow === null) {
- createWindow();
+ await Promise.all([startServerIfNeeded(), createWindow()]);
}
});
diff --git a/frontend/src/components/App/Home/ClusterTable.tsx b/frontend/src/components/App/Home/ClusterTable.tsx
index 9ee29cfcf56..9998c23f796 100644
--- a/frontend/src/components/App/Home/ClusterTable.tsx
+++ b/frontend/src/components/App/Home/ClusterTable.tsx
@@ -28,6 +28,7 @@ import { ApiError } from '../../../lib/k8s/api/v2/ApiError';
import { Cluster } from '../../../lib/k8s/cluster';
import { getClusterPrefixedPath } from '../../../lib/util';
import { useTypedSelector } from '../../../redux/hooks';
+import { Loader } from '../../common';
import Link from '../../common/Link';
import Table from '../../common/Table';
import ClusterContextMenu from './ClusterContextMenu';
@@ -139,6 +140,11 @@ export default function ClusterTable({
}
const viewClusters = t('View Clusters');
+ const loading = clusters === null;
+ if (loading) {
+ return ;
+ }
+
return (
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ |
+
+
+
+
+ Name
+
+
+
+
+
+
+ 0
+
+
+
+
+
+ |
+
+
+
+
+ Origin
+
+
+
+
+
+
+ 0
+
+
+
+
+
+ |
+
+
+
+
+ Status
+
+
+
+
+
+
+ 0
+
+
+
+
+
+ |
+
+
+
+
+ Warnings
+
+
+
+
+
+
+ 0
+
+
+
+
+
+ |
+
+
+
+
+ Kubernetes Version
+
+
+
+
+
+
+ 0
+
+
+
+
+
+ |
+
+
+
+
+
+
+
+
+
+ 0
+
+
+
+
+
+ |
+
+
+
+
+
+
+
+
+
+
+ |
+
+
+ cluster0
+
+ |
+
+
+ Unknown
+
+ |
+
+
+ |
+
+ 0
+ |
+
+ ⋯
+ |
+
+
+
+ |
+
+
+
+
+
+
+
+
+ |
+
+
+ cluster1
+
+ |
+
+
+ Unknown
+
+ |
+
+
+ |
+
+ 0
+ |
+
+ ⋯
+ |
+
+
+
+ |
+
+
+
+
+
+
+
+
+ |
+
+
+ cluster2
+
+ |
+
+
+ Unknown
+
+ |
+
+
+ |
+
+ 0
+ |
+
+ ⋯
+ |
+
+
+
+ |
+
+
+
+
+
+
+
+
+
+
+
+
+