Skip to content

Commit

Permalink
v0.0.15
Browse files Browse the repository at this point in the history
  • Loading branch information
iamEvanYT committed Dec 4, 2024
1 parent e109aa2 commit a4c282c
Show file tree
Hide file tree
Showing 4 changed files with 111 additions and 36 deletions.
99 changes: 68 additions & 31 deletions backend/src/ipc/apps/linux.ts
Original file line number Diff line number Diff line change
@@ -1,48 +1,73 @@
import fs from "fs";
import fsPromises from "fs/promises";
import path from "path";
import os from "os";
import { exec } from "child_process";
import util from "util";
import os from "os";

const execAsync = util.promisify(exec);

async function parseDesktopFile(filePath: string): Promise<{ name: string; icon: string; path: string } | null> {
interface DesktopEntry {
Name?: string;
Icon?: string;
Exec?: string;
NoDisplay?: string;
Hidden?: string;
}

async function parseDesktopFile(filePath: string): Promise<DesktopEntry | null> {
try {
const content = await fsPromises.readFile(filePath, "utf-8");
const lines = content.split("\n");
let name = "";
let icon = "";
let execPath = "";
const entry: DesktopEntry = {};
let inDesktopEntry = false;

for (const line of lines) {
if (line.startsWith("Name=")) {
name = line.split("=")[1];
} else if (line.startsWith("Icon=")) {
icon = line.split("=")[1];
} else if (line.startsWith("Exec=")) {
execPath = line.split("=")[1].split(" ")[0];
if (line.trim() === "[Desktop Entry]") {
inDesktopEntry = true;
continue;
}
}
if (line.trim().startsWith("[") && line.trim().endsWith("]")) {
inDesktopEntry = false;
continue;
}
if (!inDesktopEntry) continue;

if (name && execPath) {
return { name, icon, path: execPath };
const [key, ...valueParts] = line.split("=");
if (key && valueParts.length > 0) {
entry[key.trim() as keyof DesktopEntry] = valueParts.join("=").trim();
}
}

return entry;
} catch (error) {
console.error(`Error parsing desktop file ${filePath}:`, error);
return null;
}

return null;
}

async function getIconPath(iconName: string): Promise<string> {
try {
const { stdout } = await execAsync(`find /usr/share/icons -name "${iconName}.*" | head -n 1`);
return stdout.trim();
} catch (error) {
console.error(`Error finding icon for ${iconName}:`, error);
return "";
async function findIconPath(iconName: string): Promise<string> {
const iconDirs = [
"/usr/share/icons",
"/usr/share/pixmaps",
path.join(os.homedir(), ".icons"),
path.join(os.homedir(), ".local/share/icons"),
];

for (const dir of iconDirs) {
try {
const { stdout } = await execAsync(`find "${dir}" -name "${iconName}.*" | head -n 1`);
const iconPath = stdout.trim();
if (iconPath) {
return iconPath;
}
} catch (error) {
// Ignore errors and continue searching
}
}

// If no icon found, return the original name
return iconName;
}

async function getLinuxApplicationsInDirectory(dir: string): Promise<{ name: string; icon: string; path: string }[]> {
Expand All @@ -57,10 +82,15 @@ async function getLinuxApplicationsInDirectory(dir: string): Promise<{ name: str
for (const file of files) {
if (file.endsWith(".desktop")) {
const filePath = path.join(dir, file);
const app = await parseDesktopFile(filePath);
if (app) {
const iconPath = await getIconPath(app.icon);
apps.push({ ...app, icon: iconPath });
const entry = await parseDesktopFile(filePath);
if (entry && entry.Name && entry.Exec && entry.NoDisplay !== "true" && entry.Hidden !== "true") {
const execCommand = entry.Exec.split(" ").filter(part => !part.startsWith("%"));
const iconPath = entry.Icon ? await findIconPath(entry.Icon) : "";
apps.push({
name: entry.Name,
icon: iconPath,
path: execCommand.join(" ")
});
}
}
}
Expand All @@ -72,10 +102,17 @@ async function getLinuxApplicationsInDirectory(dir: string): Promise<{ name: str
}

export async function getLinuxApplications(): Promise<{ name: string; icon: string; path: string }[]> {
const dataHome = process.env.XDG_DATA_HOME || path.join(os.homedir(), ".local", "share");
const extraDataDirs = process.env.XDG_DATA_DIRS
? process.env.XDG_DATA_DIRS.split(":")
: ["/usr/local/share", "/usr/share", "/var/lib/flatpak/exports/share"];

const flatpakDir = path.join(dataHome, "flatpak", "exports", "share");

const directories = [
"/usr/share/applications",
"/usr/local/share/applications",
path.join(os.homedir(), ".local/share/applications")
path.join(dataHome, "applications"),
path.join(flatpakDir, "applications"),
...extraDataDirs.map(dir => path.join(dir, "applications"))
];

const applications: { name: string; icon: string; path: string }[] = [];
Expand All @@ -88,4 +125,4 @@ export async function getLinuxApplications(): Promise<{ name: string; icon: stri
await Promise.all(promises);

return applications;
}
}
29 changes: 26 additions & 3 deletions backend/src/ipc/open/app.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,28 @@
import { IpcMainInvokeEvent, shell } from "electron";
import { exec } from "child_process";
import util from "util";

export async function openApp(event: IpcMainInvokeEvent, appPath: string) {
return shell.openPath(appPath);
}
const execAsync = util.promisify(exec);

export async function openApp(event: IpcMainInvokeEvent, appPath: string): Promise<string> {
if (process.platform === "linux") {
try {
// Split the command and its arguments
const [command, ...args] = appPath.split(' ');

// Escape each argument individually
const escapedArgs = args.map(arg => `"${arg.replace(/"/g, '\\"')}"`);

// Reconstruct the command
const fullCommand = `${command} ${escapedArgs.join(' ')}`;

await execAsync(fullCommand);
return "App launched successfully";
} catch (error) {
console.error(`Error launching app: ${error}`);
return `Failed to launch app: ${error}`;
}
} else {
return shell.openPath(appPath);
}
}
17 changes: 16 additions & 1 deletion frontend/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"version": "0.0.14",
"version": "0.0.15",
"scripts": {
"start": "cd backend && npm run start",
"code": "code ./frontend && code ./backend",
Expand Down

0 comments on commit a4c282c

Please sign in to comment.