diff --git a/README.md b/README.md
index 5b16fb5..cd4a134 100644
--- a/README.md
+++ b/README.md
@@ -1,3 +1 @@
-# Newtan
-
-Newton editor written with Angular + Electron
\ No newline at end of file
+# Newton
diff --git a/angular.json b/angular.json
new file mode 100644
index 0000000..09fb881
--- /dev/null
+++ b/angular.json
@@ -0,0 +1,116 @@
+{
+ "$schema":"./node_modules/@angular/cli/lib/config/schema.json",
+ "version":1,
+ "newProjectRoot":"",
+ "cli":{
+ "analytics":false
+ },
+ "projects":{
+ "App":{
+ "projectType":"application",
+ "root":"",
+ "sourceRoot":"src",
+ "prefix":"app",
+ "architect":{
+ "build":{
+ "builder":"@angular-devkit/build-angular:browser",
+ "options":{
+ "outputPath":"dist/app",
+ "index":"src/index.html",
+ "main":"src/main.ts",
+ "polyfills":[
+ "zone.js"
+ ],
+ "tsConfig":"tsconfig.app.json",
+ "assets":[
+ {
+ "glob":"**/*",
+ "input":"public",
+ "output":"resources"
+ },
+ {
+ "glob":"**/*",
+ "input":"node_modules/ace-builds/src-noconflict",
+ "output":"ace"
+ }
+ ],
+ "stylePreprocessorOptions":{
+ },
+ "styles":[
+ "node_modules/bootstrap/scss/bootstrap.scss",
+ "node_modules/bootstrap-icons/font/bootstrap-icons.css",
+ "src/assets/css/styles.css",
+ "src/assets/css/ace-overrides.css"
+ ],
+ "scripts":[
+ ],
+ "optimization": false
+ },
+ "configurations":{
+ "production":{
+ "budgets":[
+ {
+ "type":"initial",
+ "maximumWarning":"5MB",
+ "maximumError":"50MB"
+ },
+ {
+ "type":"anyComponentStyle",
+ "maximumWarning":"4kB",
+ "maximumError":"8kB"
+ }
+ ],
+ "optimization":false
+ },
+ "development":{
+ "outputHashing": "all",
+ "sourceMap": true,
+ "extractCss": true,
+ "namedChunks": true,
+ "extractLicenses": true,
+ "vendorChunk": false,
+ }
+ },
+ "defaultConfiguration":"production"
+ },
+ "serve":{
+ "builder":"@angular-devkit/build-angular:dev-server",
+ "configurations":{
+ "production":{
+ "buildTarget":"App:build:production"
+ },
+ "development":{
+ "buildTarget":"App:build:development"
+ }
+ },
+ "defaultConfiguration":"development"
+ },
+ "extract-i18n":{
+ "builder":"@angular-devkit/build-angular:extract-i18n"
+ },
+ "test":{
+ "builder":"@angular-devkit/build-angular:karma",
+ "options":{
+ "polyfills":[
+ "zone.js",
+ "zone.js/testing"
+ ],
+ "tsConfig":"tsconfig.spec.json",
+ "assets":[
+ {
+ "glob":"**/*",
+ "input":"public"
+ }
+ ],
+ "styles":[
+ "src/styles.css"
+ ],
+ "scripts":[
+
+ ]
+ }
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/app.js b/app.js
new file mode 100644
index 0000000..c106438
--- /dev/null
+++ b/app.js
@@ -0,0 +1,73 @@
+const { BrowserWindow } = require('electron');
+const path = require('node:path');
+const fs = require('node:fs');
+const os = require('os')
+
+
+
+// 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,
+ }
+ });
+
+// win.removeMenu()
+
+ 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 getFileContents = (_path, useRelativePath = false) => {
+ 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
+ }
+};
\ No newline at end of file
diff --git a/main.js b/main.js
new file mode 100644
index 0000000..8a5f258
--- /dev/null
+++ b/main.js
@@ -0,0 +1,86 @@
+try {
+ require('electron-reloader')(module)
+} catch {}
+
+
+
+const { app, ipcMain } = require('electron');
+const { newton } = require('./app');
+const path = require('node:path');
+const fs = require('node:fs');
+
+
+
+let startType = "";
+let isDebug = false;
+let args = [];
+
+
+
+const loadArgs = () => {
+ console.log("\n\nStart KArgs:");
+ const hasStartAs = app.commandLine.hasSwitch("start-as");
+ if (hasStartAs) {
+ startType = app.commandLine.getSwitchValue("start-as");
+ console.log("Has start-as switch...");
+ console.log(startType);
+ }
+
+ const hasDebug = app.commandLine.hasSwitch("app-debug");
+ if (hasDebug) {
+ isDebug = app.commandLine.getSwitchValue("app-debug");
+ console.log("Has app-debug switch...");
+ console.log(isDebug);
+ }
+
+ console.log("\n\nStart VArgs:");
+ args = process.argv.slice(4); // remove up to --start-as ...
+ args.forEach((val, index, array) => {
+ console.log(index + ': ' + val);
+ console.log();
+ });
+
+}
+
+const loadHandlers = () => {
+ ipcMain.handle('getLspConfigData', (eve) => newton.getLspConfigData());
+ ipcMain.handle('getFileContents', (eve, file) => newton.getFileContents(file));
+}
+
+app.whenReady().then(() => {
+ loadArgs();
+ loadHandlers();
+ newton.createWindow(startType, isDebug, args);
+})
+
+app.on('activate', () => {
+ if (BrowserWindow.getAllWindows().length === 0) {
+ newton.createWindow(startType, isDebug, args);
+ };
+});
+
+app.on('window-all-closed', () => {
+ if (process.platform !== 'darwin') {
+ app.quit()
+ };
+})
+
+process.on('uncaughtException', (error) => {
+ if (error.message != null) {
+ console.log(error.message);
+ }
+
+ if (error.stack != null) {
+ console.log(error.stack);
+ }
+});
+
+process.on('unhandledRejection', function(error = {}) {
+ if (error.message != null) {
+ console.log(error.message);
+ }
+
+ if (error.stack != null) {
+ console.log(error.stack);
+ }
+});
diff --git a/package.json b/package.json
new file mode 100644
index 0000000..b59b86f
--- /dev/null
+++ b/package.json
@@ -0,0 +1,57 @@
+{
+ "name": "Newton Editor",
+ "version": "0.0.1",
+ "description": "A uniquly simple quasi-IDE for hardcore developers.",
+ "main": "main.js",
+ "scripts": {
+ "app": "ng build --base-href ./ && electron . --trace-warnings --start-as=build",
+ "electron-build": "electron . --trace-warnings --start-as=build",
+ "electron-concurrently": "concurrently 'ng serve' 'electron . --trace-warnings --start-as=ng-serve'",
+ "ng-serve": "ng serve",
+ "ng-build": "ng build",
+ "ng-watch-build": "ng build --watch --configuration development",
+ "ng-test": "ng test",
+ "test": "echo \"Error: no test specified\" && exit 1"
+ },
+ "private": true,
+ "author": "ITDominator",
+ "license": "GPL-2.0-only",
+ "dependencies": {
+ "@angular/cli": "19.2.9",
+ "@angular/common": "19.2.0",
+ "@angular/compiler": "19.2.0",
+ "@angular/core": "19.2.0",
+ "@angular/forms": "19.2.0",
+ "@angular/platform-browser": "19.2.0",
+ "@angular/platform-browser-dynamic": "19.2.0",
+ "@angular/platform-server": "19.2.0",
+ "@angular/router": "19.2.0",
+ "ace-builds": "1.41.0",
+ "ace-linters": "1.5.3",
+ "bootstrap": "5.3.6",
+ "bootstrap-icons": "1.12.1",
+ "express": "4.18.2",
+ "rxjs": "7.8.0",
+ "tslib": "2.3.0",
+ "uuid": "11.1.0",
+ "zone.js": "0.15.0"
+ },
+ "devDependencies": {
+ "@angular-devkit/build-angular": "19.2.8",
+ "@angular/cli": "19.2.8",
+ "@angular/compiler-cli": "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-reloader": "1.2.3",
+ "jasmine-core": "5.6.0",
+ "karma": "6.4.0",
+ "karma-chrome-launcher": "3.2.0",
+ "karma-coverage": "2.2.0",
+ "karma-jasmine": "5.1.0",
+ "karma-jasmine-html-reporter": "2.1.0",
+ "typescript": "5.7.2"
+ }
+}
\ No newline at end of file
diff --git a/preload.js b/preload.js
new file mode 100644
index 0000000..208ad77
--- /dev/null
+++ b/preload.js
@@ -0,0 +1,14 @@
+const { contextBridge, ipcRenderer, webUtils } = require('electron')
+
+
+contextBridge.exposeInMainWorld('electron', {
+ node: () => process.versions.node,
+ chrome: () => process.versions.chrome,
+ electron: () => process.versions.electron,
+});
+
+contextBridge.exposeInMainWorld('fs', {
+ getLspConfigData: () => ipcRenderer.invoke("getLspConfigData"),
+ getFileContents: (file) => ipcRenderer.invoke("getFileContents", file),
+ getPathForFile: (file) => webUtils.getPathForFile(file)
+});
diff --git a/public/lsp-servers-config.json b/public/lsp-servers-config.json
new file mode 100644
index 0000000..d92b98c
--- /dev/null
+++ b/public/lsp-servers-config.json
@@ -0,0 +1,290 @@
+{
+ "java": {
+ "info": "https://download.eclipse.org/jdtls/",
+ "info-init-options": "https://github.com/eclipse-jdtls/eclipse.jdt.ls/wiki/Running-the-JAVA-LS-server-from-the-command-line",
+ "info-import-build": "https://www.javahotchocolate.com/tutorials/build-path.html",
+ "info-external-class-paths": "https://github.com/eclipse-jdtls/eclipse.jdt.ls/issues/3291",
+ "link": "https://download.eclipse.org/jdtls/milestones/?d",
+ "command": "lsp-ws-proxy --listen 4114 -- jdtls",
+ "alt-command": "lsp-ws-proxy -- jdtls",
+ "alt-command2": "java-language-server",
+ "socket": "ws://127.0.0.1:4114/?name=jdtls",
+ "alt-socket": "ws://127.0.0.1:3030/?name=java-language-server",
+ "initialization-options": {
+ "bundles": [
+ "intellicode-core.jar"
+ ],
+ "workspaceFolders": [
+ "file://"
+ ],
+ "extendedClientCapabilities": {
+ "classFileContentsSupport": true,
+ "executeClientCommandSupport": true
+ },
+ "settings": {
+ "java": {
+ "autobuild": {
+ "enabled": false
+ },
+ "completion": {
+ "enabled": true,
+ "importOrder": [
+ "java",
+ "javax",
+ "org",
+ "com"
+ ]
+ },
+ "configuration": {
+ "maven": {
+ "userSettings": "{user.home}/.config/jdtls/settings.xml",
+ "globalSettings": "{user.home}/.config/jdtls/settings.xml"
+ },
+ "runtimes": [
+ {
+ "name": "JavaSE-17",
+ "path": "/usr/lib/jvm/default-runtime",
+ "javadoc": "https://docs.oracle.com/en/java/javase/17/docs/api/",
+ "default": true
+ }
+ ]
+ },
+ "classPath": [
+ "{user.home}/.config/jdtls/m2/repository/**/*-sources.jar",
+ "lib/**/*-sources.jar"
+ ],
+ "docPath": [
+ "{user.home}/.config/jdtls/m2/repository/**/*-javadoc.jar",
+ "lib/**/*-javadoc.jar"
+ ],
+ "silentNotification": true,
+ "project": {
+ "encoding": "ignore",
+ "outputPath": "bin",
+ "referencedLibraries": [
+ "lib/**/*.jar",
+ "{user.home}/.config/jdtls/m2/repository/**/*.jar"
+ ],
+ "importOnFirstTimeStartup": "automatic",
+ "importHint": true,
+ "resourceFilters": [
+ "node_modules",
+ "\\.git"
+ ],
+ "sourcePaths": [
+ "src",
+ "{user.home}/.config/jdtls/m2/repository/**/*.jar"
+ ]
+ },
+ "sources": {
+ "organizeImports": {
+ "starThreshold": 99,
+ "staticStarThreshold": 99
+ }
+ },
+ "imports": {
+ "gradle": {
+ "wrapper": {
+ "checksums": []
+ }
+ }
+ },
+ "import": {
+ "maven": {
+ "enabled": true,
+ "offline": {
+ "enabled": false
+ },
+ "disableTestClasspathFlag": false
+ },
+ "gradle": {
+ "enabled": true,
+ "wrapper": {
+ "enabled": true
+ },
+ "version": "",
+ "home": "abs(static/gradle-7.3.3)",
+ "java": {
+ "home": "abs(static/launch_jres/17.0.6-linux-x86_64)"
+ },
+ "offline": {
+ "enabled": false
+ },
+ "arguments": [],
+ "jvmArguments": [],
+ "user": {
+ "home": ""
+ },
+ "annotationProcessing": {
+ "enabled": true
+ }
+ },
+ "exclusions": [
+ "**/node_modules/**",
+ "**/.metadata/**",
+ "**/archetype-resources/**",
+ "**/META-INF/maven/**"
+ ],
+ "generatesMetadataFilesAtProjectRoot": false
+ },
+ "maven": {
+ "downloadSources": true,
+ "updateSnapshots": true
+ },
+ "signatureHelp": {
+ "enabled": true,
+ "description": {
+ "enabled": true
+ }
+ },
+ "implementationsCodeLens": {
+ "enabled": true
+ }
+ }
+ }
+ }
+ },
+ "python": {
+ "info": "https://github.com/python-lsp/python-lsp-server",
+ "command": "lsp-ws-proxy -- pylsp",
+ "alt-command": "pylsp",
+ "alt-command2": "lsp-ws-proxy --listen 4114 -- pylsp",
+ "alt-command3": "pylsp --ws --port 4114",
+ "socket": "ws://127.0.0.1:9999/?name=pylsp",
+ "initialization-options": {
+ "pyls": {
+ "plugins": {
+ "pycodestyle": {
+ "enabled": false
+ },
+ "pydocstyle": {
+ "enabled": false
+ },
+ "pyflakes": {
+ "enabled": false
+ },
+ "pylint": {
+ "enabled": false
+ },
+ "mccabe": {
+ "enabled": false
+ }
+ }
+ },
+ "pylsp": {
+ "plugins": {
+ "pycodestyle": {
+ "enabled": false
+ },
+ "pydocstyle": {
+ "enabled": false
+ },
+ "pyflakes": {
+ "enabled": false
+ },
+ "pylint": {
+ "enabled": false
+ },
+ "mccabe": {
+ "enabled": false
+ },
+ "ruff": true,
+ "pylsp_rope": {
+ "rename": true
+ },
+ "rope_rename": {
+ "enabled": true
+ },
+ "rope_autoimport": {
+ "enabled": true
+ },
+ "rope_completion": {
+ "enabled": true,
+ "eager": true
+ },
+ "jedi_rename": {
+ "enabled": true
+ },
+ "jedi_completion": {
+ "enabled": true,
+ "include_class_objects": true,
+ "include_function_objects": true,
+ "fuzzy": true
+ },
+ "jedi":{
+ "extra_paths": [
+ "{user.home}/Portable_Apps/py-venvs/pylsp-venv/venv/lib/python3.10/site-packages"
+ ],
+ "root_dir": ""
+ }
+ }
+ }
+ }
+ },
+ "python - jedi-language-server": {
+ "hidden": true,
+ "info": "https://pypi.org/project/jedi-language-server/",
+ "command": "jedi-language-server",
+ "alt-command": "lsp-ws-proxy --listen 3030 -- jedi-language-server",
+ "socket": "ws://127.0.0.1:3030/?name=jedi-language-server",
+ "initialization-options": {
+ "jediSettings": {
+ "autoImportModules": [],
+ "caseInsensitiveCompletion": true,
+ "debug": false
+ },
+ "completion": {
+ "disableSnippets": false,
+ "resolveEagerly": false,
+ "ignorePatterns": []
+ },
+ "markupKindPreferred": "markdown",
+ "workspace": {
+ "extraPaths": [
+ "{user.home}/Portable_Apps/py-venvs/pylsp-venv/venv/lib/python3.10/site-packages"
+ ],
+ "environmentPath": "{user.home}/Portable_Apps/py-venvs/gtk-apps-venv/venv/bin/python",
+ "symbols": {
+ "ignoreFolders": [".nox", ".tox", ".venv", "__pycache__", "venv"],
+ "maxSymbols": 20
+ }
+ }
+ }
+ },
+ "cpp": {
+ "info": "https://clangd.llvm.org/",
+ "command": "lsp-ws-proxy -- clangd",
+ "alt-command": "clangd",
+ "socket": "ws://127.0.0.1:3030/?name=clangd",
+ "initialization-options": {}
+ },
+ "sh": {
+ "info": "",
+ "command": "",
+ "alt-command": "",
+ "socket": "ws://127.0.0.1:3030/?name=shell",
+ "initialization-options": {}
+ },
+ "go": {
+ "info": "https://pkg.go.dev/golang.org/x/tools/gopls#section-readme",
+ "command": "lsp-ws-proxy -- gopls",
+ "alt-command": "gopls",
+ "socket": "ws://127.0.0.1:3030/?name=gopls",
+ "initialization-options": {}
+ },
+ "lua": {
+ "info": "https://github.com/LuaLS/lua-language-server",
+ "command": "lsp-ws-proxy -- lua-language-server",
+ "alt-command": "lua-language-server",
+ "socket": "ws://127.0.0.1:3030/?name=gopls",
+ "initialization-options": {}
+ },
+ "c": {
+ "hidden": true,
+ "info": "https://clangd.llvm.org/",
+ "command": "lsp-ws-proxy -- clangd",
+ "alt-command": "clangd",
+ "socket": "ws://127.0.0.1:3030/?name=clangd",
+ "initialization-options": {}
+ }
+}
\ No newline at end of file
diff --git a/public/newton.png b/public/newton.png
new file mode 100644
index 0000000..30ad12a
Binary files /dev/null and b/public/newton.png differ
diff --git a/src/app/app.component.css b/src/app/app.component.css
new file mode 100644
index 0000000..e69de29
diff --git a/src/app/app.component.html b/src/app/app.component.html
new file mode 100644
index 0000000..0368c2b
--- /dev/null
+++ b/src/app/app.component.html
@@ -0,0 +1,4 @@
+
+
+
+
\ No newline at end of file
diff --git a/src/app/app.component.ts b/src/app/app.component.ts
new file mode 100644
index 0000000..5248832
--- /dev/null
+++ b/src/app/app.component.ts
@@ -0,0 +1,43 @@
+import { Component } from '@angular/core';
+import { RouterOutlet } from '@angular/router';
+
+import { TabsComponent } from './editor/tabs/tabs.component';
+import { EditorsComponent } from './editor/editors.component';
+
+
+
+declare global {
+ interface Window {
+ electron: {
+ node: () => Promise,
+ chrome: () => Promise,
+ electron: () => Promise,
+ },
+ fs: {
+ getLspConfigData: () => Promise,
+ getFileContents: (arg0: any) => Promise,
+ getPathForFile: any,
+ }
+ }
+}
+
+
+
+@Component({
+ selector: 'app-root',
+ imports: [
+ TabsComponent,
+ EditorsComponent
+ ],
+ templateUrl: './app.component.html',
+ styleUrl: './app.component.css',
+ host: {
+ 'class': 'row'
+ }
+})
+export class AppComponent {
+ title = 'Newton';
+
+ constructor() {}
+
+}
\ No newline at end of file
diff --git a/src/app/app.config.server.ts b/src/app/app.config.server.ts
new file mode 100644
index 0000000..3514d3a
--- /dev/null
+++ b/src/app/app.config.server.ts
@@ -0,0 +1,11 @@
+import { mergeApplicationConfig, ApplicationConfig } from '@angular/core';
+import { provideServerRendering } from '@angular/platform-server';
+import { appConfig } from './app.config';
+
+const serverConfig: ApplicationConfig = {
+ providers: [
+ provideServerRendering(),
+ ]
+};
+
+export const config = mergeApplicationConfig(appConfig, serverConfig);
diff --git a/src/app/app.config.ts b/src/app/app.config.ts
new file mode 100644
index 0000000..a9af518
--- /dev/null
+++ b/src/app/app.config.ts
@@ -0,0 +1,9 @@
+import { ApplicationConfig, provideZoneChangeDetection } from '@angular/core';
+import { provideRouter } from '@angular/router';
+
+import { routes } from './app.routes';
+import { provideClientHydration, withEventReplay } from '@angular/platform-browser';
+
+export const appConfig: ApplicationConfig = {
+ providers: [provideZoneChangeDetection({ eventCoalescing: true }), provideRouter(routes), provideClientHydration(withEventReplay())]
+};
diff --git a/src/app/app.routes.ts b/src/app/app.routes.ts
new file mode 100644
index 0000000..dc39edb
--- /dev/null
+++ b/src/app/app.routes.ts
@@ -0,0 +1,3 @@
+import { Routes } from '@angular/router';
+
+export const routes: Routes = [];
diff --git a/src/app/common/configs/editor.config.ts b/src/app/common/configs/editor.config.ts
new file mode 100644
index 0000000..42722aa
--- /dev/null
+++ b/src/app/common/configs/editor.config.ts
@@ -0,0 +1,28 @@
+import { Keybindings } from './keybindings.config';
+
+export const EditorSettings: any = {
+ // BASE_PATH: 'https:cdnjs.cloudflare.com/ajax/libs/ace/1.40.1/',
+ BASE_PATH: 'ace',
+ KEYBINDINGS: Keybindings,
+ CONFIG: {
+ behavioursEnabled: true,
+ fontSize: "12px",
+ theme: "ace/theme/one_dark",
+ printMarginColumn: 80,
+ enableBasicAutocompletion: true,
+ enableLiveAutocompletion: true,
+ enableSnippets: true,
+ highlightActiveLine: true,
+ enableMultiselect: true,
+ tabSize: 4,
+ useSoftTabs: 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/configs/keybinding-newton.js b/src/app/common/configs/keybinding-newton.js
new file mode 100644
index 0000000..2a69344
--- /dev/null
+++ b/src/app/common/configs/keybinding-newton.js
@@ -0,0 +1,142 @@
+const editorCommands = [
+ {
+ name: "showSettingsMenu",
+ bindKey: {win: "Ctrl-Shift-m", mac: "Ctrl-Shift-m"},
+ exec: function(editor) {
+ ace.config.loadModule("ace/ext/settings_menu", function(module) {
+ module.init(editor);
+ editor.showSettingsMenu();
+ })
+ }
+ }, {
+ name: "showKeyboardShortcuts",
+ bindKey: {win: "ctrl-shift-h", mac: "command-shift-h"},
+ exec: function(editor) {
+ ace.config.loadModule("ace/ext/keybinding_menu", function(module) {
+ module.init(editor);
+ editor.showKeyboardShortcuts();
+ })
+ }
+ }, {
+ name: "openCommandPalette2",
+ bindKey: {linux: "Command-Shift-/|F1", win: "Ctrl-Shift-/|F1"},
+ exec: function(editor) {
+ editor.execCommand("openCommandPalette");
+ }
+ }, {
+ name: "showLSPManager",
+ bindKey: {win: "ctrl-m", mac: "command-m"},
+ exec: function(editor) {
+ $('#lsp-modal').modal("toggle");
+ }
+ }, {
+ name: "search",
+ bindKey: {win: "ctrl-f", mac: "ctrl-f"},
+ exec: function(editor) {
+ blockHigherNewtonEvePropigation = true;
+ searchReplace.toggleShow();
+ },
+ readOnly: true
+ }, {
+ name: "openFile",
+ bindKey: {win: "ctrl-o", mac: "ctrl-o"},
+ exec: function(editor) {
+ fpath = aceSessions[currentSession]["fpath"]
+ sendMessage("open_file", "", "", fpath, "");
+ },
+ readOnly: true
+ }, {
+ name: "saveSession",
+ bindKey: {win: "ctrl-s", mac: "ctrl-s"},
+ exec: function(editor) {
+ saveSession(currentSession);
+ },
+ readOnly: true
+ }, {
+ name: "newSession",
+ bindKey: {win: "ctrl-t", mac: "ctrl-t"},
+ exec: function(editor) {
+ newSession();
+ },
+ readOnly: true
+ }, {
+ name: "closeSession",
+ bindKey: {win: "ctrl-w", mac: "ctrl-w"},
+ exec: function(editor) {
+ closeSession(currentSession);
+ },
+ readOnly: true
+ }, {
+ name: "toggleLineHighlight",
+ bindKey: {win: "ctrl-h", mac: "ctrl-h"},
+ exec: function(editor) {
+ toggleLineHighlight();
+ },
+ readOnly: true
+ }, {
+ name: "gotoDefinition",
+ bindKey: {win: "ctrl-g", mac: "ctrl-g"},
+ exec: function(editor) {
+ console.log("Goto stub...");
+ // lspProvider.$messageController.postMessage(Message(), callback);
+ },
+ readOnly: true
+ }, {
+ name: "movelinesUp",
+ bindKey: {win: "ctrl-up", mac: "ctrl-up"},
+ exec: function(editor) {
+ editor.execCommand("movelinesup");
+ },
+ readOnly: true
+ }, {
+ name: "movelinesDown",
+ bindKey: {win: "ctrl-down", mac: "ctrl-down"},
+ exec: function(editor) {
+ editor.execCommand("movelinesdown");
+ },
+ readOnly: true
+ }, {
+ name: "tgglTopMainMenubar",
+ bindKey: {win: "ctrl-0", mac: "ctrl-0"},
+ exec: function(editor) {
+ sendMessage("tggl_top_main_menubar", "", "", "", "");
+ },
+ readOnly: true
+ }, {
+ name: "zoomIn",
+ bindKey: {win: "ctrl-=", mac: "ctrl-="},
+ exec: function(editor) {
+ zoomIn();
+ },
+ readOnly: true
+ }, {
+ name: "zoomOut",
+ bindKey: {win: "ctrl--", mac: "ctrl--"},
+ exec: function(editor) {
+ zoomOut();
+ },
+ readOnly: true
+ }, {
+ name: "scrollUp",
+ bindKey: {win: "alt-up", mac: "alt-up"},
+ exec: function(editor) {
+ editor.execCommand("scrollup");
+ },
+ readOnly: true
+ }, {
+ name: "scrollDown",
+ bindKey: {win: "alt-down", mac: "alt-down"},
+ exec: function(editor) {
+ editor.execCommand("scrolldown");
+ },
+ readOnly: true
+ }, {
+ name: "launhLSP",
+ bindKey: {win: "ctrl-l", mac: "ctrl-l"},
+ exec: function(editor) {
+ loadLSPManager();
+ },
+ readOnly: true
+ }
+
+];
\ No newline at end of file
diff --git a/src/app/common/configs/keybindings.config.ts b/src/app/common/configs/keybindings.config.ts
new file mode 100644
index 0000000..057e4cd
--- /dev/null
+++ b/src/app/common/configs/keybindings.config.ts
@@ -0,0 +1,146 @@
+export const Keybindings: Array = [
+// export const Keybindings: any = [
+ // {
+ // name: "showSettingsMenu",
+ // bindKey: {win: "Ctrl-Shift-m", mac: "Ctrl-Shift-m"},
+ // exec: () => {
+ // ace.config.loadModule("ace/ext/settings_menu", function(module) {
+ // module.init(editor);
+ // editor.showSettingsMenu();
+ // })
+ // }
+ // }, {
+ // name: "showKeyboardShortcuts",
+ // bindKey: {win: "ctrl-shift-h", mac: "command-shift-h"},
+ // exec: () => {
+ // ace.config.loadModule("ace/ext/keybinding_menu", function(module) {
+ // module.init(editor);
+ // editor.showKeyboardShortcuts();
+ // })
+ // }
+ // }, {
+ // name: "openCommandPalette2",
+ // bindKey: {linux: "Command-Shift-/|F1", win: "Ctrl-Shift-/|F1"},
+ // exec: () => {
+ // editor.execCommand("openCommandPalette");
+ // }
+ // }, {
+ // name: "showLSPManager",
+ // bindKey: {win: "ctrl-m", mac: "command-m"},
+ // exec: () => {
+ // $('//lsp-modal').modal("toggle");
+ // }
+ // }, {
+ {
+ name: "search",
+ bindKey: {win: "ctrl-f", mac: "ctrl-f"},
+ exec: () => {
+ // blockHigherNewtonEvePropigation = true;
+ // searchReplace.toggleShow();
+ console.log("Search");
+ },
+ readOnly: true
+ }
+ // }, {
+ // name: "openFile",
+ // bindKey: {win: "ctrl-o", mac: "ctrl-o"},
+ // exec: () => {
+ // fpath = aceSessions[currentSession]["fpath"]
+ // sendMessage("open_file", "", "", fpath, "");
+ // },
+ // readOnly: true
+ // }, {
+ // name: "saveSession",
+ // bindKey: {win: "ctrl-s", mac: "ctrl-s"},
+ // exec: () => {
+ // saveSession(currentSession);
+ // },
+ // readOnly: true
+ // }, {
+ // name: "newSession",
+ // bindKey: {win: "ctrl-t", mac: "ctrl-t"},
+ // exec: () => {
+ // newSession();
+ // },
+ // readOnly: true
+ // }, {
+ // name: "closeSession",
+ // bindKey: {win: "ctrl-w", mac: "ctrl-w"},
+ // exec: () => {
+ // closeSession(currentSession);
+ // },
+ // readOnly: true
+ // }, {
+ // name: "toggleLineHighlight",
+ // bindKey: {win: "ctrl-h", mac: "ctrl-h"},
+ // exec: () => {
+ // toggleLineHighlight();
+ // },
+ // readOnly: true
+ // }, {
+ // name: "gotoDefinition",
+ // bindKey: {win: "ctrl-g", mac: "ctrl-g"},
+ // exec: () => {
+ // console.log("Goto stub...");
+ // lspProvider.$messageController.postMessage(Message(), callback);
+ // },
+ // readOnly: true
+ // }, {
+ // name: "movelinesUp",
+ // bindKey: {win: "ctrl-up", mac: "ctrl-up"},
+ // exec: () => {
+ // editor.execCommand("movelinesup");
+ // },
+ // readOnly: true
+ // }, {
+ // name: "movelinesDown",
+ // bindKey: {win: "ctrl-down", mac: "ctrl-down"},
+ // exec: () => {
+ // editor.execCommand("movelinesdown");
+ // },
+ // readOnly: true
+ // }, {
+ // name: "tgglTopMainMenubar",
+ // bindKey: {win: "ctrl-0", mac: "ctrl-0"},
+ // exec: () => {
+ // sendMessage("tggl_top_main_menubar", "", "", "", "");
+ // },
+ // readOnly: true
+ // }, {
+ // name: "zoomIn",
+ // bindKey: {win: "ctrl-=", mac: "ctrl-="},
+ // exec: () => {
+ // zoomIn();
+ // },
+ // readOnly: true
+ // }, {
+ // name: "zoomOut",
+ // bindKey: {win: "ctrl--", mac: "ctrl--"},
+ // exec: () => {
+ // zoomOut();
+ // },
+ // readOnly: true
+ // }, {
+ // name: "scrollUp",
+ // bindKey: {win: "alt-up", mac: "alt-up"},
+ // exec: () => {
+ // editor.execCommand("scrollup");
+ // },
+ // readOnly: true
+ // }, {
+ // name: "scrollDown",
+ // bindKey: {win: "alt-down", mac: "alt-down"},
+ // exec: () => {
+ // editor.execCommand("scrolldown");
+ // },
+ // readOnly: true
+ // }, {
+ // name: "launhLSP",
+ // bindKey: {win: "ctrl-l", mac: "ctrl-l"},
+ // exec: () => {
+ // loadLSPManager();
+ // },
+ // readOnly: true
+ // }
+
+];
\ No newline at end of file
diff --git a/src/app/common/directives/dnd.directive.ts b/src/app/common/directives/dnd.directive.ts
new file mode 100644
index 0000000..c227396
--- /dev/null
+++ b/src/app/common/directives/dnd.directive.ts
@@ -0,0 +1,46 @@
+import {
+ Directive,
+ Output,
+ Input,
+ EventEmitter,
+ HostBinding,
+ HostListener
+} from '@angular/core';
+
+import { NewtonFile } from '../types/file.type';
+
+
+@Directive({
+ selector: '[dropzone]'
+})
+export class DndDirective {
+ @HostBinding('class.fileover') fileOver!: boolean;
+ @Output() fileDropped = new EventEmitter();
+
+ @HostListener('dragover', ['$event']) onDragOver(evt: any) {
+ evt.preventDefault();
+ evt.stopPropagation();
+
+ this.fileOver = true;
+ }
+
+ @HostListener('dragleave', ['$event']) public onDragLeave(evt: any) {
+ evt.preventDefault();
+ evt.stopPropagation();
+
+ this.fileOver = false;
+ }
+
+ @HostListener('drop', ['$event']) public ondrop(evt: any) {
+ evt.preventDefault();
+ evt.stopPropagation();
+
+ this.fileOver = false;
+ let files: Array = evt.dataTransfer.files;
+
+ if (files.length == 0) return;
+
+ this.fileDropped.emit(files);
+ }
+
+}
\ No newline at end of file
diff --git a/src/app/common/services/editor/editors.service.ts b/src/app/common/services/editor/editors.service.ts
new file mode 100644
index 0000000..87bd0f7
--- /dev/null
+++ b/src/app/common/services/editor/editors.service.ts
@@ -0,0 +1,22 @@
+import { Injectable } from '@angular/core';
+import { BehaviorSubject, ReplaySubject, Observable } from 'rxjs';
+
+import { ServiceMessage } from '../../types/service-message.type';
+
+
+@Injectable({
+ providedIn: 'root'
+})
+export class EditorsService {
+ private dataSubject: ReplaySubject = new ReplaySubject(1);
+
+ constructor() {}
+
+ setData(data: ServiceMessage): void {
+ this.dataSubject.next(data);
+ }
+
+ getData$(): Observable {
+ return this.dataSubject.asObservable();
+ }
+}
\ No newline at end of file
diff --git a/src/app/common/services/editor/tabs/tabs.service.ts b/src/app/common/services/editor/tabs/tabs.service.ts
new file mode 100644
index 0000000..e26a214
--- /dev/null
+++ b/src/app/common/services/editor/tabs/tabs.service.ts
@@ -0,0 +1,22 @@
+import { Injectable } from '@angular/core';
+import { BehaviorSubject, ReplaySubject, Observable } from 'rxjs';
+
+import { ServiceMessage } from '../../../types/service-message.type';
+
+
+@Injectable({
+ providedIn: 'root'
+})
+export class TabsService {
+ private dataSubject: ReplaySubject = new ReplaySubject(1);
+
+ constructor() {}
+
+ setData(data: ServiceMessage): void {
+ this.dataSubject.next(data);
+ }
+
+ getData$(): Observable {
+ return this.dataSubject.asObservable();
+ }
+}
\ No newline at end of file
diff --git a/src/app/common/types/file.type.ts b/src/app/common/types/file.type.ts
new file mode 100644
index 0000000..aa167f0
--- /dev/null
+++ b/src/app/common/types/file.type.ts
@@ -0,0 +1,9 @@
+import { EditSession } from 'ace-builds';
+
+
+export interface NewtonFile extends File {
+ fname: string,
+ path: string,
+ hash: string,
+ session: EditSession
+}
\ No newline at end of file
diff --git a/src/app/common/types/service-message.type.ts b/src/app/common/types/service-message.type.ts
new file mode 100644
index 0000000..794a74d
--- /dev/null
+++ b/src/app/common/types/service-message.type.ts
@@ -0,0 +1,5 @@
+export class ServiceMessage {
+ action: string = "none";
+ message: string = "";
+ uuid!: string;
+}
\ No newline at end of file
diff --git a/src/app/editor/ace/ace-editor.component.css b/src/app/editor/ace/ace-editor.component.css
new file mode 100644
index 0000000..5156172
--- /dev/null
+++ b/src/app/editor/ace/ace-editor.component.css
@@ -0,0 +1,23 @@
+/*
+.editor {
+ position: fixed;
+ top: 0;
+ right: 0;
+ bottom: 0;
+ left: 0;
+}
+
+.editor {
+ height: 100vh;
+ width: auto;
+}
+*/
+
+
+.editor {
+ position: relative;
+ height: 100%;
+ width: 100%;
+}
+
+
diff --git a/src/app/editor/ace/ace-editor.component.html b/src/app/editor/ace/ace-editor.component.html
new file mode 100644
index 0000000..a086340
--- /dev/null
+++ b/src/app/editor/ace/ace-editor.component.html
@@ -0,0 +1,2 @@
+
+
diff --git a/src/app/editor/ace/ace-editor.component.ts b/src/app/editor/ace/ace-editor.component.ts
new file mode 100644
index 0000000..cc16bbc
--- /dev/null
+++ b/src/app/editor/ace/ace-editor.component.ts
@@ -0,0 +1,108 @@
+import { Component, ElementRef, ViewChild, Input } from '@angular/core';
+
+import { AceLanguageClient, LanguageClientConfig } from 'ace-linters/build/ace-language-client';
+
+// Import Ace and its modes/themes so that `ace` global is defined
+import * as ace from 'ace-builds/src-noconflict/ace';
+import 'ace-builds/src-noconflict/theme-one_dark';
+import { LanguageProvider } from "ace-linters";
+
+import { EditorSettings } from "../../common/configs/editor.config";
+
+import { EditorsService } from '../../common/services/editor/editors.service';
+import { ServiceMessage } from '../../common/types/service-message.type';
+
+
+
+@Component({
+ selector: 'ace-editor',
+ standalone: true,
+ imports: [
+ ],
+ templateUrl: './ace-editor.component.html',
+ styleUrl: './ace-editor.component.css',
+ host: {
+ 'class': 'col',
+ '(click)': 'onClick($event)'
+ }
+})
+export class AceEditorComponent {
+
+ @Input() editorSettings!: typeof EditorSettings;
+ @ViewChild('editor') editorElm!: ElementRef;
+ editor!: any;
+ uuid!: string;
+ lspConfigData!: {};
+
+
+ constructor(private editorsService: EditorsService) {
+ }
+
+
+ public ngAfterViewInit(): void {
+ this.loadAce();
+ this.loadLanguageProviders();
+ }
+
+ public loadAce(): void {
+ ace.config.set('basePath', this.editorSettings.BASE_PATH);
+
+ this.editor = ace.edit( this.editorElm.nativeElement );
+ this.editor.setOptions( this.editorSettings.CONFIG );
+// this.editor.commands.addCommands( this.editorSettings.KEYBINDINGS );
+ }
+
+ protected onClick(event: any) {
+ let message = new ServiceMessage();
+ message.action = "set-editor";
+ message.message = this.uuid;
+ message.uuid = this.uuid;
+
+ this.editorsService.setData(message);
+ }
+
+ public loadLanguageProviders(): void {
+ let languageProvider = this.getLanguageProviderWithClientServers();
+// let languageProvider = this.getLanguageProviderWithWebWorker();
+ languageProvider.registerEditor(this.editor);
+
+ }
+
+
+
+ public getLanguageProviderWithClientServers() {
+ let _initializationOptions = {};
+
+ if (Object.keys(this.lspConfigData).length !== 0) {
+// _initializationOptions = this.lspConfigData[ this.editor.session.getMode() ]["initialization-options"];
+ _initializationOptions = this.lspConfigData[ "python" ]["initialization-options"];
+// _initializationOptions = this.lspConfigData[ "java" ]["initialization-options"];
+// _initializationOptions = this.lspConfigData[ "cpp" ]["initialization-options"];
+ console.log(_initializationOptions.toString());
+ }
+
+ let servers: LanguageClientConfig[] = [
+ {
+ module: () => import("ace-linters/build/language-client"),
+ modes: "python",
+// modes: "java",
+// modes: "cpp",
+ type: "socket",
+ socket: new WebSocket("ws://127.0.0.1:9999/python"),
+// socket: new WebSocket("ws://127.0.0.1:9999/?name=clangd"),
+// socket: new WebSocket("ws://127.0.0.1:9999/?name=jdtls"),
+// socket: new WebSocket("ws://127.0.0.1:9999/?name=pylsp"),
+// initializationOptions: _initializationOptions
+ }
+ ];
+
+ return AceLanguageClient.for(servers);
+ }
+
+
+ public getLanguageProviderWithWebWorker() {
+ let worker = new Worker(new URL('./webworker.js', import.meta.url));
+ return LanguageProvider.create(worker);
+ }
+
+}
\ No newline at end of file
diff --git a/src/app/editor/ace/webworker.ts b/src/app/editor/ace/webworker.ts
new file mode 100644
index 0000000..530b356
--- /dev/null
+++ b/src/app/editor/ace/webworker.ts
@@ -0,0 +1,165 @@
+import { ServiceManager } from "ace-linters/build/service-manager";
+
+let manager = new ServiceManager(self);
+
+manager.registerService("html", {
+ features: {signatureHelp: false},
+ module: () => import("ace-linters/build/html-service"),
+ className: "HtmlService",
+ modes: "html"
+});
+
+manager.registerService("css", {
+ features: {signatureHelp: false},
+ module: () => import("ace-linters/build/css-service"),
+ className: "CssService",
+ modes: "css"
+});
+
+manager.registerService("less", {
+ features: {signatureHelp: false},
+ module: () => import("ace-linters/build/css-service"),
+ className: "CssService",
+ modes: "less"
+});
+
+manager.registerService("scss", {
+ features: {signatureHelp: false},
+ module: () => import("ace-linters/build/css-service"),
+ className: "CssService",
+ modes: "scss"
+});
+
+manager.registerService("json", {
+ features: {signatureHelp: false, documentHighlight: false},
+ module: () => import("ace-linters/build/json-service"),
+ className: "JsonService",
+ modes: "json",
+});
+
+manager.registerService("json5", {
+ features: {signatureHelp: false, documentHighlight: false},
+ module: () => import("ace-linters/build/json-service"),
+ className: "JsonService",
+ modes: "json5",
+});
+
+manager.registerService("typescript", {
+ module: () => import("ace-linters/build/typescript-service"),
+ className: "TypescriptService",
+ modes: "typescript|tsx|javascript|jsx",
+});
+
+manager.registerService("yaml", {
+ features: {signatureHelp: false, documentHighlight: false},
+ module: () => import("ace-linters/build/yaml-service"),
+ className: "YamlService",
+ modes: "yaml",
+});
+
+manager.registerService("xml", {
+ features: {completion: false, completionResolve: false, diagnostics: true, format: false, hover: false, documentHighlight: false, signatureHelp: false},
+ module: () => import("ace-linters/build/xml-service"),
+ className: "XmlService",
+ modes: "xml",
+});
+
+manager.registerService("php", {
+ features: {completion: false, completionResolve: false, diagnostics: true, format: false, hover: false, documentHighlight: false, signatureHelp: false},
+ module: () => import("ace-linters/build/php-service"),
+ className: "PhpService",
+ modes: "php"
+});
+
+manager.registerService("javascript", {
+ features: {completion: false, completionResolve: false, diagnostics: true, format: false, hover: false, documentHighlight: false, signatureHelp: false},
+ module: () => import("ace-linters/build/javascript-service"),
+ className: "JavascriptService",
+ modes: "javascript",
+});
+
+
+manager.registerServer("pythonls", {
+ module: () => import("ace-linters/build/language-client"),
+ modes: "python",
+ type: "socket",
+ socket: new WebSocket("ws://127.0.0.1:9999/?name=pylsp")
+});
+
+
+
+/*
+
+
+manager.registerService("clang", {
+ module: () => import("ace-clang-linter/build/ace-clang-linter"),
+ className: "AceClangLinter",
+ modes: "c_cpp",
+});
+
+manager.registerService("lua", {
+ features: {completion: false, completionResolve: false, diagnostics: true, format: true, hover: false, documentHighlight: false, signatureHelp: false},
+ module: () => import("ace-lua-linter/build/ace-lua-linter"),
+ className: "AceLuaLinter",
+ modes: "lua",
+});
+
+manager.registerService("mysql", {
+ module: () => import("ace-sql-linter/build/mysql-service"),
+ className: "MySQLService",
+ modes: "mysql",
+});
+
+manager.registerService("zig", {
+ module: () => import("ace-zig-linter/build/ace-zig-linter"),
+ className: "AceZigLinter",
+ modes: "zig",
+});
+
+manager.registerService("python", {
+ features: {completion: false, completionResolve: false, diagnostics: true, format: true, hover: false, documentHighlight: false, signatureHelp: false},
+ module: () => import("ace-python-ruff-linter/build/python-service"),
+ className: "PythonService",
+ modes: "python",
+});
+
+manager.registerServer("svelte", {
+ module: () => import("ace-linters/build/language-client"),
+ modes: "html",
+ type: "socket",
+ socket: new WebSocket("ws://127.0.0.1:3030/svelte")
+});
+
+manager.registerServer("astro", {
+ module: () => import("ace-linters/build/language-client"),
+ modes: "astro",
+ type: "socket",
+ socket: new WebSocket("ws://127.0.0.1:3030/astro"),
+ initializationOptions: {
+ typescript: {
+ tsdk: "node_modules/typescript/lib", //path to typescript server
+ }
+ }
+});
+
+manager.registerServer("go", {
+ module: () => import("ace-linters/build/language-client"),
+ modes: "golang",
+ type: "socket",
+ socket: new WebSocket("ws://127.0.0.1:3030/go")
+});
+
+manager.registerService("dart", {
+ module: () => import("ace-dart-linter/build/ace-dart-linter"),
+ className: "AceDartLinter",
+ modes: "dart",
+});
+
+manager.registerService("golang", {
+ module: () => import("ace-go-linter/build/ace-go-linter"),
+ className: "AceGoLinter",
+ modes: "go",
+});
+
+
+*/
\ No newline at end of file
diff --git a/src/app/editor/editors.component.css b/src/app/editor/editors.component.css
new file mode 100644
index 0000000..aa8d03d
--- /dev/null
+++ b/src/app/editor/editors.component.css
@@ -0,0 +1,3 @@
+.dropzone {
+ height: 80vh;
+}
diff --git a/src/app/editor/editors.component.html b/src/app/editor/editors.component.html
new file mode 100644
index 0000000..296fcfd
--- /dev/null
+++ b/src/app/editor/editors.component.html
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/app/editor/editors.component.ts b/src/app/editor/editors.component.ts
new file mode 100644
index 0000000..fc302ea
--- /dev/null
+++ b/src/app/editor/editors.component.ts
@@ -0,0 +1,150 @@
+import { Component, ElementRef, ViewChild, TemplateRef, ComponentRef, ViewContainerRef } from '@angular/core';
+import { Subject, takeUntil } from 'rxjs';
+import * as uuid from 'uuid';
+
+import { EditSession } from 'ace-builds';
+import { getModeForPath } from 'ace-builds/src-noconflict/ext-modelist';
+
+import { AceEditorComponent } from "./ace/ace-editor.component";
+import { EditorsService } from '../common/services/editor/editors.service';
+import { TabsService } from '../common/services/editor/tabs/tabs.service';
+
+import { DndDirective } from '../common/directives/dnd.directive';
+import { NewtonFile } from '../common/types/file.type';
+import { ServiceMessage } from '../common/types/service-message.type';
+import { EditorSettings } from "../common/configs/editor.config";
+
+
+
+@Component({
+ selector: 'editors',
+ standalone: true,
+ imports: [
+ DndDirective
+ ],
+ templateUrl: './editors.component.html',
+ styleUrl: './editors.component.css',
+ host: {
+ 'class': 'row'
+ }
+})
+export class EditorsComponent {
+ private unsubscribe = new Subject
();
+
+ @ViewChild('containerRef', {read: ViewContainerRef}) containerRef!: ViewContainerRef;
+ editors: Map>;
+ editorSettings: typeof EditorSettings;
+ files: Map;
+ activeEditor!: string;
+ lspConfigData!: any;
+
+
+ constructor(
+ private editorsService: EditorsService,
+ private tabsService: TabsService
+ ) {
+ this.editorSettings = EditorSettings;
+ this.editors = new Map>();
+ this.files = new Map();
+ }
+
+
+ public ngAfterViewInit(): void {
+ this.editorsService.getData$().pipe(
+ takeUntil(this.unsubscribe)
+ ).subscribe((data: ServiceMessage) => {
+ if (data.action === "set-editor")
+ this.activeEditor = data.uuid;
+ });
+
+ this.getLspConfigData().then((lspConfigData: string) => {
+ this.lspConfigData = JSON.parse(lspConfigData);
+
+ if (this.lspConfigData["message"]) {
+ console.log(
+ "Warning: LSP this.lspConfigData is a 'message'",
+ this.lspConfigData
+ );
+ this.lspConfigData = {};
+ }
+
+ let editor = this.createEditor();
+ this.activeEditor = editor.instance.uuid;
+ this.createEditor();
+ })
+ }
+
+ ngOnDestroy() {
+ this.unsubscribe.next();
+ this.unsubscribe.complete();
+ }
+
+ private getLspConfigData(): Promise {
+ return window.fs.getLspConfigData();
+ }
+
+ private createEditor() {
+ const component = this.containerRef.createComponent(AceEditorComponent);
+ component.instance.editorSettings = this.editorSettings;
+ component.instance.uuid = uuid.v4();
+ component.instance.lspConfigData = this.lspConfigData;
+ this.editors.set(component.instance.uuid, component)
+
+ return component;
+ }
+
+ protected onFileDropped(event: any) {
+ this.loadFilesList(event).then((session: EditSession | undefined | null) => {
+ if ( !session ) return;
+
+ let editor = this.editors.get(this.activeEditor)?.instance.editor;
+ editor?.setSession(session);
+ });
+ }
+
+ private async loadFilesList(files: Array): Promise {
+ for (let i = 0; i < files.length; i++) {
+ const file = files[i];
+ const path = window.fs.getPathForFile(file);
+
+ if (!file || !path) continue;
+ if ( this.files.get(path) ) continue;
+
+ await this.addFile(path, file);
+ this.addTab(file);
+ }
+
+ return files[ files.length - 1 ].session;
+ }
+
+ private async addFile(path: string, file: NewtonFile) {
+ try {
+ let pathParts = path.split("/");
+ file.fname = pathParts[ pathParts.length - 1 ];
+ file.path = path;
+ file.hash = btoa(file.path);
+ let data = await window.fs.getFileContents(file.path);
+ file.session = new EditSession(data);
+
+ file.session.setMode(
+ getModeForPath( file.path ).mode
+ );
+
+ this.files.set(file.path, file);
+ } catch (error) {
+ console.log(
+ `---- Error ----\nPath: ${path}\nMessage: ${error}`
+ );
+ }
+ }
+
+ private async addTab(file: NewtonFile) {
+ let message = new ServiceMessage();
+ message.action = "create-tab";
+ message.message = file.fname;
+ message.uuid = file.hash;
+
+ this.tabsService.setData(message);
+ }
+
+}
\ No newline at end of file
diff --git a/src/app/editor/tabs/tab/tab.component.css b/src/app/editor/tabs/tab/tab.component.css
new file mode 100644
index 0000000..42ebab4
--- /dev/null
+++ b/src/app/editor/tabs/tab/tab.component.css
@@ -0,0 +1,32 @@
+.tab,
+.title,
+.close-button {
+ color: rgba(255, 255, 255, 0.64);
+}
+
+
+/*
+.tab {
+ float: left;
+ clear: left;
+ display: flow;
+}
+*/
+
+
+.title {
+ margin-left: 2em;
+ margin-right: 2em;
+}
+
+.close-button {
+ background: rgba(116, 0, 0, 0.64);
+ border-style: solid;
+ border-color: rgba(0, 0, 0, 0.64);
+ border-width: 1px;
+ border-radius: 0em 1em 0em 0em;
+}
+
+.close-button:hover {
+ background: rgba(256, 0, 0, 0.64);
+}
\ No newline at end of file
diff --git a/src/app/editor/tabs/tab/tab.component.html b/src/app/editor/tabs/tab/tab.component.html
new file mode 100644
index 0000000..10ea72c
--- /dev/null
+++ b/src/app/editor/tabs/tab/tab.component.html
@@ -0,0 +1,4 @@
+
+ {{title}}
+
+
\ No newline at end of file
diff --git a/src/app/editor/tabs/tab/tab.component.ts b/src/app/editor/tabs/tab/tab.component.ts
new file mode 100644
index 0000000..9fef6a9
--- /dev/null
+++ b/src/app/editor/tabs/tab/tab.component.ts
@@ -0,0 +1,30 @@
+import { Component } from '@angular/core';
+
+
+
+@Component({
+ selector: 'tab',
+ standalone: true,
+ imports: [
+ ],
+ templateUrl: './tab.component.html',
+ styleUrl: './tab.component.css',
+ host: {
+ 'class': 'col'
+// 'class': 'col tab'
+ }
+})
+export class TabComponent {
+
+ title: string;
+
+
+ constructor() {
+ this.title = "[NO TITLE]";
+ }
+
+ ngOnDestroy() {
+ }
+
+
+}
\ No newline at end of file
diff --git a/src/app/editor/tabs/tabs.component.css b/src/app/editor/tabs/tabs.component.css
new file mode 100644
index 0000000..e69de29
diff --git a/src/app/editor/tabs/tabs.component.html b/src/app/editor/tabs/tabs.component.html
new file mode 100644
index 0000000..c72e44d
--- /dev/null
+++ b/src/app/editor/tabs/tabs.component.html
@@ -0,0 +1,2 @@
+
+
diff --git a/src/app/editor/tabs/tabs.component.ts b/src/app/editor/tabs/tabs.component.ts
new file mode 100644
index 0000000..6f8f172
--- /dev/null
+++ b/src/app/editor/tabs/tabs.component.ts
@@ -0,0 +1,57 @@
+import { Component, ElementRef, ViewChild, TemplateRef, ComponentRef, ViewContainerRef } from '@angular/core';
+import { Subject, takeUntil } from 'rxjs';
+
+import { TabComponent } from './tab/tab.component';
+import { TabsService } from '../../common/services/editor/tabs/tabs.service';
+
+import { ServiceMessage } from '../../common/types/service-message.type';
+
+
+
+@Component({
+ selector: 'tabs',
+ standalone: true,
+ imports: [
+ ],
+ templateUrl: './tabs.component.html',
+ styleUrl: './tabs.component.css',
+ host: {
+ 'class': 'tabs scroller'
+ }
+})
+export class TabsComponent {
+ private unsubscribe = new Subject();
+
+ @ViewChild('containerRef', {read: ViewContainerRef}) containerRef!: ViewContainerRef;
+ tabs: Map>;
+ activeTab!: string;
+
+
+ constructor(private tabsService: TabsService) {
+ this.tabs = new Map>();
+ }
+
+
+ public ngAfterViewInit(): void {
+ this.tabsService.getData$().pipe(
+ takeUntil(this.unsubscribe)
+ ).subscribe((data: ServiceMessage) => {
+ if (data.action === "create-tab")
+ this.createTab(data.message, data.uuid);
+ });
+ }
+
+ ngOnDestroy() {
+ this.unsubscribe.next();
+ this.unsubscribe.complete();
+ }
+
+ private createTab(title: string, uuid: string) {
+ const component = this.containerRef.createComponent(TabComponent);
+ component.instance.title = title;
+ this.tabs.set(uuid, component)
+
+ return component;
+ }
+
+}
\ No newline at end of file
diff --git a/src/assets/css/ace-overrides.css b/src/assets/css/ace-overrides.css
new file mode 100644
index 0000000..8ed89de
--- /dev/null
+++ b/src/assets/css/ace-overrides.css
@@ -0,0 +1,14 @@
+/* TAGS */
+
+
+/* IDs */
+
+
+/* CLASSES */
+.ace_editor, .ace_gutter {
+ background-color: rgba(0, 0, 0, 0.0) !important;
+}
+
+.ace_autocomplete {
+ background-color: #25282c !important;
+}
\ No newline at end of file
diff --git a/src/assets/css/styles.css b/src/assets/css/styles.css
new file mode 100644
index 0000000..3f9f12c
--- /dev/null
+++ b/src/assets/css/styles.css
@@ -0,0 +1,116 @@
+/* TAGS */
+
+html {
+/*
+ background-color: rgba(64, 64, 64, 0.64);
+ background-color: rgb(40, 44, 52);
+*/
+ background-color: rgba(40, 44, 52, 0.64);
+ color: rgba(255, 255, 255, 0.64);
+}
+
+body {
+ background-color: rgba(64, 64, 64, 0.0)
+}
+
+
+/* IDs */
+
+
+/* CLASSES */
+
+.tabs {
+ display: flex;
+ overflow: auto;
+ margin-bottom: 0.5em;
+ padding-bottom: 0.4em;
+ padding-top: 0.4em;
+}
+
+
+/*
+.tab {
+ float: left;
+ clear: left;
+ display: contents;
+}
+*/
+
+.tab {
+ display: flex;
+ overflow: auto;
+ float: left;
+ margin-right: 0.5em;
+
+ border-radius: 1em 1em 0em 0em;
+
+ border-top-style: solid;
+ border-top-color: #ffffff64;
+ border-top-width: 2px;
+
+ border-left-style: solid;
+ border-left-color: #ffffff64;
+ border-left-width: 2px;
+
+ border-right-style: solid;
+ border-right-color: #ffffff64;
+ border-right-width: 2px;
+
+}
+
+
+
+
+
+.scroller {
+/*
+ -webkit-scrollbar-color: #00000084 #ffffff64;
+ scrollbar-color: #00000084 #ffffff64;
+*/
+ -webkit-scrollbar-color: #00000084 #ffffff06;
+ -webkit-scrollbar-width: thin;
+ scrollbar-color: #00000084 #ffffff06;
+ scrollbar-width: thin;
+}
+
+.noselect {
+ -webkit-touch-callout: none; /* iOS Safari */
+ -webkit-user-select: none; /* Safari */
+ -khtml-user-select: none; /* Konqueror HTML */
+ -moz-user-select: none; /* Old versions of Firefox */
+ -ms-user-select: none; /* Internet Explorer/Edge */
+ user-select: none; /* Non-prefixed version, currently
+ supported by Chrome, Edge, Opera and Firefox */
+}
+
+.page-alert-zone-container {
+ position: sticky;
+ top: 0em;
+ z-index: 2;
+}
+
+
+
+.searching,
+.search-success,
+.search-fail {
+ border-style: solid;
+ color: rgba(125, 125, 125, 1) !important;
+}
+
+.searching {
+ border-color: rgba(0, 225, 225, 0.64) !important;
+}
+.search-success {
+ background: rgba(136, 204, 39, 0.12) !important;
+ border-color: rgba(136, 204, 39, 1) !important;
+}
+.search-fail {
+ background: rgba(170, 18, 18, 0.12) !important;
+ border-color: rgba(200, 18, 18, 1) !important;
+}
+
+/* Other message text colors */
+.error { color: rgb(170, 18, 18); }
+.warning { color: rgb(255, 168, 0); }
+.success { color: rgb(136, 204, 39); }
\ No newline at end of file
diff --git a/src/index.html b/src/index.html
new file mode 100644
index 0000000..8714d3d
--- /dev/null
+++ b/src/index.html
@@ -0,0 +1,17 @@
+
+
+
+
+ Newton
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/main.ts b/src/main.ts
new file mode 100644
index 0000000..1eee1b4
--- /dev/null
+++ b/src/main.ts
@@ -0,0 +1,9 @@
+import { bootstrapApplication } from '@angular/platform-browser';
+import { appConfig } from './app/app.config';
+
+import { AppComponent } from './app/app.component';
+
+
+bootstrapApplication(AppComponent, appConfig).catch(
+ (err) => console.error(err)
+);
diff --git a/src/polyfills.ts b/src/polyfills.ts
new file mode 100644
index 0000000..75b392d
--- /dev/null
+++ b/src/polyfills.ts
@@ -0,0 +1,11 @@
+/**
+ * This file includes polyfills needed by Angular and is loaded before the app.
+ * You can add your own extra polyfills to this file.
+ */
+
+/***************************************************************************************************
+ * Zone JS is required by Angular itself.
+ */
+import 'zone.js'; // Included with Angular CLI.
+
+// If you need other polyfills, add them here.
\ No newline at end of file
diff --git a/src/styles.scss b/src/styles.scss
new file mode 100644
index 0000000..90d4ee0
--- /dev/null
+++ b/src/styles.scss
@@ -0,0 +1 @@
+/* You can add global styles to this file, and also import other style files */
diff --git a/tsconfig.app.json b/tsconfig.app.json
new file mode 100644
index 0000000..374cc9d
--- /dev/null
+++ b/tsconfig.app.json
@@ -0,0 +1,14 @@
+/* To learn more about this file see: https://angular.io/config/tsconfig. */
+{
+ "extends": "./tsconfig.json",
+ "compilerOptions": {
+ "outDir": "./out-tsc/app",
+ "types": []
+ },
+ "files": [
+ "src/main.ts"
+ ],
+ "include": [
+ "src/**/*.d.ts"
+ ]
+}
diff --git a/tsconfig.json b/tsconfig.json
new file mode 100644
index 0000000..bb36a82
--- /dev/null
+++ b/tsconfig.json
@@ -0,0 +1,59 @@
+/* To learn more about this file see: https://angular.io/config/tsconfig. */
+
+{
+ "compileOnSave": false,
+ "compilerOptions": {
+ "baseUrl": "./",
+ "downlevelIteration": true,
+ "importHelpers": true,
+ "target": "ES2022",
+ "module": "ES2022",
+ "useDefineForClassFields": false,
+ "moduleResolution": "node",
+ "experimentalDecorators": true,
+ "emitDecoratorMetadata": true,
+ "sourceMap": true,
+ "declaration": false,
+ "skipLibCheck": true,
+ "strict": false,
+ "forceConsistentCasingInFileNames": true
+ }
+}
+
+
+
+/*
+
+{
+ "compileOnSave": false,
+ "compilerOptions": {
+ "outDir": "./dist/out-tsc",
+ "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
+ }
+}
+
+*/
\ No newline at end of file
diff --git a/tsconfig.spec.json b/tsconfig.spec.json
new file mode 100644
index 0000000..be7e9da
--- /dev/null
+++ b/tsconfig.spec.json
@@ -0,0 +1,14 @@
+/* To learn more about this file see: https://angular.io/config/tsconfig. */
+{
+ "extends": "./tsconfig.json",
+ "compilerOptions": {
+ "outDir": "./out-tsc/spec",
+ "types": [
+ "jasmine"
+ ]
+ },
+ "include": [
+ "src/**/*.spec.ts",
+ "src/**/*.d.ts"
+ ]
+}