Skip to content

Commit 4a85066

Browse files
committed
Add linux support
1 parent 3c4911f commit 4a85066

7 files changed

Lines changed: 188 additions & 107 deletions

File tree

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
{
2-
"name": "Node.js & TypeScript",
2+
"name": "Node.js & TypeScript with Electron",
33
"image": "mcr.microsoft.com/devcontainers/typescript-node:18-bookworm",
44
"features": {
55
"ghcr.io/devcontainers-extra/features/angular-cli:2": {
@@ -9,7 +9,7 @@
99
"customizations": {
1010
"vscode": {
1111
"settings": {
12-
"terminal.integrated.shell.linux": "/bin/bash"
12+
"terminal.integrated.defaultProfile.linux": "bash"
1313
},
1414
"extensions": [
1515
"angular.ng-template",
@@ -42,6 +42,21 @@
4242
]
4343
}
4444
},
45-
"postCreateCommand": "npm i",
46-
"remoteUser": "node"
45+
"forwardPorts": [4200],
46+
"appPort": ["4200:4200"],
47+
"postCreateCommand": "sudo apt-get update && sudo apt-get install -y libnss3 libnspr4 libatk1.0-0 libatk-bridge2.0-0 libcups2 libdrm2 libgtk-3-0 libgbm1 libasound2 libxcomposite1 libxdamage1 libxfixes3 libxrandr2 libxshmfence1 libx11-xcb1 libxcursor1 libxi6 libxtst6 libxss1 wine64 mono-complete x11-apps && npm i",
48+
"remoteUser": "node",
49+
"containerEnv": {
50+
"ELECTRON_OZONE_PLATFORM_HINT": "auto",
51+
"NG_CLI_HOST": "0.0.0.0",
52+
"NG_CLI_DISABLE_HOST_CHECK": "true"
53+
},
54+
"runArgs": [
55+
"-v /tmp/.X11-unix:/tmp/.X11-unix",
56+
"-v /mnt/wslg:/mnt/wslg",
57+
"-e DISPLAY",
58+
"-e WAYLAND_DISPLAY",
59+
"-e XDG_RUNTIME_DIR",
60+
"-e PULSE_SERVER"
61+
]
4762
}

angular.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,8 @@
33
"cli": {
44
"schematicCollections": [
55
"@angular-eslint/schematics"
6-
]
6+
],
7+
"analytics": false
78
},
89
"version": 1,
910
"newProjectRoot": "projects",

app/src/main/drive-list.ts

Lines changed: 160 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,160 @@
1+
import { spawn } from 'child_process';
2+
import { Observable, catchError, filter, lastValueFrom, map, of } from 'rxjs';
3+
4+
export async function getDriveList() {
5+
// Determine the operating system
6+
switch (process.platform) {
7+
case 'win32': {
8+
return await getWindowsDriveList();
9+
}
10+
case 'linux': {
11+
return await getLinuxDriveList();
12+
}
13+
default: {
14+
console.warn('Unsupported operating system:', process.platform);
15+
return [];
16+
}
17+
}
18+
}
19+
20+
async function getWindowsDriveList() {
21+
return lastValueFrom(
22+
runCommand([
23+
'wmic',
24+
'logicaldisk',
25+
'get',
26+
'name,description,volumename',
27+
'/FORMAT:CSV',
28+
]).pipe(
29+
filter((result): result is string[] => !!result),
30+
map(([headerLine, ...contentLines]) => {
31+
const headers = headerLine.split(',').map((s) => s.toLocaleLowerCase());
32+
const nameIndex = headers.indexOf('name');
33+
const descriptionIndex = headers.indexOf('description');
34+
const volumenameIndex = headers.indexOf('volumename');
35+
36+
return contentLines.map((line) => {
37+
const parts = line.split(',');
38+
return {
39+
name: parts[nameIndex],
40+
description: parts[descriptionIndex],
41+
volumeName: parts[volumenameIndex],
42+
} as OSDrive;
43+
});
44+
}),
45+
catchError((error) => {
46+
console.error('Error processing Windows drive list:', error);
47+
return of([]);
48+
})
49+
)
50+
);
51+
}
52+
53+
async function getLinuxDriveList() {
54+
return lastValueFrom(
55+
runCommand(['findmnt', '--real', '--output', 'TARGET,SOURCE,FSTYPE,LABEL', '--json']).pipe(
56+
filter((result): result is string[] => !!result),
57+
map((lines) => {
58+
const drives: OSDrive[] = []
59+
const processFileSystems = (filesystems: FileSystems[]) => {
60+
filesystems
61+
.filter((fs) => {
62+
const target = fs.target;
63+
return (target === '/' ||
64+
target.startsWith('/mnt/') ||
65+
target.startsWith('/media/'));
66+
})
67+
.map((fs) => {
68+
drives.push({
69+
name: fs.target,
70+
description: fs.source,
71+
volumeName: fs.fstype,
72+
});
73+
if(fs.children) {
74+
processFileSystems(fs.children);
75+
}
76+
});
77+
}
78+
try {
79+
const fsJson = JSON.parse(lines.join('')) as FindMountStructure;
80+
if (fsJson && fsJson.filesystems) {
81+
processFileSystems(fsJson.filesystems)
82+
}
83+
return drives;
84+
} catch (err) {
85+
console.error('Error parsing findmnt JSON output:', err);
86+
return [];
87+
}
88+
}),
89+
catchError((error) => {
90+
console.error('Error processing Linux mounts:', error);
91+
return of([]);
92+
})
93+
)
94+
);
95+
}
96+
97+
function runCommand([command, ...args]: string[]) {
98+
const observable = new Observable<string[]>((subscriber) => {
99+
const cmd = spawn(command, args);
100+
let stdoutData = '';
101+
102+
cmd.stdout.on('data', (chunk) => {
103+
stdoutData += String(chunk);
104+
});
105+
106+
cmd.stderr.once('data', (data) => {
107+
subscriber.error(String(data));
108+
cmd.kill('SIGINT');
109+
});
110+
111+
cmd.once('exit', (code, signal) => {
112+
console.log('Child Process: ', [command, ...args].join(' '));
113+
console.log('Exited with code', code);
114+
console.log('Received termination signal', signal);
115+
116+
if (stdoutData) {
117+
const formattedData = stdoutData
118+
.trim()
119+
.split('\n')
120+
.map((line) => line.trim())
121+
.filter((line) => line.length > 0);
122+
123+
subscriber.next(formattedData);
124+
} else {
125+
subscriber.next([]);
126+
}
127+
128+
subscriber.complete();
129+
});
130+
131+
process.once('SIGINT', () => cmd.kill('SIGINT'));
132+
}).pipe(
133+
catchError((err) => {
134+
console.error(`Error occurred while running ${command} with args`, args);
135+
console.error(err);
136+
return of(null);
137+
})
138+
);
139+
140+
return observable;
141+
}
142+
143+
// Define the OSDrive interface
144+
interface OSDrive {
145+
name: string;
146+
description: string;
147+
volumeName: string;
148+
}
149+
150+
interface FindMountStructure {
151+
filesystems: FileSystems[]
152+
}
153+
154+
interface FileSystems {
155+
source: string;
156+
target: string;
157+
fstype: string;
158+
label: string | null;
159+
children: FileSystems[];
160+
}

app/src/main/ipc-handlers.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ import {
1616
mergeMap,
1717
mergeAll,
1818
} from 'rxjs';
19-
import { getDriveList } from './windows-drive-list';
19+
import { getDriveList } from './drive-list';
2020

2121
const readDir = bindNodeCallback(fs.readdir);
2222
const fsStat = bindNodeCallback(fs.stat);
@@ -70,8 +70,8 @@ const getFilesAndFoldersInDir = async (currentPath: string) => {
7070
!a.isDirectory === !b.isDirectory
7171
? a.name.localeCompare(b.name)
7272
: a.isDirectory
73-
? -1
74-
: 1;
73+
? -1
74+
: 1;
7575
});
7676
})
7777
);

app/src/main/windows-drive-list.ts

Lines changed: 0 additions & 96 deletions
This file was deleted.

package-lock.json

Lines changed: 0 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@
77
"main": "app/dist/main/main.js",
88
"private": true,
99
"scripts": {
10-
"postinstall": "electron-builder install-app-deps",
1110
"ng": "ng",
1211
"start": "npm-run-all -p electron:serve ng:serve",
1312
"ng:serve": "ng serve -c web",
@@ -20,6 +19,10 @@
2019
"electron:serve": "wait-on tcp:4200 -l && npm run electron:serve-tsc && electron . --serve",
2120
"electron:local": "npm run build:prod && electron .",
2221
"electron:build": "npm run build:prod && electron-builder build --publish=never",
22+
"electron:build:win": "npm run build:prod && electron-builder build --windows --publish=never",
23+
"electron:build:linux": "npm run build:prod && electron-builder build --linux --publish=never",
24+
"electron:build:mac": "npm run build:prod && electron-builder build --mac --publish=never",
25+
"postinstall": "electron-builder install-app-deps",
2326
"test": "ng test --watch=false",
2427
"test:watch": "ng test",
2528
"e2e": "npm run build:prod && playwright test -c e2e/playwright.config.ts e2e/",
@@ -29,7 +32,6 @@
2932
},
3033
"dependencies": {
3134
"@angular-builders/custom-webpack": "16.0.0",
32-
"@angular-devkit/build-angular": "16.0.5",
3335
"@angular/animations": "16.0.4",
3436
"@angular/cdk": "16.0.3",
3537
"@angular/common": "16.0.4",

0 commit comments

Comments
 (0)