Compare commits

...

8 Commits

22 changed files with 195 additions and 278 deletions

View File

@@ -43,12 +43,11 @@
"styles":[
"node_modules/bootstrap/scss/bootstrap.scss",
"node_modules/bootstrap-icons/font/bootstrap-icons.css",
"src/assets/css/overrides.css",
"src/assets/css/styles.css",
"src/assets/css/overrides.css"
"src/assets/css/ace-overrides.css"
],
"scripts":[
"src/libs/showdown.min.js"
],
"optimization": true
},

View File

@@ -54,6 +54,20 @@ const createWindow = (startType = "build", debug = false, args = []) => {
window.webContents.send('load-files', args);
});
window.webContents.on('did-finish-load', () => {
const cssFiles = [
path.join(newtonFs.CONFIG_PATH, 'override.css')
];
const jsFiles = [
// path.join(newtonFs.CONFIG_PATH, 'scripts', 'script1.js'),
// path.join(newtonFs.CONFIG_PATH, 'scripts', 'script2.js')
];
cssFiles.forEach(cssFile => newtonFs.readAndInjectCSS(window, cssFile));
jsFiles.forEach(jsFile => newtonFs.readAndInjectJS(window, jsFile));
});
menu.load(window);
systemTray.load(menu.menuStruct);
terminal.load(window);

View File

