diff --git a/newton/app.js b/newton/app.js new file mode 100644 index 0000000..1d4d322 --- /dev/null +++ b/newton/app.js @@ -0,0 +1,79 @@ +const { BrowserWindow } = require('electron'); +const path = require('node:path'); + +const { menu } = require('./menu'); +const { newtonFs } = require('./fs'); + + +const BASE_PATH = '../dist/app'; +const ICON_PATH = `${BASE_PATH}/resources/newton.png`; + +let args = []; + + + +// Note: https://tinydew4.gitbooks.io/electron/content/api/browser-window.html +const createWindow = (startType = "build", debug = false, args = []) => { + this.args = args; + + const win = new BrowserWindow({ + title: "Newton", + width: 800, + height: 600, + minWidth: 800, + minHeight: 600, + show: false, + transparent: true, + icon: path.join(__dirname, ICON_PATH), + webPreferences: { + preload: path.join(__dirname, 'preload.js'), + contextIsolation: true, + enableRemoteModule: false, + plugins: true, + webSecurity: true, + sandbox: true, + } + }); + + win.once('ready-to-show', () => { + win.show() + }); + + menu.load(win); + + // win.setAutoHideMenuBar(true) + + if (debug == true) { + win.webContents.openDevTools(); + } + + if (startType === "build") { + win.loadFile( + path.join(__dirname, `${BASE_PATH}/index.html`) + ); + } + + if (startType === "ng-serve") { + win.loadURL('http://localhost:4200'); + } + +// const child = new BrowserWindow({parent: win, modal: true, show: false}) +// child.loadFile( +// path.join(__dirname, `${BASE_PATH}/index.html`) +// ); +// child.once('ready-to-show', () => { +// child.show() +// }); + + return win; + +} + + + +module.exports = { + newton: { + createWindow: createWindow, + fs: newtonFs, + } +}; \ No newline at end of file diff --git a/newton/fs.js b/newton/fs.js new file mode 100644 index 0000000..cbdf2a6 --- /dev/null +++ b/newton/fs.js @@ -0,0 +1,96 @@ +const { dialog } = require('electron'); +const path = require('node:path'); +const fs = require('node:fs'); +const os = require('os') + + + +const BASE_PATH = '../dist/app'; +const LSP_CONFIG_PATH = `${BASE_PATH}/resources/lsp-servers-config.json`; +let window = null; + + + +const getFileContents = (_path, useRelativePath = false) => { + console.log(`Getting Contents For: ${_path}`); + + try { + if (!useRelativePath) { + return fs.readFileSync(_path, 'utf8'); + } else { + return fs.readFileSync(path.join(__dirname, _path), 'utf8'); + } + } catch(err) { + return `{"message": {"type": "error", "text": "Error: Could not read ${_path}"}}`; + } +} + +const getLspConfigData = () => { + const config = getFileContents(LSP_CONFIG_PATH, true); + return config.replaceAll("{user.home}", os.homedir()); +} + +const saveFile = (fpath, content) => { + fs.writeFile(fpath, content, (err) => { + if (!err) return + console.error("An error ocurred writing to the file " + err.message) + }); +} + +const saveFileAs = (content) => { + dialog.showSaveDialog().then((response) => { + if (response.canceled) { + console.log("You didn't save the file"); + return; + } + + saveFile(response.filePath, content); + }); +} + +const openFiles = (startPath) => { + dialog.showOpenDialog( + { + title: "Open File(s):", + defaultPath: (startPath) ? startPath : os.homedir(), + filters: [ + {"c": [".h", ".c"]}, + {"cpp": ["hpp", "cpp"]}, + {"html": ["js", "css", "scss", "html", "ts"]}, + {"java": ["java"]}, + {"python": ["py", "pyc"]}, + {"rust": ["r", "rc"]}, + {"text": ["txt", "log", "md"]}, + {"go": ["go"]}, + ], + properties: [ + 'openFile', + 'multiSelections' + ] + } + ).then((response) => { + if (response.canceled) { + console.log("Canceled file open request..."); + return; + } + + window.webContents.send('load-files', response.filePaths); + }); +} + +const setWindow = (win) => { + window = win; +} + + + +module.exports = { + newtonFs: { + openFiles: openFiles, + saveFile: saveFile, + saveFileAs: saveFileAs, + getFileContents: getFileContents, + getLspConfigData: getLspConfigData, + setWindow: setWindow + } +}; \ No newline at end of file diff --git a/main.js b/newton/main.js similarity index 58% rename from main.js rename to newton/main.js index 64084ac..5ccfb3f 100644 --- a/main.js +++ b/newton/main.js @@ -3,11 +3,9 @@ try { } catch {} +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'); @@ -39,45 +37,24 @@ const loadArgs = () => { console.log(index + ': ' + val); console.log(); }); - -} - -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")); + ipcMain.handle('getLspConfigData', (eve) => newton.fs.getLspConfigData()); + ipcMain.handle('getFileContents', (eve, file) => newton.fs.getFileContents(file)); + ipcMain.handle('openFiles', (eve, startPath) => newton.fs.openFiles(startPath)); + ipcMain.handle('saveFile', (eve, path, content) => newton.fs.saveFile(path, content)); + ipcMain.handle('saveFileAs', (eve, content) => newton.fs.saveFileAs(content)); } + app.whenReady().then(() => { loadArgs(); loadHandlers(); - newton.createWindow(startType, isDebug, args); + + let window = newton.createWindow(startType, isDebug, args); + + newton.fs.setWindow(window); }) app.on('activate', () => { diff --git a/app.js b/newton/menu.js similarity index 62% rename from app.js rename to newton/menu.js index bd8b1b1..26e5878 100644 --- a/app.js +++ b/newton/menu.js @@ -1,53 +1,8 @@ -const { BrowserWindow, Menu } = require('electron'); -const path = require('node:path'); -const fs = require('node:fs'); -const os = require('os') +const { Menu } = require('electron'); -// const BASE_PATH = 'dist/app/browser'; -const BASE_PATH = 'dist/app'; -const ICON_PATH = `${BASE_PATH}/resources/newton.png`; -const LSP_CONFIG_PATH = `${BASE_PATH}/resources/lsp-servers-config.json`; -let args = []; - - - -const createWindow = (startType = "build", debug = false, args = []) => { - this.args = args; - - const win = new BrowserWindow({ - width: 800, - height: 600, - transparent: true, - icon: path.join(__dirname, ICON_PATH), - webPreferences: { - preload: path.join(__dirname, 'preload.js'), - contextIsolation: true, - enableRemoteModule: false, - } - }); - - setupMenu(win); - - // win.setAutoHideMenuBar(true) - - if (debug == true) { - win.webContents.openDevTools(); - } - - if (startType === "build") { - win.loadFile( - path.join(__dirname, `${BASE_PATH}/index.html`) - ); - } - - if (startType === "ng-serve") { - win.loadURL('http://localhost:4200'); - } -} - -const setupMenu = (win) => { +const load = (win) => { const menu = Menu.buildFromTemplate([ { label: "File", @@ -57,16 +12,16 @@ const setupMenu = (win) => { 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: () => {} + click: () => win.webContents.send('menu-actions', "open-files") }, { label: 'save', click: () => win.webContents.send('menu-actions', "save-file") }, { label: 'Save As', click: () => win.webContents.send('menu-actions', "save-file-as") + }, { + label: 'Terminal', + click: () => {} } ] }, { @@ -140,31 +95,10 @@ const setupMenu = (win) => { Menu.setApplicationMenu(menu) } -const getFileContents = (_path, useRelativePath = false) => { - console.log(`Getting Contents For: ${_path}`); - - try { - if (!useRelativePath) { - return fs.readFileSync(_path, 'utf8'); - } else { - return fs.readFileSync(path.join(__dirname, _path), 'utf8'); - } - } catch(err) { - return `{"message": {"type": "error", "text": "Error: Could not read ${_path}"}}`; - } -} - -const getLspConfigData = () => { - const config = getFileContents(LSP_CONFIG_PATH, true); - return config.replaceAll("{user.home}", os.homedir()); -} - module.exports = { - newton: { - createWindow: createWindow, - getFileContents: getFileContents, - getLspConfigData: getLspConfigData + menu: { + load: load } }; \ No newline at end of file diff --git a/preload.js b/newton/preload.js similarity index 92% rename from preload.js rename to newton/preload.js index 308254b..07d5def 100644 --- a/preload.js +++ b/newton/preload.js @@ -13,8 +13,9 @@ contextBridge.exposeInMainWorld('main', { contextBridge.exposeInMainWorld('fs', { getLspConfigData: () => ipcRenderer.invoke("getLspConfigData"), getFileContents: (file) => ipcRenderer.invoke("getFileContents", file), + openFiles: (startPath) => ipcRenderer.invoke("openFiles", startPath), 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)), -}); +}); \ No newline at end of file diff --git a/package.json b/package.json index b59b86f..0577adc 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "name": "Newton Editor", "version": "0.0.1", "description": "A uniquly simple quasi-IDE for hardcore developers.", - "main": "main.js", + "main": "newton/main.js", "scripts": { "app": "ng build --base-href ./ && electron . --trace-warnings --start-as=build", "electron-build": "electron . --trace-warnings --start-as=build", diff --git a/src/app/app.component.ts b/src/app/app.component.ts index 540721a..285b81f 100644 --- a/src/app/app.component.ts +++ b/src/app/app.component.ts @@ -19,6 +19,7 @@ declare global { fs: { getLspConfigData: () => Promise, getFileContents: (arg0: any) => Promise, + openFiles: (arg0) => Promise, saveFile: (arg0: any, arg1: any) => Promise, saveFileAs: (arg0: any) => Promise, getPathForFile: any, diff --git a/src/app/editor/ace/ace-editor.base.ts b/src/app/editor/ace/ace-editor.base.ts index 2e797a0..f65da15 100644 --- a/src/app/editor/ace/ace-editor.base.ts +++ b/src/app/editor/ace/ace-editor.base.ts @@ -1,6 +1,7 @@ import { Directive, ElementRef, Input, ViewChild } from '@angular/core'; import { EditorSettings } from "../../common/configs/editor.config"; +import { NewtonFile } from '../../common/types/file.type'; @@ -12,6 +13,7 @@ export class AceEditorBase { uuid!: string; cutBuffer: string = ""; timerId: number = -1; + activeFile!: NewtonFile; constructor( @@ -22,9 +24,25 @@ export class AceEditorBase { console.log(this.editor.getSession()["$modeId"]) } + protected openFiles() { + let startDir = ""; + if (this.activeFile) { + let pathParts = this.activeFile.path.split("/"); + pathParts.pop(); + startDir = pathParts.join( '/' ); + } + + window.fs.openFiles(startDir); + } + protected saveFile() { - const text = this.editor.session.getValue(); -// window.fs.saveFile(text); + if (!this.activeFile) { + this.saveFileAs(); + return; + } + + const text = this.activeFile.session.getValue(); + window.fs.saveFile(this.activeFile.path, text); } protected saveFileAs() { diff --git a/src/app/editor/ace/ace-editor.component.ts b/src/app/editor/ace/ace-editor.component.ts index 35a1939..03acc3a 100644 --- a/src/app/editor/ace/ace-editor.component.ts +++ b/src/app/editor/ace/ace-editor.component.ts @@ -109,6 +109,13 @@ export class AceEditorComponent extends AceEditorBase { this.editor.session.destroy(); }, readOnly: true + }, { + name: "openFiles", + bindKey: {win: "ctrl-o", mac: "ctrl-o"}, + exec: () => { + this.openFiles(); + }, + readOnly: true }, { name: "saveFile", bindKey: {win: "ctrl-s", mac: "ctrl-s"}, diff --git a/src/app/editor/editors.component.ts b/src/app/editor/editors.component.ts index ae15db7..d545aa5 100644 --- a/src/app/editor/editors.component.ts +++ b/src/app/editor/editors.component.ts @@ -75,8 +75,8 @@ export class EditorsComponent { this.addTab(file); } - let session = this.files.get(paths[ paths.length - 1 ]).session; - this.setSession(session); + let file = this.files.get(paths[ paths.length - 1 ]); + this.setSession(file); }); window.main.onMenuActions(async (action: string) => { @@ -86,6 +86,9 @@ export class EditorsComponent { switch ( action ) { case "new-file": break; + case "open-files": + editorComponent.openFiles(); + break; case "save-file": editorComponent.saveFile(); break; @@ -130,23 +133,25 @@ export class EditorsComponent { } protected onFileDropped(files: any) { - this.loadFilesList(files).then((session: EditSession | undefined | null) => { - this.setSession(session); + this.loadFilesList(files).then((file: NewtonFile | undefined | null) => { + this.setSession(file); }); } - private async setSession(session: EditSession | undefined | null) { - if ( !session ) return; + private async setSession(file: NewtonFile | undefined | null) { + if ( !file ) return; + let editorComponent = this.getActiveEditorComponent(); + let editor = editorComponent.editor; - let editor = this.getActiveEditor(); - editor?.setSession(session); + editorComponent.activeFile = file; + editor.setSession(file.session); } private getSession() { let editorComponent = this.editors.get(this.activeEditor)?.instance; let editor = editorComponent.editor; - return editor?.getSession(); + return editor.getSession(); } private getActiveEditorComponent(): any { @@ -159,7 +164,7 @@ export class EditorsComponent { return editor; } - private async loadFilesList(files: Array): Promise { + private async loadFilesList(files: Array): Promise { for (let i = 0; i < files.length; i++) { const file = files[i]; const path = window.fs.getPathForFile(file); @@ -171,7 +176,7 @@ export class EditorsComponent { this.addTab(file); } - return files[ files.length - 1 ].session; + return files[ files.length - 1 ]; } private async addFile(path: string, file: NewtonFile): Promise {