Compare commits

...

10 Commits

24 changed files with 285 additions and 321 deletions

View File

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

View File

@@ -54,6 +54,20 @@ const createWindow = (startType = "build", debug = false, args = []) => {
window.webContents.send('load-files', 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); menu.load(window);
systemTray.load(menu.menuStruct); systemTray.load(menu.menuStruct);
terminal.load(window); terminal.load(window);

View File

@@ -185,10 +185,33 @@ const closeFile = (fpath) => {
unwatchFile(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 = { module.exports = {
newtonFs: { newtonFs: {
CONFIG_PATH: CONFIG_PATH,
setWindow: setWindow, setWindow: setWindow,
chooseFolder: chooseFolder, chooseFolder: chooseFolder,
openFiles: openFiles, openFiles: openFiles,
@@ -200,6 +223,8 @@ module.exports = {
getLspConfigData: getLspConfigData, getLspConfigData: getLspConfigData,
getSettingsConfigData: getSettingsConfigData, getSettingsConfigData: getSettingsConfigData,
saveSettingsConfigData: saveSettingsConfigData, saveSettingsConfigData: saveSettingsConfigData,
readAndInjectJS: readAndInjectJS,
readAndInjectCSS: readAndInjectCSS,
loadFilesWatcher: loadFilesWatcher, loadFilesWatcher: loadFilesWatcher,
unwatchFile: unwatchFile, unwatchFile: unwatchFile,
} }

View File

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

View File

@@ -13,14 +13,14 @@
"electron-start": "electron . --trace-warnings --start-as=build --ipc-port=4588", "electron-start": "electron . --trace-warnings --start-as=build --ipc-port=4588",
"electron-pack": "ng build --base-href ./ && electron-builder --dir", "electron-pack": "ng build --base-href ./ && electron-builder --dir",
"electron-dist": "ng build --base-href ./ && electron-builder", "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-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-linux": "ng build --base-href ./ && electron-builder --linux deb zip AppImage",
"electron-dist-all": "ng build --base-href ./ && electron-builder -mwl", "electron-dist-all": "ng build --base-href ./ && electron-builder -mwl",
"electron-concurrently": "concurrently 'ng serve' 'electron . --trace-warnings --start-as=ng-serve'", "electron-concurrently": "concurrently 'ng serve' 'electron . --trace-warnings --start-as=ng-serve'",
"ng-serve": "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-watch-build": "ng build --watch --configuration development",
"ng-test": "ng test", "ng-test": "ng test",
"test": "echo \"Error: no test specified\" && exit 1" "test": "echo \"Error: no test specified\" && exit 1"
@@ -30,10 +30,7 @@
"icon": "./icos/", "icon": "./icos/",
"files": [ "files": [
"newton/", "newton/",
"build/", "build/"
"!node_modules/ace-builds/",
"!node_modules/web-streams-polyfill/",
"!node_modules/@angular/"
], ],
"mac": { "mac": {
"category": "public.app-category.developer-tools" "category": "public.app-category.developer-tools"
@@ -47,15 +44,11 @@
"maintainer": "ITDominator" "maintainer": "ITDominator"
} }
}, },
"postinstall": "electron-builder install-app-deps", "overrides": {
"glob": "9.0.0",
"rimraf": "4.3.1"
},
"dependencies": { "dependencies": {
"@angular/cdk": "19.2.0",
"@angular/common": "19.2.0",
"@angular/core": "19.2.0",
"@angular/forms": "19.2.0",
"@angular/platform-browser": "19.2.0",
"@xterm/xterm": "5.5.0",
"ace-builds": "1.43.0",
"ace-diff": "3.0.3", "ace-diff": "3.0.3",
"ace-layout": "1.5.0", "ace-layout": "1.5.0",
"ace-linters": "1.8.3", "ace-linters": "1.8.3",
@@ -65,24 +58,27 @@
"electron-fetch": "1.9.1", "electron-fetch": "1.9.1",
"express": "4.18.2", "express": "4.18.2",
"marked": "16.4.0", "marked": "16.4.0",
"node-fetch": "3.3.2",
"node-pty": "^1.0.0",
"rxjs": "7.8.0",
"socket.io": "4.8.1", "socket.io": "4.8.1",
"uuid": "11.1.0", "uuid": "11.1.0",
"zone.js": "0.15.0" "zone.js": "0.15.0"
}, },
"devDependencies": { "devDependencies": {
"ace-builds": "1.43.0",
"@angular-devkit/build-angular": "19.2.8", "@angular-devkit/build-angular": "19.2.8",
"@angular/cdk": "19.2.0",
"@angular/common": "19.2.0",
"@angular/core": "19.2.0",
"@angular/cli": "19.2.8", "@angular/cli": "19.2.8",
"@angular/compiler-cli": "19.2.0", "@angular/compiler-cli": "19.2.0",
"@angular/forms": "19.2.0",
"@angular/platform-browser": "19.2.0",
"@types/express": "4.17.17", "@types/express": "4.17.17",
"@types/jasmine": "5.1.0", "@types/jasmine": "5.1.0",
"@types/node": "18.18.0", "@types/node": "18.18.0",
"concurrently": "9.1.2", "concurrently": "9.1.2",
"electron": "36.2.0", "electron": "36.2.0",
"@electron/remote": "2.1.2", "@electron/remote": "2.1.2",
"electron-builder": "26.0.12", "electron-builder": "22.7.0",
"jasmine-core": "5.6.0", "jasmine-core": "5.6.0",
"jimp": "1.6.0", "jimp": "1.6.0",
"karma": "6.4.0", "karma": "6.4.0",
@@ -91,6 +87,7 @@
"karma-jasmine": "5.1.0", "karma-jasmine": "5.1.0",
"karma-jasmine-html-reporter": "2.1.0", "karma-jasmine-html-reporter": "2.1.0",
"nanoevents": "9.1.0", "nanoevents": "9.1.0",
"rxjs": "7.8.0",
"tree-sitter": "0.21.1", "tree-sitter": "0.21.1",
"tree-sitter-bash": "0.23.2", "tree-sitter-bash": "0.23.2",
"tree-sitter-c": "0.23.1", "tree-sitter-c": "0.23.1",

View File

@@ -5,7 +5,6 @@
<editors></editors> <editors></editors>
<search-replace></search-replace> <search-replace></search-replace>
<markdown-preview></markdown-preview> <markdown-preview></markdown-preview>
<terminal></terminal>
<lsp-manager></lsp-manager> <lsp-manager></lsp-manager>
</div> </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 { InfoBarComponent } from './editor/info-bar/info-bar.component';
import { TabsComponent } from './editor/tabs/tabs.component'; import { TabsComponent } from './editor/tabs/tabs.component';
import { EditorsComponent } from './editor/editors.component'; import { EditorsComponent } from './editor/editors.component';
import { SearchReplaceComponent } from "./editor/search-replace/search-replace.component"; import { SearchReplaceComponent } from "./editor/search-replace/search-replace.component";
import { MarkdownPreviewComponent } from "./editor/markdown-preview/markdown-preview.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"; import { LspManagerComponent } from "./editor/lsp-manager/lsp-manager.component";
@@ -18,7 +19,6 @@ import { LspManagerComponent } from "./editor/lsp-manager/lsp-manager.component"
EditorsComponent, EditorsComponent,
SearchReplaceComponent, SearchReplaceComponent,
MarkdownPreviewComponent, MarkdownPreviewComponent,
TerminalComponent,
LspManagerComponent, LspManagerComponent,
], ],
templateUrl: './app.component.html', templateUrl: './app.component.html',
@@ -30,6 +30,69 @@ import { LspManagerComponent } from "./editor/lsp-manager/lsp-manager.component"
export class AppComponent { export class AppComponent {
title = 'Newton'; 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> { private getLspConfigData(): Promise<string> {
return window.fs.getLspConfigData(); return window?.fs.getLspConfigData();
} }
private parseAndReturnLSPConfigData(): {} { 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) { public unset(file: NewtonFile) {
file.session.destroy(); file.session.destroy();
window.fs.closeFile(file.path); window?.fs.closeFile(file.path);
this.files.delete(file.path); this.files.delete(file.path);
} }
@@ -76,7 +76,7 @@ export class FilesService {
): Promise<NewtonFile | undefined | null> { ): Promise<NewtonFile | undefined | null> {
for (let i = 0; i < files.length; i++) { for (let i = 0; i < files.length; i++) {
const file = files[i]; const file = files[i];
const path = window.fs.getPathForFile(file); const path = window?.fs.getPathForFile(file);
if (!file || !path) continue; if (!file || !path) continue;
if ( this.files.get(path) ) continue; if ( this.files.get(path) ) continue;
@@ -101,7 +101,7 @@ export class FilesService {
file.hash = btoa(file.path); file.hash = btoa(file.path);
if (loadFileContents) if (loadFileContents)
data = await window.fs.getFileContents(file.path); data = await window?.fs.getFileContents(file.path);
file.session = new EditSession(data); file.session = new EditSession(data);
file.session["id"] = path; 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 { FilesService } from '../../common/services/files.service';
import { SearchReplaceService } from '../../common/services/editor/search-replace/search-replace.service'; import { SearchReplaceService } from '../../common/services/editor/search-replace/search-replace.service';
import { MarkdownPreviewService } from '../../common/services/editor/markdown-preview/markdown-preview.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 { LspManagerService } from '../../common/services/editor/lsp-manager/lsp-manager.service';
import { EditorSettings } from "../../common/configs/editor.config"; import { EditorSettings } from "../../common/configs/editor.config";
@@ -39,7 +38,6 @@ export class CodeViewBase {
protected filesService: FilesService = inject(FilesService); protected filesService: FilesService = inject(FilesService);
protected searchReplaceService: SearchReplaceService = inject(SearchReplaceService); protected searchReplaceService: SearchReplaceService = inject(SearchReplaceService);
protected markdownPreviewService: MarkdownPreviewService = inject(MarkdownPreviewService); protected markdownPreviewService: MarkdownPreviewService = inject(MarkdownPreviewService);
protected terminalService: TerminalService = inject(TerminalService);
protected lspManagerService: LspManagerService = inject(LspManagerService); protected lspManagerService: LspManagerService = inject(LspManagerService);
@ViewChild('editor') editorElm!: ElementRef; @ViewChild('editor') editorElm!: ElementRef;
@@ -134,12 +132,6 @@ export class CodeViewBase {
// this.editor.execCommand("replace"); // this.editor.execCommand("replace");
} }
public terminalPopup() {
let message = new ServiceMessage();
message.action = "toggle-terminal";
this.terminalService.sendMessage(message);
}
public showFilesList() { public showFilesList() {
let paths = this.filesService.getAllPaths(); let paths = this.filesService.getAllPaths();
let stubPaths = []; let stubPaths = [];
@@ -195,7 +187,7 @@ export class CodeViewBase {
} }
public toggleFullScreen() { public toggleFullScreen() {
window.main.toggleFullScreen(); window?.main.toggleFullScreen();
} }
public setAsReadOnly() { public setAsReadOnly() {
@@ -308,7 +300,7 @@ export class CodeViewBase {
startDir = pathParts.join( '/' ); startDir = pathParts.join( '/' );
} }
window.fs.openFiles(startDir); window?.fs.openFiles(startDir);
} }
protected saveFile() { protected saveFile() {
@@ -320,18 +312,18 @@ export class CodeViewBase {
} }
const text = this.activeFile.session.getValue(); 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(); this.activeFile.session.getUndoManager().markClean();
} }
protected saveFileAs() { protected saveFileAs() {
window.fs.saveFileAs().then((path: string) => { window?.fs.saveFileAs().then((path: string) => {
if (!path) return; if (!path) return;
let file: NewtonFile = new File([""], path, {}); let file: NewtonFile = new File([""], path, {});
const text = this.editor.session.getValue(); const text = this.editor.session.getValue();
window.fs.saveFile(path, text); window?.fs.saveFile(path, text);
this.filesService.addFile( this.filesService.addFile(
path, path,
file, file,
@@ -372,6 +364,6 @@ export class CodeViewBase {
} }
private quit() { private quit() {
window.main.quit(); window?.main.quit();
} }
} }

View File

@@ -142,7 +142,7 @@ export class CodeViewComponent extends CodeViewBase {
private loadNewtonEventBindings(): void { private loadNewtonEventBindings(): void {
// Note: https://ajaxorg.github.io/ace-api-docs/interfaces/ace.Ace.EditorEvents.html // 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(); let message = new ServiceMessage();
message.action = "set-active-editor"; message.action = "set-active-editor";
message.editorUUID = this.uuid; message.editorUUID = this.uuid;
@@ -150,7 +150,6 @@ export class CodeViewComponent extends CodeViewBase {
this.editorsService.sendMessage(message); this.editorsService.sendMessage(message);
this.searchReplaceService.sendMessage(message); this.searchReplaceService.sendMessage(message);
this.terminalService.sendMessage(message);
message = new ServiceMessage(); message = new ServiceMessage();
message.action = "set-active-editor"; message.action = "set-active-editor";
@@ -308,4 +307,4 @@ export class CodeViewComponent extends CodeViewBase {
}, (timeout) ? timeout : this.debounceWait); }, (timeout) ? timeout : this.debounceWait);
} }
} }

View File

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

View File

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

View File

@@ -1,7 +1,21 @@
<div class="col"> <div class="col">
<div class="row"> <div class="row">
<div class="col col-3"> <div class="col col-3">
<label id="find-status-lbl">Find in Current File</label> @if (isQueryLong) {
<label id="find-status-lbl">
<b class="error">Query exceeds 80 characters...</b>
</label>
} @else if (isQueryNotFound) {
<label id="find-status-lbl">
<b class="warning">Query not found...</b>
</label>
} @else if (query && !isQueryLong && !isQueryNotFound) {
<label id="find-status-lbl">Found in current file:
<b class="success">{{totalCount}}</b>
</label>
} @else {
<label id="find-status-lbl">Find in Current File:</label>
}
</div> </div>
<div class="col col-4"> <div class="col col-4">
@@ -45,15 +59,27 @@
id="find-entry" id="find-entry"
class="form-control" class="form-control"
type="search" type="search"
(keyup)="searchForString()" (focus)="searchForString()"
(keyup)="findEntryKeyUpHandler($event)"
(input)="searchForString()"
placeholder="Find in current file..." placeholder="Find in current file..."
aria-label="Find in current file..." aria-label="Find in current file..."
/> />
</div> </div>
</div> </div>
<div class="col col-auto"> <div class="col col-auto">
<button id="find-btn" class="width-8em btn btn-sm btn-dark" (click)="findNextEntry()">Find</button> <button
<button id="find-all-btn" class="width-8em btn btn-sm btn-dark" (click)="findAllEntries()">Find All</button> [disabled]="!query || isQueryLong || isQueryNotFound"
id="find-btn"
class="width-8em btn btn-sm btn-dark"
(click)="findNextEntry()">Find
</button>
<button
[disabled]="!query || isQueryLong || isQueryNotFound"
id="find-all-btn"
class="width-8em btn btn-sm btn-dark"
(click)="findAllEntries()">Find All
</button>
</div> </div>
</div> </div>
</div> </div>
@@ -68,15 +94,25 @@
id="replace-entry" id="replace-entry"
class="form-control" class="form-control"
type="search" type="search"
(keyup)="replaceEntry($event)" (keyup.enter)="replaceEntry($event)"
title="Replace in current file..." title="Replace in current file..."
placeholder="Replace in current file..." placeholder="Replace in current file..."
/> />
</div> </div>
</div> </div>
<div class="col col-auto"> <div class="col col-auto">
<button id="replace-btn" class="width-8em btn btn-sm btn-dark" (click)="replaceEntry($event)">Replace</button> <button
<button id="replace-all-btn" class="width-8em btn btn-sm btn-dark" (click)="replaceAll()">Replace All</button> [disabled]="!query || isQueryLong || isQueryNotFound"
id="replace-btn"
class="width-8em btn btn-sm btn-dark"
(click)="replaceEntry($event)">Replace
</button>
<button
[disabled]="!query || isQueryLong || isQueryNotFound"
id="replace-all-btn"
class="width-8em btn btn-sm btn-dark"
(click)="replaceAll()">Replace All
</button>
</div> </div>
</div> </div>
</div> </div>

View File

@@ -36,15 +36,19 @@ export class SearchReplaceComponent {
@ViewChild('findEntryElm') findEntryElm!: ElementRef; @ViewChild('findEntryElm') findEntryElm!: ElementRef;
@ViewChild('replaceEntryElm') replaceEntryElm!: ElementRef; @ViewChild('replaceEntryElm') replaceEntryElm!: ElementRef;
@Input() query: string = "";
@Input() findOptions: string = "";
@Input() isQueryLong: boolean = false;
@Input() isQueryNotFound: boolean = false;
@Input() totalCount: number = 0;
private editor!: any; private editor!: any;
@Input() findOptions: string = "";
private useWholeWordSearch: boolean = false; private useWholeWordSearch: boolean = false;
private searchOnlyInSelection: boolean = false; private searchOnlyInSelection: boolean = false;
private useCaseSensitive: boolean = false; private useCaseSensitive: boolean = false;
private useRegex: boolean = false; private useRegex: boolean = false;
private selection: string = ""; private selection: string = "";
private query: string = "";
private toStr: string = ""; private toStr: string = "";
private isBackwards: boolean = false; private isBackwards: boolean = false;
private isWrap: boolean = true; private isWrap: boolean = true;
@@ -179,17 +183,25 @@ export class SearchReplaceComponent {
this.findOptions = findOptionsStr; this.findOptions = findOptionsStr;
} }
public findPreviousEntry() {
this.editor.findPrevious();
}
public findNextEntry() { public findNextEntry() {
this.editor.findNext(); this.editor.findNext();
} }
public findEntryKeyUpHandler(event: KeyboardEvent) {
if (!event.ctrlKey || !this.query) return;
if (event.key === "ArrowUp") this.findPreviousEntry();
if (event.key === "ArrowDown") this.findNextEntry();
}
public findAllEntries() { public findAllEntries() {
this.query = this.findEntryElm.nativeElement.value; this.query = this.findEntryElm.nativeElement.value;
if (!this.query) return; this.totalCount = this.editor.findAll(this.query, {
let totalCount = this.editor.findAll(this.query, {
backwards: this.isBackwards, backwards: this.isBackwards,
wrap: this.isWrap, wrap: this.isWrap,
caseSensitive: this.useCaseSensitive, caseSensitive: this.useCaseSensitive,
@@ -197,25 +209,19 @@ export class SearchReplaceComponent {
regExp: this.useRegex, regExp: this.useRegex,
range: this.searchOnlyInSelection range: this.searchOnlyInSelection
}); });
if (this.totalCount === 0) this.isQueryNotFound = true;
} }
public findPreviousEntry() { public replaceEntry(event: KeyboardEvent) {
this.editor.findPrevious(); if (this.isQueryLong || this.isQueryNotFound) return;
}
public replaceEntry(event: any) {
if (event instanceof KeyboardEvent) {
if (event.key !== "Enter") {
return;
}
}
let fromStr = this.findEntryElm.nativeElement.value; let fromStr = this.findEntryElm.nativeElement.value;
let toStr = this.replaceEntryElm.nativeElement.value; let toStr = this.replaceEntryElm.nativeElement.value;
if (!fromStr) return; if (!fromStr) return;
let totalCount = this.editor.replace(toStr, fromStr, { this.editor.replace(toStr, fromStr, {
backwards: this.isBackwards, backwards: this.isBackwards,
wrap: this.isWrap, wrap: this.isWrap,
caseSensitive: this.useCaseSensitive, caseSensitive: this.useCaseSensitive,
@@ -229,12 +235,14 @@ export class SearchReplaceComponent {
} }
public replaceAll() { public replaceAll() {
if (this.isQueryLong || this.isQueryNotFound) return;
let fromStr = this.findEntryElm.nativeElement.value; let fromStr = this.findEntryElm.nativeElement.value;
let toStr = this.replaceEntryElm.nativeElement.value; let toStr = this.replaceEntryElm.nativeElement.value;
if (!fromStr) return; if (!fromStr) return;
let totalCount = this.editor.replaceAll(toStr, fromStr, { this.editor.replaceAll(toStr, fromStr, {
backwards: this.isBackwards, backwards: this.isBackwards,
wrap: this.isWrap, wrap: this.isWrap,
caseSensitive: this.useCaseSensitive, caseSensitive: this.useCaseSensitive,
@@ -242,23 +250,27 @@ export class SearchReplaceComponent {
regExp: this.useRegex, regExp: this.useRegex,
range: this.searchOnlyInSelection range: this.searchOnlyInSelection
}); });
this.isQueryNotFound = true;
} }
public searchForString() { public searchForString() {
if (event instanceof KeyboardEvent) { if (this.searchTimeoutId) { clearTimeout(this.searchTimeoutId); }
if (event.key !== "Enter") {
return;
}
}
this.query = this.findEntryElm.nativeElement.value; this.query = this.findEntryElm.nativeElement.value;
if (!this.query) return; if (!this.query) {
this.isQueryLong = false;
this.isQueryNotFound = false;
if (this.searchTimeoutId) { clearTimeout(this.searchTimeoutId); } return;
}
this.isQueryLong = (this.query.length > 80);
if (this.isQueryLong) return;
this.searchTimeoutId = setTimeout(() => { this.searchTimeoutId = setTimeout(() => {
let totalCount = this.editor.find(this.query, { this.totalCount = this.editor.findAll(this.query, {
backwards: this.isBackwards, backwards: this.isBackwards,
wrap: this.isWrap, wrap: this.isWrap,
caseSensitive: this.useCaseSensitive, caseSensitive: this.useCaseSensitive,
@@ -266,6 +278,8 @@ export class SearchReplaceComponent {
regExp: this.useRegex, regExp: this.useRegex,
range: this.searchOnlyInSelection range: this.searchOnlyInSelection
}); });
this.isQueryNotFound = (this.totalCount === 0);
}, this.searchTimeout); }, this.searchTimeout);
} }

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. 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 { declare global {
interface Window { interface Window {
electron: { electron: {
node: () => Promise<string>, node: any,
chrome: () => Promise<string>, chrome: any,
electron: () => Promise<string>, electron: any,
}, },
main: { main: {
onMenuActions: (arg0: any) => Promise<string>, onMenuActions: any,
onTerminalActions: (arg0: any) => Promise<string>, onTerminalActions: any,
quit: any, quit: any,
toggleFullScreen: any, toggleFullScreen: any,
}, },
fs: { fs: {
getLspConfigData: () => Promise<string>, getLspConfigData: any,
getFileContents: (arg0: any) => Promise<string>, getFileContents: any,
openFiles: (arg0) => Promise<string>, openFiles: any,
saveFile: (arg0: any, arg1: any) => Promise<string>, saveFile: any,
saveFileAs: () => Promise<string>, saveFileAs: any,
chooseFolder: () => Promise<string>, chooseFolder: any,
closeFile: (arg0: any) => Promise<string>, closeFile: any,
getPathForFile: any, getPathForFile: any,
onLoadFiles: (arg0: any) => Promise<string>, onLoadFiles: any,
onUpdateFilePath: (arg0: any) => Promise<string>, onUpdateFilePath: any,
onSavedFile: (arg0: any) => Promise<string>, onSavedFile: any,
onChangedFile: (arg0: any) => Promise<string>, onChangedFile: any,
onDeletedFile: (arg0: any) => Promise<string>, 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, "declaration": false,
"skipLibCheck": true, "skipLibCheck": true,
"strict": false, "strict": false,
"forceConsistentCasingInFileNames": true, "forceConsistentCasingInFileNames": true
}, }
"includes": [
"src/typings.d.ts"
]
} }
/*
{
"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"
]
}
*/