@@ -185,10 +185,33 @@ const closeFile = (fpath) => {
unwatchFile(fpath);
}
const readAndInjectCSS = (window, filePath) => {
fs.readFile(filePath, 'utf8', (err, data) => {
if (err) {
console.error(`Error reading CSS file: ${filePath}`, err);
return;
}
window.webContents.insertCSS(data);
});
}
const readAndInjectJS = (window, filePath) => {
fs.readFile(filePath, 'utf8', (err, data) => {
if (err) {
console.error(`Error reading JS file: ${filePath}`, err);
return;
}
window.webContents.executeJavaScript(data);
});
}
module.exports = {
newtonFs: {
CONFIG_PATH: CONFIG_PATH,
setWindow: setWindow,
chooseFolder: chooseFolder,
openFiles: openFiles,
@@ -200,6 +223,8 @@ module.exports = {
getLspConfigData: getLspConfigData,
getSettingsConfigData: getSettingsConfigData,
saveSettingsConfigData: saveSettingsConfigData,
readAndInjectJS: readAndInjectJS,
readAndInjectCSS: readAndInjectCSS,
loadFilesWatcher: loadFilesWatcher,
unwatchFile: unwatchFile,
}

View File

@@ -42,6 +42,9 @@ const loadIPCServer = (fpath) => {
console.debug("Load File(s) : ", req.body);
window.webContents.send('load-files', req.body);
window.show();
window.focus();
res.status(200).send('');
});

View File

@@ -13,14 +13,14 @@
"electron-start": "electron . --trace-warnings --start-as=build --ipc-port=4588",
"electron-pack": "ng build --base-href ./ && electron-builder --dir",
"electron-dist": "ng build --base-href ./ && electron-builder",
"electron-dist-appimage-linux": "ng build --base-href ./ && electron-builder --linux AppImage",
"electron-dist-deb-linux": "ng build --base-href ./ && electron-builder --linux deb",
"electron-dist-zip-linux": "ng build --base-href ./ && electron-builder --linux zip",
"electron-dist-deb-linux": "ng build --base-href ./ && electron-builder --linux deb",
"electron-dist-appimage-linux": "ng build --base-href ./ && electron-builder --linux AppImage",
"electron-dist-all-linux": "ng build --base-href ./ && electron-builder --linux deb zip AppImage",
"electron-dist-all": "ng build --base-href ./ && electron-builder -mwl",
"electron-concurrently": "concurrently 'ng serve' 'electron . --trace-warnings --start-as=ng-serve'",
"ng-serve": "ng serve",
"ng-build": "ng build",
"ng-build": "ng build --base-href ./",
"ng-watch-build": "ng build --watch --configuration development",
"ng-test": "ng test",
"test": "echo \"Error: no test specified\" && exit 1"
@@ -44,9 +44,11 @@
"maintainer": "ITDominator"
}
},
"postinstall": "electron-builder install-app-deps",
"overrides": {
"glob": "9.0.0",
"rimraf": "4.3.1"
},
"dependencies": {
"@xterm/xterm": "5.5.0",
"ace-diff": "3.0.3",
"ace-layout": "1.5.0",
"ace-linters": "1.8.3",
@@ -56,8 +58,6 @@
"electron-fetch": "1.9.1",
"express": "4.18.2",
"marked": "16.4.0",
"node-fetch": "3.3.2",
"node-pty": "1.0.0",
"socket.io": "4.8.1",
"uuid": "11.1.0",
"zone.js": "0.15.0"
@@ -78,7 +78,7 @@
"concurrently": "9.1.2",
"electron": "36.2.0",
"@electron/remote": "2.1.2",
"electron-builder": "26.0.12",
"electron-builder": "22.7.0",
"jasmine-core": "5.6.0",
"jimp": "1.6.0",
"karma": "6.4.0",

View File

@@ -5,7 +5,6 @@
<editors></editors>
<search-replace></search-replace>
<markdown-preview></markdown-preview>
<terminal></terminal>
<lsp-manager></lsp-manager>
</div>

View File

@@ -1,11 +1,12 @@
import { Component } from '@angular/core';
import { Component, inject } from '@angular/core';
import { WebsocketService } from './common/services/websocket.service';
import { InfoBarComponent } from './editor/info-bar/info-bar.component';
import { TabsComponent } from './editor/tabs/tabs.component';
import { EditorsComponent } from './editor/editors.component';
import { SearchReplaceComponent } from "./editor/search-replace/search-replace.component";
import { MarkdownPreviewComponent } from "./editor/markdown-preview/markdown-preview.component";
import { TerminalComponent } from "./editor/terminal/terminal.component";
import { LspManagerComponent } from "./editor/lsp-manager/lsp-manager.component";
@@ -18,7 +19,6 @@ import { LspManagerComponent } from "./editor/lsp-manager/lsp-manager.component"
EditorsComponent,
SearchReplaceComponent,
MarkdownPreviewComponent,
TerminalComponent,
LspManagerComponent,
],
templateUrl: './app.component.html',
@@ -30,6 +30,69 @@ import { LspManagerComponent } from "./editor/lsp-manager/lsp-manager.component"
export class AppComponent {
title = 'Newton';
constructor() {}
protected ws: WebsocketService = inject(WebsocketService);
constructor() {
this.checkIfNotElectronMode();
}
ngOnInit() {}
checkIfNotElectronMode() {
if (
window.electron ||
window.main ||
window.fs
) { return; }
this.setupWebsocket();
this.setupWindowBindings();
}
setupWindowBindings() {
window.electron ??= {
node: () => { return "" },
chrome: () => { return "" },
electron: () => { return "" },
};
window.main ??= {
onMenuActions: () => {},
onTerminalActions: () => {},
quit: () => {},
toggleFullScreen: () => {},
};
window.fs ??= {
getLspConfigData: () => {
return new Promise((resolve, reject) => {
resolve("{}");
});
},
getFileContents: () => {},
openFiles: () => {},
saveFile: () => {},
saveFileAs: () => {},
chooseFolder: () => {},
closeFile: () => {},
getPathForFile: () => {},
onLoadFiles: () => {},
onUpdateFilePath: () => {},
onSavedFile: () => {},
onChangedFile: () => {},
onDeletedFile: () => {},
};
}
setupWebsocket() {
// TODO: Set with dynamic address and port
this.ws.connect('ws://localhost:7272').subscribe(msg => {
console.log(msg);
console.log(window.fs);
// this.ws.send("{ 'text': 'Hello server!' }");
});
}
}

View File

