From ae437228819e064684f1e52ab13ebb117f9415cd Mon Sep 17 00:00:00 2001 From: itdominator <1itdominator@gmail.com> Date: Sun, 1 Jun 2025 00:49:30 -0500 Subject: [PATCH] most menu events mapped; cleanup --- app.js | 101 ++++++++++++++++++++- main.js | 31 ++++++- preload.js | 8 +- src/app/app.component.ts | 6 ++ src/app/common/configs/editor.config.ts | 4 +- src/app/common/services/lsp.service.ts | 2 +- src/app/editor/ace/ace-editor.base.ts | 52 ++++++++--- src/app/editor/ace/ace-editor.component.ts | 14 +++ src/app/editor/editors.component.ts | 90 ++++++++++++++++-- 9 files changed, 279 insertions(+), 29 deletions(-) diff --git a/app.js b/app.js index c106438..bd8b1b1 100644 --- a/app.js +++ b/app.js @@ -1,4 +1,4 @@ -const { BrowserWindow } = require('electron'); +const { BrowserWindow, Menu } = require('electron'); const path = require('node:path'); const fs = require('node:fs'); const os = require('os') @@ -28,7 +28,9 @@ const createWindow = (startType = "build", debug = false, args = []) => { } }); -// win.removeMenu() + setupMenu(win); + + // win.setAutoHideMenuBar(true) if (debug == true) { win.webContents.openDevTools(); @@ -45,7 +47,102 @@ const createWindow = (startType = "build", debug = false, args = []) => { } } +const setupMenu = (win) => { + const menu = Menu.buildFromTemplate([ + { + label: "File", + submenu: [ + { + label: 'New', + click: () => win.webContents.send('menu-actions', "new-file") + }, { + label: 'Open', + click: () => win.webContents.send('load-files', [path.join(__dirname, `${BASE_PATH}/index.html`)]) + }, { + label: 'Terminal', + click: () => {} + }, { + label: 'save', + click: () => win.webContents.send('menu-actions', "save-file") + }, { + label: 'Save As', + click: () => win.webContents.send('menu-actions', "save-file-as") + } + ] + }, { + label: "Edit", + submenu: [ + { + label: 'Undo', + click: () => win.webContents.send('menu-actions', "undo") + }, { + label: 'Redo', + click: () => win.webContents.send('menu-actions', "redo") + }, { + label: 'Cut', + click: () => win.webContents.send('menu-actions', "cut") + }, { + label: 'Copy', + click: () => win.webContents.send('menu-actions', "copy") + }, { + label: 'Paste', + click: () => win.webContents.send('menu-actions', "paste") + + }, { + label: 'Delete', + click: () => win.webContents.send('menu-actions', "delete") + }, { + label: 'Select All', + click: () => win.webContents.send('menu-actions', "select-all") + }, { + label: 'Indent', + click: () => win.webContents.send('menu-actions', "blockindent") + }, { + label: 'De-Indent', + click: () => win.webContents.send('menu-actions', "blockoutdent") + }, { + label: 'To Upper Case', + click: () => win.webContents.send('menu-actions', "touppercase") + }, { + label: 'To Lower Case', + click: () => win.webContents.send('menu-actions', "tolowercase") + }, + ] + }, { + label: "View", + submenu: [ + { + label: 'Zoom In', + click: () => win.webContents.send('menu-actions', "zoom-in") + }, { + label: 'Zoom Out', + click: () => win.webContents.send('menu-actions', "zoom-out") + }, { + label: 'Toggle Full Screen', + click: () => { win.setFullScreen(!win.fullScreen) } + }, { + label: 'Toggle Developer Tools', + click: () => win.webContents.toggleDevTools() + } + ] + }, { + label: "Help", + submenu: [ + { + label: 'About', + click: () => win.webContents.send('menu-actions', "show-about") + } + ] + }, + + ]); + + Menu.setApplicationMenu(menu) +} + const getFileContents = (_path, useRelativePath = false) => { + console.log(`Getting Contents For: ${_path}`); + try { if (!useRelativePath) { return fs.readFileSync(_path, 'utf8'); diff --git a/main.js b/main.js index 8a5f258..64084ac 100644 --- a/main.js +++ b/main.js @@ -4,7 +4,7 @@ try { -const { app, ipcMain } = require('electron'); +const { app, ipcMain, dialog } = require('electron'); const { newton } = require('./app'); const path = require('node:path'); const fs = require('node:fs'); @@ -42,9 +42,36 @@ const loadArgs = () => { } +const saveFile = (path, content) => { + console.log("..."); +} + +const saveFileAs = (content) => { + console.log(content); + dialog.showSaveDialog((fileName) => { + console.log(fileName); + + if (fileName === undefined){ + console.log("You didn't save the file"); + return; + } + + // fileName is a string that contains the path and filename created in the save file dialog. + fs.writeFile(fileName, content, (err) => { + if(err){ + alert("An error ocurred creating the file "+ err.message) + } + + alert("The file has been succesfully saved"); + }); + }); +} + const loadHandlers = () => { ipcMain.handle('getLspConfigData', (eve) => newton.getLspConfigData()); ipcMain.handle('getFileContents', (eve, file) => newton.getFileContents(file)); + ipcMain.handle('saveFile', (eve, path, content) => saveFile(path, "Some text to save into the file")); + ipcMain.handle('saveFileAs', (eve, content) => saveFileAs("Some text to save into the file")); } app.whenReady().then(() => { @@ -83,4 +110,4 @@ process.on('unhandledRejection', function(error = {}) { if (error.stack != null) { console.log(error.stack); } -}); +}); \ No newline at end of file diff --git a/preload.js b/preload.js index 208ad77..308254b 100644 --- a/preload.js +++ b/preload.js @@ -6,9 +6,15 @@ contextBridge.exposeInMainWorld('electron', { chrome: () => process.versions.chrome, electron: () => process.versions.electron, }); +contextBridge.exposeInMainWorld('main', { + onMenuActions: (callback) => ipcRenderer.on('menu-actions', (_event, action) => callback(action)), +}); contextBridge.exposeInMainWorld('fs', { getLspConfigData: () => ipcRenderer.invoke("getLspConfigData"), getFileContents: (file) => ipcRenderer.invoke("getFileContents", file), - getPathForFile: (file) => webUtils.getPathForFile(file) + saveFile: (path, content) => ipcRenderer.invoke("saveFile", path, content), + saveFileAs: (content) => ipcRenderer.invoke("saveFileAs", content), + getPathForFile: (file) => webUtils.getPathForFile(file), + onLoadFiles: (callback) => ipcRenderer.on('load-files', (_event, files) => callback(files)), }); diff --git a/src/app/app.component.ts b/src/app/app.component.ts index 5248832..540721a 100644 --- a/src/app/app.component.ts +++ b/src/app/app.component.ts @@ -13,10 +13,16 @@ declare global { chrome: () => Promise, electron: () => Promise, }, + main: { + onMenuActions: (arg0: any) => Promise, + }, fs: { getLspConfigData: () => Promise, getFileContents: (arg0: any) => Promise, + saveFile: (arg0: any, arg1: any) => Promise, + saveFileAs: (arg0: any) => Promise, getPathForFile: any, + onLoadFiles: (arg0: any) => Promise, } } } diff --git a/src/app/common/configs/editor.config.ts b/src/app/common/configs/editor.config.ts index 6e91015..ecb629a 100644 --- a/src/app/common/configs/editor.config.ts +++ b/src/app/common/configs/editor.config.ts @@ -15,15 +15,15 @@ export const EditorSettings: any = { enableSnippets: true, highlightActiveLine: true, enableMultiselect: true, - tabSize: 4, useSoftTabs: true, + tabSize: 4, + navigateWithinSoftTabs: true, tooltipFollowsMouse: true, wrapBehavioursEnabled: false, scrollPastEnd: 0.5, mergeUndoDeltas: false, showGutter: true, customScrollbar: true, - navigateWithinSoftTabs: true, scrollSpeed: 5 } }; \ No newline at end of file diff --git a/src/app/common/services/lsp.service.ts b/src/app/common/services/lsp.service.ts index 0a220d7..6656ca8 100644 --- a/src/app/common/services/lsp.service.ts +++ b/src/app/common/services/lsp.service.ts @@ -50,7 +50,7 @@ export class LSPService { private getLanguageProviderWithClientServer(mode: string) { let _initializationOptions = {}; - if (Object.keys(this.lspConfigData).length !== 0) { + if ( Object.keys(this.lspConfigData).length !== 0 && this.lspConfigData[mode] ) { _initializationOptions = this.lspConfigData[mode]["initialization-options"]; } diff --git a/src/app/editor/ace/ace-editor.base.ts b/src/app/editor/ace/ace-editor.base.ts index baf3d6f..2e797a0 100644 --- a/src/app/editor/ace/ace-editor.base.ts +++ b/src/app/editor/ace/ace-editor.base.ts @@ -10,7 +10,6 @@ export class AceEditorBase { @Input() editorSettings!: typeof EditorSettings; editor!: any; uuid!: string; - fontSize: number = 12; cutBuffer: string = ""; timerId: number = -1; @@ -19,18 +18,51 @@ export class AceEditorBase { ) {} + protected search() { + console.log(this.editor.getSession()["$modeId"]) + } + + protected saveFile() { + const text = this.editor.session.getValue(); +// window.fs.saveFile(text); + } + + protected saveFileAs() { + const text = this.editor.session.getValue(); + window.fs.saveFileAs(text); + } + protected zoomIn() { - this.fontSize += 1; - this.editorElm.nativeElement.style.fontSize = `${this.fontSize}px`; + this.editor.setFontSize( + parseInt(this.editor.getFontSize()) + 1 + ) } protected zoomOut() { - this.fontSize -= 1; - this.editorElm.nativeElement.style.fontSize = `${this.fontSize}px`; + this.editor.setFontSize( + parseInt(this.editor.getFontSize()) - 1 + ) } - protected search() { - console.log(this.editor.getSession()["$modeId"]) + protected cutText() { + let cutText = this.editor.getSelectedText(); + this.editor.remove(); + navigator.clipboard.writeText(cutText).catch(() => { + console.error("Unable to cut text..."); + }); + } + + protected copyText() { + let copyText = this.editor.getSelectedText(); + navigator.clipboard.writeText(copyText).catch(() => { + console.error("Unable to copy text..."); + }); + } + + protected pasteText() { + navigator.clipboard.readText().then((pasteText) => { + this.editor.insert(pasteText, true); + }); } protected movelinesUp() { @@ -51,17 +83,15 @@ export class AceEditorBase { const cursorPosition = this.editor.getCursorPosition(); let lineText = this.editor.session.getLine(cursorPosition.row); this.cutBuffer += `${lineText}\n`; - this.editor.session.removeFullLines(cursorPosition.row, cursorPosition.row) + this.editor.session.removeFullLines(cursorPosition.row, cursorPosition.row) this.setBufferClearTimeout(); } protected pasteCutBuffer() { if (this.timerId) { clearTimeout(this.timerId); } - const cursorPosition = this.editor.getCursorPosition(); - this.editor.session.insert(cursorPosition, this.cutBuffer) - + this.editor.insert(this.cutBuffer, true); this.setBufferClearTimeout(); } diff --git a/src/app/editor/ace/ace-editor.component.ts b/src/app/editor/ace/ace-editor.component.ts index 8a5ef13..35a1939 100644 --- a/src/app/editor/ace/ace-editor.component.ts +++ b/src/app/editor/ace/ace-editor.component.ts @@ -109,6 +109,20 @@ export class AceEditorComponent extends AceEditorBase { this.editor.session.destroy(); }, readOnly: true + }, { + name: "saveFile", + bindKey: {win: "ctrl-s", mac: "ctrl-s"}, + exec: () => { + this.saveFile(); + }, + readOnly: true + }, { + name: "saveFileAs", + bindKey: {win: "ctrl-shift-s", mac: "ctrl-shift-s"}, + exec: () => { + this.saveFileAs(); + }, + readOnly: true } ]); diff --git a/src/app/editor/editors.component.ts b/src/app/editor/editors.component.ts index e528c8b..ae15db7 100644 --- a/src/app/editor/editors.component.ts +++ b/src/app/editor/editors.component.ts @@ -51,8 +51,9 @@ export class EditorsComponent { public ngAfterViewInit(): void { this.loadSubscribers(); + this.loadMainSubscribers(); - let editor = this.createEditor(); + let editor = this.createEditor(); this.activeEditor = editor.instance.uuid; this.createEditor(); } @@ -65,6 +66,55 @@ export class EditorsComponent { }); } + loadMainSubscribers() { + window.fs.onLoadFiles(async (paths: []) => { + for (let i = 0; i < paths.length; i++) { + let file = new File([], "") as NewtonFile; + + await this.addFile(paths[i], file); + this.addTab(file); + } + + let session = this.files.get(paths[ paths.length - 1 ]).session; + this.setSession(session); + }); + + window.main.onMenuActions(async (action: string) => { + let editorComponent = this.getActiveEditorComponent(); + let editor = editorComponent.editor; + + switch ( action ) { + case "new-file": + break; + case "save-file": + editorComponent.saveFile(); + break; + case "save-file-as": + editorComponent.saveFileAs(); + break; + case "cut": + editorComponent.cutText(); + break; + case "copy": + editorComponent.copyText(); + break; + case "paste": + editorComponent.pasteText(); + break; + case "zoom-in": + editorComponent.zoomIn() + break; + case "zoom-out": + editorComponent.zoomOut() + break; + case "show-about": + break; + default: + editor.execCommand(action); + } + }); + } + ngOnDestroy() { this.unsubscribe.next(); this.unsubscribe.complete(); @@ -79,16 +129,36 @@ export class EditorsComponent { return component; } - protected onFileDropped(event: any) { - this.loadFilesList(event).then((session: EditSession | undefined | null) => { - if ( !session ) return; - - let editorComponent = this.editors.get(this.activeEditor)?.instance; - let editor = editorComponent.editor; - editor?.setSession(session); + protected onFileDropped(files: any) { + this.loadFilesList(files).then((session: EditSession | undefined | null) => { + this.setSession(session); }); } + private async setSession(session: EditSession | undefined | null) { + if ( !session ) return; + + let editor = this.getActiveEditor(); + editor?.setSession(session); + } + + private getSession() { + let editorComponent = this.editors.get(this.activeEditor)?.instance; + let editor = editorComponent.editor; + + return editor?.getSession(); + } + + private getActiveEditorComponent(): any { + return this.editors.get(this.activeEditor)?.instance; + } + + private getActiveEditor(): any { + let editorComponent = this.editors.get(this.activeEditor)?.instance; + let editor = editorComponent.editor; + return editor; + } + private async loadFilesList(files: Array): Promise { for (let i = 0; i < files.length; i++) { const file = files[i]; @@ -96,7 +166,7 @@ export class EditorsComponent { if (!file || !path) continue; if ( this.files.get(path) ) continue; - + await this.addFile(path, file); this.addTab(file); } @@ -104,7 +174,7 @@ export class EditorsComponent { return files[ files.length - 1 ].session; } - private async addFile(path: string, file: NewtonFile) { + private async addFile(path: string, file: NewtonFile): Promise { try { let pathParts = path.split("/"); file.fname = pathParts[ pathParts.length - 1 ];