Compare commits
No commits in common. "f0e6b704cdc447f08b06702a7cba65d808ad8158" and "8bf3343ba3f5bd1b03f4b3f4c5e42e6c65c0c27b" have entirely different histories.
f0e6b704cd
...
8bf3343ba3
Binary file not shown.
File diff suppressed because it is too large
Load Diff
11
package.json
11
package.json
|
|
@ -7,8 +7,7 @@
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "npm-run-all --parallel dev:react dev:electron",
|
"dev": "npm-run-all --parallel dev:react dev:electron",
|
||||||
"dev:react": "vite",
|
"dev:react": "vite",
|
||||||
"dev:electron": "npm run build && cross-env NODE_ENV=development electron .",
|
"dev:electron": "npm run transpile:electron && cross-env NODE_ENV=development electron --inspect=5858 .",
|
||||||
"prod:electron": "npm run build && cross-env NODE_ENV=production electron .",
|
|
||||||
"build": "tsc && vite build",
|
"build": "tsc && vite build",
|
||||||
"preview": "vite preview",
|
"preview": "vite preview",
|
||||||
"transpile:electron": "tsc --project src/electron/tsconfig.json",
|
"transpile:electron": "tsc --project src/electron/tsconfig.json",
|
||||||
|
|
@ -21,7 +20,6 @@
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@reduxjs/toolkit": "^2.10.1",
|
"@reduxjs/toolkit": "^2.10.1",
|
||||||
"@tailwindcss/vite": "^4.1.16",
|
"@tailwindcss/vite": "^4.1.16",
|
||||||
"active-win": "^7.2.0",
|
|
||||||
"axios": "^1.13.2",
|
"axios": "^1.13.2",
|
||||||
"chalk": "^5.6.2",
|
"chalk": "^5.6.2",
|
||||||
"electron-store": "^11.0.2",
|
"electron-store": "^11.0.2",
|
||||||
|
|
@ -35,14 +33,12 @@
|
||||||
"react-router-dom": "^7.9.5",
|
"react-router-dom": "^7.9.5",
|
||||||
"recharts": "^2.12.7",
|
"recharts": "^2.12.7",
|
||||||
"sharp": "^0.34.4",
|
"sharp": "^0.34.4",
|
||||||
"socket.io-client": "^4.8.1",
|
"tailwindcss": "^4.1.16"
|
||||||
"tailwindcss": "^4.1.16",
|
|
||||||
"windowmanager": "^0.14.8"
|
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@playwright/test": "^1.47.2",
|
"@playwright/test": "^1.47.2",
|
||||||
"@types/luxon": "^3.7.1",
|
"@types/luxon": "^3.7.1",
|
||||||
"@types/node": "^22.19.0",
|
"@types/node": "^22.5.5",
|
||||||
"@types/os-utils": "^0.0.4",
|
"@types/os-utils": "^0.0.4",
|
||||||
"@types/react": "^18.3.8",
|
"@types/react": "^18.3.8",
|
||||||
"@types/react-dom": "^18.3.0",
|
"@types/react-dom": "^18.3.0",
|
||||||
|
|
@ -51,7 +47,6 @@
|
||||||
"daisyui": "^5.4.3",
|
"daisyui": "^5.4.3",
|
||||||
"electron": "^32.1.2",
|
"electron": "^32.1.2",
|
||||||
"electron-builder": "^25.0.5",
|
"electron-builder": "^25.0.5",
|
||||||
"electron-rebuild": "^3.2.9",
|
|
||||||
"npm-run-all": "^4.1.5",
|
"npm-run-all": "^4.1.5",
|
||||||
"typescript": "^5.6.2",
|
"typescript": "^5.6.2",
|
||||||
"vite": "^5.4.7",
|
"vite": "^5.4.7",
|
||||||
|
|
|
||||||
File diff suppressed because one or more lines are too long
|
Before Width: | Height: | Size: 27 KiB |
File diff suppressed because one or more lines are too long
|
Before Width: | Height: | Size: 27 KiB |
|
|
@ -1,29 +0,0 @@
|
||||||
<!DOCTYPE html>
|
|
||||||
<html>
|
|
||||||
<head>
|
|
||||||
<style>
|
|
||||||
body {
|
|
||||||
margin: 0;
|
|
||||||
display: flex;
|
|
||||||
justify-content: center;
|
|
||||||
align-items: center;
|
|
||||||
background: #ffffff;
|
|
||||||
font-family: Arial, sans-serif;
|
|
||||||
}
|
|
||||||
.loader {
|
|
||||||
width: 50px;
|
|
||||||
height: 50px;
|
|
||||||
border: 5px solid #e0e0e0;
|
|
||||||
border-top-color: #6a5acd;
|
|
||||||
border-radius: 50%;
|
|
||||||
animation: spin 1s linear infinite;
|
|
||||||
}
|
|
||||||
@keyframes spin {
|
|
||||||
to { transform: rotate(360deg); }
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<div class="loader"></div>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
import { ipcMain, BrowserWindow } from "electron";
|
import { ipcMain, BrowserWindow } from "electron";
|
||||||
import { getToken, saveToken, clearToken } from "../utils/tokenHelper.js";
|
import { getToken, saveToken, clearToken } from "../utils/tokenHelper.js";
|
||||||
import { getActiveApp, handleLogin, takeScreenshot, startWindowPolling, getRunningAppsList, captureSpecificApp } from "../utils/utilityFunctions.js";
|
import { getActiveApp, handleLogin, takeScreenshot, startWindowPolling } from "../utils/utilityFunctions.js";
|
||||||
import api from "../api/axiosClient.js";
|
import api from "../api/axiosClient.js";
|
||||||
import { getMainWindow } from '../main.js';
|
import { getMainWindow } from '../main.js';
|
||||||
|
|
||||||
|
|
@ -29,7 +29,7 @@ export const registerIPCHandlers = (mainWindow: BrowserWindow) => {
|
||||||
return { success: true };
|
return { success: true };
|
||||||
});
|
});
|
||||||
|
|
||||||
ipcMain.handle("login-request", async (_, credentials: { email: string; password: string }) => {
|
ipcMain.handle("login-request", async (_, credentials: { email: string; password: string }) => {
|
||||||
return handleLogin(credentials);
|
return handleLogin(credentials);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -83,7 +83,7 @@ export const registerIPCHandlers = (mainWindow: BrowserWindow) => {
|
||||||
|
|
||||||
|
|
||||||
ipcMain.handle("get-project-list", async () => {
|
ipcMain.handle("get-project-list", async () => {
|
||||||
console.log("📡 get-project-list called");
|
// console.log("📡 get-project-list called");
|
||||||
try {
|
try {
|
||||||
const res = await api.get("/list/projects");
|
const res = await api.get("/list/projects");
|
||||||
|
|
||||||
|
|
@ -226,14 +226,6 @@ ipcMain.handle("forgot-password", async (event, email: string) => {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
ipcMain.handle('get-running-apps', async () => {
|
|
||||||
return await getRunningAppsList()
|
|
||||||
});
|
|
||||||
|
|
||||||
ipcMain.handle('capture-specific-window', async (event, appTitle: string) => {
|
|
||||||
console.log(appTitle, "IPC HANDLE")
|
|
||||||
return await captureSpecificApp(appTitle)
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
ipcMain.handle("get-profile", async () => {
|
ipcMain.handle("get-profile", async () => {
|
||||||
|
|
|
||||||
|
|
@ -1,179 +1,98 @@
|
||||||
import { app, BrowserWindow, globalShortcut, session } from "electron";
|
import { app, BrowserWindow, ipcMain, session } from 'electron';
|
||||||
import { isDev } from "./util.js";
|
import { ipcMainHandle, ipcMainOn, isDev } from './util.js';
|
||||||
// import { pollResources } from "./resourceManager.js";
|
import { getStaticData, pollResources } from './resourceManager.js';
|
||||||
import { getPreloadPath, getUIPath } from "./pathResolver.js";
|
import { getPreloadPath, getUIPath } from './pathResolver.js';
|
||||||
import { registerIPCHandlers } from "./ipc/ipcHandler.js";
|
|
||||||
|
|
||||||
import "./ipc/ipcHandler.js";
|
import "./ipc/ipcHandler.js";
|
||||||
import path from "path";
|
import { registerIPCHandlers } from './ipc/ipcHandler.js';
|
||||||
import { fileURLToPath } from "url";
|
|
||||||
|
|
||||||
app.setAppUserModelId("com.tracker.app");
|
let mainWindow:any = null;
|
||||||
|
|
||||||
const __filename = fileURLToPath(import.meta.url);
|
app.on('ready', async () => {
|
||||||
const __dirname = path.dirname(__filename);
|
console.log("ready");
|
||||||
|
|
||||||
// ---------------------------
|
mainWindow = new BrowserWindow({
|
||||||
// SINGLE INSTANCE LOCK
|
width: 500, // fixed width
|
||||||
// ---------------------------
|
height: 600, // fixed height
|
||||||
const gotTheLock = app.requestSingleInstanceLock();
|
|
||||||
|
resizable: false, // prevents resizing
|
||||||
if (!gotTheLock) {
|
fullscreenable: false, // disables fullscreen toggle (F11 or double-click title bar)
|
||||||
app.quit();
|
minimizable: false, // disables minimize button
|
||||||
process.exit(0);
|
maximizable: false, // prevents maximize
|
||||||
}
|
|
||||||
|
|
||||||
let mainWindow: BrowserWindow | null = null;
|
|
||||||
let splashWindow: BrowserWindow | null = null;
|
|
||||||
|
|
||||||
// ---------------------------
|
|
||||||
// HANDLE DOUBLE OPEN ATTEMPT
|
|
||||||
// ---------------------------
|
|
||||||
app.on("second-instance", () => {
|
|
||||||
if (mainWindow) {
|
|
||||||
if (mainWindow.isMinimized()) mainWindow.restore();
|
|
||||||
mainWindow.show();
|
|
||||||
mainWindow.focus();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// ---------------------------
|
|
||||||
// MAIN APP READY
|
|
||||||
// ---------------------------
|
|
||||||
app.on("ready", async () => {
|
|
||||||
|
|
||||||
|
|
||||||
splashWindow = new BrowserWindow({
|
|
||||||
width: 300,
|
|
||||||
height: 300,
|
|
||||||
frame: false,
|
|
||||||
transparent: false,
|
|
||||||
alwaysOnTop: false, // splash does NOT need to be on top || Yooo Don't make it onTop
|
|
||||||
resizable: false,
|
|
||||||
show: true,
|
|
||||||
skipTaskbar: true,
|
|
||||||
webPreferences: {
|
|
||||||
devTools: false,
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
splashWindow.loadFile(
|
|
||||||
path.join(__dirname, "../assets/splash.html")
|
|
||||||
);
|
|
||||||
|
|
||||||
// Create the main window
|
|
||||||
const win = new BrowserWindow({
|
|
||||||
width: 500,
|
|
||||||
height: 600,
|
|
||||||
resizable: false,
|
|
||||||
fullscreenable: false,
|
|
||||||
minimizable: false,
|
|
||||||
maximizable: false,
|
|
||||||
closable: true,
|
closable: true,
|
||||||
alwaysOnTop: true,
|
alwaysOnTop: true,
|
||||||
skipTaskbar: true,
|
|
||||||
autoHideMenuBar: true,
|
autoHideMenuBar: true,
|
||||||
frame: process.platform === "darwin" ? false : true,
|
|
||||||
show: false, // speed improvement
|
frame: process.platform === 'darwin' ? false : true,
|
||||||
webPreferences: {
|
webPreferences: {
|
||||||
devTools: false,
|
|
||||||
preload: getPreloadPath(),
|
preload: getPreloadPath(),
|
||||||
contextIsolation: true,
|
|
||||||
nodeIntegration: false,
|
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
mainWindow = win;
|
session.defaultSession.webRequest.onBeforeRequest((details, callback) => {
|
||||||
|
console.log('🌐 Request:', details.method, details.url);
|
||||||
|
callback({ cancel: false });
|
||||||
|
});
|
||||||
|
|
||||||
// ------------------------------------
|
|
||||||
// ONLY LOG NETWORK REQUESTS IN DEV
|
|
||||||
// ------------------------------------
|
|
||||||
if (isDev() && process.env.LOG_REQUESTS === "true") {
|
|
||||||
session.defaultSession.webRequest.onBeforeRequest((details, callback) => {
|
|
||||||
console.log("🌐 Request:", details.method, details.url);
|
|
||||||
callback({ cancel: false });
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// ------------------------------------
|
|
||||||
// LOAD FRONTEND
|
|
||||||
// ------------------------------------
|
|
||||||
if (isDev()) {
|
if (isDev()) {
|
||||||
win.loadURL("http://localhost:5123");
|
mainWindow.loadURL('http://localhost:5123');
|
||||||
// win.webContents.openDevTools(); // Optional (slows startup)
|
mainWindow.webContents.openDevTools();
|
||||||
} else {
|
} else {
|
||||||
win.loadFile(getUIPath());
|
mainWindow.loadFile(getUIPath());
|
||||||
}
|
}
|
||||||
|
|
||||||
// ------------------------------------
|
// pollResources(mainWindow);
|
||||||
// SHOW WINDOW WHEN READY (no white flash)
|
// 🔹 Register all IPC handlers (includes polling)
|
||||||
// ------------------------------------
|
registerIPCHandlers(mainWindow);
|
||||||
win.once("ready-to-show", () => {
|
|
||||||
// Delay heavy work to improve startup speed
|
|
||||||
setTimeout(() => {
|
|
||||||
splashWindow?.close();
|
|
||||||
splashWindow = null;
|
|
||||||
|
|
||||||
win.show();
|
pollResources(mainWindow);
|
||||||
// pollResources(win);
|
|
||||||
registerIPCHandlers(win);
|
|
||||||
}, 500);
|
|
||||||
});
|
|
||||||
|
|
||||||
// ------------------------------------
|
// IPC handlers
|
||||||
// HANDLE CLOSE EVENTS
|
// ipcMainHandle('getStaticData', () => getStaticData());
|
||||||
// ------------------------------------
|
|
||||||
handleCloseEvents(win);
|
// ipcMainOn('sendFrameAction', (payload) => {
|
||||||
|
// switch (payload) {
|
||||||
|
// case 'CLOSE':
|
||||||
|
// mainWindow.close();
|
||||||
|
// break;
|
||||||
|
// case 'MAXIMIZE':
|
||||||
|
// // prevent maximize
|
||||||
|
// break;
|
||||||
|
// case 'MINIMIZE':
|
||||||
|
// mainWindow.minimize();
|
||||||
|
// break;
|
||||||
|
// }
|
||||||
|
// });
|
||||||
|
|
||||||
|
handleCloseEvents(mainWindow);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
function handleCloseEvents(mainWindow: BrowserWindow) {
|
||||||
app.on('browser-window-focus', () => {
|
|
||||||
globalShortcut.register('CommandOrControl+R', () => {
|
|
||||||
// Do nothing; disables reload
|
|
||||||
});
|
|
||||||
globalShortcut.register('F5', () => {
|
|
||||||
// Do nothing; disables reload
|
|
||||||
});
|
|
||||||
globalShortcut.register('CommandOrControl+Shift+I', () => {
|
|
||||||
// Do nothing; disables DevTools
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
app.on('browser-window-blur', () => {
|
|
||||||
globalShortcut.unregisterAll();
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
// ---------------------------
|
|
||||||
// CUSTOM CLOSE BEHAVIOR
|
|
||||||
// ---------------------------
|
|
||||||
function handleCloseEvents(window: BrowserWindow) {
|
|
||||||
let willClose = false;
|
let willClose = false;
|
||||||
|
|
||||||
window.on("close", (e) => {
|
mainWindow.on('close', (e) => {
|
||||||
if (willClose) return;
|
if (willClose) return;
|
||||||
|
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
window.hide();
|
mainWindow.hide();
|
||||||
|
|
||||||
if (app.dock) {
|
if (app.dock) {
|
||||||
app.dock.hide();
|
app.dock.hide();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
app.on("before-quit", () => {
|
app.on('before-quit', () => {
|
||||||
willClose = true;
|
willClose = true;
|
||||||
});
|
});
|
||||||
|
|
||||||
window.on("show", () => {
|
mainWindow.on('show', () => {
|
||||||
willClose = false;
|
willClose = false;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---------------------------
|
|
||||||
// GET MAIN WINDOW EXPORT
|
|
||||||
// ---------------------------
|
|
||||||
export function getMainWindow() {
|
export function getMainWindow() {
|
||||||
return mainWindow;
|
return mainWindow;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -11,10 +11,7 @@ export function getPreloadPath() {
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getUIPath() {
|
export function getUIPath() {
|
||||||
return path.join(app.getAppPath(), "dist-react", "index.html");
|
return path.join(app.getAppPath(), '/dist-react/index.html');
|
||||||
|
|
||||||
// return "C:\\Users\\user\\OneDrive\\Desktop\\electron-main\\dist-react\\index.html";
|
|
||||||
// return path.join(app.getAppPath(), '/dist-react/index.html');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getAssetPath() {
|
export function getAssetPath() {
|
||||||
|
|
|
||||||
|
|
@ -12,8 +12,6 @@ contextBridge.exposeInMainWorld('electron', {
|
||||||
toggleCloseBtn: (shouldEnable: boolean) => ipcRenderer.invoke('toggle-close-btn', shouldEnable),
|
toggleCloseBtn: (shouldEnable: boolean) => ipcRenderer.invoke('toggle-close-btn', shouldEnable),
|
||||||
resizeWindow: (width: number, height: number) => ipcRenderer.invoke('resize-window', width, height),
|
resizeWindow: (width: number, height: number) => ipcRenderer.invoke('resize-window', width, height),
|
||||||
getProjectList: () => ipcRenderer.invoke("get-project-list"),
|
getProjectList: () => ipcRenderer.invoke("get-project-list"),
|
||||||
getRunningApps: () => ipcRenderer.invoke('get-running-apps'),
|
|
||||||
captureAppScreenShot: (appTitle: string) : Promise<string> => ipcRenderer.invoke('capture-specific-window', appTitle),
|
|
||||||
onWindowUpdate: (callback) =>
|
onWindowUpdate: (callback) =>
|
||||||
ipcRenderer.on("window:updated", (_, data) => callback(data)),
|
ipcRenderer.on("window:updated", (_, data) => callback(data)),
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,10 @@
|
||||||
import { app, BrowserWindow, desktopCapturer } from "electron";
|
import { BrowserWindow, desktopCapturer } from "electron";
|
||||||
import * as fs from "fs";
|
import * as fs from "fs";
|
||||||
import path from "path";
|
import path from "path";
|
||||||
import axios from 'axios';
|
import axios from 'axios';
|
||||||
import sharp from 'sharp';
|
import sharp from 'sharp';
|
||||||
import { saveToken } from "./tokenHelper.js";
|
import { saveToken } from "./tokenHelper.js";
|
||||||
import { activeWindow } from "get-windows";
|
import { activeWindow } from "get-windows";
|
||||||
import { exec } from "child_process";
|
|
||||||
import { getUIPath } from "../pathResolver.js";
|
|
||||||
|
|
||||||
// Blur using an offscreen canvas
|
// Blur using an offscreen canvas
|
||||||
async function blurAndCompressBase64Image(base64: string): Promise<Buffer> {
|
async function blurAndCompressBase64Image(base64: string): Promise<Buffer> {
|
||||||
|
|
@ -55,12 +53,6 @@ export const handleLogin = async (credentials: { email: string, password: string
|
||||||
// Call your login API
|
// Call your login API
|
||||||
console.log("🟡 login Request received", credentials);
|
console.log("🟡 login Request received", credentials);
|
||||||
|
|
||||||
console.log("Doom");
|
|
||||||
console.log("app.getAppPath()", app.getAppPath());
|
|
||||||
console.log("UIPATH", getUIPath());
|
|
||||||
console.log("Doom 1");
|
|
||||||
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
console.log("➡️ Calling API...");
|
console.log("➡️ Calling API...");
|
||||||
const response = await axios.post(
|
const response = await axios.post(
|
||||||
|
|
@ -105,81 +97,23 @@ export const handleLogin = async (credentials: { email: string, password: string
|
||||||
|
|
||||||
// Get active window info with timestamp
|
// Get active window info with timestamp
|
||||||
export const getActiveApp = async () => {
|
export const getActiveApp = async () => {
|
||||||
const result = await activeWindow();
|
const result = await activeWindow();
|
||||||
const dataWithTime = { ...result, timestamp: new Date().toISOString() };
|
const dataWithTime = { ...result, timestamp: new Date().toISOString() };
|
||||||
// console.log("🪟 Active Window with Time:", dataWithTime);
|
// console.log("🪟 Active Window with Time:", dataWithTime);
|
||||||
return dataWithTime;
|
return dataWithTime;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Periodically send active window updates
|
// Periodically send active window updates
|
||||||
export const startWindowPolling = (win: BrowserWindow) => {
|
export const startWindowPolling = (win: BrowserWindow) => {
|
||||||
console.log("⏱️ Started active window polling (every 5s)");
|
console.log("⏱️ Started active window polling (every 5s)");
|
||||||
setInterval(async () => {
|
setInterval(async () => {
|
||||||
try {
|
try {
|
||||||
const app = await getActiveApp();
|
const app = await getActiveApp();
|
||||||
if (app) win.webContents.send("window:updated", app);
|
if (app) win.webContents.send("window:updated", app);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error("❌ Polling error:", err);
|
console.error("❌ Polling error:", err);
|
||||||
}
|
}
|
||||||
}, 5000);
|
}, 5000);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
export const getRunningAppsList = async () => {
|
|
||||||
try {
|
|
||||||
// PowerShell command: Get only apps with visible windows (non-background)
|
|
||||||
const sources = await desktopCapturer.getSources({
|
|
||||||
types: ['window', 'screen'],
|
|
||||||
thumbnailSize: { width: 150, height: 150 } // Optional: set thumbnail size
|
|
||||||
});
|
|
||||||
|
|
||||||
const app = sources.map(source => ({
|
|
||||||
name: source.name,
|
|
||||||
pid: source.id,
|
|
||||||
// thumbnail: source.thumbnail // Can be used as screenshot
|
|
||||||
}));
|
|
||||||
return app
|
|
||||||
} catch (error) {
|
|
||||||
return []
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
export const captureSpecificApp = async (windowTitle: string): Promise<string> => {
|
|
||||||
try {
|
|
||||||
const sources = await desktopCapturer.getSources({
|
|
||||||
types: ['window', 'screen'],
|
|
||||||
thumbnailSize: { width: 650, height: 430 }
|
|
||||||
});
|
|
||||||
const targetSource = sources.find(source => source.name === windowTitle);
|
|
||||||
if (targetSource) {
|
|
||||||
|
|
||||||
let image = targetSource.thumbnail.toDataURL();
|
|
||||||
const blurredBuffer = await blurAndCompressBase64Image(image);
|
|
||||||
|
|
||||||
// Convert buffer → base64 string
|
|
||||||
const base64String = `data:image/png;base64,${blurredBuffer.toString('base64')}`;
|
|
||||||
|
|
||||||
// ✅ Return the processed base64 instead of saving
|
|
||||||
return base64String;
|
|
||||||
|
|
||||||
// const folderPath = "C:\\Users\\Public\\vatracker\\screenshots";
|
|
||||||
|
|
||||||
// // Ensure directory exists
|
|
||||||
// if (!fs.existsSync(folderPath)) {
|
|
||||||
// fs.mkdirSync(folderPath, { recursive: true });
|
|
||||||
// }
|
|
||||||
|
|
||||||
// const filePath = path.join(folderPath, `screenshot-${Date.now()}.png`);
|
|
||||||
// fs.writeFileSync(filePath, blurredBuffer);
|
|
||||||
|
|
||||||
// console.log(`Screenshot saved to ${filePath}`);
|
|
||||||
} else {
|
|
||||||
console.log(`Window with title "${windowTitle}" not found.`);
|
|
||||||
return "Not Found"
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
console.error('Error capturing screenshot:', error);
|
|
||||||
return 'Failed to capture screenshot.';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,84 +1,5 @@
|
||||||
// import React, { useEffect } from "react";
|
|
||||||
// import { BrowserRouter, Route, Routes, Navigate } from "react-router-dom";
|
|
||||||
// import { useDispatch, useSelector } from "react-redux";
|
|
||||||
// import { AppDispatch, RootState } from "./store";
|
|
||||||
// import { loadUserFromStorage } from "./store/userSlice";
|
|
||||||
|
|
||||||
// import Login from "./pages/Login";
|
|
||||||
// import Dashboard from "./pages/Dashboard";
|
|
||||||
// import Projects from "./pages/Projects/Projects";
|
|
||||||
// import DashboardLayout from "./layout/DashboardLayout";
|
|
||||||
// import ProtectedRoute from "./routes/ProtectedRoute";
|
|
||||||
// import PublicRoute from "./routes/PublicRoute";
|
|
||||||
// import { ThemeToggle } from "./helper/ThemeToggle";
|
|
||||||
// import LeaveAndAdjustments from "./pages/LeaveAndAdjustments/LeaveAndAdjustments";
|
|
||||||
// import Timesheets from "./pages/Timesheets/Timesheets";
|
|
||||||
// import Settings from "./pages/Settings/Settings";
|
|
||||||
// import HelpSupport from "./pages/HelpSupport/HelpSupport";
|
|
||||||
|
|
||||||
// const App: React.FC = () => {
|
|
||||||
// const theme = ThemeToggle();
|
|
||||||
// const dispatch = useDispatch<AppDispatch>();
|
|
||||||
|
|
||||||
// // ✅ Get user directly from Redux
|
|
||||||
// const user = useSelector((state: RootState) => state.user.user);
|
|
||||||
|
|
||||||
// useEffect(() => {
|
|
||||||
// document.documentElement.setAttribute("data-theme", theme);
|
|
||||||
// }, [theme]);
|
|
||||||
|
|
||||||
// // ✅ Load user from localStorage into Redux when app starts
|
|
||||||
// useEffect(() => {
|
|
||||||
// dispatch(loadUserFromStorage());
|
|
||||||
// }, [dispatch]);
|
|
||||||
|
|
||||||
// console.log("App.tsx → user:", user);
|
|
||||||
|
|
||||||
// return (
|
|
||||||
// <BrowserRouter>
|
|
||||||
// <Routes>
|
|
||||||
// {/* Public Route */}
|
|
||||||
// <Route
|
|
||||||
// path="/login"
|
|
||||||
// element={
|
|
||||||
// <PublicRoute user={user}>
|
|
||||||
// <Login />
|
|
||||||
// </PublicRoute>
|
|
||||||
// }
|
|
||||||
// />
|
|
||||||
|
|
||||||
// {/* Protected Routes */}
|
|
||||||
// <Route
|
|
||||||
// path="/dashboard"
|
|
||||||
// element={
|
|
||||||
// <ProtectedRoute user={user}>
|
|
||||||
// <DashboardLayout />
|
|
||||||
// </ProtectedRoute>
|
|
||||||
// }
|
|
||||||
// >
|
|
||||||
// <Route index element={<Dashboard />} />
|
|
||||||
// <Route path="projects" element={<Projects />} />
|
|
||||||
// <Route path="leave-adjustments" element={<LeaveAndAdjustments />} />
|
|
||||||
// <Route path="timesheet" element={<Timesheets />} />
|
|
||||||
// <Route path="settings" element={<Settings />} />
|
|
||||||
// <Route path="help-support" element={<HelpSupport />} />
|
|
||||||
// </Route>
|
|
||||||
|
|
||||||
// {/* Default Redirect */}
|
|
||||||
// <Route
|
|
||||||
// path="*"
|
|
||||||
// element={<Navigate to={user ? "/dashboard" : "/login"} replace />}
|
|
||||||
// />
|
|
||||||
// </Routes>
|
|
||||||
// </BrowserRouter>
|
|
||||||
// );
|
|
||||||
// };
|
|
||||||
|
|
||||||
// export default App;
|
|
||||||
|
|
||||||
|
|
||||||
import React, { useEffect } from "react";
|
import React, { useEffect } from "react";
|
||||||
import { HashRouter as Router, Route, Routes, Navigate } from "react-router-dom";
|
import { BrowserRouter, Route, Routes, Navigate } from "react-router-dom";
|
||||||
import { useDispatch, useSelector } from "react-redux";
|
import { useDispatch, useSelector } from "react-redux";
|
||||||
import { AppDispatch, RootState } from "./store";
|
import { AppDispatch, RootState } from "./store";
|
||||||
import { loadUserFromStorage } from "./store/userSlice";
|
import { loadUserFromStorage } from "./store/userSlice";
|
||||||
|
|
@ -99,6 +20,8 @@ const App: React.FC = () => {
|
||||||
const theme = ThemeToggle();
|
const theme = ThemeToggle();
|
||||||
const dispatch = useDispatch<AppDispatch>();
|
const dispatch = useDispatch<AppDispatch>();
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// ✅ Get user directly from Redux
|
// ✅ Get user directly from Redux
|
||||||
const user = useSelector((state: RootState) => state.user.user);
|
const user = useSelector((state: RootState) => state.user.user);
|
||||||
|
|
||||||
|
|
@ -106,14 +29,15 @@ const App: React.FC = () => {
|
||||||
document.documentElement.setAttribute("data-theme", theme);
|
document.documentElement.setAttribute("data-theme", theme);
|
||||||
}, [theme]);
|
}, [theme]);
|
||||||
|
|
||||||
|
// ✅ Load user from localStorage into Redux when app starts
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
dispatch(loadUserFromStorage());
|
dispatch(loadUserFromStorage());
|
||||||
}, [dispatch]);
|
}, [dispatch]);
|
||||||
|
|
||||||
console.log("App.tsx → user:", user);
|
console.log("App.tsx → user:", user);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Router>
|
<BrowserRouter>
|
||||||
<Routes>
|
<Routes>
|
||||||
{/* Public Route */}
|
{/* Public Route */}
|
||||||
<Route
|
<Route
|
||||||
|
|
@ -148,7 +72,7 @@ const App: React.FC = () => {
|
||||||
element={<Navigate to={user ? "/dashboard" : "/login"} replace />}
|
element={<Navigate to={user ? "/dashboard" : "/login"} replace />}
|
||||||
/>
|
/>
|
||||||
</Routes>
|
</Routes>
|
||||||
</Router>
|
</BrowserRouter>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,4 @@
|
||||||
import React, { useState, useEffect, useRef } from "react";
|
import React, { useState, useEffect, useRef } from "react";
|
||||||
import io, { Socket } from 'socket.io-client';
|
|
||||||
|
|
||||||
import {
|
import {
|
||||||
Outlet,
|
Outlet,
|
||||||
NavLink,
|
NavLink,
|
||||||
|
|
@ -126,11 +124,6 @@ const DashboardLayout = () => {
|
||||||
selectedProject?.projectName ?? null
|
selectedProject?.projectName ?? null
|
||||||
);
|
);
|
||||||
|
|
||||||
const socketRef = useRef<Socket | null>(null);
|
|
||||||
const intervalRef = useRef<number | null>(null);
|
|
||||||
const [isConnected, setIsConnected] = useState(false);
|
|
||||||
// const [seclectedScreen, setSelectedScreen] = useState<string | null>(null)
|
|
||||||
|
|
||||||
const [projectsData, setProjectsData] = useState<Project[]>([]);
|
const [projectsData, setProjectsData] = useState<Project[]>([]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
|
@ -262,7 +255,7 @@ const DashboardLayout = () => {
|
||||||
dispatch(clearUser()); // ✅ Removes from Redux + localStorage
|
dispatch(clearUser()); // ✅ Removes from Redux + localStorage
|
||||||
navigate("/login");
|
navigate("/login");
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Logout error:", error);
|
console.error("Logout error:", error);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -566,22 +559,7 @@ const DashboardLayout = () => {
|
||||||
{isTracking &&
|
{isTracking &&
|
||||||
<header className="w-full flex justify-between items-center h-12 px-6 bg-base-100 border-b border-base-300 sticky top-0 z-30">
|
<header className="w-full flex justify-between items-center h-12 px-6 bg-base-100 border-b border-base-300 sticky top-0 z-30">
|
||||||
{/* Left Section */}
|
{/* Left Section */}
|
||||||
{/* {!isConnected ? (
|
<div className="flex items-center space-x-3">
|
||||||
<button
|
|
||||||
onClick={handleConnect}
|
|
||||||
className="bg-green-600 hover:bg-green-700 text-white px-4 py-2 rounded"
|
|
||||||
>
|
|
||||||
Connect
|
|
||||||
</button>
|
|
||||||
) : (
|
|
||||||
<button
|
|
||||||
onClick={handleDisconnect}
|
|
||||||
className="bg-red-600 hover:bg-red-700 text-white px-4 py-2 rounded"
|
|
||||||
>
|
|
||||||
Disconnect
|
|
||||||
</button>
|
|
||||||
)} */}
|
|
||||||
<div className="flex items-center space-x-3">
|
|
||||||
{selectedProject ? (
|
{selectedProject ? (
|
||||||
isTracking ? (
|
isTracking ? (
|
||||||
<button
|
<button
|
||||||
|
|
@ -655,9 +633,9 @@ const DashboardLayout = () => {
|
||||||
{/* Layout Body */}
|
{/* Layout Body */}
|
||||||
<div className={`flex ${!isTracking ? "max-h-screen min-h-screen" : " h-[calc(100vh-3rem)]"} bg-base-200 overflow-hidden`}>
|
<div className={`flex ${!isTracking ? "max-h-screen min-h-screen" : " h-[calc(100vh-3rem)]"} bg-base-200 overflow-hidden`}>
|
||||||
{/* Sidebar */}
|
{/* Sidebar */}
|
||||||
<aside className="w-20 bg-base-100 border-r border-base-300 flex flex-col items-center py-4 space-y-4">
|
<aside className="w-20 bg-base-100 border-r border-base-300 flex-shrink-0 flex flex-col items-center py-4 space-y-4">
|
||||||
{menuItems.map((item , index) => (
|
{menuItems.map((item, index) => (
|
||||||
<div className="tooltip z-100 tooltip-right" key={item.label} data-tip={item.label}>
|
<div className="tooltip z-100 tooltip-right" data-tip={item.label} key={index}>
|
||||||
<NavLink
|
<NavLink
|
||||||
key={item.label}
|
key={item.label}
|
||||||
to={item.path}
|
to={item.path}
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
import React, { useEffect, useRef, useState } from "react";
|
import React from "react";
|
||||||
import { Clock, TrendingUp, Play, Square, ProjectorIcon, Pause } from "lucide-react";
|
import { Clock, TrendingUp, Play, Square, ProjectorIcon, Pause } from "lucide-react";
|
||||||
import { useOutletContext } from "react-router-dom";
|
import { useOutletContext } from "react-router-dom";
|
||||||
import NoData from "./NoData";
|
import NoData from "./NoData";
|
||||||
|
|
@ -92,14 +92,6 @@ const Dashboard: React.FC = () => {
|
||||||
projectsData,
|
projectsData,
|
||||||
} = useOutletContext<TrackingState>();
|
} = useOutletContext<TrackingState>();
|
||||||
|
|
||||||
// interface AppsListInt {
|
|
||||||
// name: string;
|
|
||||||
// pid: string;
|
|
||||||
// title: string;
|
|
||||||
// }
|
|
||||||
|
|
||||||
// const [runningApps, setRunningApps] = useState<AppsListInt[]>([]);
|
|
||||||
|
|
||||||
const selectedData = selectedProject
|
const selectedData = selectedProject
|
||||||
? projectTrackingMap.get(selectedProject.projectId)
|
? projectTrackingMap.get(selectedProject.projectId)
|
||||||
: null;
|
: null;
|
||||||
|
|
@ -215,10 +207,10 @@ const Dashboard: React.FC = () => {
|
||||||
<span className="text-base-content/70">Tracking:</span>{" "}
|
<span className="text-base-content/70">Tracking:</span>{" "}
|
||||||
<span
|
<span
|
||||||
className={`badge ${selectedData?.isPaused
|
className={`badge ${selectedData?.isPaused
|
||||||
? "badge-warning"
|
? "badge-warning"
|
||||||
: selectedData?.isTracking
|
: selectedData?.isTracking
|
||||||
? "badge-success"
|
? "badge-success"
|
||||||
: "badge-neutral"
|
: "badge-neutral"
|
||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
{selectedData?.isPaused
|
{selectedData?.isPaused
|
||||||
|
|
|
||||||
|
|
@ -99,8 +99,6 @@ interface Window {
|
||||||
getActiveAppDetails: () => {};
|
getActiveAppDetails: () => {};
|
||||||
toggleCloseBtn: (shouldEnable: boolean) => {},
|
toggleCloseBtn: (shouldEnable: boolean) => {},
|
||||||
resizeWindow: (width: number, height: number) => {},
|
resizeWindow: (width: number, height: number) => {},
|
||||||
getRunningApps: () => {},
|
|
||||||
captureAppScreenShot: (appTitle: string) => Promise<string>,
|
|
||||||
onWindowUpdate: (callback: (data: any) => void) => void;
|
onWindowUpdate: (callback: (data: any) => void) => void;
|
||||||
getProjectList: () => Promise<ProjectListResponse>;
|
getProjectList: () => Promise<ProjectListResponse>;
|
||||||
startProjectTracking: (data: {
|
startProjectTracking: (data: {
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue