terminalos 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +324 -0
- package/bin/cli.js +28 -0
- package/build/assets/html2pdf-S6WA7sS3.js +240 -0
- package/build/assets/index-CfXPiaFw.css +32 -0
- package/build/assets/index-DfoqUTmD.js +141 -0
- package/build/index.html +17 -0
- package/electron/fs-watcher.js +181 -0
- package/electron/fs-watcher.ts +183 -0
- package/electron/main.js +245 -0
- package/electron/main.ts +241 -0
- package/electron/preload.js +77 -0
- package/electron/preload.ts +105 -0
- package/electron/process-detector.js +63 -0
- package/electron/process-detector.ts +70 -0
- package/electron/pty-manager.js +188 -0
- package/electron/pty-manager.ts +181 -0
- package/electron/tsconfig.json +14 -0
- package/electron/versions-manager.js +74 -0
- package/electron/versions-manager.ts +98 -0
- package/electron/window-state.js +49 -0
- package/electron/window-state.ts +52 -0
- package/package.json +104 -0
- package/runtime-dist/server.js +626 -0
package/build/index.html
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
<!doctype html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8" />
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
6
|
+
<meta
|
|
7
|
+
http-equiv="Content-Security-Policy"
|
|
8
|
+
content="default-src 'self'; script-src 'self' 'unsafe-eval'; style-src 'self' 'unsafe-inline'; font-src 'self' data:; img-src 'self' data: https: http: file:; frame-src localfile:; connect-src 'self' ws://localhost:*"
|
|
9
|
+
/>
|
|
10
|
+
<title>terminalOS</title>
|
|
11
|
+
<script type="module" crossorigin src="./assets/index-DfoqUTmD.js"></script>
|
|
12
|
+
<link rel="stylesheet" crossorigin href="./assets/index-CfXPiaFw.css">
|
|
13
|
+
</head>
|
|
14
|
+
<body>
|
|
15
|
+
<div id="root"></div>
|
|
16
|
+
</body>
|
|
17
|
+
</html>
|
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.FsWatcher = void 0;
|
|
7
|
+
const promises_1 = __importDefault(require("fs/promises"));
|
|
8
|
+
const path_1 = __importDefault(require("path"));
|
|
9
|
+
const chokidar_1 = __importDefault(require("chokidar"));
|
|
10
|
+
// Polyfill DOMMatrix for pdfjs-dist (pdf-parse dep) in Node.js/Electron main process
|
|
11
|
+
if (typeof globalThis.DOMMatrix === 'undefined') {
|
|
12
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
13
|
+
;
|
|
14
|
+
globalThis.DOMMatrix = class DOMMatrix {
|
|
15
|
+
a = 1;
|
|
16
|
+
b = 0;
|
|
17
|
+
c = 0;
|
|
18
|
+
d = 1;
|
|
19
|
+
e = 0;
|
|
20
|
+
f = 0;
|
|
21
|
+
m11 = 1;
|
|
22
|
+
m12 = 0;
|
|
23
|
+
m13 = 0;
|
|
24
|
+
m14 = 0;
|
|
25
|
+
m21 = 0;
|
|
26
|
+
m22 = 1;
|
|
27
|
+
m23 = 0;
|
|
28
|
+
m24 = 0;
|
|
29
|
+
m31 = 0;
|
|
30
|
+
m32 = 0;
|
|
31
|
+
m33 = 1;
|
|
32
|
+
m34 = 0;
|
|
33
|
+
m41 = 0;
|
|
34
|
+
m42 = 0;
|
|
35
|
+
m43 = 0;
|
|
36
|
+
m44 = 1;
|
|
37
|
+
is2D = true;
|
|
38
|
+
isIdentity = true;
|
|
39
|
+
constructor(_init) { }
|
|
40
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
41
|
+
static fromFloat32Array() { return new globalThis.DOMMatrix(); }
|
|
42
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
43
|
+
static fromFloat64Array() { return new globalThis.DOMMatrix(); }
|
|
44
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
45
|
+
static fromMatrix() { return new globalThis.DOMMatrix(); }
|
|
46
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
47
|
+
multiply() { return new globalThis.DOMMatrix(); }
|
|
48
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
49
|
+
translate() { return new globalThis.DOMMatrix(); }
|
|
50
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
51
|
+
scale() { return new globalThis.DOMMatrix(); }
|
|
52
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
53
|
+
rotate() { return new globalThis.DOMMatrix(); }
|
|
54
|
+
toFloat32Array() { return new Float32Array(16); }
|
|
55
|
+
toFloat64Array() { return new Float64Array(16); }
|
|
56
|
+
toString() { return 'matrix(1, 0, 0, 1, 0, 0)'; }
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
60
|
+
const pdfParse = require('pdf-parse');
|
|
61
|
+
const mammoth_1 = __importDefault(require("mammoth"));
|
|
62
|
+
async function getContentSize(entryPath, ext) {
|
|
63
|
+
try {
|
|
64
|
+
if (ext === 'pdf') {
|
|
65
|
+
const buffer = await promises_1.default.readFile(entryPath);
|
|
66
|
+
const result = await pdfParse(buffer);
|
|
67
|
+
return result.text.length;
|
|
68
|
+
}
|
|
69
|
+
if (ext === 'docx') {
|
|
70
|
+
const buffer = await promises_1.default.readFile(entryPath);
|
|
71
|
+
const result = await mammoth_1.default.extractRawText({ buffer });
|
|
72
|
+
return result.value.length;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
catch {
|
|
76
|
+
// fallback: no contentSize, frontend will use size
|
|
77
|
+
}
|
|
78
|
+
return undefined;
|
|
79
|
+
}
|
|
80
|
+
class FsWatcher {
|
|
81
|
+
win;
|
|
82
|
+
watcher = null;
|
|
83
|
+
watchRoot = null;
|
|
84
|
+
constructor(win) {
|
|
85
|
+
this.win = win;
|
|
86
|
+
}
|
|
87
|
+
async readDir(dirPath) {
|
|
88
|
+
// Validate path to prevent traversal
|
|
89
|
+
const resolved = path_1.default.resolve(dirPath);
|
|
90
|
+
const entries = await promises_1.default.readdir(resolved, { withFileTypes: true });
|
|
91
|
+
const result = [];
|
|
92
|
+
await Promise.all(entries.map(async (entry) => {
|
|
93
|
+
const entryPath = path_1.default.join(resolved, entry.name);
|
|
94
|
+
const isDirectory = entry.isDirectory();
|
|
95
|
+
const ext = isDirectory ? '' : path_1.default.extname(entry.name).slice(1).toLowerCase();
|
|
96
|
+
const stat = isDirectory ? null : await promises_1.default.stat(entryPath).catch(() => null);
|
|
97
|
+
const contentSize = isDirectory ? undefined : await getContentSize(entryPath, ext);
|
|
98
|
+
result.push({
|
|
99
|
+
name: entry.name,
|
|
100
|
+
path: entryPath,
|
|
101
|
+
isDirectory,
|
|
102
|
+
ext,
|
|
103
|
+
size: stat?.size,
|
|
104
|
+
contentSize,
|
|
105
|
+
});
|
|
106
|
+
}));
|
|
107
|
+
// Sort: directories first, then files, both alphabetically
|
|
108
|
+
result.sort((a, b) => {
|
|
109
|
+
if (a.isDirectory && !b.isDirectory)
|
|
110
|
+
return -1;
|
|
111
|
+
if (!a.isDirectory && b.isDirectory)
|
|
112
|
+
return 1;
|
|
113
|
+
return a.name.localeCompare(b.name);
|
|
114
|
+
});
|
|
115
|
+
return result;
|
|
116
|
+
}
|
|
117
|
+
async readFile(filePath) {
|
|
118
|
+
const resolved = path_1.default.resolve(filePath);
|
|
119
|
+
return promises_1.default.readFile(resolved, 'utf8');
|
|
120
|
+
}
|
|
121
|
+
async writeFile(filePath, content) {
|
|
122
|
+
const resolved = path_1.default.resolve(filePath);
|
|
123
|
+
await promises_1.default.writeFile(resolved, content, 'utf8');
|
|
124
|
+
}
|
|
125
|
+
async mkdir(dirPath) {
|
|
126
|
+
const resolved = path_1.default.resolve(dirPath);
|
|
127
|
+
await promises_1.default.mkdir(resolved, { recursive: true });
|
|
128
|
+
}
|
|
129
|
+
async rename(srcPath, destPath) {
|
|
130
|
+
const src = path_1.default.resolve(srcPath);
|
|
131
|
+
const dest = path_1.default.resolve(destPath);
|
|
132
|
+
await promises_1.default.rename(src, dest);
|
|
133
|
+
}
|
|
134
|
+
async copyExternal(srcPath, destDir) {
|
|
135
|
+
const src = path_1.default.resolve(srcPath);
|
|
136
|
+
const dest = path_1.default.join(path_1.default.resolve(destDir), path_1.default.basename(src));
|
|
137
|
+
await promises_1.default.cp(src, dest, { recursive: true });
|
|
138
|
+
}
|
|
139
|
+
async delete(targetPath) {
|
|
140
|
+
const resolved = path_1.default.resolve(targetPath);
|
|
141
|
+
await promises_1.default.rm(resolved, { recursive: true, force: true });
|
|
142
|
+
}
|
|
143
|
+
setWatchRoot(rootPath) {
|
|
144
|
+
const resolved = path_1.default.resolve(rootPath);
|
|
145
|
+
if (this.watchRoot === resolved)
|
|
146
|
+
return;
|
|
147
|
+
this.watcher?.close();
|
|
148
|
+
this.watchRoot = resolved;
|
|
149
|
+
this.watcher = chokidar_1.default.watch(resolved, {
|
|
150
|
+
ignoreInitial: true,
|
|
151
|
+
ignored: [
|
|
152
|
+
/(^|[\/\\])\../, // hidden files
|
|
153
|
+
/node_modules/,
|
|
154
|
+
/\.git/,
|
|
155
|
+
/dist/,
|
|
156
|
+
/build/,
|
|
157
|
+
],
|
|
158
|
+
depth: 5,
|
|
159
|
+
awaitWriteFinish: {
|
|
160
|
+
stabilityThreshold: 100,
|
|
161
|
+
pollInterval: 100,
|
|
162
|
+
},
|
|
163
|
+
});
|
|
164
|
+
const emit = (type, filePath) => {
|
|
165
|
+
if (!this.win.isDestroyed()) {
|
|
166
|
+
this.win.webContents.send('fs:watch', { type, path: filePath });
|
|
167
|
+
}
|
|
168
|
+
};
|
|
169
|
+
this.watcher
|
|
170
|
+
.on('add', (p) => emit('add', p))
|
|
171
|
+
.on('addDir', (p) => emit('addDir', p))
|
|
172
|
+
.on('change', (p) => emit('change', p))
|
|
173
|
+
.on('unlink', (p) => emit('unlink', p))
|
|
174
|
+
.on('unlinkDir', (p) => emit('unlinkDir', p));
|
|
175
|
+
}
|
|
176
|
+
close() {
|
|
177
|
+
this.watcher?.close();
|
|
178
|
+
this.watcher = null;
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
exports.FsWatcher = FsWatcher;
|
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
import { BrowserWindow } from 'electron'
|
|
2
|
+
import fs from 'fs/promises'
|
|
3
|
+
import path from 'path'
|
|
4
|
+
import chokidar, { FSWatcher } from 'chokidar'
|
|
5
|
+
// Polyfill DOMMatrix for pdfjs-dist (pdf-parse dep) in Node.js/Electron main process
|
|
6
|
+
if (typeof globalThis.DOMMatrix === 'undefined') {
|
|
7
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
8
|
+
;(globalThis as any).DOMMatrix = class DOMMatrix {
|
|
9
|
+
a = 1; b = 0; c = 0; d = 1; e = 0; f = 0
|
|
10
|
+
m11 = 1; m12 = 0; m13 = 0; m14 = 0
|
|
11
|
+
m21 = 0; m22 = 1; m23 = 0; m24 = 0
|
|
12
|
+
m31 = 0; m32 = 0; m33 = 1; m34 = 0
|
|
13
|
+
m41 = 0; m42 = 0; m43 = 0; m44 = 1
|
|
14
|
+
is2D = true; isIdentity = true
|
|
15
|
+
constructor(_init?: number[] | string) {}
|
|
16
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
17
|
+
static fromFloat32Array() { return new (globalThis as any).DOMMatrix() }
|
|
18
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
19
|
+
static fromFloat64Array() { return new (globalThis as any).DOMMatrix() }
|
|
20
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
21
|
+
static fromMatrix() { return new (globalThis as any).DOMMatrix() }
|
|
22
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
23
|
+
multiply() { return new (globalThis as any).DOMMatrix() }
|
|
24
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
25
|
+
translate() { return new (globalThis as any).DOMMatrix() }
|
|
26
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
27
|
+
scale() { return new (globalThis as any).DOMMatrix() }
|
|
28
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
29
|
+
rotate() { return new (globalThis as any).DOMMatrix() }
|
|
30
|
+
toFloat32Array() { return new Float32Array(16) }
|
|
31
|
+
toFloat64Array() { return new Float64Array(16) }
|
|
32
|
+
toString() { return 'matrix(1, 0, 0, 1, 0, 0)' }
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
36
|
+
const pdfParse = require('pdf-parse') as (buf: Buffer) => Promise<{ text: string }>
|
|
37
|
+
import mammoth from 'mammoth'
|
|
38
|
+
|
|
39
|
+
interface FsEntry {
|
|
40
|
+
name: string
|
|
41
|
+
path: string
|
|
42
|
+
isDirectory: boolean
|
|
43
|
+
ext: string
|
|
44
|
+
size?: number
|
|
45
|
+
contentSize?: number
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
async function getContentSize(entryPath: string, ext: string): Promise<number | undefined> {
|
|
49
|
+
try {
|
|
50
|
+
if (ext === 'pdf') {
|
|
51
|
+
const buffer = await fs.readFile(entryPath)
|
|
52
|
+
const result = await pdfParse(buffer)
|
|
53
|
+
return result.text.length
|
|
54
|
+
}
|
|
55
|
+
if (ext === 'docx') {
|
|
56
|
+
const buffer = await fs.readFile(entryPath)
|
|
57
|
+
const result = await mammoth.extractRawText({ buffer })
|
|
58
|
+
return result.value.length
|
|
59
|
+
}
|
|
60
|
+
} catch {
|
|
61
|
+
// fallback: no contentSize, frontend will use size
|
|
62
|
+
}
|
|
63
|
+
return undefined
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
export class FsWatcher {
|
|
67
|
+
private win: BrowserWindow
|
|
68
|
+
private watcher: FSWatcher | null = null
|
|
69
|
+
private watchRoot: string | null = null
|
|
70
|
+
|
|
71
|
+
constructor(win: BrowserWindow) {
|
|
72
|
+
this.win = win
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
async readDir(dirPath: string): Promise<FsEntry[]> {
|
|
76
|
+
// Validate path to prevent traversal
|
|
77
|
+
const resolved = path.resolve(dirPath)
|
|
78
|
+
|
|
79
|
+
const entries = await fs.readdir(resolved, { withFileTypes: true })
|
|
80
|
+
const result: FsEntry[] = []
|
|
81
|
+
|
|
82
|
+
await Promise.all(entries.map(async (entry) => {
|
|
83
|
+
const entryPath = path.join(resolved, entry.name)
|
|
84
|
+
const isDirectory = entry.isDirectory()
|
|
85
|
+
const ext = isDirectory ? '' : path.extname(entry.name).slice(1).toLowerCase()
|
|
86
|
+
const stat = isDirectory ? null : await fs.stat(entryPath).catch(() => null)
|
|
87
|
+
const contentSize = isDirectory ? undefined : await getContentSize(entryPath, ext)
|
|
88
|
+
|
|
89
|
+
result.push({
|
|
90
|
+
name: entry.name,
|
|
91
|
+
path: entryPath,
|
|
92
|
+
isDirectory,
|
|
93
|
+
ext,
|
|
94
|
+
size: stat?.size,
|
|
95
|
+
contentSize,
|
|
96
|
+
})
|
|
97
|
+
}))
|
|
98
|
+
|
|
99
|
+
// Sort: directories first, then files, both alphabetically
|
|
100
|
+
result.sort((a, b) => {
|
|
101
|
+
if (a.isDirectory && !b.isDirectory) return -1
|
|
102
|
+
if (!a.isDirectory && b.isDirectory) return 1
|
|
103
|
+
return a.name.localeCompare(b.name)
|
|
104
|
+
})
|
|
105
|
+
|
|
106
|
+
return result
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
async readFile(filePath: string): Promise<string> {
|
|
110
|
+
const resolved = path.resolve(filePath)
|
|
111
|
+
return fs.readFile(resolved, 'utf8')
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
async writeFile(filePath: string, content: string): Promise<void> {
|
|
115
|
+
const resolved = path.resolve(filePath)
|
|
116
|
+
await fs.writeFile(resolved, content, 'utf8')
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
async mkdir(dirPath: string): Promise<void> {
|
|
120
|
+
const resolved = path.resolve(dirPath)
|
|
121
|
+
await fs.mkdir(resolved, { recursive: true })
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
async rename(srcPath: string, destPath: string): Promise<void> {
|
|
125
|
+
const src = path.resolve(srcPath)
|
|
126
|
+
const dest = path.resolve(destPath)
|
|
127
|
+
await fs.rename(src, dest)
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
async copyExternal(srcPath: string, destDir: string): Promise<void> {
|
|
131
|
+
const src = path.resolve(srcPath)
|
|
132
|
+
const dest = path.join(path.resolve(destDir), path.basename(src))
|
|
133
|
+
await fs.cp(src, dest, { recursive: true })
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
async delete(targetPath: string): Promise<void> {
|
|
137
|
+
const resolved = path.resolve(targetPath)
|
|
138
|
+
await fs.rm(resolved, { recursive: true, force: true })
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
setWatchRoot(rootPath: string): void {
|
|
142
|
+
const resolved = path.resolve(rootPath)
|
|
143
|
+
|
|
144
|
+
if (this.watchRoot === resolved) return
|
|
145
|
+
|
|
146
|
+
this.watcher?.close()
|
|
147
|
+
this.watchRoot = resolved
|
|
148
|
+
|
|
149
|
+
this.watcher = chokidar.watch(resolved, {
|
|
150
|
+
ignoreInitial: true,
|
|
151
|
+
ignored: [
|
|
152
|
+
/(^|[\/\\])\../, // hidden files
|
|
153
|
+
/node_modules/,
|
|
154
|
+
/\.git/,
|
|
155
|
+
/dist/,
|
|
156
|
+
/build/,
|
|
157
|
+
],
|
|
158
|
+
depth: 5,
|
|
159
|
+
awaitWriteFinish: {
|
|
160
|
+
stabilityThreshold: 100,
|
|
161
|
+
pollInterval: 100,
|
|
162
|
+
},
|
|
163
|
+
})
|
|
164
|
+
|
|
165
|
+
const emit = (type: string, filePath: string) => {
|
|
166
|
+
if (!this.win.isDestroyed()) {
|
|
167
|
+
this.win.webContents.send('fs:watch', { type, path: filePath })
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
this.watcher
|
|
172
|
+
.on('add', (p) => emit('add', p))
|
|
173
|
+
.on('addDir', (p) => emit('addDir', p))
|
|
174
|
+
.on('change', (p) => emit('change', p))
|
|
175
|
+
.on('unlink', (p) => emit('unlink', p))
|
|
176
|
+
.on('unlinkDir', (p) => emit('unlinkDir', p))
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
close(): void {
|
|
180
|
+
this.watcher?.close()
|
|
181
|
+
this.watcher = null
|
|
182
|
+
}
|
|
183
|
+
}
|
package/electron/main.js
ADDED
|
@@ -0,0 +1,245 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
36
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
37
|
+
};
|
|
38
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
39
|
+
const electron_1 = require("electron");
|
|
40
|
+
const path_1 = __importDefault(require("path"));
|
|
41
|
+
const window_state_1 = require("./window-state");
|
|
42
|
+
const pty_manager_1 = require("./pty-manager");
|
|
43
|
+
const fs_watcher_1 = require("./fs-watcher");
|
|
44
|
+
const versions_manager_1 = require("./versions-manager");
|
|
45
|
+
// Must be called before app.ready
|
|
46
|
+
electron_1.protocol.registerSchemesAsPrivileged([
|
|
47
|
+
{ scheme: 'localfile', privileges: { secure: true, supportFetchAPI: true, stream: true } },
|
|
48
|
+
]);
|
|
49
|
+
const isDev = process.env.NODE_ENV === 'development' || !electron_1.app.isPackaged;
|
|
50
|
+
let mainWindow = null;
|
|
51
|
+
let ptyManager;
|
|
52
|
+
let fsWatcher;
|
|
53
|
+
let versionsManager;
|
|
54
|
+
function semverGt(a, b) {
|
|
55
|
+
const pa = a.split('.').map(Number);
|
|
56
|
+
const pb = b.split('.').map(Number);
|
|
57
|
+
for (let i = 0; i < 3; i++) {
|
|
58
|
+
if ((pa[i] ?? 0) > (pb[i] ?? 0))
|
|
59
|
+
return true;
|
|
60
|
+
if ((pa[i] ?? 0) < (pb[i] ?? 0))
|
|
61
|
+
return false;
|
|
62
|
+
}
|
|
63
|
+
return false;
|
|
64
|
+
}
|
|
65
|
+
function createWindow() {
|
|
66
|
+
electron_1.protocol.handle('localfile', (request) => {
|
|
67
|
+
const filePath = new URL(request.url).pathname;
|
|
68
|
+
return electron_1.net.fetch('file://' + filePath);
|
|
69
|
+
});
|
|
70
|
+
const windowState = new window_state_1.WindowState();
|
|
71
|
+
const bounds = windowState.get();
|
|
72
|
+
mainWindow = new electron_1.BrowserWindow({
|
|
73
|
+
x: bounds.x,
|
|
74
|
+
y: bounds.y,
|
|
75
|
+
width: bounds.width,
|
|
76
|
+
height: bounds.height,
|
|
77
|
+
minWidth: 640,
|
|
78
|
+
minHeight: 400,
|
|
79
|
+
frame: false,
|
|
80
|
+
titleBarStyle: 'hidden',
|
|
81
|
+
titleBarOverlay: false,
|
|
82
|
+
trafficLightPosition: { x: 12, y: 9 },
|
|
83
|
+
backgroundColor: '#090909',
|
|
84
|
+
show: false,
|
|
85
|
+
webPreferences: {
|
|
86
|
+
preload: path_1.default.join(__dirname, 'preload.js'),
|
|
87
|
+
contextIsolation: true,
|
|
88
|
+
nodeIntegration: false,
|
|
89
|
+
sandbox: false,
|
|
90
|
+
spellcheck: false,
|
|
91
|
+
backgroundThrottling: false,
|
|
92
|
+
},
|
|
93
|
+
});
|
|
94
|
+
ptyManager = new pty_manager_1.PtyManager(mainWindow);
|
|
95
|
+
fsWatcher = new fs_watcher_1.FsWatcher(mainWindow);
|
|
96
|
+
versionsManager = new versions_manager_1.VersionsManager();
|
|
97
|
+
if (isDev) {
|
|
98
|
+
mainWindow.loadURL('http://localhost:5173');
|
|
99
|
+
mainWindow.webContents.openDevTools();
|
|
100
|
+
}
|
|
101
|
+
else {
|
|
102
|
+
mainWindow.loadFile(path_1.default.join(__dirname, '../build/index.html'));
|
|
103
|
+
}
|
|
104
|
+
mainWindow.once('ready-to-show', () => {
|
|
105
|
+
mainWindow?.show();
|
|
106
|
+
});
|
|
107
|
+
mainWindow.on('resize', () => {
|
|
108
|
+
windowState.save(mainWindow);
|
|
109
|
+
});
|
|
110
|
+
mainWindow.on('move', () => {
|
|
111
|
+
windowState.save(mainWindow);
|
|
112
|
+
});
|
|
113
|
+
mainWindow.on('closed', () => {
|
|
114
|
+
mainWindow = null;
|
|
115
|
+
});
|
|
116
|
+
setupIpcHandlers();
|
|
117
|
+
}
|
|
118
|
+
function setupIpcHandlers() {
|
|
119
|
+
// PTY handlers
|
|
120
|
+
electron_1.ipcMain.handle('pty:create', async (_, opts) => {
|
|
121
|
+
return ptyManager.create(opts);
|
|
122
|
+
});
|
|
123
|
+
electron_1.ipcMain.on('pty:write', (_, sessionId, data) => {
|
|
124
|
+
ptyManager.write(sessionId, data);
|
|
125
|
+
});
|
|
126
|
+
electron_1.ipcMain.on('pty:resize', (_, sessionId, cols, rows) => {
|
|
127
|
+
ptyManager.resize(sessionId, cols, rows);
|
|
128
|
+
});
|
|
129
|
+
electron_1.ipcMain.handle('pty:kill', async (_, sessionId) => {
|
|
130
|
+
return ptyManager.kill(sessionId);
|
|
131
|
+
});
|
|
132
|
+
// FS handlers
|
|
133
|
+
electron_1.ipcMain.handle('fs:openFolder', async () => {
|
|
134
|
+
const result = await electron_1.dialog.showOpenDialog(mainWindow, {
|
|
135
|
+
properties: ['openDirectory'],
|
|
136
|
+
});
|
|
137
|
+
if (result.canceled || result.filePaths.length === 0)
|
|
138
|
+
return null;
|
|
139
|
+
return result.filePaths[0];
|
|
140
|
+
});
|
|
141
|
+
electron_1.ipcMain.handle('fs:readDir', async (_, dirPath) => {
|
|
142
|
+
return fsWatcher.readDir(dirPath);
|
|
143
|
+
});
|
|
144
|
+
electron_1.ipcMain.handle('fs:readFile', async (_, filePath) => {
|
|
145
|
+
return fsWatcher.readFile(filePath);
|
|
146
|
+
});
|
|
147
|
+
electron_1.ipcMain.handle('fs:writeFile', async (_, filePath, content) => {
|
|
148
|
+
return fsWatcher.writeFile(filePath, content);
|
|
149
|
+
});
|
|
150
|
+
electron_1.ipcMain.handle('fs:mkdir', async (_, dirPath) => {
|
|
151
|
+
return fsWatcher.mkdir(dirPath);
|
|
152
|
+
});
|
|
153
|
+
electron_1.ipcMain.handle('fs:rename', async (_, src, dest) => {
|
|
154
|
+
return fsWatcher.rename(src, dest);
|
|
155
|
+
});
|
|
156
|
+
electron_1.ipcMain.handle('fs:copyExternal', async (_, srcPath, destDir) => {
|
|
157
|
+
return fsWatcher.copyExternal(srcPath, destDir);
|
|
158
|
+
});
|
|
159
|
+
electron_1.ipcMain.handle('fs:delete', async (_, targetPath) => {
|
|
160
|
+
return fsWatcher.delete(targetPath);
|
|
161
|
+
});
|
|
162
|
+
electron_1.ipcMain.handle('fs:writeBinaryFile', async (_, filePath, data) => {
|
|
163
|
+
const { promises: fs } = await Promise.resolve().then(() => __importStar(require('fs')));
|
|
164
|
+
await fs.writeFile(filePath, Buffer.from(data));
|
|
165
|
+
});
|
|
166
|
+
electron_1.ipcMain.on('fs:setWatchRoot', (_, rootPath) => {
|
|
167
|
+
fsWatcher.setWatchRoot(rootPath);
|
|
168
|
+
});
|
|
169
|
+
// App handlers
|
|
170
|
+
electron_1.ipcMain.handle('app:getVersion', async () => {
|
|
171
|
+
return electron_1.app.getVersion();
|
|
172
|
+
});
|
|
173
|
+
electron_1.ipcMain.handle('app:getGitBranch', async (_, cwd) => {
|
|
174
|
+
const { execSync } = await Promise.resolve().then(() => __importStar(require('child_process')));
|
|
175
|
+
try {
|
|
176
|
+
const branch = execSync('git rev-parse --abbrev-ref HEAD', { cwd, encoding: 'utf8' }).trim();
|
|
177
|
+
return branch;
|
|
178
|
+
}
|
|
179
|
+
catch {
|
|
180
|
+
return null;
|
|
181
|
+
}
|
|
182
|
+
});
|
|
183
|
+
electron_1.ipcMain.handle('app:checkForUpdates', async () => {
|
|
184
|
+
try {
|
|
185
|
+
const res = await electron_1.net.fetch('https://api.github.com/repos/vbfs/terminalOS/releases/latest', {
|
|
186
|
+
headers: { 'User-Agent': 'aiTerm-updater' },
|
|
187
|
+
});
|
|
188
|
+
if (!res.ok)
|
|
189
|
+
return null;
|
|
190
|
+
const data = await res.json();
|
|
191
|
+
const latest = data.tag_name.replace(/^v/, '');
|
|
192
|
+
const current = electron_1.app.getVersion();
|
|
193
|
+
return semverGt(latest, current) ? { version: latest, url: data.html_url } : null;
|
|
194
|
+
}
|
|
195
|
+
catch {
|
|
196
|
+
return null;
|
|
197
|
+
}
|
|
198
|
+
});
|
|
199
|
+
// Window controls
|
|
200
|
+
electron_1.ipcMain.on('window:minimize', () => mainWindow?.minimize());
|
|
201
|
+
electron_1.ipcMain.on('window:maximize', () => {
|
|
202
|
+
if (mainWindow?.isMaximized()) {
|
|
203
|
+
mainWindow.unmaximize();
|
|
204
|
+
}
|
|
205
|
+
else {
|
|
206
|
+
mainWindow?.maximize();
|
|
207
|
+
}
|
|
208
|
+
});
|
|
209
|
+
electron_1.ipcMain.on('window:close', () => mainWindow?.close());
|
|
210
|
+
// Shell operations
|
|
211
|
+
electron_1.ipcMain.on('shell:openPath', (_, filePath) => {
|
|
212
|
+
electron_1.shell.showItemInFolder(filePath);
|
|
213
|
+
});
|
|
214
|
+
electron_1.ipcMain.on('shell:openInFinder', (_, folderPath) => {
|
|
215
|
+
electron_1.shell.openPath(folderPath);
|
|
216
|
+
});
|
|
217
|
+
electron_1.ipcMain.on('shell:openExternal', (_, url) => {
|
|
218
|
+
electron_1.shell.openExternal(url);
|
|
219
|
+
});
|
|
220
|
+
// Version history handlers
|
|
221
|
+
electron_1.ipcMain.handle('fs:versions:save', async (_, filePath, content) => {
|
|
222
|
+
return versionsManager.saveVersion(filePath, content);
|
|
223
|
+
});
|
|
224
|
+
electron_1.ipcMain.handle('fs:versions:list', async (_, filePath) => {
|
|
225
|
+
return versionsManager.listVersions(filePath);
|
|
226
|
+
});
|
|
227
|
+
electron_1.ipcMain.handle('fs:versions:get', async (_, filePath, versionId) => {
|
|
228
|
+
return versionsManager.getVersion(filePath, versionId);
|
|
229
|
+
});
|
|
230
|
+
}
|
|
231
|
+
electron_1.app.whenReady().then(createWindow);
|
|
232
|
+
electron_1.app.on('before-quit', () => {
|
|
233
|
+
ptyManager?.killAll();
|
|
234
|
+
fsWatcher?.close();
|
|
235
|
+
});
|
|
236
|
+
electron_1.app.on('window-all-closed', () => {
|
|
237
|
+
if (process.platform !== 'darwin') {
|
|
238
|
+
electron_1.app.quit();
|
|
239
|
+
}
|
|
240
|
+
});
|
|
241
|
+
electron_1.app.on('activate', () => {
|
|
242
|
+
if (electron_1.BrowserWindow.getAllWindows().length === 0) {
|
|
243
|
+
createWindow();
|
|
244
|
+
}
|
|
245
|
+
});
|