Compare commits

...

53 Commits

Author SHA1 Message Date
73f25aae1c Cleaning up type asignment 2025-08-23 14:58:23 -05:00
3c3a5d2f50 Removed empty constructors 2025-08-23 14:20:37 -05:00
d44e7d4e51 Moved loading of subscribers to constructors; improved subscriber destruction pattern; code quality improvements 2025-08-23 14:07:19 -05:00
60289953ec Update ace-linters; WIP color tokenizer 2025-08-11 19:22:06 -05:00
df5f2d481a Various LSP or LSP tangental changes 2025-08-09 15:57:07 -05:00
2ede33f3c2 Setting context menu bg color; aligning minimap scroll styling; adding wrap mode for minimap to remove hscroll bar 2025-07-26 01:28:03 -05:00
822d778008 WIP context menu added; upgraded ace-linters; integrated auto session register 2025-07-26 00:43:45 -05:00
90c8c9b3ee Wiring lsp config editor to update lspManagerService.lspConfigDataStr; removed redundant check; update lsp config 2025-07-19 02:13:52 -05:00
79adb86d25 Fixed editors.component close-tab when 1 file remains 2025-07-16 23:39:00 -05:00
a0914e8096 Refactored editors.component and tabs.service 2025-07-15 23:11:37 -05:00
a4d602f98a Added mouse scroll zoom 2025-07-15 21:21:14 -05:00
d6f766753c Update close file logic to select prior if exists; bug fix in LSP regarding editor and assignment 2025-07-15 19:46:17 -05:00
ae60905eb4 Adding quit to menus 2025-07-14 23:42:51 -05:00
080cc22841 Making IPC port kwarg configurable; improving kwarhs filter out 2025-07-14 23:00:36 -05:00
c5fdab59f6 Bumping ace-linters to fix desync issue 2025-07-14 21:33:20 -05:00
ab17e48338 Changing cursor color and blink settings 2025-07-13 20:55:17 -05:00
ec841445ed WIP adding tree-sitter and language bindings 2025-07-13 20:13:35 -05:00
41401f7257 Re-mapping java LSP socket port 2025-07-13 15:10:15 -05:00
766e70d766 WIP lsp-manager effort 2 2025-07-13 14:41:46 -05:00
9e01628ffb Restructured loading of editor modes; changed LSP config loading 2025-07-06 15:41:59 -05:00
27c4cda01a Adding more linux build options; changed default to be zip 2025-07-06 11:55:02 -05:00
7b4529e8f3 WIP lsp-manager effort 2025-07-05 15:21:08 -05:00
23d9bb24f2 Fixing .vr-pane-handle height fill disparity 2025-07-05 00:48:43 -05:00
330817a745 Improving search-replace button sizing 2025-07-05 00:28:12 -05:00
f647aba153 Fixing search-replace replace to allow empty string insert 2025-07-05 00:14:04 -05:00
a99cbc4cad Improving build size and moved to minified ace-build 2025-07-05 00:06:12 -05:00
ad5d2c9e92 adding overrides.css file; moved css around; added additional ace overrides 2025-07-04 22:48:00 -05:00
857f0ded57 Wiring of markdown-preview 2025-07-04 21:59:09 -05:00
e64a18b18b Wiring of search-replace popup; moved some services up one level 2025-07-04 15:43:01 -05:00
5aa6c7ca10 Fixing new file keybinding logic; fixing file selection when only oe remains 2025-07-03 10:22:53 -05:00
d0c73fe4da changing keybinding names; fixed close tab issue; moved editor component methods to base 2025-07-03 09:28:20 -05:00
f94ac677a9 Moved tab component logic to service; handle move session to select next tab 2025-07-02 22:04:03 -05:00
051e42bfa3 Wiring debounce change upate to minimap-view 2025-07-02 19:52:44 -05:00
a6b0bda263 Wiring in majority of minimap-view Part 2; Fixing keybinding select and move of session; moved modals around 2025-07-02 00:33:11 -05:00
289c061ab6 Wiring in majority of minimap-view 2025-06-30 22:39:58 -05:00
96eaa64b2a Renamed code widget; moved modal to common folder 2025-06-30 17:20:11 -05:00
de2bb27b6d Replacing resizor with pane directive; replacing container-ref with tags 2025-06-30 01:11:14 -05:00
c99013df04 Moving files modal tag; moving editor logic to service; pulling out resize logic 2025-06-28 21:10:15 -05:00
d1dbb7efcc Added lsp service methods; fixed margin width line; cleanup 2025-06-27 23:52:41 -05:00
d7c8ea1902 Fixing undo issue; cleaning up keybindings; removed diplicate focus event 2025-06-24 01:07:30 -05:00
9915b98700 Added files list search; keybinding cleanup 2025-06-22 00:01:49 -05:00
ad92bfc648 De-wiring lspService from newton-editor component 2025-06-21 20:40:23 -05:00
5965bb7552 Making methods explicitly private, public, or protected 2025-06-21 19:49:58 -05:00
dae0cd9516 Wired external change detection and update 2025-06-21 18:17:01 -05:00
d354a940c4 Changed 80 column margin line color to red 2025-06-21 17:04:04 -05:00
52a3785168 Reverting method name to newSession 2025-06-21 16:28:41 -05:00
598a66f517 Moved injection out of constructor; refactored nedton-editor component and saveAs logic 2025-06-20 23:52:22 -05:00
4193d46d0d Fixing command pakllet call; fixing session swap session defaulting 2025-06-20 19:51:03 -05:00
41f6ea5854 Wiring file watch events WIP 2025-06-20 00:50:43 -05:00
3a29e0dcad Resolving filter/sub filter behavior on open files dialog 2025-06-19 21:00:32 -05:00
c599013af4 Wiring change detection to render process 2025-06-19 20:03:02 -05:00
5c19d82834 Aligning debug mode method call naming 2025-06-19 01:26:17 -05:00
3ba44c43d8 Wired quit keybinding and fullscreen toggle binding 2025-06-19 01:22:07 -05:00
86 changed files with 7227 additions and 1255 deletions

View File

@@ -34,7 +34,7 @@
},
{
"glob":"**/*",
"input":"node_modules/ace-builds/src-noconflict",
"input":"node_modules/ace-builds/src-min-noconflict",
"output":"ace"
}
],
@@ -44,9 +44,11 @@
"node_modules/bootstrap/scss/bootstrap.scss",
"node_modules/bootstrap-icons/font/bootstrap-icons.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
},
@@ -64,7 +66,7 @@
"maximumError":"8kB"
}
],
"optimization":false
"optimization": true
},
"development":{
"outputHashing": "all",
@@ -110,7 +112,6 @@
"src/styles.css"
],
"scripts":[
]
}
}

View File