@@ -42,7 +42,7 @@ export class LspManagerService {
}
private getLspConfigData(): Promise<string> {
return window.fs.getLspConfigData();
return window?.fs.getLspConfigData();
}
private parseAndReturnLSPConfigData(): {} {

View File

@@ -1,23 +0,0 @@
import { Injectable } from '@angular/core';
import { ReplaySubject, Observable } from 'rxjs';
import { ServiceMessage } from '../../../types/service-message.type';
@Injectable({
providedIn: 'root'
})
export class TerminalService {
private messageSubject: ReplaySubject<ServiceMessage> = new ReplaySubject<ServiceMessage>(1);
public sendMessage(data: ServiceMessage): void {
this.messageSubject.next(data);
}
public getMessage$(): Observable<ServiceMessage> {
return this.messageSubject.asObservable();
}
}

View File

@@ -55,7 +55,7 @@ export class FilesService {
public unset(file: NewtonFile) {
file.session.destroy();
window.fs.closeFile(file.path);
window?.fs.closeFile(file.path);
this.files.delete(file.path);
}
@@ -76,7 +76,7 @@ export class FilesService {
): Promise<NewtonFile | undefined | null> {
for (let i = 0; i < files.length; i++) {
const file = files[i];
const path = window.fs.getPathForFile(file);
const path = window?.fs.getPathForFile(file);
if (!file || !path) continue;
if ( this.files.get(path) ) continue;
@@ -101,7 +101,7 @@ export class FilesService {
file.hash = btoa(file.path);
if (loadFileContents)
data = await window.fs.getFileContents(file.path);
data = await window?.fs.getFileContents(file.path);
file.session = new EditSession(data);
file.session["id"] = path;

View File

@@ -0,0 +1,35 @@
import { Injectable } from '@angular/core';
import { webSocket, WebSocketSubject } from 'rxjs/webSocket';
import { Observable } from 'rxjs';
@Injectable({
providedIn: 'root'
})
export class WebsocketService {
private socket$!: WebSocketSubject<any>;
connect(url: string): Observable<any> {
if (!this.socket$ || this.socket$.closed) {
this.socket$ = webSocket(
{
url,
deserializer: msg => msg.data
}
);
}
return this.socket$.asObservable();
}
send(message: any) {
if (this.socket$) {
this.socket$.next(message);
}
}
close() {
if (this.socket$) {
this.socket$.complete();
}
}
}

View File

@@ -14,7 +14,6 @@ import { EditorsService } from '../../common/services/editor/editors.service';
import { FilesService } from '../../common/services/files.service';
import { SearchReplaceService } from '../../common/services/editor/search-replace/search-replace.service';
import { MarkdownPreviewService } from '../../common/services/editor/markdown-preview/markdown-preview.service';
import { TerminalService } from '../../common/services/editor/terminal/terminal.service';
import { LspManagerService } from '../../common/services/editor/lsp-manager/lsp-manager.service';
import { EditorSettings } from "../../common/configs/editor.config";
@@ -39,7 +38,6 @@ export class CodeViewBase {
protected filesService: FilesService = inject(FilesService);
protected searchReplaceService: SearchReplaceService = inject(SearchReplaceService);
protected markdownPreviewService: MarkdownPreviewService = inject(MarkdownPreviewService);
protected terminalService: TerminalService = inject(TerminalService);
protected lspManagerService: LspManagerService = inject(LspManagerService);
@ViewChild('editor') editorElm!: ElementRef;
@@ -134,12 +132,6 @@ export class CodeViewBase {
// this.editor.execCommand("replace");
}
public terminalPopup() {
let message = new ServiceMessage();
message.action = "toggle-terminal";
this.terminalService.sendMessage(message);
}
public showFilesList() {
let paths = this.filesService.getAllPaths();
let stubPaths = [];
@@ -195,7 +187,7 @@ export class CodeViewBase {
}
public toggleFullScreen() {
window.main.toggleFullScreen();
window?.main.toggleFullScreen();
}
public setAsReadOnly() {
@@ -308,7 +300,7 @@ export class CodeViewBase {
startDir = pathParts.join( '/' );
}
window.fs.openFiles(startDir);
window?.fs.openFiles(startDir);
}
protected saveFile() {
@@ -320,18 +312,18 @@ export class CodeViewBase {
}
const text = this.activeFile.session.getValue();
window.fs.saveFile(this.activeFile.path, text);
window?.fs.saveFile(this.activeFile.path, text);
this.activeFile.session.getUndoManager().markClean();
}
protected saveFileAs() {
window.fs.saveFileAs().then((path: string) => {
window?.fs.saveFileAs().then((path: string) => {
if (!path) return;
let file: NewtonFile = new File([""], path, {});
const text = this.editor.session.getValue();
window.fs.saveFile(path, text);
window?.fs.saveFile(path, text);
this.filesService.addFile(
path,
file,
@@ -372,6 +364,6 @@ export class CodeViewBase {
}
private quit() {
window.main.quit();
window?.main.quit();
}
}

View File

@@ -142,7 +142,7 @@ export class CodeViewComponent extends CodeViewBase {
private loadNewtonEventBindings(): void {
// Note: https://ajaxorg.github.io/ace-api-docs/interfaces/ace.Ace.EditorEvents.html
this.editor.on("focus", (e) => {
this.editor.on("focus", (event) => {
let message = new ServiceMessage();
message.action = "set-active-editor";
message.editorUUID = this.uuid;
@@ -150,7 +150,6 @@ export class CodeViewComponent extends CodeViewBase {
this.editorsService.sendMessage(message);
this.searchReplaceService.sendMessage(message);
this.terminalService.sendMessage(message);
message = new ServiceMessage();
message.action = "set-active-editor";

View File

@@ -76,7 +76,7 @@ export class EditorsComponent {
}
private loadMainSubscribers() {
window.main.onMenuActions(async (action: string) => {
window?.main.onMenuActions(async (action: string) => {
let editorComponent = this.editorsService.getActiveEditorComponent();
let editor = editorComponent.editor;
@@ -112,7 +112,7 @@ export class EditorsComponent {
case "show-about":
break;
case "quit":
window.main.quit();
window?.main.quit();
break;
default:
editor.execCommand(action);
@@ -120,7 +120,7 @@ export class EditorsComponent {
}
});
window.fs.onLoadFiles(async (paths: []) => {
window?.fs.onLoadFiles(async (paths: []) => {
for (let i = 0; i < paths.length; i++) {
let file = new File([], "") as NewtonFile;
@@ -135,7 +135,7 @@ export class EditorsComponent {
this.editorsService.setSession(file);
});
window.fs.onChangedFile(async (path: string, data: string) => {
window?.fs.onChangedFile(async (path: string, data: string) => {
let file = this.filesService.get(path);
file.session.setValue(data);
@@ -147,7 +147,7 @@ export class EditorsComponent {
this.tabsService.sendMessage(message);
});
window.fs.onDeletedFile(async (path: string) => {
window?.fs.onDeletedFile(async (path: string) => {
let message = new ServiceMessage();
message.action = "file-deleted";
message.filePath = path;
@@ -156,7 +156,7 @@ export class EditorsComponent {
this.filesService.sendMessage(message);
});
window.fs.onSavedFile(async (path: string) => {
window?.fs.onSavedFile(async (path: string) => {
let message = new ServiceMessage();
message.action = "file-saved";
message.filePath = path;
@@ -164,7 +164,7 @@ export class EditorsComponent {
this.tabsService.sendMessage(message);
});
window.fs.onUpdateFilePath(async (path: string) => {
window?.fs.onUpdateFilePath(async (path: string) => {
console.log("TODO (onUpdateFilePath) :", path);
// this.tabsService.sendMessage(message);
// this.filesService.sendMessage(message);

View File

@@ -134,7 +134,7 @@ export class LspManagerComponent {
}
public setWorkspaceFolder() {
window.fs.chooseFolder().then((folder: string) => {
window?.fs.chooseFolder().then((folder: string) => {
if (!folder) return;
this.lspManagerService.workspaceFolder = folder;

View File

@@ -1,6 +0,0 @@
<div class="col">
<div class="row">
<div class="terminal-container" #terminalElm>
</div>
</div>
</div>

View File

@@ -1,137 +0,0 @@
import {
Component,
DestroyRef,
ElementRef,
HostBinding,
ViewChild,
inject
} from '@angular/core';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
// import { Terminal } from 'xterm';
import { Terminal } from '@xterm/xterm';
// import { FitAddon } from 'xterm-addon-fit';
import { TerminalService } from '../../common/services/editor/terminal/terminal.service';
import { ServiceMessage } from '../../common/types/service-message.type';
@Component({
selector: 'terminal',
standalone: true,
imports: [
],
templateUrl: './terminal.component.html',
styleUrl: './terminal.component.css',
host: {
'class': 'row terminal',
"(keyup)": "globalTerminalKeyHandler($event)"
}
})
export class TerminalComponent {
readonly #destroyRef: DestroyRef = inject(DestroyRef);
private terminalService: TerminalService = inject(TerminalService);
private terminal: Terminal = new Terminal();
@HostBinding("class.hidden") isHidden: boolean = true;
@ViewChild("terminalElm") terminalElm!: ElementRef;
private editor!: any;
constructor() {
this.loadSubscribers();
this.loadMainSubscribers();
}
private ngAfterViewInit(): void {
this.loadTerminal();
}
// Note: https://stackoverflow.com/questions/63390143/how-do-i-connect-xterm-jsin-electron-to-a-real-working-command-prompt
private loadTerminal() {
// const fitAddon = new FitAddon();
// this.terminal.loadAddon(fitAddon);
this.terminal.open(this.terminalElm.nativeElement);
this.terminal.onData(e => {
console.log(e);
// ipcRenderer.send("terminal-into", e);
// window.main.quit();
} );
// ipcRenderer.on('terminal-actions', (event, data) => {
// this.terminal.write(data);
// })
// Make the terminal's size and geometry fit the size of #terminal-container
// fitAddon.fit();
}
private loadSubscribers() {
this.terminalService.getMessage$().pipe(
takeUntilDestroyed(this.#destroyRef)
).subscribe((message: ServiceMessage) => {
switch ( message.action ) {
case "toggle-terminal":
this.toggleTerminal(message);
break;
case "set-active-editor":
this.setActiveEditor(message);
break;
default:
break;
}
});
}
private loadMainSubscribers() {
window.main.onTerminalActions(async (action: string) => {
this.terminal.write(action);
// switch ( action ) {
// case "terminal-actions":
// break;
// default:
// break;
// }
});
}
private toggleTerminal(message: ServiceMessage) {
this.isHidden = !this.isHidden;
if (this.isHidden) {
this.editor.focus();
return;
}
// setTimeout(() => {
// }, 200);
}
private setActiveEditor(message: ServiceMessage) {
if (this.editor == message.rawData) return;
this.editor = message.rawData;
if (this.isHidden) return;
}
public hideTerminal() {
this.isHidden = true;
this.editor.focus();
}
public globalTerminalKeyHandler(event: any) {
if (event.ctrlKey && event.key === ".") {
this.hideTerminal();
}
}
}

File diff suppressed because one or more lines are too long

View File

@@ -11,33 +11,35 @@
import 'zone.js'; // Included with Angular CLI.
// Note: Is set to 'any' b/c of desire to set 'render'
// side if running outside of electron mode.
declare global {
interface Window {
electron: {
node: () => Promise<string>,
chrome: () => Promise<string>,
electron: () => Promise<string>,
node: any,
chrome: any,
electron: any,
},
main: {
onMenuActions: (arg0: any) => Promise<string>,
onTerminalActions: (arg0: any) => Promise<string>,
onMenuActions: any,
onTerminalActions: any,
quit: any,
toggleFullScreen: any,
},
fs: {
getLspConfigData: () => Promise<string>,
getFileContents: (arg0: any) => Promise<string>,
openFiles: (arg0) => Promise<string>,
saveFile: (arg0: any, arg1: any) => Promise<string>,
saveFileAs: () => Promise<string>,
chooseFolder: () => Promise<string>,
closeFile: (arg0: any) => Promise<string>,
getLspConfigData: any,
getFileContents: any,
openFiles: any,
saveFile: any,
saveFileAs: any,
chooseFolder: any,
closeFile: any,
getPathForFile: any,
onLoadFiles: (arg0: any) => Promise<string>,
onUpdateFilePath: (arg0: any) => Promise<string>,
onSavedFile: (arg0: any) => Promise<string>,
onChangedFile: (arg0: any) => Promise<string>,
onDeletedFile: (arg0: any) => Promise<string>,
onLoadFiles: any,
onUpdateFilePath: any,
onSavedFile: any,
onChangedFile: any,
onDeletedFile: any,
}
}
}

1
src/typings.d.ts vendored
View File

@@ -1 +0,0 @@
// declare var showdown: any;

View File

@@ -16,50 +16,6 @@
"declaration": false,
"skipLibCheck": true,
"strict": false,
"forceConsistentCasingInFileNames": true,
},
"includes": [
"src/typings.d.ts"
]
"forceConsistentCasingInFileNames": true
}
}
/*
{
"compileOnSave": false,
"compilerOptions": {
"outDir": "./build/app",
"strict": true,
"noImplicitOverride": true,
"noPropertyAccessFromIndexSignature": true,
"noImplicitReturns": true,
"noFallthroughCasesInSwitch": true,
"skipLibCheck": true,
"esModuleInterop": true,
"sourceMap": true,
"declaration": false,
"experimentalDecorators": true,
"moduleResolution": "bundler",
"importHelpers": true,
"target": "ES2022",
"module": "ES2022",
"useDefineForClassFields": false,
"lib": [
"ES2022",
"dom"
]
},
"angularCompilerOptions": {
"enableI18nLegacyMessageIdFormat": false,
"strictInjectionParameters": true,
"strictInputAccessModifiers": true,
"strictTemplates": true
},
"includes": [
"src/typings.d.ts"
]
}
*/