Compare commits
10 Commits
9f201a3dac
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| d179e1c42e | |||
| da4e87d3cd | |||
| f1e3557db1 | |||
| dd969302cf | |||
| 21cf5c794a | |||
| a4765952bf | |||
| de5184ce22 | |||
| de0802cb2c | |||
| c4ab66141c | |||
| 6bedb69909 |
@@ -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
|
||||
},
|
||||
|
||||
@@ -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);
|
||||
|
||||
25
newton/fs.js
25
newton/fs.js
@@ -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,
|
||||
}
|
||||
|
||||
@@ -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('');
|
||||
});
|
||||
|
||||
|
||||
35
package.json
35
package.json
@@ -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"
|
||||
@@ -30,10 +30,7 @@
|
||||
"icon": "./icos/",
|
||||
"files": [
|
||||
"newton/",
|
||||
"build/",
|
||||
"!node_modules/ace-builds/",
|
||||
"!node_modules/web-streams-polyfill/",
|
||||
"!node_modules/@angular/"
|
||||
"build/"
|
||||
],
|
||||
"mac": {
|
||||
"category": "public.app-category.developer-tools"
|
||||
@@ -47,15 +44,11 @@
|
||||
"maintainer": "ITDominator"
|
||||
}
|
||||
},
|
||||
"postinstall": "electron-builder install-app-deps",
|
||||
"overrides": {
|
||||
"glob": "9.0.0",
|
||||
"rimraf": "4.3.1"
|
||||
},
|
||||
"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-layout": "1.5.0",
|
||||
"ace-linters": "1.8.3",
|
||||
@@ -65,24 +58,27 @@
|
||||
"electron-fetch": "1.9.1",
|
||||
"express": "4.18.2",
|
||||
"marked": "16.4.0",
|
||||
"node-fetch": "3.3.2",
|
||||
"node-pty": "^1.0.0",
|
||||
"rxjs": "7.8.0",
|
||||
"socket.io": "4.8.1",
|
||||
"uuid": "11.1.0",
|
||||
"zone.js": "0.15.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"ace-builds": "1.43.0",
|
||||
"@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/compiler-cli": "19.2.0",
|
||||
"@angular/forms": "19.2.0",
|
||||
"@angular/platform-browser": "19.2.0",
|
||||
"@types/express": "4.17.17",
|
||||
"@types/jasmine": "5.1.0",
|
||||
"@types/node": "18.18.0",
|
||||
"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",
|
||||
@@ -91,6 +87,7 @@
|
||||
"karma-jasmine": "5.1.0",
|
||||
"karma-jasmine-html-reporter": "2.1.0",
|
||||
"nanoevents": "9.1.0",
|
||||
"rxjs": "7.8.0",
|
||||
"tree-sitter": "0.21.1",
|
||||
"tree-sitter-bash": "0.23.2",
|
||||
"tree-sitter-c": "0.23.1",
|
||||
|
||||
@@ -5,7 +5,6 @@
|
||||
<editors></editors>
|
||||
<search-replace></search-replace>
|
||||
<markdown-preview></markdown-preview>
|
||||
<terminal></terminal>
|
||||
|
||||
<lsp-manager></lsp-manager>
|
||||
</div>
|
||||
@@ -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!' }");
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
@@ -42,7 +42,7 @@ export class LspManagerService {
|
||||
}
|
||||
|
||||
private getLspConfigData(): Promise<string> {
|
||||
return window.fs.getLspConfigData();
|
||||
return window?.fs.getLspConfigData();
|
||||
}
|
||||
|
||||
private parseAndReturnLSPConfigData(): {} {
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
35
src/app/common/services/websocket.service.ts
Normal file
35
src/app/common/services/websocket.service.ts
Normal 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();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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";
|
||||
@@ -308,4 +307,4 @@ export class CodeViewComponent extends CodeViewBase {
|
||||
}, (timeout) ? timeout : this.debounceWait);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -1,7 +1,21 @@
|
||||
<div class="col">
|
||||
<div class="row">
|
||||
<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 class="col col-4">
|
||||
@@ -45,15 +59,27 @@
|
||||
id="find-entry"
|
||||
class="form-control"
|
||||
type="search"
|
||||
(keyup)="searchForString()"
|
||||
(focus)="searchForString()"
|
||||
(keyup)="findEntryKeyUpHandler($event)"
|
||||
(input)="searchForString()"
|
||||
placeholder="Find in current file..."
|
||||
aria-label="Find in current file..."
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col col-auto">
|
||||
<button id="find-btn" class="width-8em btn btn-sm btn-dark" (click)="findNextEntry()">Find</button>
|
||||
<button id="find-all-btn" class="width-8em btn btn-sm btn-dark" (click)="findAllEntries()">Find All</button>
|
||||
<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>
|
||||
@@ -68,15 +94,25 @@
|
||||
id="replace-entry"
|
||||
class="form-control"
|
||||
type="search"
|
||||
(keyup)="replaceEntry($event)"
|
||||
(keyup.enter)="replaceEntry($event)"
|
||||
title="Replace in current file..."
|
||||
placeholder="Replace in current file..."
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col col-auto">
|
||||
<button id="replace-btn" class="width-8em btn btn-sm btn-dark" (click)="replaceEntry($event)">Replace</button>
|
||||
<button id="replace-all-btn" class="width-8em btn btn-sm btn-dark" (click)="replaceAll()">Replace All</button>
|
||||
<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>
|
||||
|
||||
@@ -36,15 +36,19 @@ export class SearchReplaceComponent {
|
||||
@ViewChild('findEntryElm') findEntryElm!: 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;
|
||||
|
||||
@Input() findOptions: string = "";
|
||||
private useWholeWordSearch: boolean = false;
|
||||
private searchOnlyInSelection: boolean = false;
|
||||
private useCaseSensitive: boolean = false;
|
||||
private useRegex: boolean = false;
|
||||
private selection: string = "";
|
||||
private query: string = "";
|
||||
private toStr: string = "";
|
||||
private isBackwards: boolean = false;
|
||||
private isWrap: boolean = true;
|
||||
@@ -179,17 +183,25 @@ export class SearchReplaceComponent {
|
||||
this.findOptions = findOptionsStr;
|
||||
}
|
||||
|
||||
public findPreviousEntry() {
|
||||
this.editor.findPrevious();
|
||||
}
|
||||
|
||||
public findNextEntry() {
|
||||
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() {
|
||||
this.query = this.findEntryElm.nativeElement.value;
|
||||
|
||||
if (!this.query) return;
|
||||
|
||||
let totalCount = this.editor.findAll(this.query, {
|
||||
this.totalCount = this.editor.findAll(this.query, {
|
||||
backwards: this.isBackwards,
|
||||
wrap: this.isWrap,
|
||||
caseSensitive: this.useCaseSensitive,
|
||||
@@ -197,25 +209,19 @@ export class SearchReplaceComponent {
|
||||
regExp: this.useRegex,
|
||||
range: this.searchOnlyInSelection
|
||||
});
|
||||
|
||||
if (this.totalCount === 0) this.isQueryNotFound = true;
|
||||
}
|
||||
|
||||
public findPreviousEntry() {
|
||||
this.editor.findPrevious();
|
||||
}
|
||||
|
||||
public replaceEntry(event: any) {
|
||||
if (event instanceof KeyboardEvent) {
|
||||
if (event.key !== "Enter") {
|
||||
return;
|
||||
}
|
||||
}
|
||||
public replaceEntry(event: KeyboardEvent) {
|
||||
if (this.isQueryLong || this.isQueryNotFound) return;
|
||||
|
||||
let fromStr = this.findEntryElm.nativeElement.value;
|
||||
let toStr = this.replaceEntryElm.nativeElement.value;
|
||||
|
||||
if (!fromStr) return;
|
||||
|
||||
let totalCount = this.editor.replace(toStr, fromStr, {
|
||||
this.editor.replace(toStr, fromStr, {
|
||||
backwards: this.isBackwards,
|
||||
wrap: this.isWrap,
|
||||
caseSensitive: this.useCaseSensitive,
|
||||
@@ -229,12 +235,14 @@ export class SearchReplaceComponent {
|
||||
}
|
||||
|
||||
public replaceAll() {
|
||||
if (this.isQueryLong || this.isQueryNotFound) return;
|
||||
|
||||
let fromStr = this.findEntryElm.nativeElement.value;
|
||||
let toStr = this.replaceEntryElm.nativeElement.value;
|
||||
|
||||
if (!fromStr) return;
|
||||
|
||||
let totalCount = this.editor.replaceAll(toStr, fromStr, {
|
||||
this.editor.replaceAll(toStr, fromStr, {
|
||||
backwards: this.isBackwards,
|
||||
wrap: this.isWrap,
|
||||
caseSensitive: this.useCaseSensitive,
|
||||
@@ -242,23 +250,27 @@ export class SearchReplaceComponent {
|
||||
regExp: this.useRegex,
|
||||
range: this.searchOnlyInSelection
|
||||
});
|
||||
|
||||
this.isQueryNotFound = true;
|
||||
}
|
||||
|
||||
public searchForString() {
|
||||
if (event instanceof KeyboardEvent) {
|
||||
if (event.key !== "Enter") {
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (this.searchTimeoutId) { clearTimeout(this.searchTimeoutId); }
|
||||
|
||||
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(() => {
|
||||
let totalCount = this.editor.find(this.query, {
|
||||
this.totalCount = this.editor.findAll(this.query, {
|
||||
backwards: this.isBackwards,
|
||||
wrap: this.isWrap,
|
||||
caseSensitive: this.useCaseSensitive,
|
||||
@@ -266,6 +278,8 @@ export class SearchReplaceComponent {
|
||||
regExp: this.useRegex,
|
||||
range: this.searchOnlyInSelection
|
||||
});
|
||||
|
||||
this.isQueryNotFound = (this.totalCount === 0);
|
||||
}, this.searchTimeout);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +0,0 @@
|
||||
<div class="col">
|
||||
<div class="row">
|
||||
<div class="terminal-container" #terminalElm>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
3
src/libs/showdown.min.js
vendored
3
src/libs/showdown.min.js
vendored
File diff suppressed because one or more lines are too long
@@ -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
1
src/typings.d.ts
vendored
@@ -1 +0,0 @@
|
||||
// declare var showdown: any;
|
||||
@@ -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"
|
||||
]
|
||||
}
|
||||
|
||||
*/
|
||||
Reference in New Issue
Block a user