@@ -1,9 +1,10 @@
const { app } = require('electron');
let startType = "build";
let isDebug = false;
let args = [];
let startType = "build";
let ipcPort = "4563";
let isDebug = false;
let args = [];
@@ -17,6 +18,13 @@ const loadKWArgs = () => {
console.log(startType);
}
const hasIpcPort = app.commandLine.hasSwitch("ipc-port");
if (hasIpcPort) {
ipcPort = app.commandLine.getSwitchValue("ipc-port");
console.log("Has ipc-port switch...");
console.log(ipcPort);
}
const hasDebug = app.commandLine.hasSwitch("app-debug");
if (hasDebug) {
isDebug = app.commandLine.getSwitchValue("app-debug");
@@ -25,38 +33,29 @@ const loadKWArgs = () => {
}
}
const loadVArgs = () => {
console.log("\n\nStart VArgs:");
const filterOutLaunchAndKWArgs = () => {
if (
process.argv[0].endsWith("electron")
) {
process.argv = process.argv.slice(2);
}
if (
process.argv[0].endsWith("/newton") ||
process.argv[0].endsWith(".AppImage")
) {
do {
process.argv = process.argv.slice(1);
}
if ( process.argv.length > 0 && (
process.argv[0].includes("--trace-warnings") ||
process.argv[0].includes("--start-as")
} while (
process.argv.length > 0 &&
(
process.argv[0].endsWith("/newton") ||
process.argv[0].endsWith(".AppImage") ||
process.argv[0].includes("--trace-warnings") ||
process.argv[0].includes("--start-as") ||
process.argv[0].includes("--ipc-port")
)
) {
process.argv = process.argv.slice(1);
}
if ( process.argv.length > 0 && (
process.argv[0].includes("--trace-warnings") ||
process.argv[0].includes("--start-as")
)
) {
process.argv = process.argv.slice(1);
}
);
}
const loadVArgs = () => {
console.log("\n\nStart VArgs:");
args = process.argv;
args.forEach((val, index, array) => {
console.log(index + ': ' + val);
@@ -67,6 +66,7 @@ const loadVArgs = () => {
const loadArgs = () => {
loadKWArgs();
filterOutLaunchAndKWArgs();
loadVArgs();
}
@@ -79,16 +79,21 @@ const getStartType = () => {
return startType;
}
const debugMode = () => {
const getDebugMode = () => {
return isDebug;
}
const getIpcPort = () => {
return ipcPort;
}
module.exports = {
argsParser: {
loadArgs: loadArgs,
getArgs: getArgs,
getStartType: getStartType,
debugMode: debugMode,
getDebugMode: getDebugMode,
getIpcPort: getIpcPort,
}
};

View File

@@ -5,13 +5,15 @@ const os = require('os');
const chokidar = require('chokidar');
const HOME_DIR = os.homedir();
const BASE_PATH = '../build/app';
const CONFIG_PATH = path.join(HOME_DIR, "/.config/newton/");
const SETTINGS_CONFIG_PATH = path.join(CONFIG_PATH, "/settings.json");
const LSP_CONFIG_PATH = path.join(BASE_PATH, "/resources/lsp-servers-config.json");
let window = null;
let watcher = null;
const HOME_DIR = os.homedir();
const BASE_PATH = '../build/app';
const CONFIG_PATH = path.join(HOME_DIR, "/.config/newton/");
const SETTINGS_CONFIG_PATH = path.join(CONFIG_PATH, "/settings.json");
const LSP_CONFIG_PATH = path.join(BASE_PATH, "/resources/lsp-servers-config.json");
let window = null;
let watcher = null;
let skipOnceFileWatchChange = false;
const getIconPath = () => {
@@ -19,25 +21,35 @@ const getIconPath = () => {
}
const getSettingsConfigData = () => {
return getFileContents(SETTINGS_CONFIG_PATH);
return getFileContents(
SETTINGS_CONFIG_PATH,
useRelativePath = false,
watchFile = false
);
}
const getLspConfigData = () => {
const config = getFileContents(LSP_CONFIG_PATH, useRelativePath = true);
return config.replaceAll("{user.home}", HOME_DIR);
return getFileContents(
LSP_CONFIG_PATH,
useRelativePath = true,
watchFile = false
).replaceAll("{user.home}", HOME_DIR);
}
const getFileContents = (_path, useRelativePath = false) => {
const getFileContents = (_path, useRelativePath = false, watchFile = true) => {
console.log(`Getting Contents For: ${_path}`);
try {
if (!useRelativePath) {
return fs.readFileSync(_path, 'utf8');
} else {
let fpath = path.join(__dirname, _path);
watcher.add(fpath);
return fs.readFileSync(fpath, 'utf8');
}
if (useRelativePath)
return fs.readFileSync(
path.join(__dirname, _path),
'utf8'
);
if (watchFile)
watcher.add(_path);
return fs.readFileSync(_path, 'utf8');
} catch(err) {
return `{"message": {"type": "error", "text": "Error: Could not read ${_path}"}}`;
}
@@ -52,21 +64,58 @@ const saveSettingsConfigData = (data) => {
}
const saveFile = (fpath, content) => {
fs.writeFile(fpath, content, (err) => {
if (!err) return
console.error("An error ocurred writing to the file " + err.message);
});
}
skipOnceFileWatchChange = true;
const saveFileAs = (content) => {
dialog.showSaveDialog().then((response) => {
if (response.canceled) {
console.log("You didn't save the file");
fs.writeFile(fpath, content, (err) => {
if (err) {
console.error("An error ocurred writing to the file " + err.message);
return;
}
saveFile(response.filePath, content);
watcher.add(response.filePath);
let parentDir = path.dirname(fpath);
let watchers = watcher.getWatched();
let targetDir = watchers[parentDir];
if (
!targetDir || !targetDir.includes( path.basename(fpath) )
) {
skipOnceFileWatchChange = false;
watcher.add(fpath);
window.webContents.send('update-file-path', fpath);
}
try {
window.webContents.send('file-saved', fpath);
} catch(e) {}
});
}
const saveFileAs = () => {
return dialog.showSaveDialog().then((response) => {
if (response.canceled) {
console.debug("You didn't save the file");
return;
}
return response.filePath;
});
}
const chooseFolder = () => {
return dialog.showOpenDialog(
{
title: "Choose Folder:",
defaultPath: HOME_DIR,
properties: [
'openDirectory'
]
}
).then((response) => {
if (response.canceled) {
console.debug("Canceled folder selection...");
return "";
}
return response.filePaths[0];
});
}
@@ -77,14 +126,21 @@ const openFiles = (startPath) => {
defaultPath: (startPath) ? startPath : HOME_DIR,
filters: [
{ name: "All Files", extensions: ["*"] },
{ name: "c", extensions: [".h", ".c"] },
{ name: "cpp", extensions: ["hpp", "cpp"] },
{ name: "html", extensions: ["js", "css", "scss", "html", "ts"] },
{ name: "java", extensions: ["java"] },
{ name: "python", extensions: ["py", "pyc"] },
{ name: "rust", extensions: ["r", "rc"] },
{ name: "text", extensions: ["txt", "log", "md"] },
{ name: "go", extensions: ["go"] },
{ name: "All Sub Filters",
extensions: [
"h", "c", "hpp", "cpp", "js", "css", "scss", "html",
"ts", "java", "py", "pyc", "txt", "log", "md", "r",
"rc", "go"
]
},
{ name: "C", extensions: ["h", "c"] },
{ name: "CPP", extensions: ["hpp", "cpp"] },
{ name: "HTML", extensions: ["js", "css", "scss", "html", "ts"] },
{ name: "Java", extensions: ["java"] },
{ name: "Python", extensions: ["py", "pyc"] },
{ name: "Text", extensions: ["txt", "log", "md"] },
{ name: "Rust", extensions: ["r", "rc"] },
{ name: "Go", extensions: ["go"] }
],
properties: [
'openFile',
@@ -93,7 +149,7 @@ const openFiles = (startPath) => {
}
).then((response) => {
if (response.canceled) {
console.log("Canceled file open request...");
console.debug("Canceled file(s) open request...");
return;
}
@@ -106,14 +162,22 @@ const loadFilesWatcher = () => {
watcher = chokidar.watch([], {});
watcher.on('change', (fpath) => {
if (skipOnceFileWatchChange) {
skipOnceFileWatchChange = false;
return;
}
console.debug("File (changed) : ", fpath);
const data = getFileContents(fpath, false, false);
window.webContents.send('file-changed', fpath, data);
}).on('unlink', (fpath) => {
console.debug("File (unlinked) : ", fpath);
console.debug("File (deleted) : ", fpath);
window.webContents.send('file-deleted', fpath);
});
}
const unwatchFile = async (fpath) => {
console.log("File (unwatch) : ", fpath);
console.debug("File (unwatch) : ", fpath);
await watcher.unwatch(fpath);
}
@@ -126,6 +190,7 @@ const closeFile = (fpath) => {
module.exports = {
newtonFs: {
setWindow: setWindow,
chooseFolder: chooseFolder,
openFiles: openFiles,
saveFile: saveFile,
saveFileAs: saveFileAs,

View File

@@ -8,12 +8,17 @@ const fetch = require('electron-fetch').default
const IPC_SERVER_IP = "127.0.0.1";
let window = null;
let ipcServer = null;
let ipcServerPort = "4563";
let ipcServerURL = `http://${IPC_SERVER_IP}:${ipcServerPort}`;
let ipcServerPort = "";
let ipcServerURL = "";
const setWindow = (win) => {
window = win;
window = win;
}
const configure = (ipcPort) => {
ipcServerPort = ipcPort;
ipcServerURL = `http://${IPC_SERVER_IP}:${ipcServerPort}`;
}
const loadIPCServer = (fpath) => {
@@ -47,7 +52,8 @@ const loadIPCServer = (fpath) => {
}
const isIPCServerUp = async () => {
const response = await fetch(`${ipcServerURL}/is-up`).catch((err) => {
const response = await fetch(`${ipcServerURL}/is-up`)
.catch((err) => {
console.debug("IPCServer (status) : Not up; okay to start.");
return {
text: () => {
@@ -73,9 +79,10 @@ const sendFilesToIPC = async (files) => {
module.exports = {
newtonIPC: {
setWindow: setWindow,
configure: configure,
loadIPCServer: loadIPCServer,
isIPCServerUp: isIPCServerUp,
sendFilesToIPC: sendFilesToIPC,
setWindow: setWindow
}
};

View File

@@ -16,7 +16,7 @@ let hasExitSaved = false;
const createWindow = () => {
window = newton.createWindow(
newton.args.getStartType(),
newton.args.debugMode(),
newton.args.getDebugMode(),
newton.args.getArgs(),
);
}
@@ -65,18 +65,22 @@ const loadProcessSignalHandlers = () => {
}
const loadHandlers = () => {
ipcMain.handle('quit', (eve) => app.quit());
ipcMain.handle('toggleFullScreen', (eve) => { window.setFullScreen(!window.isFullScreen()); });
ipcMain.handle('getLspConfigData', (eve) => newton.fs.getLspConfigData());
ipcMain.handle('getFileContents', (eve, path) => newton.fs.getFileContents(path));
ipcMain.handle('openFiles', (eve, startPath) => newton.fs.openFiles(startPath));
ipcMain.handle('saveFile', (eve, path, content) => newton.fs.saveFile(path, content));
ipcMain.handle('closeFile', (eve, path) => newton.fs.closeFile(path));
ipcMain.handle('saveFileAs', (eve, content) => newton.fs.saveFileAs(content));
ipcMain.handle('saveFileAs', (eve) => newton.fs.saveFileAs());
ipcMain.handle('chooseFolder', (eve) => newton.fs.chooseFolder());
}
app.whenReady().then(async () => {
loadProcessSignalHandlers();
newton.args.loadArgs();
newton.ipc.configure( newton.args.getIpcPort() );
if ( !await newton.ipc.isIPCServerUp() ) {
newton.ipc.loadIPCServer();
} else {
@@ -87,7 +91,7 @@ app.whenReady().then(async () => {
app.quit();
}
newton.settings.loadsettings();
newton.settings.loadSettings();
newton.fs.loadFilesWatcher();
loadHandlers();
@@ -108,5 +112,4 @@ app.on('window-all-closed', () => {
if (process.platform !== 'darwin') {
app.quit()
};
});
});

View File

@@ -22,6 +22,9 @@ const load = (win) => {
}, {
label: 'Terminal',
click: () => {}
}, {
label: "Quit",
click: () => win.webContents.send('menu-actions', "quit")
}
]
}, {

View File

@@ -9,6 +9,8 @@ contextBridge.exposeInMainWorld('electron', {
contextBridge.exposeInMainWorld('main', {
onMenuActions: (callback) => ipcRenderer.on('menu-actions', (_event, action) => callback(action)),
quit: () => ipcRenderer.invoke("quit"),
toggleFullScreen: () => ipcRenderer.invoke("toggleFullScreen"),
});
contextBridge.exposeInMainWorld('fs', {
@@ -16,8 +18,13 @@ contextBridge.exposeInMainWorld('fs', {
getFileContents: (path) => ipcRenderer.invoke("getFileContents", path),
openFiles: (startPath) => ipcRenderer.invoke("openFiles", startPath),
saveFile: (path, content) => ipcRenderer.invoke("saveFile", path, content),
saveFileAs: (content) => ipcRenderer.invoke("saveFileAs", content),
saveFileAs: () => ipcRenderer.invoke("saveFileAs"),
chooseFolder: () => ipcRenderer.invoke("chooseFolder"),
closeFile: (path) => ipcRenderer.invoke("closeFile", path),
getPathForFile: (file) => webUtils.getPathForFile(file),
onLoadFiles: (callback) => ipcRenderer.on('load-files', (_event, paths) => callback(paths)),
onUpdateFilePath: (callback) => ipcRenderer.on('update-file-path', (_event, paths) => callback(paths)),
onSavedFile: (callback) => ipcRenderer.on('file-saved', (_event, path) => callback(path)),
onChangedFile: (callback) => ipcRenderer.on('file-changed', (_event, path, data) => callback(path, data)),
onDeletedFile: (callback) => ipcRenderer.on('file-deleted', (_event, path) => callback(path)),
});

View File

@@ -4,7 +4,7 @@ const { newtonFs } = require('./fs');
let config = {};
const loadsettings = () => {
const loadSettings = () => {
config = JSON.parse(
newtonFs.getSettingsConfigData()
);
@@ -38,7 +38,7 @@ const getIconPath = () => {
module.exports = {
settingsManager: {
getIconPath: getIconPath,
loadsettings: loadsettings,
loadSettings: loadSettings,
getConfig: getConfig,
saveConfig: saveConfig,
}

View File

@@ -33,6 +33,9 @@ const load = (win) => {
}, {
label: 'Help',
click: () => win.webContents.send('menu-actions', "show-about")
}, {
label: 'Quit',
click: () => win.webContents.send('menu-actions', "quit")
}
];

View File

@@ -4,13 +4,16 @@
"version": "0.0.1",
"author": "ITDominator",
"license": "GPL-2.0-only",
"homepage": "https://www.itdominator.com",
"email": "1itdominator@gmail.com",
"main": "newton/main.js",
"private": true,
"scripts": {
"app": "ng build --base-href ./ && electron . --trace-warnings --start-as=build",
"electron-start": "electron . --trace-warnings --start-as=build",
"app": "ng build --base-href ./ && electron . --trace-warnings --start-as=build --ipc-port=4588",
"electron-start": "electron . --trace-warnings --start-as=build --ipc-port=4588",
"electron-pack": "ng build --base-href ./ && electron-builder --dir",
"electron-dist": "ng build --base-href ./ && electron-builder",
"electron-dist-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",
@@ -24,7 +27,10 @@
"icon": "./icos/",
"files": [
"newton/",
"build/"
"build/",
"!node_modules/ace-builds/",
"!node_modules/web-streams-polyfill/",
"!node_modules/@angular/"
],
"mac": {
"category": "public.app-category.developer-tools"
@@ -33,8 +39,9 @@
"target": "portable"
},
"linux": {
"target": "AppImage",
"category": "Development"
"target": "zip",
"category": "Development",
"maintainer": "ITDominator"
}
},
"postinstall": "electron-builder install-app-deps",
@@ -44,8 +51,10 @@
"@angular/core": "19.2.0",
"@angular/forms": "19.2.0",
"@angular/platform-browser": "19.2.0",
"ace-builds": "1.41.0",
"ace-linters": "1.5.3",
"ace-builds": "1.43.0",
"ace-diff": "3.0.3",
"ace-layout": "1.5.0",
"ace-linters": "1.8.3",
"bootstrap": "5.3.6",
"bootstrap-icons": "1.12.1",
"chokidar": "4.0.3",
@@ -73,7 +82,27 @@
"karma-coverage": "2.2.0",
"karma-jasmine": "5.1.0",
"karma-jasmine-html-reporter": "2.1.0",
"typescript": "5.7.2",
"tslib": "2.3.0"
"tree-sitter": "0.21.1",
"tree-sitter-bash": "0.23.2",
"tree-sitter-c": "0.23.1",
"tree-sitter-cli": "0.25.8",
"tree-sitter-cpp": "0.23.4",
"tree-sitter-css": "0.23.0",
"tree-sitter-go": "0.23.4",
"tree-sitter-html": "0.23.2",
"tree-sitter-java": "0.23.5",
"tree-sitter-javascript": "0.23.1",
"tree-sitter-json": "0.24.8",
"tree-sitter-lua": "2.1.3",
"tree-sitter-php": "0.23.12",
"tree-sitter-python": "0.23.2",
"tree-sitter-r": "0.0.1-security",
"tree-sitter-sql": "0.1.0",
"tree-sitter-sqlite": "0.0.1-security",
"tree-sitter-toml": "0.5.1",
"tree-sitter-typescript": "0.23.2",
"tree-sitter-yaml": "0.5.0",
"tslib": "2.3.0",
"typescript": "5.7.2"
}
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.1 KiB

BIN
public/imgs/whole-word.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.5 KiB

View File

@@ -0,0 +1,185 @@
ace.define("ace/theme/penguins_in_space-css", ["require", "exports", "module"], function(require, exports, module) {
module.exports = """
.penguins-in-space .ace_gutter {
background: #282c34;
color: #6a6f7a;
}
.penguins-in-space .ace_print-margin {
width: 1px;
background: #e8e8e8;
}
.penguins-in-space {
background-color: #282c34;
color: #abb2bf;
}
.penguins-in-space .ace_cursor {
color: #528bff;
}
.penguins-in-space .ace_marker-layer .ace_selection {
background: #3d4350;
}
.penguins-in-space.ace_multiselect .ace_selection.ace_start {
box-shadow: 0 0 3px 0 #282c34;
border-radius: 2px;
}
.penguins-in-space .ace_marker-layer .ace_step {
background: #c6dbae;
}
.penguins-in-space .ace_marker-layer .ace_bracket {
margin: -1px 0 0 -1px;
border: 1px solid #747369;
}
.penguins-in-space .ace_marker-layer .ace_active-line {
background: rgba(76,87,103,.19);
}
.penguins-in-space .ace_gutter-active-line {
background-color: rgba(76,87,103,.19);
}
.penguins-in-space .ace_marker-layer .ace_selected-word {
border: 1px solid #3d4350;
}
.penguins-in-space .ace_fold {
background-color: #61afef;
border-color: #abb2bf;
}
.penguins-in-space .ace_keyword {
color: #00a8c6;
}
.penguins-in-space .ace_keyword.ace_operator {
color: #00a8c6;
}
.penguins-in-space .ace_keyword.ace_other.ace_unit {
color: #d19a66;
}
.penguins-in-space .ace_constant.ace_language {
color: #d19a66;
}
.penguins-in-space .ace_constant.ace_numeric {
color: #d19a66;
}
.penguins-in-space .ace_constant.ace_character {
color: #56b6c2;
}
.penguins-in-space .ace_constant.ace_other {
color: #56b6c2;
}
.penguins-in-space .ace_support.ace_function {
color: #61afef;
}
.penguins-in-space .ace_support.ace_constant {
color: #d19a66;
}
.penguins-in-space .ace_support.ace_class {
color: #ff5d38;
}
.penguins-in-space .ace_support.ace_type {
color: #ff5d38;
}
.penguins-in-space .ace_storage {
color: #c678dd;
}
.penguins-in-space .ace_storage.ace_type {
color: #E6DB74;
}
.penguins-in-space .ace_invalid {
color: #fff;
background-color: #f2777a;
}
.penguins-in-space .ace_invalid.ace_deprecated {
color: #272b33;
background-color: #d27b53;
}
.penguins-in-space .ace_string {
color: #e6db74;
}
.penguins-in-space .ace_string.ace_regexp {
color: #e6db74;
}
.penguins-in-space .ace_comment {
font-style: italic;
color: #454a54;
}
.penguins-in-space .ace_variable {
color: #e06c75;
}
.penguins-in-space .ace_variable.ace_parameter {
color: #d19a66;
}
.penguins-in-space .ace_meta.ace_tag {
color: #e06c75;
}
.penguins-in-space .ace_entity.ace_other.ace_attribute-name {
color: #e06c75;
}
.penguins-in-space .ace_entity.ace_name.ace_function {
color: #61afef;
}
.penguins-in-space .ace_entity.ace_name.ace_tag {
color: #e06c75;
}
.penguins-in-space .ace_markup.ace_heading {
color: #98c379;
}
.penguins-in-space .ace_indent-guide {
background: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAACCAYAAACZgbYnAAAAEklEQVQImWPQ09NrYAgMjP4PAAtGAwchHMyAAAAAAElFTkSuQmCC) right repeat-y;
}
.penguins-in-space .ace_indent-guide-active {
background: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAACCAYAAACZgbYnAAAAEklEQVQIW2PQ1dX9zzBz5sz/ABCcBFFentLlAAAAAElFTkSuQmCC) right repeat-y;
}
""";
});
ace.define("ace/theme/penguins_in_space", ["require", "exports", "module", "ace/theme/penguins_in_space-css", "ace/lib/dom"], function(require, exports, module) {
exports.isDark = true;
exports.cssClass = "penguins-in-space";
exports.cssText = require("./penguins_in_space-css");
var dom = require("../lib/dom");
dom.importCssString(exports.cssText, exports.cssClass, false);
});
(function() {
ace.require(["ace/theme/penguins_in_space"], function(m) {
if (typeof module == "object" && typeof exports == "object" && module) {
module.exports = m;
}
});
})();

View File

@@ -8,62 +8,78 @@
"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",
"socket": "ws://127.0.0.1:9999/java",
"socket-two": "ws://127.0.0.1:9999/?name=jdtls",
"alt-socket": "ws://127.0.0.1:9999/?name=java-language-server",
"initialization-options": {
"bundles": [
"intellicode-core.jar"
],
"workspaceFolders": [
"file://"
"file://{workspace.folder}"
],
"extendedClientCapabilities": {
"classFileContentsSupport": true,
"executeClientCommandSupport": true
"executeClientCommandSupport": false
},
"settings": {
"java": {
"autobuild": {
"enabled": false
"enabled": true
},
"completion": {
"enabled": true,
"importOrder": [
"java",
"javax",
"org",
"com"
]
"jdt": {
"ls": {
"javac": {
"enabled": true
},
"java": {
"home": "{user.home}/Portable_Apps/sdks/javasdk/jdk-22.0.2"
},
"lombokSupport": {
"enabled": true
},
"protobufSupport":{
"enabled": true
},
"androidSupport": {
"enabled": true
}
}
},
"configuration": {
"updateBuildConfiguration": "automatic",
"maven": {
"userSettings": "{user.home}/.config/jdtls/settings.xml",
"globalSettings": "{user.home}/.config/jdtls/settings.xml"
"userSettings": "{user.home}/.config/lsps/jdtls/settings.xml",
"globalSettings": "{user.home}/.config/lsps/jdtls/settings.xml"
},
"runtimes": [
{
"name": "JavaSE-17",
"path": "/usr/lib/jvm/default-runtime",
"path": "/usr/lib/jvm/java-17-openjdk",
"javadoc": "https://docs.oracle.com/en/java/javase/17/docs/api/",
"default": false
},
{
"name": "JavaSE-22",
"path": "{user.home}/Portable_Apps/sdks/javasdk/jdk-22.0.2",
"javadoc": "https://docs.oracle.com/en/java/javase/22/docs/api/",
"default": true
}
]
},
"classPath": [
"{user.home}/.config/jdtls/m2/repository/**/*-sources.jar",
"{user.home}/.config/lsps/jdtls/m2/repository/**/*-sources.jar",
"lib/**/*-sources.jar"
],
"docPath": [
"{user.home}/.config/jdtls/m2/repository/**/*-javadoc.jar",
"{user.home}/.config/lsps/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"
"{user.home}/.config/lsps/jdtls/m2/repository/**/*.jar",
"lib/**/*.jar"
],
"importOnFirstTimeStartup": "automatic",
"importHint": true,
@@ -73,7 +89,7 @@
],
"sourcePaths": [
"src",
"{user.home}/.config/jdtls/m2/repository/**/*.jar"
"{user.home}/.config/lsps/jdtls/m2/repository/**/*.jar"
]
},
"sources": {
@@ -103,9 +119,9 @@
"enabled": true
},
"version": "",
"home": "abs(static/gradle-7.3.3)",
"home": "{user.home}/Portable_Apps/sdks/gradle/gradle-9.0.0",
"java": {
"home": "abs(static/launch_jres/17.0.6-linux-x86_64)"
"home": "{user.home}/Portable_Apps/sdks/javasdk/jdk-22.0.2"
},
"offline": {
"enabled": false
@@ -131,14 +147,65 @@
"downloadSources": true,
"updateSnapshots": true
},
"silentNotification": true,
"contentProvider": {
"preferred": "fernflower"
},
"signatureHelp": {
"enabled": true,
"description": {
"enabled": true
}
},
"completion": {
"enabled": true,
"matchCase": "firstletter",
"maxResults": 25,
"guessMethodArguments": true,
"lazyResolveTextEdit": {
"enabled": true
},
"postfix": {
"enabled": true
},
"favoriteStaticMembers": [
"org.junit.Assert.*",
"org.junit.Assume.*",
"org.junit.jupiter.api.Assertions.*",
"org.junit.jupiter.api.Assumptions.*",
"org.junit.jupiter.api.DynamicContainer.*",
"org.junit.jupiter.api.DynamicTest.*"
],
"importOrder": [
"#",
"java",
"javax",
"org",
"com"
]
},
"references": {
"includeAccessors": true,
"includeDecompiledSources": true
},
"codeGeneration": {
"toString": {
"template": "${object.className}{${member.name()}=${member.value}, ${otherMembers}}"
},
"insertionLocation": "afterCursor",
"useBlocks": true
},
"implementationsCodeLens": {
"enabled": true
},
"referencesCodeLens": {
"enabled": true
},
"progressReports": {
"enabled": false
},
"saveActions": {
"organizeImports": true
}
}
}
@@ -150,53 +217,39 @@
"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",
"socket": "ws://127.0.0.1:9999/python",
"socket-two": "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": {
"rope": {
"ropeFolder": "{user.home}/.config/lsps/ropeproject"
},
"plugins": {
"pycodestyle": {
"enabled": false
"ruff": {
"enabled": true,
"extendSelect": ["I"],
"lineLength": 80
},
"pydocstyle": {
"pycodestyle": {
"enabled": false
},
"pyflakes": {
"enabled": false
},
"pylint": {
"enabled": false
"enabled": true
},
"mccabe": {
"enabled": false
},
"ruff": true,
"pylsp_rope": {
"rename": true
"rename": false
},
"rope_rename": {
"enabled": true
"enabled": false
},
"rope_autoimport": {
"enabled": true
"enabled": false
},
"rope_completion": {
"enabled": false,
@@ -209,13 +262,13 @@
"enabled": true,
"include_class_objects": true,
"include_function_objects": true,
"fuzzy": true
"fuzzy": false
},
"jedi":{
"jedi": {
"root_dir": "file://{workspace.folder}",
"extra_paths": [
"{user.home}/Portable_Apps/py-venvs/pylsp-venv/venv/lib/python3.10/site-packages"
],
"root_dir": ""
]
}
}
}
@@ -226,7 +279,8 @@
"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",
"socket": "ws://127.0.0.1:9999/python",
"socket-two": "ws://127.0.0.1:9999/?name=jedi-language-server",
"initialization-options": {
"jediSettings": {
"autoImportModules": [],
@@ -245,7 +299,13 @@
],
"environmentPath": "{user.home}/Portable_Apps/py-venvs/gtk-apps-venv/venv/bin/python",
"symbols": {
"ignoreFolders": [".nox", ".tox", ".venv", "__pycache__", "venv"],
"ignoreFolders": [
".nox",
".tox",
".venv",
"__pycache__",
"venv"
],
"maxSymbols": 20
}
}
@@ -255,28 +315,8 @@
"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",
"socket": "ws://127.0.0.1:9999/cpp",
"socket-two": "ws://127.0.0.1:9999/?name=clangd",
"initialization-options": {}
},
"c": {
@@ -284,7 +324,40 @@
"info": "https://clangd.llvm.org/",
"command": "lsp-ws-proxy -- clangd",
"alt-command": "clangd",
"socket": "ws://127.0.0.1:3030/?name=clangd",
"socket": "ws://127.0.0.1:9999/c",
"socket-two": "ws://127.0.0.1:9999/?name=clangd",
"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:9999/go",
"socket-two": "ws://127.0.0.1:9999/?name=gopls",
"initialization-options": {}
},
"typescript": {
"info": "https://github.com/typescript-language-server/typescript-language-server",
"command": "lsp-ws-proxy -- typescript-language-server",
"alt-command": "typescript-language-server --stdio",
"socket": "ws://127.0.0.1:9999/typescript",
"socket-two": "ws://127.0.0.1:9999/?name=ts",
"initialization-options": {}
},
"sh": {
"info": "",
"command": "",
"alt-command": "",
"socket": "ws://127.0.0.1:9999/bash",
"socket-two": "ws://127.0.0.1:9999/?name=shell",
"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:9999/lua",
"socket-two": "ws://127.0.0.1:9999/?name=lua",
"initialization-options": {}
}
}

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -2,4 +2,8 @@
<info-bar></info-bar>
<tabs></tabs>
<editors></editors>
<search-replace></search-replace>
<markdown-preview></markdown-preview>
<lsp-manager></lsp-manager>
</div>

View File

@@ -3,6 +3,9 @@ import { Component } from '@angular/core';
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 { LspManagerComponent } from "./editor/lsp-manager/lsp-manager.component";
@@ -11,7 +14,10 @@ import { EditorsComponent } from './editor/editors.component';
imports: [
InfoBarComponent,
TabsComponent,
EditorsComponent
EditorsComponent,
SearchReplaceComponent,
MarkdownPreviewComponent,
LspManagerComponent,
],
templateUrl: './app.component.html',
styleUrl: './app.component.css',

View File

@@ -0,0 +1,28 @@
<div #diffModal
id="diffModal" class="modal fade" tabindex="-1" role="dialog"
>
<div class="modal-dialog modal-lg" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">Diff:</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
<div class="row">
<div class="col">
<div class="diff-view"></div>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Close</button>
</div>
</div>
</div>
</div>
</div>

View File

@@ -0,0 +1,74 @@
import {
Component,
DestroyRef,
inject
} from "@angular/core";
import { CommonModule } from "@angular/common";
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
import * as bootstrap from "bootstrap";
import AceDiff from 'ace-diff';
import 'ace-diff/dist/ace-diff.min.css';
import 'ace-diff/dist/ace-diff-dark.min.css';
@Component({
selector: 'diff-modal',
standalone: true,
imports: [
CommonModule
],
templateUrl: './diff-modal.component.html',
styleUrl: './diff-modal.component.css',
host: {
'class': ''
}
})
export class DiffModalComponent {
readonly #destroyRef: DestroyRef = inject(DestroyRef);
diffModal!: bootstrap.Modal;
constructor() {
this.loadSubscribers();
}
private ngAfterViewInit(): void {
this.loadDiffView();
}
private loadDiffView() {
// Notes: https://github.com/ace-diff/ace-diff
// https://ajaxorg.github.io/ace-api-docs/classes/src_ext_diff_diff_view.DiffView.html#scrollB
/*
const differ = new AceDiff({
ace: window.ace
element: '.diff-view',
left: {
content: 'your first file content here',
},
right: {
content: 'your second file content here',
},
});
*/
}
private loadSubscribers() {
}
private createModal() {
this.diffModal = new bootstrap.Modal("#diffModal", {});
}
public showModal() {
this.diffModal?.toggle();
}
}

View File

@@ -6,10 +6,12 @@ export const EditorSettings: any = {
KEYBINDINGS: Keybindings,
CONFIG: {
behavioursEnabled: true,
wrapBehavioursEnabled: true,
fontSize: "12px",
theme: "ace/theme/one_dark",
theme: "ace/theme/gruvbox",
mode: "ace/mode/text",
printMarginColumn: 80,
enableCodeLens: true,
enableBasicAutocompletion: true,
enableLiveAutocompletion: true,
enableSnippets: true,
@@ -19,11 +21,10 @@ export const EditorSettings: any = {
tabSize: 4,
navigateWithinSoftTabs: true,
tooltipFollowsMouse: true,
wrapBehavioursEnabled: false,
scrollPastEnd: 0.5,
mergeUndoDeltas: false,
showGutter: true,
customScrollbar: true,
// customScrollbar: true,
scrollSpeed: 5
}
};

View File

@@ -1,36 +1,45 @@
export const Keybindings: Array<{}> = [
{
name: "showSettingsMenu",
bindKey: {win: "Ctrl-Shift-m", mac: "Ctrl-Shift-m"},
name: "quit",
bindKey: {win: "ctrl-q", mac: "ctrl-q"},
readOnly: false
}, {
name: "showKeyboardShortcuts",
name: "toggleFullScreen",
bindKey: {win: "F11", mac: "F11"},
readOnly: false
}, {
name: "showKeyShortcuts",
bindKey: {win: "ctrl-shift-k", mac: "command-shift-k"},
readOnly: false
}, {
name: "openCommandPalette",
bindKey: {linux: "Command-shift-/|F1", win: "ctrl-shift-/|F1"},
name: "openCommandPalette2",
bindKey: {linux: "command-shift-/|F1", win: "ctrl-shift-/|F1"},
readOnly: false
}, {
name: "showFilesModal",
name: "showFilesList",
bindKey: {win: "ctrl-b", mac: "ctrl-b"},
service: "filesModalService",
readOnly: false
}, {
name: "showLSPModal",
name: "lspManagerPopup",
bindKey: {win: "ctrl-shift-l", mac: "ctrl-shift-l"},
service: "",
readOnly: false
}, {
name: "search",
name: "markdownPreviewPopup",
bindKey: {win: "ctrl-shift-m", mac: "ctrl-shift-m"},
readOnly: false
}, {
name: "searchPopup",
bindKey: {win: "ctrl-f", mac: "ctrl-f"},
readOnly: true
readOnly: false
}, {
name: "newSession",
name: "replacePopup",
bindKey: {win: "ctrl-r", mac: "ctrl-r"},
readOnly: false
}, {
name: "newFile",
bindKey: {win: "ctrl-t", mac: "ctrl-t"},
readOnly: true
}, {
name: "destroySession",
bindKey: {win: "ctrl-w", mac: "ctrl-w"},
service: "editorsService",
readOnly: false
}, {
name: "openFiles",
@@ -44,6 +53,10 @@ export const Keybindings: Array<{}> = [
name: "saveFileAs",
bindKey: {win: "ctrl-shift-s", mac: "ctrl-shift-s"},
readOnly: false
}, {
name: "closeFile",
bindKey: {win: "ctrl-w", mac: "ctrl-w"},
readOnly: false
}, {
name: "selectLeftEditor",
bindKey: {win: "ctrl-pageup", mac: "ctrl-pageup"},

View File

@@ -0,0 +1,5 @@
export abstract class ButtonMap {
static LEFT: number = 0;
static MIDDLE: number = 1;
static RIGHT: number = 2;
}

View File

@@ -16,7 +16,7 @@ import { NewtonFile } from '../types/file.type';
})
export class DndDirective {
@HostBinding('class.fileover') fileOver!: boolean;
@Output() fileDropped = new EventEmitter<any>();
@Output() fileDropped: EventEmitter<any> = new EventEmitter();
@HostListener('dragover', ['$event'])
onDragOver(evt: any) {

View File

@@ -11,17 +11,18 @@ import {
selector: '[draggable-item]'
})
export class DraggableDirective {
@Output() dragStart = new EventEmitter<PointerEvent>();
@Output() dragMove = new EventEmitter<PointerEvent>();
@Output() dragEnd = new EventEmitter<PointerEvent>();
@Output() dragStart: EventEmitter<PointerEvent> = new EventEmitter();
@Output() dragMove: EventEmitter<PointerEvent> = new EventEmitter();
@Output() dragEnd: EventEmitter<PointerEvent> = new EventEmitter();
private dragging = false;
private dragging: boolean = false;
selected: any;
@HostListener('pointerdown', ['$event'])
onPointerDown(event: PointerEvent): void {
console.log("pointerdown");
this.dragging = true;
this.dragStart.emit(event);
}
@@ -31,14 +32,12 @@ export class DraggableDirective {
if (!this.dragging) return;
console.log("pointermove");
this.dragging = true;
this.dragMove.emit(event);
}
@HostListener('document:pointerup', ['$event'])
onPointerUp(event: PointerEvent): void {
if (!this.dragging) return;
console.log("pointerup");
this.dragging = false;

View File

@@ -0,0 +1,82 @@
import {
Directive,
Output,
EventEmitter,
HostListener
} from '@angular/core';
@Directive({
selector: '[pane-handle]'
})
export class PaneHandleDirective {
@Output() dragStart: EventEmitter<PointerEvent> = new EventEmitter();
@Output() dragMove: EventEmitter<PointerEvent> = new EventEmitter();
@Output() dragEnd: EventEmitter<PointerEvent> = new EventEmitter();
private dragging: boolean = false;
private isHrPane: boolean = false;
private parentElm: any;
private leftSiblingElm: any;
private rightSiblingElm: any;
@HostListener('pointerdown', ['$event'])
onPointerDown(event: PointerEvent): void {
event.preventDefault();
let target = event.target as Element;
if (
!target.classList.contains("hr-pane-handle") &&
!target.classList.contains("vr-pane-handle")
) {
console.error("Must have 'hr-pane-handle' or 'vr-pane-handle' in classList!");
return;
}
target.requestPointerLock();
this.dragging = true;
this.isHrPane = ( target.classList.contains("hr-pane-handle") ) ? true : false ;
this.parentElm = target.parentElement as Element;
this.leftSiblingElm = target.previousElementSibling as Element;
this.rightSiblingElm = target.nextElementSibling as Element;
this.dragStart.emit(event);
}
@HostListener('pointermove', ['$event'])
onPointerMove(event: PointerEvent): void {
if (!this.dragging) return;
let x = event.movementX;
let y = event.movementY;
let parentBounds = this.parentElm.getBoundingClientRect();
let leftBounds = this.leftSiblingElm.getBoundingClientRect();
let rightBounds = this.rightSiblingElm.getBoundingClientRect();
if (this.isHrPane) {
this.leftSiblingElm.style.maxHeight = `${leftBounds.height + y}px`;
this.rightSiblingElm.style.maxHeight = `${rightBounds.height - y}px`;
} else {
this.leftSiblingElm.style.maxWidth = `${leftBounds.width + x}px`;
this.rightSiblingElm.style.maxWidth = `${rightBounds.width - x}px`;
}
this.dragMove.emit(event);
}
@HostListener('pointerup', ['$event'])
onPointerUp(event: PointerEvent): void {
if (!this.dragging) return;
this.dragging = false;
this.leftSiblingElm = null;
this.rightSiblingElm = null;
document.exitPointerLock();
this.dragEnd.emit(event);
}
}

View File

@@ -0,0 +1,64 @@
import { Injectable } from '@angular/core';
import * as ace from "ace-builds/src-min-noconflict/ace";
@Injectable({
providedIn: 'root'
})
export class ColorTokenizerService {
readonly #RULES: {} = {
start: [
{ token: "hex3", regex: "#[A-Fa-f0-9]{3}(?![A-Fa-f0-9])" },
{ token: "hex6", regex: "#[A-Fa-f0-9]{6}(?![A-Fa-f0-9])" },
{ token: "hex8", regex: "#[A-Fa-f0-9]{8}(?![A-Fa-f0-9])" },
{
token: "rgb",
regex: /rgb\s*\(\s*\d{1,3}\s*,\s*\d{1,3}\s*,\s*\d{1,3}\s*\)/
},
{
token: "rgba",
regex: /rgba\s*\(\s*\d{1,3}\s*,\s*\d{1,3}\s*,\s*\d{1,3}\s*,\s*(?:0(?:\.\d+)?|1(?:\.0+)?)\s*\)/
}
]
};
tokenizer!: any;
cssLines: {} = {};
constructor() {
Object.freeze(this.#RULES)
const Tokenizer = ace.require("ace/tokenizer").Tokenizer;
this.tokenizer = new Tokenizer(this.#RULES);
}
public async parse(data: string) {
const lines = data.split("\n");
for (let i = 0; i < lines.length; i++) {
const token = this.parseLine( lines[i] );
if (!token) continue;
this.cssLines[i] = token;
this.cssLines[i]["hash"] = btoa(
token["value"]
);
}
console.log(this.cssLines);
}
public parseLine(line: string): {} | null {
const tokens = this.tokenizer.getLineTokens(line, "start").tokens;
for (let i = 0; i < tokens.length; i++) {
if ("text" === tokens[i]["type"]) continue;
return tokens[i];
}
return null;
}
}

View File

@@ -1,47 +1,118 @@
import { ComponentRef, Injectable } from '@angular/core';
import { BehaviorSubject, ReplaySubject, Observable } from 'rxjs';
import { Injectable } from '@angular/core';
import { ReplaySubject, Observable } from 'rxjs';
import { NewtonEditorComponent } from "../../../editor/newton-editor/newton-editor.component";
import { CodeViewComponent } from "../../../editor/code-view/view.component";
import { ServiceMessage } from '../../types/service-message.type';
import { EditorSettings } from "../../configs/editor.config";
import { NewtonFile } from '../../types/file.type';
import { EditorType } from '../../types/editor.type';
@Injectable({
providedIn: 'root'
})
export class EditorsService {
private messageSubject: ReplaySubject<ServiceMessage> = new ReplaySubject<ServiceMessage>(1);
private messageSubject: ReplaySubject<ServiceMessage> = new ReplaySubject(1);
editors: Map<string, ComponentRef<NewtonEditorComponent>>;
editorSettings: typeof EditorSettings;
editors: Map<string, CodeViewComponent> = new Map();
editorSettings: typeof EditorSettings = EditorSettings;
activeEditor: string = "";
miniMapView!: CodeViewComponent;
constructor() {
this.editorSettings = EditorSettings;
this.editors = new Map<string, ComponentRef<NewtonEditorComponent>>();
}
getEditorsAsArray(): ComponentRef<NewtonEditorComponent>[] {
public getEditorsAsArray(): CodeViewComponent[] {
return [...this.editors.values()];
}
get(uuid: string): NewtonEditorComponent {
return this.editors.get(uuid).instance;
public get(uuid: string): CodeViewComponent {
return this.editors.get(uuid);
}
set(uuid: string, component: ComponentRef<NewtonEditorComponent>) {
public set(uuid: string, component: CodeViewComponent) {
if (component.mode == EditorType.MiniMap) {
this.miniMapView = component;
return;
}
this.editors.set(uuid, component);
if (Array.from(this.editors.keys()).length <= 1) return;
let _editors = this.getEditorsAsArray();
let leftEditor = null;
let rightEditor = null;
for (let i = 0; i < _editors.length; i++) {
if (_editors[i].uuid !== uuid) continue;
leftEditor = _editors[i - 1];
rightEditor = _editors[i];
}
leftEditor.rightSiblingUUID = rightEditor.uuid;
rightEditor.leftSiblingUUID = leftEditor.uuid;
}
public setSession(file: NewtonFile | undefined | null) {
if ( !file ) return;
let editorComponent = this.getActiveEditorComponent();
editorComponent.assignSession(file);
this.miniMapView.cloneSession(file);
}
public newFile() {
let editorComponent = this.getActiveEditorComponent();
editorComponent.newFile();
this.miniMapView.newFile();
}
public getSession() {
let editorComponent = this.get(this.activeEditor);
let editor = editorComponent.editor;
return editor.getSession();
}
public async setActiveEditor(activeEditor: string) {
this.activeEditor = activeEditor;
let editorComponent = this.getActiveEditorComponent();
if (!this.miniMapView) return;
if (editorComponent.activeFile) {
this.miniMapView.cloneSession(editorComponent.activeFile);
return;
}
// Note: likely a new file/buffer
this.miniMapView.editor.session.setValue(
editorComponent.editor.session.getValue()
);
}
public getActiveEditorComponent(): any {
return this.get(this.activeEditor);
}
protected getActiveEditor(): any {
let editorComponent = this.get(this.activeEditor);
let editor = editorComponent.editor;
return editor;
}
sendMessage(data: ServiceMessage): void {
public sendMessage(data: ServiceMessage): void {
this.messageSubject.next(data);
}
getMessage$(): Observable<ServiceMessage> {
public getMessage$(): Observable<ServiceMessage> {
return this.messageSubject.asObservable();
}

View File

@@ -1,90 +0,0 @@
import { Injectable } from '@angular/core';
import { EditSession } from 'ace-builds';
import { getModeForPath } from 'ace-builds/src-noconflict/ext-modelist';
import { TabsService } from './tabs/tabs.service';
import { NewtonFile } from '../../types/file.type';
import { ServiceMessage } from '../../types/service-message.type';
@Injectable({
providedIn: 'root'
})
export class FilesService {
files: Map<string, NewtonFile>;
constructor(
private tabsService: TabsService,
) {
this.files = new Map<string, NewtonFile>();
}
get(path: string): NewtonFile {
return this.files.get(path);
}
delete(file: NewtonFile) {
file.session.destroy();
window.fs.closeFile(file.path);
this.files.delete(file.path);
}
set(file: NewtonFile) {
this.files.set(file.path, file);
}
async loadFilesList(files: Array<NewtonFile>): Promise<NewtonFile | undefined | null> {
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 ];
}
async addFile(path: string, file: NewtonFile): Promise<void> {
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}`
);
}
}
async addTab(file: NewtonFile) {
let message = new ServiceMessage();
message.action = "create-tab";
message.fileName = file.fname;
message.fileUUID = file.hash;
message.filePath = file.path;
this.tabsService.sendMessage(message);
}
}

View File

@@ -8,53 +8,50 @@ import { ServiceMessage } from '../../../types/service-message.type';
providedIn: 'root'
})
export class InfoBarService {
private dataSubject: ReplaySubject<ServiceMessage> = new ReplaySubject<ServiceMessage>(1);
private fpathSubject: ReplaySubject<string> = new ReplaySubject<string>(1);
private cursorPosSubject: ReplaySubject<any> = new ReplaySubject<any>(1);
private encodeingSubject: ReplaySubject<string> = new ReplaySubject<string>(1);
private ftypeSubject: ReplaySubject<string> = new ReplaySubject<string>(1);
private dataSubject: ReplaySubject<ServiceMessage> = new ReplaySubject(1);
private fpathSubject: ReplaySubject<string> = new ReplaySubject(1);
private cursorPosSubject: ReplaySubject<any> = new ReplaySubject(1);
private encodeingSubject: ReplaySubject<string> = new ReplaySubject(1);
private ftypeSubject: ReplaySubject<string> = new ReplaySubject(1);
constructor() {}
setData(data: ServiceMessage): void {
public setData(data: ServiceMessage): void {
this.dataSubject.next(data);
}
getData$(): Observable<ServiceMessage> {
public getData$(): Observable<ServiceMessage> {
return this.dataSubject.asObservable();
}
setInfoBarFPath(data: string): void {
public setInfoBarFPath(data: string): void {
this.fpathSubject.next(data);
}
updateInfoBarFPath$(): Observable<string> {
public updateInfoBarFPath$(): Observable<string> {
return this.fpathSubject.asObservable();
}
setInfoBarCursorPos(data: any): void {
public setInfoBarCursorPos(data: any): void {
this.cursorPosSubject.next(data);
}
updateInfoBarCursorPos$(): Observable<any> {
public updateInfoBarCursorPos$(): Observable<any> {
return this.cursorPosSubject.asObservable();
}
setInfoBarEncodeing(data: string): void {
public setInfoBarEncodeing(data: string): void {
this.encodeingSubject.next(data);
}
updateInfoBarEncodeing$(): Observable<string> {
public updateInfoBarEncodeing$(): Observable<string> {
return this.encodeingSubject.asObservable();
}
setInfoBarFType(data: string): void {
public setInfoBarFType(data: string): void {
this.ftypeSubject.next(data);
}
updateInfoBarFType$(): Observable<string> {
public updateInfoBarFType$(): Observable<string> {
return this.ftypeSubject.asObservable();
}

View File

@@ -0,0 +1,182 @@
import { Injectable } from '@angular/core';
import { ReplaySubject, Observable } from 'rxjs';
import { CompletionProvider } from "ace-builds/src-min-noconflict/ace";
import { CommandBarTooltip } from "ace-builds/src-min-noconflict/ext-command_bar";
import { InlineAutocomplete } from "ace-builds/src-min-noconflict/ext-inline_autocomplete";
import { AceLanguageClient, LanguageClientConfig } from 'ace-linters/build/ace-language-client';
import { LanguageProvider } from "ace-linters";
import { ServiceMessage } from '../../../types/service-message.type';
@Injectable({
providedIn: 'root'
})
export class LspManagerService {
private messageSubject: ReplaySubject<ServiceMessage> = new ReplaySubject(1);
workspaceFolder: string = "";
lspConfigDataStr: string = "";
languageProviders: {} = {};
public loadLspConfigData(): Promise<string | void> {
return this.getLspConfigData().then((lspConfigData: string) => {
this.lspConfigDataStr = lspConfigData;
return lspConfigData;
});
}
public registerEditorToLSPClient(editor: any) {
let mode = this.getMode(editor.session);
this.languageProviders[mode]?.registerEditor(
editor,
editor.session.lspConfig
);
}
private getLspConfigData(): Promise<string> {
return window.fs.getLspConfigData();
}
private parseAndReturnLSPConfigData(): {} {
let configData = JSON.parse(
this.lspConfigDataStr.replaceAll("{workspace.folder}", this.workspaceFolder)
);
if (configData["message"]) {
console.warn(
"Warning: LSP this.lspConfigDataStr is a 'message'",
this.lspConfigDataStr
);
configData = {};
}
return configData;
}
private getInitializationOptions(mode: string, configData: {}): {} {
let initializationOptions = {};
if ( Object.keys(configData).length !== 0 && configData[mode] ) {
initializationOptions = configData[mode]["initialization-options"];
}
return initializationOptions;
}
public createLanguageProviderWithClientServer(mode: string): LanguageProvider {
if ( this.languageProviders[mode] ) return;
let servers: LanguageClientConfig[] = [];
try {
let lspConfigData = this.parseAndReturnLSPConfigData();
let initializationOptions = this.getInitializationOptions(mode, lspConfigData);
servers = [
{
module: () => import("ace-linters/build/language-client"),
modes: mode,
type: "socket",
socket: new WebSocket( lspConfigData[mode]["socket"] ),
initializationOptions: initializationOptions
}
];
} catch(error) {
console.error(
"Error: Language Server could not be loaded OR doesn't exist in Newton-LSP config setup...",
);
return;
}
this.languageProviders[mode] = AceLanguageClient.for(
servers,
{
workspacePath: this.workspaceFolder,
functionality: {
hover: true,
completion: {
overwriteCompleters: true,
lspCompleterOptions: {
triggerCharacters: {
add: [
" ",
".",
"@"
]
}
}
},
// inlineCompletion: {
// overwriteCompleters: true
// },
completionResolve: true,
format: true,
documentHighlights: true,
signatureHelp: true,
semanticTokens: true,
codeActions: true
},
// aceComponents: {
// InlineAutocomplete,
// CommandBarTooltip,
// CompletionProvider
// },
manualSessionControl: true
}
);
return this.languageProviders[mode];
}
public closeLanguageProviderWithClientServer(mode: string): LanguageProvider {
if ( !this.languageProviders[mode] ) return;
let connection = this.languageProviders[mode];
delete this.languageProviders[mode];
connection.closeConnection();
}
private getLanguageProviderWithWebWorker(): LanguageProvider {
let worker = new Worker(new URL('./webworker.js', import.meta.url));
return LanguageProvider.create(worker);
}
public registerSession(editor: any) {
let mode = this.getMode(editor.session);
if ( !this.languageProviders[mode] ) return;
this.languageProviders[mode].registerSession(
editor.session,
editor,
editor.session.lspConfig
);
}
public getMode(session: any): string {
return session.getMode()["$id"].replace("ace/mode/", "");
}
public closeDocument(session: any) {
if ( !session ) return;
let mode = this.getMode(session);
if ( !this.languageProviders[mode] ) return;
this.languageProviders[mode].closeDocument(session);
}
public sendMessage(data: ServiceMessage): void {
this.messageSubject.next(data);
}
public getMessage$(): Observable<ServiceMessage> {
return this.messageSubject.asObservable();
}
}

View File

@@ -0,0 +1,23 @@
import { Injectable } from '@angular/core';
import { ReplaySubject, Observable } from 'rxjs';
import { ServiceMessage } from '../../../types/service-message.type';
@Injectable({
providedIn: 'root'
})
export class MarkdownPreviewService {
private messageSubject: ReplaySubject<ServiceMessage> = new ReplaySubject(1);
public sendMessage(data: ServiceMessage): void {
this.messageSubject.next(data);
}
public getMessage$(): Observable<ServiceMessage> {
return this.messageSubject.asObservable();
}
}

View File

@@ -8,26 +8,22 @@ import { ReplaySubject, Observable } from 'rxjs';
})
export class FilesModalService {
private showFilesModalSubject: ReplaySubject<null> = new ReplaySubject<null>(1);
private addFileSubject: ReplaySubject<string> = new ReplaySubject<string>(1);
private addFileSubject: ReplaySubject<string> = new ReplaySubject<string>(1);
constructor() {
}
showFilesModal(): void {
public showFilesModal(): void {
this.showFilesModalSubject.next(null);
}
showFilesModalRequested$(): Observable<null> {
public showFilesModalRequested$(): Observable<null> {
return this.showFilesModalSubject.asObservable();
}
addFileToModal(data: string): void {
public addFileToModal(data: string): void {
this.addFileSubject.next(data);
}
addFileToModalRequested$(): Observable<string> {
public addFileToModalRequested$(): Observable<string> {
return this.addFileSubject.asObservable();
}

View File

@@ -0,0 +1,23 @@
import { Injectable } from '@angular/core';
import { ReplaySubject, Observable } from 'rxjs';
import { ServiceMessage } from '../../../types/service-message.type';
@Injectable({
providedIn: 'root'
})
export class SearchReplaceService {
private messageSubject: ReplaySubject<ServiceMessage> = new ReplaySubject<ServiceMessage>(1);
public sendMessage(data: ServiceMessage): void {
this.messageSubject.next(data);
}
public getMessage$(): Observable<ServiceMessage> {
return this.messageSubject.asObservable();
}
}

View File

@@ -1,6 +1,9 @@
import { Injectable } from '@angular/core';
import { Injectable, inject } from '@angular/core';
import { moveItemInArray } from '@angular/cdk/drag-drop';
import { BehaviorSubject, ReplaySubject, Observable } from 'rxjs';
import { EditorsService } from '../editors.service';
import { ServiceMessage } from '../../../types/service-message.type';
@@ -8,16 +11,84 @@ import { ServiceMessage } from '../../../types/service-message.type';
providedIn: 'root'
})
export class TabsService {
private messageSubject: ReplaySubject<ServiceMessage> = new ReplaySubject<ServiceMessage>(1);
private messageSubject: ReplaySubject<ServiceMessage> = new ReplaySubject(1);
constructor() {}
private editorsService: EditorsService = inject(EditorsService);
tabs: any[] = [];
newIndex: number = -1;
sendMessage(data: ServiceMessage): void {
public push(tabData: {}): void {
this.tabs.push(tabData);
}
public closeTab(fpath: string): void {
this.sendEditorsServiceAMessage("close-tab", fpath);
for (let i = 0; i < this.tabs.length; i++) {
if (this.tabs[i].path == fpath) {
this.splice(i);
break;
}
}
}
public sendEditorsServiceAMessage(action: string, fpath: string) {
let message = new ServiceMessage();
message.action = action;
message.filePath = fpath;
this.editorsService.sendMessage(message);
}
public getLeftSiblingTab(fpath: string): string {
if (this.tabs.length === 0 ) return;
let i = this.tabs.indexOf(fpath);
(i === 0) ? i = this.tabs.length - 1 : i -= 1;
return this.tabs[i].path;
}
public getRightSiblingTab(fpath: string): string {
if (this.tabs.length === 0 ) return;
let i = this.tabs.indexOf(fpath);
(i === (this.tabs.length - 1)) ? i = 0 : i += 1;
return this.tabs[i].path;
}
public setNewTargetIndex(fpath: string): void {
for (let i = 0; i < this.tabs.length; i++) {
if (this.tabs[i].path == fpath) {
this.newIndex = i;
break;
}
}
}
public move(oldIndex: number): void {
if (this.newIndex == -1) return;
moveItemInArray(this.tabs, oldIndex, this.newIndex);
this.newIndex = -1;
// event.currentIndex not updating for some reason...
// moveItemInArray(this.tabs, event.previousIndex, event.currentIndex);
}
public splice(index: number): void {
this.tabs.splice(index, 1);
}
public sendMessage(data: ServiceMessage): void {
this.messageSubject.next(data);
}
getMessage$(): Observable<ServiceMessage> {
public getMessage$(): Observable<ServiceMessage> {
return this.messageSubject.asObservable();
}
}

View File

@@ -0,0 +1,136 @@
import { Injectable, inject } from '@angular/core';
import { ReplaySubject, Observable } from 'rxjs';
import { EditSession, UndoManager } from 'ace-builds';
import { getModeForPath } from 'ace-builds/src-noconflict/ext-modelist';
import { TabsService } from './editor/tabs/tabs.service';
import { ColorTokenizerService } from './color-tokenizer.service';
import { NewtonFile } from '../types/file.type';
import { ServiceMessage } from '../types/service-message.type';
@Injectable({
providedIn: 'root'
})
export class FilesService {
private messageSubject: ReplaySubject<ServiceMessage> = new ReplaySubject(1);
private tabsService: TabsService = inject(TabsService);
files: Map<string, NewtonFile> = new Map();
public get(path: string): NewtonFile {
return this.files.get(path);
}
public getAllPaths(): string[] {
return [...this.files.keys()];
}
public getAllFiles(): NewtonFile[] {
return [...this.files.values()];
}
public getPreviousFile(path: string): NewtonFile {
let paths = this.getAllPaths();
if (paths.length === 0 ) return;
let i = paths.indexOf(path);
(i === 0) ? i = paths.length - 1 : i -= 1;
return this.files.get( paths[i] );
}
public getNextFile(path: string): NewtonFile {
let paths = this.getAllPaths();
if (paths.length === 0 ) return;
let i = paths.indexOf(path);
(i === (paths.length - 1)) ? i = 0 : i += 1;
return this.files.get( paths[i] );
}
public unset(file: NewtonFile) {
file.session.destroy();
window.fs.closeFile(file.path);
this.files.delete(file.path);
}
public set(file: NewtonFile) {
this.files.set(file.path, file);
}
public sendMessage(data: ServiceMessage): void {
this.messageSubject.next(data);
}
public getMessage$(): Observable<ServiceMessage> {
return this.messageSubject.asObservable();
}
public async loadFilesList(
files: Array<NewtonFile>
): Promise<NewtonFile | undefined | null> {
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 ];
}
public async addFile(
path: string,
file: NewtonFile,
loadFileContents: boolean = true,
data: string = ""
): Promise<void> {
try {
let pathParts = path.split("/");
file.fname = pathParts[ pathParts.length - 1 ];
file.path = path;
file.hash = btoa(file.path);
if (loadFileContents)
data = await window.fs.getFileContents(file.path);
file.session = new EditSession(data);
file.session["id"] = path;
file.session.setUndoManager( new UndoManager() );
file.session.setMode( getModeForPath( file.path ).mode );
file.session["lspConfig"] = {
filePath: path,
joinWorkspaceURI: false
}
file.session["colorTokenizer"] = new ColorTokenizerService();
file.session["colorTokenizer"].parse(data);
this.files.set(file.path, file);
} catch (error) {
console.error(
`---- Error ----\nPath: ${path}\nMessage: ${error}`
);
}
}
public async addTab(file: NewtonFile) {
let message = new ServiceMessage();
message.action = "create-tab";
message.fileName = file.fname;
message.fileUUID = file.hash;
message.filePath = file.path;
this.tabsService.sendMessage(message);
}
}

View File

@@ -1,75 +0,0 @@
import { Injectable } from '@angular/core';
import { BehaviorSubject, ReplaySubject, Observable } from 'rxjs';
import { AceLanguageClient, LanguageClientConfig } from 'ace-linters/build/ace-language-client';
import { LanguageProvider } from "ace-linters";
@Injectable({
providedIn: 'root'
})
export class LSPService {
lspConfigData!: {};
languageProviders: {} = {};
constructor() {
this.loadLSPService();
}
private loadLSPService() {
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 = {};
}
});
}
public registerEditor(editor: any): void {
let modeParts = editor.getSession()["$modeId"].split("/");
let mode = modeParts[ modeParts.length - 1 ];
if ( !this.languageProviders[mode] ) {
this.languageProviders[mode] = this.getLanguageProviderWithClientServer(mode);
}
this.languageProviders[mode].registerEditor(editor);
}
private getLspConfigData(): Promise<string> {
return window.fs.getLspConfigData();
}
private getLanguageProviderWithClientServer(mode: string) {
let _initializationOptions = {};
if ( Object.keys(this.lspConfigData).length !== 0 && this.lspConfigData[mode] ) {
_initializationOptions = this.lspConfigData[mode]["initialization-options"];
}
let servers: LanguageClientConfig[] = [
{
module: () => import("ace-linters/build/language-client"),
modes: mode,
type: "socket",
socket: new WebSocket(`ws://127.0.0.1:9999/${mode}`),
// socket: new WebSocket("ws://127.0.0.1:9999/?name=pylsp"),
initializationOptions: _initializationOptions
}
];
return AceLanguageClient.for(servers);
}
private getLanguageProviderWithWebWorker() {
let worker = new Worker(new URL('./webworker.js', import.meta.url));
return LanguageProvider.create(worker);
}
}

View File

@@ -0,0 +1,5 @@
export abstract class EditorType {
static MiniMap: string = "mini-map";
static ReadOnly: string = "read-only";
static Standalone: string = "standalone";
}

View File

@@ -2,8 +2,8 @@ import { EditSession } from 'ace-builds';
export interface NewtonFile extends File {
fname: string,
path: string,
hash: string,
session: EditSession
fname?: string,
path?: string,
hash?: string,
session?: EditSession,
}

View File

@@ -0,0 +1,366 @@
import {
Directive,
ElementRef,
Input,
ViewChild,
inject
} from '@angular/core';
import * as uuid from 'uuid';
import { InfoBarService } from '../../common/services/editor/info-bar/info-bar.service';
import { FilesModalService } from '../../common/services/editor/modals/files-modal.service';
import { TabsService } from '../../common/services/editor/tabs/tabs.service';
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 { LspManagerService } from '../../common/services/editor/lsp-manager/lsp-manager.service';
import { EditorSettings } from "../../common/configs/editor.config";
import { NewtonFile } from '../../common/types/file.type';
import { ServiceMessage } from '../../common/types/service-message.type';
@Directive()
export class CodeViewBase {
public uuid: string = uuid.v4();
@Input() public isDefault: boolean = false;
@Input() public mode: string = "";
public leftSiblingUUID!: string;
public rightSiblingUUID!: string;
protected infoBarService: InfoBarService = inject(InfoBarService);
protected filesModalService: FilesModalService = inject(FilesModalService);
protected tabsService: TabsService = inject(TabsService);
protected editorsService: EditorsService = inject(EditorsService);
protected filesService: FilesService = inject(FilesService);
protected searchReplaceService: SearchReplaceService = inject(SearchReplaceService);
protected markdownPreviewService: MarkdownPreviewService = inject(MarkdownPreviewService);
protected lspManagerService: LspManagerService = inject(LspManagerService);
@ViewChild('editor') editorElm!: ElementRef;
@Input() editorSettings!: typeof EditorSettings;
public editor!: any;
public aceApi!: any;
public activeFile!: NewtonFile;
public cutBuffer: string = "";
public timerId: number = -1;
public debounceId: number = -1;
public debounceWait: number = 800;
@ViewChild('contextMenu') contextMenu!: ElementRef;
public showContextMenu: boolean = false;
public selectLeftEditor() {
let message = new ServiceMessage();
message.action = "select-left-editor";
message.editorUUID = this.uuid;
this.editorsService.sendMessage(message);
}
public selectRightEditor() {
let message = new ServiceMessage();
message.action = "select-right-editor";
message.editorUUID = this.uuid;
this.editorsService.sendMessage(message);
}
public moveSessionLeft() {
let message = new ServiceMessage();
message.action = "move-session-left";
message.editorUUID = this.uuid;
this.editorsService.sendMessage(message);
}
public moveSessionRight() {
let message = new ServiceMessage();
message.action = "move-session-right";
message.editorUUID = this.uuid;
this.editorsService.sendMessage(message);
}
public addActiveStyling() {
this.editorElm.nativeElement.classList.add("active-editor")
}
public removeActiveStyling() {
this.editorElm.nativeElement.classList.remove("active-editor")
}
public openCommandPalette2() {
this.editor.execCommand("openCommandPalette");
}
public showKeyShortcuts() {
this.editor.showKeyboardShortcuts();
}
public lspManagerPopup() {
let message = new ServiceMessage();
message.action = "toggle-lsp-manager";
this.lspManagerService.sendMessage(message);
}
public markdownPreviewPopup() {
let message = new ServiceMessage();
message.action = "toggle-markdown-preview";
this.markdownPreviewService.sendMessage(message);
}
public searchPopup() {
let message = new ServiceMessage();
message.action = "toggle-search-replace";
this.searchReplaceService.sendMessage(message);
// this.editor.execCommand("find");
}
public replacePopup() {
let message = new ServiceMessage();
message.action = "toggle-search-replace";
this.searchReplaceService.sendMessage(message);
// this.editor.execCommand("replace");
}
public showFilesList() {
let paths = this.filesService.getAllPaths();
let stubPaths = [];
for (let i = 0; i < paths.length; i++) {
let fpath = paths[i];
if (fpath.length > 67) {
fpath = "..." + fpath.slice(fpath.length - 67, fpath.length);
}
stubPaths.push(fpath);
}
this.editor.prompt("",
{
name: "Files:",
placeholder: "Search...",
getCompletions: (search) => {
let query = search.getValue();
let result = [];
if (!query) return stubPaths;
for (let i = 0; i < stubPaths.length; i++) {
if (stubPaths[i].includes(query)) {
result.push(stubPaths[i]);
}
}
return result;
},
onAccept: (data) => {
let fpath = (data.value) ? data.value : data.item;
let path = "";
for (let i = 0; i < stubPaths.length; i++) {
if (stubPaths[i] === fpath) {
path = paths[i];
}
}
if (!path) return;
this.editorsService.setSession(
this.filesService.get(path)
);
}
});
}
public closeFile() {
this.tabsService.closeTab(this.activeFile.path);
}
public toggleFullScreen() {
window.main.toggleFullScreen();
}
public setAsReadOnly() {
this.editor.setReadOnly(true);
this.editor.setShowPrintMargin(false);
}
public setAsMiniMapView() {
this.editor.renderer.showLineNumbers = false;
this.editor.renderer.setShowGutter(false);
this.editor.setReadOnly(true);
this.editor.setFontSize(2);
this.editor.setHighlightActiveLine(false);
this.editor.setHighlightGutterLine(false);
this.editor.setShowFoldWidgets(false);
this.editor.setShowPrintMargin(false);
this.editor.session.setUseWrapMode(true);
this.editorElm.nativeElement.parentElement.classList.add("col-1");
this.editorElm.nativeElement.parentElement.classList.add("zero-margin-padding");
this.editor.on("click", (event) => {
event.preventDefault();
event.stopPropagation();
let editorComponent = this.editorsService.getActiveEditorComponent();
let pos = this.editor.getCursorPosition();
editorComponent.editor.moveCursorToPosition(pos);
editorComponent.editor.clearSelection();
editorComponent.editor.renderer.scrollCursorIntoView(null, 0.5);
});
this.editor.on("mousewheel", (event) => {
event.preventDefault();
event.stopPropagation();
let editorComponent = this.editorsService.getActiveEditorComponent();
editorComponent.editor.renderer.scrollBy(null, event.domEvent.deltaY);
});
}
public zoomIn() {
this.editor.setFontSize(
parseInt(this.editor.getFontSize()) + 1
);
}
public zoomOut() {
this.editor.setFontSize(
parseInt(this.editor.getFontSize()) - 1
);
}
public movelinesUp() {
this.editor.execCommand("movelinesup");
}
public movelinesDown() {
this.editor.execCommand("movelinesdown");
}
public duplicateLines() {
this.editor.execCommand("copylinesdown");
}
public cutText() {
let cutText = this.editor.getSelectedText();
this.editor.remove();
navigator.clipboard.writeText(cutText).catch(() => {
console.error("Unable to cut text...");
});
}
public copyText() {
let copyText = this.editor.getSelectedText();
navigator.clipboard.writeText(copyText).catch(() => {
console.error("Unable to copy text...");
});
}
public pasteText() {
navigator.clipboard.readText().then((pasteText) => {
this.editor.insert(pasteText, true);
});
}
protected updateInfoBar() {
this.infoBarService.setInfoBarFPath(this.activeFile?.path)
this.infoBarService.setInfoBarCursorPos(
this.editor.getCursorPosition()
);
this.infoBarService.setInfoBarFType(
this.editor.session.getMode()["$id"]
);
}
public newFile() {
this.activeFile = null;
let session = this.aceApi.createEditSession([""]);
this.editor.setSession(session);
}
protected openFiles() {
let startDir = "";
if (this.activeFile) {
let pathParts = this.activeFile.path.split("/");
pathParts.pop();
startDir = pathParts.join( '/' );
}
window.fs.openFiles(startDir);
}
protected saveFile() {
if (!this.activeFile) {
this.saveFileAs();
return;
}
const text = this.activeFile.session.getValue();
window.fs.saveFile(this.activeFile.path, text);
}
protected saveFileAs() {
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);
this.filesService.addFile(
path,
file,
false,
text
).then(() => {
this.activeFile = this.filesService.get(path);
this.editor.setSession(this.activeFile.session);
this.filesService.addTab(this.activeFile);
});
});
}
protected cutToBuffer() {
if (this.timerId) { clearTimeout(this.timerId); }
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.setBufferClearTimeout();
}
protected pasteCutBuffer() {
if (this.timerId) { clearTimeout(this.timerId); }
this.editor.insert(this.cutBuffer, true);
this.setBufferClearTimeout();
}
private setBufferClearTimeout(timeout: number = 5000) {
this.timerId = setTimeout(() => {
this.cutBuffer = "";
this.timerId = -1;
}, timeout);
}
private quit() {
window.main.quit();
}
}

View File

@@ -0,0 +1,33 @@
.editor {
position: relative;
height: 100%;
width: 100%;
}
.active-editor {
border-style: solid;
border-width: thin;
border-color: rgba(124, 124, 124, 1);
}
.contextMenu {
display: inline-table;
z-index: 500;
position: absolute;
min-width: 2em;
padding: 0.2em;
top: 0em;
right: 0em;
background-color: gray;
}
.contextMenu li:hover {
background-color: rgba(0, 124, 0, 0.64);
cursor: pointer;
}
.contextMenu li {
padding: 0em 0.2em;
}

View File

@@ -0,0 +1,15 @@
<div class="editor" #editor >
</div>
<ul #contextMenu
class="contextMenu"
[hidden]="!showContextMenu"
(blur)="hideContextMenu"
(click)="contextMenuClicked($event)"
>
<li command="cutText">Cut</li>
<li command="copyText">Copy</li>
<li command="pasteText">Paste</li>
<hr/>
<li command="prettyJSON">Prettify JSON</li>
</ul>

View File

@@ -0,0 +1,299 @@
import { Component } from "@angular/core";
// Import Ace and its modes/themes so that `ace` global is defined
import * as ace from "ace-builds/src-min-noconflict/ace";
// Note: https://github.com/mkslanc/ace-linters/blob/c286d85c558530aa1b0597d02108bc782abd4736/packages/demo/file-api-websockets/client.ts#L27
// import { AceLayout, Box, TabManager, Button, dom, AceTreeWrapper, FileSystemWeb, Pane, AceEditor, Tab } from "ace-layout";
import "ace-builds/src-min-noconflict/ext-settings_menu";
import "ace-builds/src-min-noconflict/ext-keybinding_menu";
import "ace-builds/src-min-noconflict/ext-command_bar";
import "ace-builds/src-min-noconflict/ext-prompt";
import "ace-builds/src-min-noconflict/ext-code_lens";
// import "ace-builds/src-min-noconflict/ext-searchbox";
import "ace-builds/src-min-noconflict/ext-language_tools";
// import "ace-builds/src-min-noconflict/theme-one_dark";
// import "ace-builds/src-min-noconflict/theme-penguins_in_space";
import "ace-builds/src-min-noconflict/theme-gruvbox";
// https://www.npmjs.com/package/web-tree-sitter
// import { Language, Parser } from 'web-tree-sitter';
import { CodeViewBase } from './view.base';
import { NewtonFile } from '../../common/types/file.type';
import { EditorType } from '../../common/types/editor.type';
import { ServiceMessage } from '../../common/types/service-message.type';
import { ButtonMap } from '../../common/constants/button.map';
@Component({
selector: 'code-view',
standalone: true,
imports: [
],
templateUrl: './view.component.html',
styleUrl: './view.component.css',
host: {
'class': 'col zero-margin-padding scroller'
}
})
export class CodeViewComponent extends CodeViewBase {
constructor() {
super();
// const { Parser } = window.TreeSitter;
// const { Parser } = TreeSitter;
// console.log(treeSitter);
// treeSitter.Parser.init().then(() => {
// console.log("Parser ready...");
// });
// const parser = new Parser();
// const JavaScript = await Language.load('/path/to/tree-sitter-javascript.wasm');
// Language.load('resources/wasm/tree-sitter-javascript.wasm').then((language) => {
// console.log(language);
// });
this.aceApi = ace;
}
private ngAfterViewInit(): void {
this.loadAce();
}
private loadAce(): void {
this.configAceAndBindToElement()
this.setupRequestedMode();
}
private configAceAndBindToElement(): void {
this.editorSettings = this.editorsService.editorSettings;
ace.config.set('basePath', this.editorSettings.BASE_PATH);
this.editor = ace.edit( this.editorElm.nativeElement );
this.editor.setOptions( this.editorSettings.CONFIG );
this.editorsService.set(this.uuid, this);
if (this.isDefault) {
this.editorsService.setActiveEditor(this.uuid);
this.addActiveStyling();
this.editor.focus();
}
}
private setupRequestedMode() {
switch(this.mode) {
case EditorType.Standalone:
// Note: Ace editor without any additional Newton setup...
break;
case EditorType.MiniMap:
this.setAsMiniMapView();
break;
case EditorType.ReadOnly:
this.setAsReadOnly();
break;
default:
this.loadNewtonKeyBindings();
this.loadNewtonEventBindings();
break;
}
}
private loadNewtonKeyBindings(): void {
let keyBindings = [];
for (let i = 0; i < this.editorSettings.KEYBINDINGS.length; i++) {
let keyBinding = this.editorSettings.KEYBINDINGS[i];
keyBindings.push(
{
name: keyBinding.name,
bindKey: keyBinding.bindKey,
exec: (keyBinding.name && keyBinding?.service) ?
() => (
this[keyBinding?.service][keyBinding.name]()
)
:
(this[keyBinding.name]) ?
() => (
this[keyBinding.name]()
)
:
() => (
console.log(
`Name: ${keyBinding.name}, is not mapping to a method OR mapping to a Service: ${keyBinding?.service} and Name: ${keyBinding.name}.`
)
)
,
readOnly: keyBinding.readOnly
}
);
}
this.editor.commands.addCommands( keyBindings );
}
private loadNewtonEventBindings(): void {
// Note: https://ajaxorg.github.io/ace-api-docs/interfaces/ace.Ace.EditorEvents.html
this.editor.on("focus", (e) => {
let message = new ServiceMessage();
message.action = "set-active-editor";
message.editorUUID = this.uuid;
message.rawData = this.editor;
this.editorsService.sendMessage(message);
this.searchReplaceService.sendMessage(message);
message = new ServiceMessage();
message.action = "set-active-editor";
message.rawData = this;
this.lspManagerService.sendMessage(message);
this.markdownPreviewService.sendMessage(message);
this.updateInfoBar();
});
this.editor.on("click", (event) => {
this.updateInfoBar();
});
this.editor.addEventListener("mousedown", (event) => {
if (ButtonMap.LEFT === event.domEvent.button) {
this.showContextMenu = false;
} else if (ButtonMap.RIGHT === event.domEvent.button) {
let menuElm = this.contextMenu.nativeElement;
let pageX = event.domEvent.pageX;
let pageY = event.domEvent.pageY;
const origin = {
left: pageX + 5,
top: pageY - 5
};
menuElm.style.left = `${origin.left}px`;
menuElm.style.top = `${origin.top}px`;
this.showContextMenu = true;
}
});
this.editor.on("input", () => {
this.updateInfoBar();
});
this.editor.on("keyboardActivity", (e) => {
switch(e.command.name) {
case "golineup":
case "golinedown":
case "gotoleft":
case "gotoright":
this.infoBarService.setInfoBarCursorPos(
this.editor.getCursorPosition()
);
break;
default:
break;
}
});
this.editor.on("mousewheel", (event) => {
if (event.domEvent.ctrlKey && event.domEvent.deltaY < 0) {
event.preventDefault();
event.stopPropagation();
this.zoomIn();
} else if (event.domEvent.ctrlKey && event.domEvent.deltaY > 0) {
event.preventDefault();
event.stopPropagation();
this.zoomOut();
}
});
this.editor.on("change", () => {
if (this.debounceId) { clearTimeout(this.debounceId); }
this.setDebounceTimeout();
if (!this.activeFile) return;
let message = new ServiceMessage();
message.action = "file-changed";
message.filePath = this.activeFile.path;
this.tabsService.sendMessage(message);
});
this.editor.on("changeSession", (session) => {
let message = new ServiceMessage();
message.action = "editor-update";
message.rawData = this;
this.lspManagerService.sendMessage(message);
this.updateInfoBar();
});
}
public prettyJSON() {
let data = JSON.parse(this.editor.session.getValue());
this.editor.session.setValue(
JSON.stringify(data, null, 4)
);
}
public hideContextMenu() {
this.showContextMenu = false;
}
public contextMenuClicked(event: any) {
this.showContextMenu = false;
const command = event.target.getAttribute("command");
const args = event.target.getAttribute("args");
if (!command) return;
this[command]( (args) ? args : null );
}
public assignSession(file: NewtonFile) {
if (!file) return;
this.activeFile = file;
this.editor.setSession(file.session);
}
public cloneSession(file: NewtonFile) {
if (!file) return;
this.activeFile = file;
let session = this.aceApi.createEditSession(file.session.getValue());
session["$config"] = {
"lsp": {
"filePath": file.path
}
}
session.setMode( file.session.getMode()["$id"] );
session.setUseWrapMode(true);
this.editor.setSession(session);
}
private setDebounceTimeout(timeout: number = null) {
this.debounceId = setTimeout(() => {
this.editorsService.miniMapView.editor.session.setValue(
this.editor.session.getValue()
);
this.debounceId = -1;
}, (timeout) ? timeout : this.debounceWait);
}
}

View File

@@ -1,20 +1,3 @@
.dropzone {
height: 90vh;
}
.editor-size-button {
text-align: center;
width: 4em;
color: rgba(225, 225, 225, 0.64);
border-style: solid;
border-width: thin;
border-color: rgba(124, 124, 124, 0.64);
margin-left: 0.2em;
margin-right: 0.2em;
float: left;
}
.editor-size-button:hover {
cursor: pointer;
border-color: rgba(0, 124, 0, 0.64);
}
height: 92vh;
}

View File

@@ -1,40 +1,10 @@
<div class="col">
<div class="row dropzone" #dropzone dropzone (fileDropped)="onFileDropped($event)">
<ng-container #containerRef>
</ng-container>
<code-view [isDefault]="true"></code-view>
<hr class="col vr-pane-handle" pane-handle />
<code-view></code-view>
<code-view [mode]="'mini-map'"></code-view>
</div>
<div class="row">
<div class="col editor-left-size" (click)="setEditorSize($event)">
<div class="editor-size-button size-12">
100%
</div>
<div class="editor-size-button size-8">
75%
</div>
<div class="editor-size-button size-6">
50%
</div>
<div class="editor-size-button size-2">
25%
</div>
</div>
<div class="col editor-right-size" (click)="setEditorSize($event)">
<div class="editor-size-button size-12">
100%
</div>
<div class="editor-size-button size-8">
75%
</div>
<div class="editor-size-button size-6">
50%
</div>
<div class="editor-size-button size-2">
25%
</div>
</div>
</div>
<files-modal></files-modal>
<div>

View File

@@ -1,12 +1,14 @@
import { Component, ElementRef, ViewChild, TemplateRef, ComponentRef, ViewContainerRef } from '@angular/core';
import { Subject, takeUntil } from 'rxjs';
import { Component, DestroyRef, inject } from '@angular/core';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
import { NewtonEditorComponent } from "./newton-editor/newton-editor.component";
import { FilesModalComponent } from "./modals/files-modal.component";
import { EditorsService } from '../common/services/editor/editors.service';
import { FilesService } from '../common/services/editor/files.service';
import { TabsService } from '../common/services/editor/tabs/tabs.service';
import { FilesService } from '../common/services/files.service';
import { CodeViewComponent } from "./code-view/view.component";
import { DndDirective } from '../common/directives/dnd.directive';
import { PaneHandleDirective } from '../common/directives/pane-handle.directive';
import { NewtonFile } from '../common/types/file.type';
import { ServiceMessage } from '../common/types/service-message.type';
@@ -17,7 +19,8 @@ import { ServiceMessage } from '../common/types/service-message.type';
standalone: true,
imports: [
DndDirective,
FilesModalComponent
PaneHandleDirective,
CodeViewComponent
],
templateUrl: './editors.component.html',
styleUrl: './editors.component.css',
@@ -26,116 +29,55 @@ import { ServiceMessage } from '../common/types/service-message.type';
}
})
export class EditorsComponent {
private unsubscribe = new Subject<void>();
readonly #destroyRef: DestroyRef = inject(DestroyRef);
@ViewChild('containerRef', {read: ViewContainerRef}) containerRef!: ViewContainerRef;
activeEditor!: string;
private editorsService: EditorsService = inject(EditorsService);
private tabsService: TabsService = inject(TabsService);
private filesService: FilesService = inject(FilesService);
constructor(
private editorsService: EditorsService,
private filesService: FilesService
) {
}
public ngAfterViewInit(): void {
constructor() {
this.loadSubscribers();
this.loadMainSubscribers();
let leftEditor = this.createEditor();
let rightEditor = this.createEditor();
this.activeEditor = leftEditor.instance.uuid;
leftEditor.instance.isDefault = true;
leftEditor.instance.rightSiblingUUID = rightEditor.instance.uuid;
rightEditor.instance.leftSiblingUUID = leftEditor.instance.uuid;
}
loadSubscribers() {
private loadSubscribers() {
this.editorsService.getMessage$().pipe(
takeUntil(this.unsubscribe)
takeUntilDestroyed(this.#destroyRef)
).subscribe((message: ServiceMessage) => {
if (message.action == "select-left-editor") {
let editorComponent = this.editorsService.get(message.editorUUID);
if (!editorComponent.leftSiblingUUID) return;
let siblingComponent = this.editorsService.get(editorComponent.leftSiblingUUID);
siblingComponent.editor.focus()
} else if (message.action == "select-right-editor") {
let editorComponent = this.editorsService.get(message.editorUUID);
if (!editorComponent.rightSiblingUUID) return;
let siblingComponent = this.editorsService.get(editorComponent.rightSiblingUUID);
siblingComponent.editor.focus()
} else if (message.action == "move-session-left") {
let editorComponent = this.editorsService.get(message.editorUUID);
if (!editorComponent.leftSiblingUUID) return;
let siblingComponent = this.editorsService.get(editorComponent.leftSiblingUUID);
let session = editorComponent.editor.getSession();
let siblingSession = siblingComponent.editor.getSession();
if (session == siblingSession) return;
siblingComponent.editor.setSession(session);
editorComponent.newBuffer();
siblingComponent.editor.focus()
} else if (message.action == "move-session-right") {
let editorComponent = this.editorsService.get(message.editorUUID);
if (!editorComponent.rightSiblingUUID) return;
let siblingComponent = this.editorsService.get(editorComponent.rightSiblingUUID);
let session = editorComponent.editor.getSession();
let siblingSession = siblingComponent.editor.getSession();
if (session == siblingSession) return;
siblingComponent.editor.setSession(session);
editorComponent.newBuffer();
siblingComponent.editor.focus()
} else if (message.action == "set-active-editor") {
this.editorsService.get(this.activeEditor).removeActiveStyling();
this.activeEditor = message.editorUUID;
this.editorsService.get(this.activeEditor).addActiveStyling();
} else if (message.action == "set-tab-to-editor") {
let file = this.filesService.get(message.filePath);
let editorComponent = this.getActiveEditorComponent();
let editor = editorComponent.editor;
editorComponent.activeFile = file;
editor.setSession(file.session);
} else if (message.action == "close-tab") {
let file = this.filesService.get(message.filePath);
let editors = this.editorsService.getEditorsAsArray();
for (let i = 0; i < editors.length; i++) {
let editorComponent = editors[i].instance;
if (editorComponent.editor.session == file.session) {
editorComponent.newBuffer();
}
}
this.filesService.delete(file);
switch ( message.action ) {
case "select-left-editor":
this.selectLeftEditor(message);
break;
case "select-right-editor":
this.selectRightEditor(message);
break;
case "move-session-left":
this.moveSessionLeft(message);
break;
case "move-session-right":
this.moveSessionRight(message);
break;
case "set-active-editor":
this.setActiveEditor(message);
break;
case "set-tab-to-editor":
this.setTabToEditor(message);
break;
case "close-tab":
this.closeTab(message);
break;
default:
break;
}
});
}
loadMainSubscribers() {
window.fs.onLoadFiles(async (paths: []) => {
for (let i = 0; i < paths.length; i++) {
let file = new File([], "") as NewtonFile;
if ( this.filesService.get(paths[i]) ) continue;
await this.filesService.addFile(paths[i], file);
this.filesService.addTab(file);
}
let path = paths[ paths.length - 1 ];
let file = this.filesService.get(path);
this.setSession(file);
});
private loadMainSubscribers() {
window.main.onMenuActions(async (action: string) => {
let editorComponent = this.getActiveEditorComponent();
let editorComponent = this.editorsService.getActiveEditorComponent();
let editor = editorComponent.editor;
switch ( action ) {
@@ -169,108 +111,175 @@ export class EditorsComponent {
editor.showSettingsMenu();
case "show-about":
break;
case "quit":
window.main.quit();
break;
default:
editor.execCommand(action);
break;
}
});
}
ngOnDestroy() {
this.unsubscribe.next();
this.unsubscribe.complete();
}
window.fs.onLoadFiles(async (paths: []) => {
for (let i = 0; i < paths.length; i++) {
let file = new File([], "") as NewtonFile;
// Note: Only really works with 2 editors and very brittle logic.
protected setEditorSize(event: any) {
let lEditorComponent = null;
let rEditorComponent = null;
let lSize = 6;
let rSize = 6;
if ( this.filesService.get(paths[i]) ) continue;
if (
event.target.parentElement.classList.contains("editor-left-size")
) {
lSize = parseInt(
event.target.classList[1].split("-")[1]
);
rSize = 12 - lSize;
lEditorComponent = this.editorsService.get(this.activeEditor);
if (lEditorComponent.leftSiblingUUID) {
rEditorComponent = lEditorComponent;
lEditorComponent = this.editorsService.get(lEditorComponent.leftSiblingUUID);
} else {
rEditorComponent = this.editorsService.get(lEditorComponent.rightSiblingUUID);
await this.filesService.addFile(paths[i], file);
this.filesService.addTab(file);
}
} else {
rSize = parseInt(
event.target.classList[1].split("-")[1]
);
lSize = 12 - rSize;
rEditorComponent = this.editorsService.get(this.activeEditor);
if (rEditorComponent.rightSiblingUUID) {
lEditorComponent = rEditorComponent;
rEditorComponent = this.editorsService.get(rEditorComponent.rightSiblingUUID);
} else {
lEditorComponent = this.editorsService.get(rEditorComponent.leftSiblingUUID);
}
}
let lElm = lEditorComponent.editorElm.nativeElement.parentElement;
let rElm = rEditorComponent.editorElm.nativeElement.parentElement;
let path = paths[ paths.length - 1 ];
let file = this.filesService.get(path);
this.editorsService.setSession(file);
});
lElm.setAttribute(
'class',
(lSize == 0) ? "hidden" : `col col-${lSize}`
);
window.fs.onChangedFile(async (path: string, data: string) => {
let file = this.filesService.get(path);
file.session.setValue(data);
rElm.setAttribute(
'class',
(rSize == 0) ? "hidden" : `col col-${rSize}`
);
// Note: fake 'save' event to not show as changed iven external save happened...
let message = new ServiceMessage();
message.action = "file-saved";
message.filePath = path;
if (lSize == 0) rEditorComponent.editor.focus();
if (rSize == 0) lEditorComponent.editor.focus();
}
this.tabsService.sendMessage(message);
});
private createEditor() {
const component = this.containerRef.createComponent(NewtonEditorComponent);
component.instance.editorSettings = this.editorsService.editorSettings;
this.editorsService.set(component.instance.uuid, component)
window.fs.onDeletedFile(async (path: string) => {
let message = new ServiceMessage();
message.action = "file-deleted";
message.filePath = path;
this.tabsService.sendMessage(message);
this.filesService.sendMessage(message);
});
window.fs.onSavedFile(async (path: string) => {
let message = new ServiceMessage();
message.action = "file-saved";
message.filePath = path;
this.tabsService.sendMessage(message);
});
window.fs.onUpdateFilePath(async (path: string) => {
console.log("TODO (onUpdateFilePath) :", path);
// this.tabsService.sendMessage(message);
// this.filesService.sendMessage(message);
});
return component;
}
protected onFileDropped(files: any) {
this.filesService.loadFilesList(files).then((file: NewtonFile | undefined | null) => {
this.setSession(file);
// Note: if we drop an already loaded file the path doesn't get set and
// therefor the last file in drop list might get returned without path.
if (!file.path) return;
this.editorsService.setSession(file);
});
}
private async setSession(file: NewtonFile | undefined | null) {
if ( !file ) return;
let editorComponent = this.getActiveEditorComponent();
let editor = editorComponent.editor;
editorComponent.activeFile = file;
editor.setSession(file.session);
private selectLeftEditor(message: ServiceMessage) {
let editorComponent = this.editorsService.get(message.editorUUID);
if (!editorComponent.leftSiblingUUID) return;
let siblingComponent = this.editorsService.get(editorComponent.leftSiblingUUID);
siblingComponent.editor.focus();
}
private getSession() {
let editorComponent = this.editorsService.get(this.activeEditor);
private selectRightEditor(message: ServiceMessage) {
let editorComponent = this.editorsService.get(message.editorUUID);
if (!editorComponent.rightSiblingUUID) return;
let siblingComponent = this.editorsService.get(editorComponent.rightSiblingUUID);
siblingComponent.editor.focus();
}
private moveSessionLeft(message: ServiceMessage) {
let editorComponent = this.editorsService.get(message.editorUUID);
if (!editorComponent.leftSiblingUUID) return;
let siblingComponent = this.editorsService.get(editorComponent.leftSiblingUUID);
this.moveSession("left", editorComponent, siblingComponent);
}
private moveSessionRight(message: ServiceMessage) {
let editorComponent = this.editorsService.get(message.editorUUID);
if (!editorComponent.rightSiblingUUID) return;
let siblingComponent = this.editorsService.get(editorComponent.rightSiblingUUID);
this.moveSession("right", editorComponent, siblingComponent);
}
private moveSession(
direction: string,
editorComponent: CodeViewComponent,
siblingComponent: CodeViewComponent
) {
let session = editorComponent.editor.getSession();
let siblingSession = siblingComponent.editor.getSession();
if (session == siblingSession) return;
let targetPath: string = this.tabsService.getRightSiblingTab(
editorComponent.activeFile.path
);
siblingComponent.assignSession(editorComponent.activeFile);
if (targetPath) {
editorComponent.assignSession(
this.filesService.get(targetPath)
);
} else {
editorComponent.newFile();
}
siblingComponent.editor.focus()
}
private setActiveEditor(message: ServiceMessage) {
this.editorsService.getActiveEditorComponent().removeActiveStyling();
this.editorsService.setActiveEditor(message.editorUUID);
this.editorsService.getActiveEditorComponent().addActiveStyling();
}
private setTabToEditor(message: ServiceMessage) {
let file = this.filesService.get(message.filePath);
let editorComponent = this.editorsService.getActiveEditorComponent();
let editor = editorComponent.editor;
return editor.getSession();
editorComponent.assignSession(file);
this.editorsService.miniMapView.cloneSession(file);
}
private getActiveEditorComponent(): any {
return this.editorsService.get(this.activeEditor);
}
private closeTab(message: ServiceMessage) {
let activeComponent = this.editorsService.getActiveEditorComponent();
let editors = this.editorsService.getEditorsAsArray();
let file = this.filesService.get(message.filePath);
private getActiveEditor(): any {
let editorComponent = this.editorsService.get(this.activeEditor);
let editor = editorComponent.editor;
return editor;
}
for (let i = 0; i < editors.length; i++) {
let editorComponent = editors[i];
if (editorComponent.editor.session !== file.session) continue;
let targetFile = this.filesService.getPreviousFile(file.path)
if (targetFile && (targetFile.path !== message.filePath)) {
editorComponent.assignSession(targetFile);
if (activeComponent == editorComponent) {
this.editorsService.miniMapView.cloneSession(targetFile);
}
} else {
editorComponent.newFile();
if (activeComponent == editorComponent) {
this.editorsService.miniMapView.newFile();
}
}
}
activeComponent.lspManagerService.closeDocument(file.session);
this.filesService.unset(file);
}
}

View File

@@ -1,5 +1,5 @@
import { Component } from '@angular/core';
import { Subject, takeUntil } from 'rxjs';
import { Component, DestroyRef, inject } from '@angular/core';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
import { InfoBarService } from '../../common/services/editor/info-bar/info-bar.service';
@@ -17,7 +17,9 @@ import { InfoBarService } from '../../common/services/editor/info-bar/info-bar.s
}
})
export class InfoBarComponent {
private unsubscribe = new Subject<void>();
readonly #destroyRef: DestroyRef = inject(DestroyRef);
private infoBarService: InfoBarService = inject(InfoBarService);
fpath: string = "";
path: string = "";
@@ -26,20 +28,14 @@ export class InfoBarComponent {
ftype: string = "";
constructor(
private infoBarService: InfoBarService
) {}
public ngAfterViewInit(): void {
constructor() {
this.loadSubscribers();
}
loadSubscribers() {
private loadSubscribers() {
this.infoBarService.updateInfoBarFPath$().pipe(
takeUntil(this.unsubscribe)
takeUntilDestroyed(this.#destroyRef)
).subscribe((fpath: string) => {
this.fpath = fpath;
let _path = fpath;
@@ -52,19 +48,19 @@ export class InfoBarComponent {
});
this.infoBarService.updateInfoBarCursorPos$().pipe(
takeUntil(this.unsubscribe)
takeUntilDestroyed(this.#destroyRef)
).subscribe((cursorPos: any) => {
this.cursorPos = `${cursorPos.row + 1}:${cursorPos.column}`;
});
this.infoBarService.updateInfoBarEncodeing$().pipe(
takeUntil(this.unsubscribe)
takeUntilDestroyed(this.#destroyRef)
).subscribe((encodeing: string) => {
this.encodeing = encodeing;
});
this.infoBarService.updateInfoBarFType$().pipe(
takeUntil(this.unsubscribe)
takeUntilDestroyed(this.#destroyRef)
).subscribe((ftype: string) => {
let mode = ftype.split("/");
this.ftype = mode[ mode.length - 1 ];

View File

@@ -0,0 +1,11 @@
.lsp-config-text {
display: grid;
min-height: 25em;
}
.clear-left-padding {
padding-left: 0px;
}
.clear-right-padding {
padding-right: 0px;
}

View File

@@ -0,0 +1,66 @@
<div class="container-fluid">
<div class="row mt-2 mb-3">
<div class="col clear-right-padding">
<div class="input-group-sm">
<label class="form-control" [innerText]="lspManagerService.workspaceFolder || 'Project Path...'">
</label>
</div>
</div>
<div class="col col-auto clear-left-padding">
<button class="btn btn-sm btn-dark" (click)="clearWorkspaceFolder()">x</button>
</div>
<div class="col col-auto">
<div class="input-group-sm">
<button class="btn btn-sm btn-dark" (click)="setWorkspaceFolder()">Workspace Folder</button>
<button class="btn btn-sm btn-danger ms-5" (click)="hideLspManager()">X</button>
</div>
</div>
</div>
<div class="row mt-2 md-2">
<div class="col">
LSP Configs:
</div>
</div>
<div class="row">
<div class="col">
<code-view #lspEditorComponent [mode]="'standalone'" class="lsp-config-text"></code-view>
</div>
</div>
<div class="row mt-2 md-2">
<div class="col col-sm" [hidden]="!lspManagerService.workspaceFolder">
<button class="btn btn-sm btn-dark" (click)="createLanguageClient()">Create Language Client</button>
<button class="btn btn-sm btn-dark" (click)="closeLanguageClient()">Close Language Client</button>
</div>
<div class="col">
Target Editor: <label [innerText]="editor?.id || '...'"></label>
</div>
<div class="col-sm" [hidden]="!lspManagerService.workspaceFolder">
<button class="btn btn-sm btn-dark"
(click)="registerEditorToLanguageClient()">
Register Editor To LSP
</button>
</div>
<div class="col">
Active Editor Session:
</div>
</div>
<div class="row">
<div class="col">
<code-view #sessionEditorComponent [mode]="'read-only'" class="lsp-config-text"></code-view>
</div>
</div>
</div>

View File

@@ -0,0 +1,160 @@
import {
Component,
ChangeDetectorRef,
DestroyRef,
ElementRef,
HostBinding,
ViewChild,
inject } from '@angular/core';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
import { LspManagerService } from '../../common/services/editor/lsp-manager/lsp-manager.service';
import { CodeViewComponent } from '../code-view/view.component';
import { ServiceMessage } from '../../common/types/service-message.type';
@Component({
selector: 'lsp-manager',
standalone: true,
imports: [
CodeViewComponent
],
templateUrl: './lsp-manager.component.html',
styleUrl: './lsp-manager.component.css',
host: {
'class': 'lsp-manager',
"(keyup)": "globalLspManagerKeyHandler($event)"
}
})
export class LspManagerComponent {
readonly #destroyRef = inject(DestroyRef);
private changeDetectorRef: ChangeDetectorRef = inject(ChangeDetectorRef);
lspManagerService: LspManagerService = inject(LspManagerService);
@HostBinding("class.hidden") isHidden: boolean = true;
@ViewChild('lspEditorComponent') lspEditorComponent!: CodeViewComponent;
@ViewChild('sessionEditorComponent') sessionEditorComponent!: CodeViewComponent;
lspTextEditor: any;
innerEditor: any;
editor: any;
activeFile: any;
constructor() {
this.loadSubscribers();
}
private ngAfterViewInit(): void {
this.mapEditorsAndLoadConfig();
}
private mapEditorsAndLoadConfig() {
this.lspTextEditor = this.lspEditorComponent.editor;
this.innerEditor = this.sessionEditorComponent.editor;
this.lspTextEditor.on("input", () => {
this.lspManagerService.lspConfigDataStr =
this.lspTextEditor.session.getValue();
});
this.lspManagerService.loadLspConfigData().then((lspConfigData) => {
this.lspTextEditor.session.setMode("ace/mode/json");
this.lspTextEditor.session.setValue(lspConfigData);
});
}
private loadSubscribers() {
this.lspManagerService.getMessage$().pipe(
takeUntilDestroyed(this.#destroyRef)
).subscribe((message: ServiceMessage) => {
if (message.action === "toggle-lsp-manager") {
this.toggleLspManager(message);
} else if (message.action === "set-active-editor") {
this.setActiveEditor(message);
} else if (message.action === "editor-update") {
this.editorUpdate(message);
} else if (message.action === "close-file") {
this.closeFile(message);
}
});
}
public clearWorkspaceFolder() {
this.lspManagerService.workspaceFolder = "";
}
public setWorkspaceFolder() {
window.fs.chooseFolder().then((folder: string) => {
if (!folder) return;
this.lspManagerService.workspaceFolder = folder;
});
}
public createLanguageClient() {
let mode = this.lspManagerService.getMode(this.editor.session);
this.lspManagerService.createLanguageProviderWithClientServer(mode);
}
public closeLanguageClient() {
let mode = this.lspManagerService.getMode(this.editor.session);
this.lspManagerService.closeLanguageProviderWithClientServer(mode);
}
public registerEditorToLanguageClient() {
this.lspManagerService.registerEditorToLSPClient(this.editor);
}
public globalLspManagerKeyHandler(event: any) {
if (event.ctrlKey && event.shiftKey && event.key === "l") {
this.hideLspManager();
}
}
public hideLspManager() {
this.isHidden = true;
this.editor.focus();
}
private toggleLspManager(message: ServiceMessage) {
this.isHidden = !this.isHidden;
if (this.isHidden) return;
// Note: hack for issue with setActiveEditor TODO
setTimeout(() => {
this.innerEditor.setSession(this.editor.getSession());
}, 10);
}
private setActiveEditor(message: ServiceMessage) {
this.editor = message.rawData.editor;
this.activeFile = message.rawData.activeFile;
// TODO: figure out why this doesn't update the session consistently...
// It seems maybe bound to visible state as change detector ref didn't help either.
// this.innerEditor.setSession(this.editor.session);
}
private editorUpdate(message: ServiceMessage) {
if (
!this.editor ||
!message.rawData.activeFile
) return;
this.editor.setSession(message.rawData.editor.getSession())
this.activeFile = message.rawData.activeFile;
this.lspManagerService.registerSession(this.editor);
}
private closeFile(message: ServiceMessage) {
this.lspManagerService.closeDocument(message.rawData);
}
}

View File

@@ -0,0 +1,6 @@
<div class="row">
<div class="col">
<div [innerHtml]="bodyHtml || defaultHtml">
</div>
</div>
</div>

View File

@@ -0,0 +1,97 @@
import {
Component,
DestroyRef,
HostBinding,
inject
} from '@angular/core';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
import { MarkdownPreviewService } from '../../common/services/editor/markdown-preview/markdown-preview.service';
import { ServiceMessage } from '../../common/types/service-message.type';
@Component({
selector: 'markdown-preview',
standalone: true,
imports: [
],
templateUrl: './markdown-preview.component.html',
styleUrl: './markdown-preview.component.css',
host: {
'class': 'container-fluid markdown-preview'
}
})
export class MarkdownPreviewComponent {
readonly #destroyRef: DestroyRef = inject(DestroyRef);
private markdownPreviewService: MarkdownPreviewService = inject(MarkdownPreviewService);
@HostBinding("class.hidden") isHidden: boolean = true;
converter: any = new showdown.Converter();
defaultHtml: string = "<h1>NOT a Markdown file...</h1>"
bodyHtml: string = "";
private editorComponent!: any;
constructor() {
this.loadSubscribers();
}
private loadSubscribers() {
this.markdownPreviewService.getMessage$().pipe(
takeUntilDestroyed(this.#destroyRef)
).subscribe((message: ServiceMessage) => {
switch ( message.action ) {
case "toggle-markdown-preview":
this.toggleMarkdownPreview(message);
break;
case "set-active-editor":
this.setActiveEditor(message);
break;
default:
break;
}
});
}
private toggleMarkdownPreview(message: ServiceMessage) {
this.isHidden = !this.isHidden;
setTimeout(() => {
this.updatePreview();
}, 10);
}
private setActiveEditor(message: ServiceMessage) {
if (this.editorComponent == message.rawData) return;
this.editorComponent = message.rawData;
if (this.isHidden) return;
this.updatePreview();
}
public updatePreview() {
let fileMode = this.editorComponent.editor.session.getMode()["$id"];
let isMdFile = (fileMode.includes("markdown"));
this.bodyHtml = "";
if (!isMdFile) return;
let mdStr = this.editorComponent.editor.session.getValue();
let pathParts = this.editorComponent.activeFile.path.split("/");
let basePath = "file://" + pathParts.slice(0, -1).join("/");
this.bodyHtml = this.converter.makeHtml(
mdStr.replaceAll("](images", `](${basePath}/images`)
.replaceAll("](imgs", `](${basePath}/imgs`)
.replaceAll("](pictures", `](${basePath}/pictures`)
.replaceAll("](pics", `](${basePath}/pics`)
.replaceAll("](screenshots", `](${basePath}/screenshots`)
);
}
}

View File

@@ -1,52 +0,0 @@
<div #filesModal
id="filesModal" class="modal fade" tabindex="-1" role="dialog"
>
<div class="modal-dialog modal-lg" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">Files:</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
<div class="row">
<div class="col">
<div class="row">
<div class="col modal-column">
<div #filesList *ngFor="let file of files" class="row">
<div class="col-11 title"
title="{{file.path}}"
uuid="{{file.uuid}}"
path="{{file.path}}"
>
{{file.title}}
</div>
<div class="col-1 close-button">
X
</div>
</div>
</div>
</div>
<div class="row">
<input #filesSearch type="text" placeholder="Search..." />
</div>
</div>
<div class="col modal-column">
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Close</button>
</div>
</div>
</div>
</div>
</div>

View File

@@ -1,85 +0,0 @@
import { Component } from "@angular/core";
import { CommonModule } from "@angular/common";
import { Subject, takeUntil } from 'rxjs';
import * as bootstrap from "bootstrap";
import { FilesModalService } from "../../common/services/editor/modals/files-modal.service";
import { TabsService } from '../../common/services/editor/tabs/tabs.service';
import { ServiceMessage } from '../../common/types/service-message.type';
@Component({
selector: 'files-modal',
standalone: true,
imports: [
CommonModule
],
templateUrl: './files-modal.component.html',
styleUrl: './files-modal.component.css',
host: {
'class': ''
}
})
export class FilesModalComponent {
private unsubscribe = new Subject<void>();
filesModal!: bootstrap.Modal;
files: any[] = [];
constructor(
private filesModalService: FilesModalService,
private tabsService: TabsService
) {
}
public ngAfterViewInit(): void {
this.loadSubscribers();
}
loadSubscribers() {
this.tabsService.getMessage$().pipe(
takeUntil(this.unsubscribe)
).subscribe((data: ServiceMessage) => {
if (data.action === "create-tab") {
this.createFileRow(data.fileName, data.fileUUID, data.filePath);
}
});
this.filesModalService.showFilesModalRequested$().pipe(
takeUntil(this.unsubscribe)
).subscribe(() => {
if (!this.filesModal) {
this.createModal();
}
this.showModal();
});
this.filesModalService.addFileToModalRequested$().pipe(
takeUntil(this.unsubscribe)
).subscribe((uuid: string) => {
if (!this.filesModal) {
this.createModal();
}
});
}
private createFileRow(title: string, uuid: string, path: string): void {
this.files.push({title: title, uuid: uuid, path: path})
}
createModal() {
this.filesModal = new bootstrap.Modal("#filesModal", {});
}
showModal() {
this.filesModal?.toggle();
}
}

View File

@@ -1,153 +0,0 @@
import { Directive, ElementRef, Input, ViewChild } from '@angular/core';
import * as uuid from 'uuid';
import { EditorSettings } from "../../common/configs/editor.config";
import { NewtonFile } from '../../common/types/file.type';
@Directive()
export class NewtonEditorBase {
@ViewChild('editor') editorElm!: ElementRef;
@Input() editorSettings!: typeof EditorSettings;
editor!: any;
uuid!: string;
leftSiblingUUID!: string;
rightSiblingUUID!: string;
cutBuffer: string = "";
timerId: number = -1;
activeFile!: NewtonFile;
isDefault: boolean = false;
constructor(
) {
this.uuid = uuid.v4();
}
public addActiveStyling() {
this.editorElm.nativeElement.classList.add("active-editor")
}
public removeActiveStyling() {
this.editorElm.nativeElement.classList.remove("active-editor")
}
public openCommandPalette() {
this.editor.execCommand("openCommandPalette");
}
public showSettingsMenu() {
this.editor.showSettingsMenu();
}
public showKeyboardShortcuts() {
this.editor.showKeyboardShortcuts();
}
protected search() {
console.log(this.editor.session.getMode()["$id"]);
}
protected destroySession() {
this.editor.session.destroy();
}
protected openFiles() {
let startDir = "";
if (this.activeFile) {
let pathParts = this.activeFile.path.split("/");
pathParts.pop();
startDir = pathParts.join( '/' );
}
window.fs.openFiles(startDir);
}
protected saveFile() {
if (!this.activeFile) {
this.saveFileAs();
return;
}
const text = this.activeFile.session.getValue();
window.fs.saveFile(this.activeFile.path, text);
}
protected saveFileAs() {
const text = this.editor.session.getValue();
window.fs.saveFileAs(text);
}
protected zoomIn() {
this.editor.setFontSize(
parseInt(this.editor.getFontSize()) + 1
)
}
protected zoomOut() {
this.editor.setFontSize(
parseInt(this.editor.getFontSize()) - 1
)
}
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() {
this.editor.execCommand("movelinesup");
}
protected movelinesDown() {
this.editor.execCommand("movelinesdown");
}
protected duplicateLines() {
this.editor.execCommand("copylinesdown");
}
protected cutToBuffer() {
if (this.timerId) { clearTimeout(this.timerId); }
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.setBufferClearTimeout();
}
protected pasteCutBuffer() {
if (this.timerId) { clearTimeout(this.timerId); }
this.editor.insert(this.cutBuffer, true);
this.setBufferClearTimeout();
}
private setBufferClearTimeout(timeout: number = 5000) {
this.timerId = setTimeout(() => {
this.cutBuffer = "";
this.timerId = -1;
}, timeout);
}
}

View File

@@ -1,28 +0,0 @@
/*
.editor {
position: fixed;
top: 0;
right: 0;
bottom: 0;
left: 0;
}
.editor {
height: 100vh;
width: auto;
}
*/
.editor {
position: relative;
height: 100%;
width: 100%;
}
.active-editor {
border-style: solid;
border-width: thin;
border-color: rgba(124, 124, 124, 1);
}

View File

@@ -1,2 +0,0 @@
<div class="editor" #editor >
</div>

View File

@@ -1,182 +0,0 @@
import { Component } from "@angular/core";
// 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/ext-settings_menu";
import "ace-builds/src-noconflict/ext-keybinding_menu";
import "ace-builds/src-noconflict/ext-command_bar";
import "ace-builds/src-noconflict/ext-language_tools";
import "ace-builds/src-noconflict/theme-one_dark";
import "ace-builds/src-noconflict/theme-dracula";
import { InfoBarService } from '../../common/services/editor/info-bar/info-bar.service';
import { FilesModalService } from '../../common/services/editor/modals/files-modal.service';
import { LSPService } from '../../common/services/lsp.service';
import { EditorsService } from '../../common/services/editor/editors.service';
import { NewtonEditorBase } from './newton-editor.base';
import { ServiceMessage } from '../../common/types/service-message.type';
@Component({
selector: 'newton-editor',
standalone: true,
imports: [
],
templateUrl: './newton-editor.component.html',
styleUrl: './newton-editor.component.css',
host: {
'class': 'col col-6'
}
})
export class NewtonEditorComponent extends NewtonEditorBase {
constructor(
private infoBarService: InfoBarService,
private editorsService: EditorsService,
private lspService: LSPService,
private filesModalService: FilesModalService
) {
super();
}
public ngAfterViewInit(): void {
if (this.isDefault) {
this.addActiveStyling();
}
this.loadAce();
}
public loadAce(): void {
ace.config.set('basePath', this.editorSettings.BASE_PATH);
this.editor = ace.edit( this.editorElm.nativeElement );
this.editor.setOptions( this.editorSettings.CONFIG );
let keyBindings = [];
for (let i = 0; i < this.editorSettings.KEYBINDINGS.length; i++) {
let keyBinding = this.editorSettings.KEYBINDINGS[i];
keyBindings.push(
{
name: keyBinding.name,
bindKey: keyBinding.bindKey,
exec: (keyBinding.name && keyBinding?.service) ?
() => (
this[keyBinding?.service][keyBinding.name]()
)
:
(this[keyBinding.name]) ?
() => (
this[keyBinding.name]()
)
:
() => (
console.log(
`Name: ${keyBinding.name}, is not mapping to a method OR mapping to a Service: ${keyBinding?.service} and Name: ${keyBinding.name}.`
)
)
,
readOnly: keyBinding.readOnly
}
);
}
this.editor.commands.addCommands( keyBindings );
// Note: https://ajaxorg.github.io/ace-api-docs/interfaces/ace.Ace.EditorEvents.html
this.editor.on("focus", (e) => {
this.updateInfoBar();
});
this.editor.on("click", () => {
this.updateInfoBar();
});
this.editor.on("input", () => {
this.updateInfoBar();
});
this.editor.on("keyboardActivity", (e) => {
switch(e.command.name) {
case "golineup":
case "golinedown":
case "gotoleft":
case "gotoright":
this.infoBarService.setInfoBarCursorPos(
this.editor.getCursorPosition()
);
break;
default:
break;
}
});
this.editor.on("focus", () => {
let message = new ServiceMessage();
message.action = "set-active-editor";
message.editorUUID = this.uuid;
this.editorsService.sendMessage(message);
});
this.editor.on("changeSession", (session) => {
this.lspService.registerEditor(this.editor);
this.updateInfoBar();
});
}
public updateInfoBar() {
this.infoBarService.setInfoBarFPath(this.activeFile?.path)
this.infoBarService.setInfoBarCursorPos(
this.editor.getCursorPosition()
);
this.infoBarService.setInfoBarFType(
this.editor.session.getMode()["$id"]
);
}
public newBuffer() {
let buffer = ace.createEditSession([""]);
this.editor.setSession(buffer);
this.activeFile = null;
this.updateInfoBar();
}
public selectLeftEditor() {
let message = new ServiceMessage();
message.action = "select-left-editor";
message.editorUUID = this.uuid;
this.editorsService.sendMessage(message);
}
public selectRightEditor() {
let message = new ServiceMessage();
message.action = "select-right-editor";
message.editorUUID = this.uuid;
this.editorsService.sendMessage(message);
}
public moveSessionLeft() {
let message = new ServiceMessage();
message.action = "move-session-left";
message.editorUUID = this.uuid;
this.editorsService.sendMessage(message);
}
public moveSessionRight() {
let message = new ServiceMessage();
message.action = "move-session-right";
message.editorUUID = this.uuid;
this.editorsService.sendMessage(message);
}
}

View File

@@ -0,0 +1,39 @@
.buttons > button {
min-width: 3.5em;
min-height: 2.5em;
}
.width-8em {
width: 8em;
}
.margin-tb-1em {
margin-top: 1em;
margin-bottom: 1em;
}
.selected {
background-color: rgba(125, 125, 125, 1);
color: rgba(0, 0, 0, 1);
}
.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;
}

View File

@@ -0,0 +1,84 @@
<div class="col">
<div class="row">
<div class="col col-3">
<label id="find-status-lbl">Find in Current File</label>
</div>
<div class="col col-4">
<label id="find-options-lbl">Finding with Options: {{findOptions || "Case Insensitive"}}</label>
</div>
<div class="col col-5 line-height-32px buttons">
<button title="Close Panel"
class="float-end btn btn-sm btn-dark"
(click)="hideSearchReplace()">X
</button>
<button id="whole-word-btn" title="Whole Word"
class="float-end btn btn-sm btn-dark"
(click)="toggleWholeWordSearch($event)">
<img src="resources/imgs/whole-word.png" />
</button>
<button id="only-in-selection-btn" title="Only In Selection"
class="float-end btn btn-sm btn-dark"
(click)="toggleSelectionOnlyScan($event)">
<img src="resources/imgs/only-in-selection.png" />
</button>
<button id="match-case-btn" title="Match Case"
class="float-end btn btn-sm btn-dark"
(click)="toggleCaseSensitive($event)">Aa
</button>
<button id="use-regex-btn" title="Use Regex"
class="float-end btn btn-sm btn-dark"
(click)="toggleRegex($event)">.*
</button>
</div>
</div>
<div class="margin-tb-1em"></div>
<div class="row">
<div class="col">
<div class="row">
<div class="col">
<div class="input-group-sm mb-3">
<input #findEntryElm
id="find-entry"
class="form-control"
type="search"
(keyup)="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>
</div>
</div>
</div>
</div>
<div class="row">
<div class="col">
<div class="row">
<div class="col">
<div class="input-group-sm mb-3">
<input #replaceEntryElm
id="replace-entry"
class="form-control"
type="search"
(keyup)="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>
</div>
</div>
</div>
</div>
</div>

View File

@@ -0,0 +1,272 @@
import {
Component,
DestroyRef,
ElementRef,
HostBinding,
Input,
ViewChild,
inject
} from '@angular/core';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
import { SearchReplaceService } from '../../common/services/editor/search-replace/search-replace.service';
import { ServiceMessage } from '../../common/types/service-message.type';
@Component({
selector: 'search-replace',
standalone: true,
imports: [
],
templateUrl: './search-replace.component.html',
styleUrl: './search-replace.component.css',
host: {
'class': 'row search-replace',
"(keyup)": "globalSearchReplaceKeyHandler($event)"
}
})
export class SearchReplaceComponent {
readonly #destroyRef: DestroyRef = inject(DestroyRef);
private searchReplaceService: SearchReplaceService = inject(SearchReplaceService);
@HostBinding("class.hidden") isHidden: boolean = true;
@ViewChild('findEntryElm') findEntryElm!: ElementRef;
@ViewChild('replaceEntryElm') replaceEntryElm!: ElementRef;
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;
private searchTimeoutId: number = -1;
private searchTimeout: number = 400;
constructor() {
this.loadSubscribers();
}
private loadSubscribers() {
this.searchReplaceService.getMessage$().pipe(
takeUntilDestroyed(this.#destroyRef)
).subscribe((message: ServiceMessage) => {
switch ( message.action ) {
case "toggle-search-replace":
this.toggleSearchReplace(message);
break;
case "set-active-editor":
this.setActiveEditor(message);
break;
default:
break;
}
});
}
private toggleSearchReplace(message: ServiceMessage) {
this.selection = this.editor.getSelectedText();
this.findEntryElm.nativeElement.value = this.selection;
if (this.selection && !this.isHidden) {
this.findEntryElm.nativeElement.focus();
return;
}
this.isHidden = !this.isHidden;
if (this.isHidden) {
this.editor.focus();
return;
}
setTimeout(() => {
this.findEntryElm.nativeElement.focus();
}, 200);
}
private setActiveEditor(message: ServiceMessage) {
if (this.editor == message.rawData) return;
this.editor = message.rawData;
if (this.isHidden) return;
this.searchForString();
}
public hideSearchReplace() {
if (this.selection) {
this.selection = "";
return;
}
this.isHidden = true;
this.editor.focus();
}
public globalSearchReplaceKeyHandler(event: any) {
if (event.ctrlKey && event.key === "f") {
this.hideSearchReplace();
} else if (event.ctrlKey && event.key === "l") {
this.findEntryElm.nativeElement.focus();
} else if (event.ctrlKey && event.key === "r") {
this.replaceEntryElm.nativeElement.focus();
}
}
public toggleWholeWordSearch(event: any) {
let target = event.target;
if (target.nodeName === "IMG")
target = target.parentElement;
this.useWholeWordSearch = !this.useWholeWordSearch;
target.classList.toggle("selected");
this.setFindOptionsLbl();
this.findAllEntries();
}
public toggleSelectionOnlyScan(event: any) {
let target = event.target;
if (target.nodeName === "IMG")
target = target.parentElement;
this.searchOnlyInSelection = !this.searchOnlyInSelection;
target.classList.toggle("selected");
this.setFindOptionsLbl();
this.findAllEntries();
}
public toggleCaseSensitive(event: any) {
this.useCaseSensitive = !this.useCaseSensitive;
event.target.classList.toggle("selected");
this.setFindOptionsLbl();
this.findAllEntries();
}
public toggleRegex(event: any) {
this.useRegex = !this.useRegex;
event.target.classList.toggle("selected");
this.setFindOptionsLbl();
this.findAllEntries();
}
private setFindOptionsLbl() {
let findOptionsStr = "";
if (this.useRegex)
findOptionsStr += "Regex"
findOptionsStr += (findOptionsStr) ? ", " : "";
findOptionsStr += (this.useCaseSensitive) ? "Case Sensitive" : "Case InSensitive";
if (this.searchOnlyInSelection)
findOptionsStr += ", Within Current Selection"
if (this.useWholeWordSearch)
findOptionsStr += ", Whole Word"
this.findOptions = findOptionsStr;
}
public findNextEntry() {
this.editor.findNext();
}
public findAllEntries() {
this.query = this.findEntryElm.nativeElement.value;
if (!this.query) return;
let totalCount = this.editor.findAll(this.query, {
backwards: this.isBackwards,
wrap: this.isWrap,
caseSensitive: this.useCaseSensitive,
wholeWord: this.useWholeWordSearch,
regExp: this.useRegex,
range: this.searchOnlyInSelection
});
}
public findPreviousEntry() {
this.editor.findPrevious();
}
public replaceEntry(event: any) {
if (event instanceof KeyboardEvent) {
if (event.key !== "Enter") {
return;
}
}
let fromStr = this.findEntryElm.nativeElement.value;
let toStr = this.replaceEntryElm.nativeElement.value;
if (!fromStr) return;
let totalCount = this.editor.replace(toStr, fromStr, {
backwards: this.isBackwards,
wrap: this.isWrap,
caseSensitive: this.useCaseSensitive,
wholeWord: this.useWholeWordSearch,
regExp: this.useRegex,
range: this.searchOnlyInSelection
});
this.editor.clearSelection();
this.editor.findNext();
}
public replaceAll() {
let fromStr = this.findEntryElm.nativeElement.value;
let toStr = this.replaceEntryElm.nativeElement.value;
if (!fromStr) return;
let totalCount = this.editor.replaceAll(toStr, fromStr, {
backwards: this.isBackwards,
wrap: this.isWrap,
caseSensitive: this.useCaseSensitive,
wholeWord: this.useWholeWordSearch,
regExp: this.useRegex,
range: this.searchOnlyInSelection
});
}
public searchForString() {
if (event instanceof KeyboardEvent) {
if (event.key !== "Enter") {
return;
}
}
this.query = this.findEntryElm.nativeElement.value;
if (!this.query) return;
if (this.searchTimeoutId) { clearTimeout(this.searchTimeoutId); }
this.searchTimeoutId = setTimeout(() => {
let totalCount = this.editor.find(this.query, {
backwards: this.isBackwards,
wrap: this.isWrap,
caseSensitive: this.useCaseSensitive,
wholeWord: this.useWholeWordSearch,
regExp: this.useRegex,
range: this.searchOnlyInSelection
});
}, this.searchTimeout);
}
}

View File

@@ -19,17 +19,30 @@
border-right-style: solid;
border-right-color: #ffffff64;
border-right-width: 2px;
}
.active-tab {
background-color: rgba(255, 255, 255, 0.46);
color: rgba(255, 255, 255, 0.8);
}
.tab:hover {
cursor: pointer;
}
.file-changed {
color: rgba(255, 168, 0, 0.64);
}
.file-deleted {
color: rgba(255, 0, 0, 0.64);
}
.title {
margin-left: 2em;
margin-right: 2em;
font-size: 4em;
align-self: center;
}
.close-button {

View File

@@ -1,9 +1,13 @@
import { Component, ChangeDetectorRef } from '@angular/core';
import {
Component,
ChangeDetectorRef,
DestroyRef,
inject
} from '@angular/core';
import { CommonModule } from '@angular/common';
import { CdkDrag, CdkDragDrop, CdkDropList, moveItemInArray } from '@angular/cdk/drag-drop';
import { Subject, takeUntil } from 'rxjs';
import { CdkDrag, CdkDragDrop, CdkDropList } from '@angular/cdk/drag-drop';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
import { EditorsService } from '../../common/services/editor/editors.service';
import { TabsService } from '../../common/services/editor/tabs/tabs.service';
import { ServiceMessage } from '../../common/types/service-message.type';
@@ -25,75 +29,76 @@ import { ServiceMessage } from '../../common/types/service-message.type';
}
})
export class TabsComponent {
private unsubscribe = new Subject<void>();
readonly #destroyRef = inject(DestroyRef);
activeTab!: string;
tabs: any[] = [];
newIndex: number = -1;
private tabsService: TabsService = inject(TabsService);
private changeDetectorRef: ChangeDetectorRef = inject(ChangeDetectorRef);
tabs: any[] = this.tabsService.tabs;
constructor(
private editorsService: EditorsService,
private tabsService: TabsService,
private changeDetectorRef: ChangeDetectorRef
) {
constructor() {
this.loadSubscribers();
}
public ngAfterViewInit(): void {
private loadSubscribers() {
this.tabsService.getMessage$().pipe(
takeUntil(this.unsubscribe)
).subscribe((data: ServiceMessage) => {
if (data.action === "create-tab") {
this.createTab(data.fileName, data.fileUUID, data.filePath);
takeUntilDestroyed(this.#destroyRef)
).subscribe((message: ServiceMessage) => {
let elm = document.querySelectorAll(`[title="${message.filePath}"]`)[1];
switch ( message.action ) {
case "create-tab":
this.createTab(message.fileName, message.fileUUID, message.filePath);
break;
case "file-changed":
elm.classList.add("file-changed");
elm.classList.remove("file-deleted");
break;
case "file-deleted":
elm.classList.add("file-deleted");
elm.classList.remove("file-changed");
break;
case "file-saved":
elm.classList.remove("file-deleted");
elm.classList.remove("file-changed");
break;
default:
break;
}
});
}
ngOnDestroy(): void {
this.unsubscribe.next();
this.unsubscribe.complete();
}
private createTab(title: string, uuid: string, path: string): void {
this.tabs.push({title: title, uuid: uuid, path: path});
this.changeDetectorRef.detectChanges();
}
handleAction(event: any): void {
protected handleAction(event: any): void {
let target = event.target;
if ( target.classList.contains("tab") ) {
this.sendEditorsServiceAMessage(
this.tabsService.sendEditorsServiceAMessage(
"set-tab-to-editor",
event.srcElement.getAttribute("title")
);
} else if ( target.classList.contains("title") ) {
this.sendEditorsServiceAMessage(
this.tabsService.sendEditorsServiceAMessage(
"set-tab-to-editor",
event.srcElement.parentElement.getAttribute("title")
);
} else if ( target.classList.contains("close-button") ) {
this.closeTab(
this.tabsService.closeTab(
event.srcElement.parentElement.getAttribute("title")
);
}
}
closeTab(fpath: string): void {
this.sendEditorsServiceAMessage("close-tab", fpath);
for (let i = 0; i < this.tabs.length; i++) {
if (this.tabs[i].path == fpath) {
this.tabs.splice(i, 1);
}
}
public createTab(title: string, uuid: string, path: string): void {
this.tabsService.push({title: title, uuid: uuid, path: path});
this.changeDetectorRef.detectChanges();
}
moved(event: any): void {
private moved(event: any): void {
let target = event.event.target;
let fpath = "";
@@ -105,31 +110,11 @@ export class TabsComponent {
fpath = target.getAttribute("title")
)
for (let i = 0; i < this.tabs.length; i++) {
if (this.tabs[i].path == fpath) {
this.newIndex = i;
}
}
this.tabsService.setNewTargetIndex(fpath);
}
dropped(event: CdkDragDrop<any>): void {
if (this.newIndex == -1) return;
moveItemInArray(this.tabs, event.previousIndex, this.newIndex);
this.newIndex = -1;
// event.currentIndex not updating for some reason...
// moveItemInArray(this.tabs, event.previousIndex, event.currentIndex);
}
private sendEditorsServiceAMessage(action: string, fpath: string) {
let message = new ServiceMessage();
message.action = action;
message.filePath = fpath;
this.editorsService.sendMessage(message);
protected dropped(event: CdkDragDrop<any>): void {
this.tabsService.move(event.previousIndex);
}
}

View File

@@ -3,7 +3,7 @@
/* IDs */
#ace_settingsmenu, #kbshortcutmenu {
background-color: rgba(0, 0, 0, 0.0);
background-color: rgba(64, 64, 64, 0.84);
color: rgba(255, 255, 255, 1.0);
box-shadow: -1px 4px 5px rgba(124, 124, 124, 0.64);
padding: 1em 0.5em 2em 1em;
@@ -38,6 +38,19 @@
color: rgba(255, 255, 255, 1.0);
}
.ace_optionsMenuEntry button {
color: #000;
}
.ace_optionsMenuEntry button[ace_selected_button=true] {
background: #00e877;
}
.ace_print-margin {
width: 1px !important;
background-color: rgba(255, 0, 0, 0.84) !important;
}
.ace_selected {
background: rgba(249, 148, 6, 0.64);
color: rgba(0, 0, 0, 1.0);
@@ -47,4 +60,16 @@
.ace_sb-v,
.ace_sb-h {
width: 0.8em !important;
}
}
.ace_cursor {
color: rgba(249, 148, 6, 0.64);
animation: blinker 1s linear infinite;
}
@keyframes blinker {
50% {
opacity: 0;
}
}

View File

@@ -0,0 +1,21 @@
/* TAGS */
html {
background-color: rgba(40, 44, 52, 0.64);
color: rgba(255, 255, 255, 0.64);
}
body {
background-color: rgba(64, 64, 64, 0.0);
overflow: hidden;
}
ul, li {
list-style: none;
}
/* IDs */
/* CLASSES */

View File

@@ -1,24 +1,45 @@
/* 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 */
.search-replace,
.markdown-preview,
.lsp-manager {
display: inline-block;
position: fixed;
background-color: rgba(64, 64, 64, 0.84);
overflow: auto;
}
.search-replace {
bottom: 2em;
left: 2em;
right: 2em;
z-index: 900;
}
.markdown-preview {
top: 2em;
bottom: 2em;
right: 2em;
z-index: 700;
width: 50vw;
overflow: auto;
}
.lsp-manager {
top: 2em;
bottom: 2em;
left: 2em;
right: 2em;
z-index: 800;
}
.info-bar {
font-size: 0.8em;
color: rgba(255, 255, 255, 0.84);
@@ -36,19 +57,31 @@ body {
margin-right: 0.2em;
}
.hr-pane-handle,
.vr-pane-handle {
border: 2px dashed lightblue;
}
.vr-pane-handle {
max-width: max-content;
height: 100%;
margin: 0px;
}
.zero-margin-padding {
margin: 0px;
padding: 0px;
}
.hidden {
display: none;
}
.scroller {
/*
-webkit-scrollbar-color: #00000084 #ffffff64;
scrollbar-color: #00000084 #ffffff64;
*/
-webkit-scrollbar-color: #00000084 #ffffff06;
-webkit-scrollbar-width: thin;
scrollbar-color: #00000084 #ffffff06;

3
src/libs/showdown.min.js vendored Normal file

File diff suppressed because one or more lines are too long

3978
src/libs/tree-sitter.js Normal file

File diff suppressed because it is too large Load Diff

BIN
src/libs/tree-sitter.wasm Executable file

Binary file not shown.

View File

@@ -20,16 +20,23 @@ declare global {
},
main: {
onMenuActions: (arg0: any) => Promise<string>,
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: (arg0: any) => Promise<string>,
saveFileAs: () => Promise<string>,
chooseFolder: () => Promise<string>,
closeFile: (arg0: any) => Promise<string>,
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>,
}
}
}

1
src/typings.d.ts vendored Normal file
View File

@@ -0,0 +1 @@
declare var showdown: any;

View File

@@ -13,4 +13,4 @@
"src/polyfills.ts",
"src/**/*.d.ts"
]
}
}

View File

@@ -16,8 +16,11 @@
"declaration": false,
"skipLibCheck": true,
"strict": false,
"forceConsistentCasingInFileNames": true
}
"forceConsistentCasingInFileNames": true,
},
"includes": [
"src/typings.d.ts"
]
}
@@ -53,7 +56,10 @@
"strictInjectionParameters": true,
"strictInputAccessModifiers": true,
"strictTemplates": true
}
},
"includes": [
"src/typings.d.ts"
]
}
*/