diff --git a/app/hello/hello.js b/app/hello/hello.js new file mode 100644 index 0000000..295624c --- /dev/null +++ b/app/hello/hello.js @@ -0,0 +1,35 @@ +/* + + Hello message + +*/ + +const message = `Приветствуем в PoshlostiOS!!!! + +Стандартные komandi: +* cd - перемещение по папкам +* hello - эт кодмана +* ls [dir] - посмотреть список файлов +* meow - чтото пробормотать +* posh [command] - типа херня которая команды обрабатывает +* vget - скачать файлы из интернетов +* clear - стереть терминал +* rm - удалить файл +* mv - переместить файл +* bump - создать файл +* cp - скопировать файл +* mkdir - создать папку +* reset - УНИЧТОЖИТЬ СИСТЕМУ, и установить заново :3 +* cat - показать содержимое файла +* ppm [package] - пакетный менеджер + +Планируется: +* сделать hex цвета +* прога Worldwide Objective Manuals (WOMan) которая пишет туторы по пакетам, написанию прог и описание стандартных прог +* прога чтото наподобе nano или vi +` + +async function main(args) { + writeStdout(message) + return 0 +} \ No newline at end of file diff --git a/app/hello/package.json b/app/hello/package.json new file mode 100644 index 0000000..40fa5af --- /dev/null +++ b/app/hello/package.json @@ -0,0 +1,8 @@ +{ + "name": "hello", + "version": "0.1.2", + "description": "Hello Message", + "author": "MeexReay", + "apps": [ "hello.js" ], + "configs": [] +} \ No newline at end of file diff --git a/app/neofetch/neofetch.js b/app/neofetch/neofetch.js new file mode 100644 index 0000000..f0f8e8a --- /dev/null +++ b/app/neofetch/neofetch.js @@ -0,0 +1,82 @@ +/* + + neofetch - neofetch + +*/ + +function getDurationString(start_time) { + const now = new Date(); + const diffMs = now - start_time; + + const seconds = Math.floor(diffMs / 1000); + const minutes = Math.floor(seconds / 60); + const hours = Math.floor(minutes / 60); + const days = Math.floor(hours / 24); + + const remainingHours = hours % 24; + const remainingMinutes = minutes % 60; + + let result = []; + + if (days > 0) result.push(`${days}d`); + if (remainingHours > 0) result.push(`${remainingHours}h`); + result.push(`${remainingMinutes}m`); + + return result.join(" "); +} + +// Function to get CPU, GPU, and memory information +function getSystemInfo() { + const cpuInfo = navigator.hardwareConcurrency; + + const memoryInfo = navigator.deviceMemory; + const totalJSHeap = performance.memory.totalJSHeapSize / (1024 * 1024); + + const canvas = document.createElement('canvas'); + const gl = canvas.getContext('webgl') || canvas.getContext('experimental-webgl'); + let gpuInfo = null; + + if (gl) { + const debugInfo = gl.getExtension('WEBGL_debug_renderer_info'); + if (debugInfo) { + gpuInfo = { + vendor: gl.getParameter(debugInfo.UNMASKED_VENDOR_WEBGL), + renderer: gl.getParameter(debugInfo.UNMASKED_RENDERER_WEBGL) + }; + } + } + + return [ + cpuInfo + " cores", + memoryInfo ? memoryInfo * 1024 + ' MB' : 'Not supported', + totalJSHeap.toFixed(2) + ' MB', + gpuInfo ? `${gpuInfo.renderer}` : 'GPU Info not available' + ] +} + +async function main(args) { + let [cpu, max_mem, now_mem, gpu] = getSystemInfo() + + writeStdout(` + █████ user@poshlosti + ███░░░█ █ ------------------ + █░░█░░░██ OS: PoshlostiOS + █░█░░████ Uptime: ${getDurationString(start_date)} + █░███░░░░█ Packages: ${(await listPackages()).length} (ppm) + ██░░░░░░░█ Shell: posh ${(await getInstalledPackage("posh")).version} + █ █░░░░░░░██ Resolution: ${window.innerWidth}x${window.innerHeight} + █░░░░░░░░█ Memory: ${now_mem} / ${max_mem} + █░░░░░░░░█ CPU: ${cpu} + █░░░░░░░░░█ GPU: ${gpu} + ██░░░░░░░░███████ + █░░████████▓▓▓█ + █████████▓▓▓▓▓▓▓▓▓██ + █▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓█ + █▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓██ + ██▓▓▓▓▓▓▓▓▓▓▓▓▓█ + █▓▓▓▓▓▓▓▓▓█████ + ██████████ +`) + + return 0 +} \ No newline at end of file diff --git a/app/neofetch/package.json b/app/neofetch/package.json new file mode 100644 index 0000000..2caa673 --- /dev/null +++ b/app/neofetch/package.json @@ -0,0 +1,8 @@ +{ + "name": "neofetch", + "version": "0.1.1", + "description": "neofetch damn", + "author": "MeexReay", + "apps": [ "neofetch.js" ], + "configs": [] +} \ No newline at end of file diff --git a/app/posh/cd.js b/app/posh/cd.js new file mode 100644 index 0000000..a7a2c40 --- /dev/null +++ b/app/posh/cd.js @@ -0,0 +1,9 @@ +async function main(args) { + if (!hasFile(args[1])) { + writeStdout(`нет такой папки бро\n`) + return 1 + } + cwd = simplifyPath(args[1]) + writeStdout(`бро переместил тебя в ${args[1]} без б ваще обращайся\n`) + return 0 +} \ No newline at end of file diff --git a/app/posh/clear.js b/app/posh/clear.js new file mode 100644 index 0000000..aa0488b --- /dev/null +++ b/app/posh/clear.js @@ -0,0 +1,4 @@ +async function main(args) { + clearTerminal() + return 0 +} \ No newline at end of file diff --git a/app/posh/meow.js b/app/posh/meow.js new file mode 100644 index 0000000..4408e86 --- /dev/null +++ b/app/posh/meow.js @@ -0,0 +1,4 @@ +async function main(args) { + writeStdout(args.slice(1).join(" ")+"\n") + return 0 +} \ No newline at end of file diff --git a/app/posh/package.json b/app/posh/package.json new file mode 100644 index 0000000..b4ad6d0 --- /dev/null +++ b/app/posh/package.json @@ -0,0 +1,8 @@ +{ + "name": "posh", + "version": "0.1.1", + "description": "Poshlosti Shell", + "author": "MeexReay", + "apps": [ "posh.js", "meow.js", "cd.js", "clear.js" ], + "configs": [ "posh.json" ] +} \ No newline at end of file diff --git a/app/posh/posh.js b/app/posh/posh.js new file mode 100644 index 0000000..b0f63a0 --- /dev/null +++ b/app/posh/posh.js @@ -0,0 +1,93 @@ +/* + + PoSh - Poshlosti Shell + +*/ + +async function processCommand(command, args) { + let executable = command + if (!executable.startsWith("/")) { + if (executable.includes("/")) { + executable = cwd + "/" + executable + } else { + executable = "/app/" + executable + } + } + if (!hasFile(executable) && hasFile(executable+".js")) { + executable = executable + ".js" + } + executable = simplifyPath(executable) + + if (hasFile(executable)) { + try { + let code = await executeCommand([executable].concat(args)).promise + if (code != 0) { + await writeStdout("\nСтатус код: "+code+"\n") + } + } catch (e) { + console.log(e) + await writeStdout("Не запустилася\n") + } + } else { + await writeStdout("Твоя команда "+executable+" не найдена :3\n") + } +} + +async function main(args) { + if (args.length > 1) { + await processCommand(args[1], args.slice(2)) + } + + let history = [""] + let history_index = 0 + + let config = JSON.parse(readFile("/config/posh.json")) + let prompt = config["prompt"] + let startup = config["startup"] + + for (let command of startup) { + let args = command.split(" ") + await processCommand(args[0], args.slice(1)) + } + + while (true) { + await writeStdout(prompt.replace("{cwd}", cwd)) + + let command = await readLine((key, ctrl, alt, shift, content, pos) => { + console.log(history, content, pos, key) + + if (key == "ArrowDown") { + history_index = Math.max(0, history_index - 1) + return [history[history_index], history[history_index].length] + } + + if (key == "ArrowUp") { + history_index = Math.min(history.length-1, history_index + 1) + return [history[history_index], history[history_index].length] + } + + return [content, pos] + }) + + if (command.length > 0) { + history.splice(1, 0, command); + history_index = 0 + + let args = command.split(" ") + command = args[0] + args = args.slice(1) + + if (command == "exit") { + if (args.length == 0) { + return 0 + } else { + return parseInt(args[0]) + } + } else { + await processCommand(command, args) + } + } + } + + return 0 +} \ No newline at end of file diff --git a/app/posh/posh.json b/app/posh/posh.json new file mode 100644 index 0000000..f9ddf57 --- /dev/null +++ b/app/posh/posh.json @@ -0,0 +1,7 @@ +{ + "prompt": "[posh {cwd}]$ ", + "startup": [ + "hello", + "meow" + ] +} \ No newline at end of file diff --git a/app/ppm/package.json b/app/ppm/package.json new file mode 100644 index 0000000..da80131 --- /dev/null +++ b/app/ppm/package.json @@ -0,0 +1,8 @@ +{ + "name": "ppm", + "version": "0.1.3", + "description": "Poshliy Package Manager", + "author": "MeexReay", + "apps": [ "ppm.js" ], + "configs": [ "ppm.json" ] +} \ No newline at end of file diff --git a/app/ppm/ppm.js b/app/ppm/ppm.js new file mode 100644 index 0000000..3250ac7 --- /dev/null +++ b/app/ppm/ppm.js @@ -0,0 +1,124 @@ +/* + + PPM - Poshliy Package Manager + +*/ + +async function main(args) { + if (args.length == 3 && "iurs".includes(args[1])) { + let package = args[2] + + if (args[1] == "i") { + let config = JSON.parse(readFile("/config/ppm.json")) + + for (const repo of config["repositories"]) { + // await writeStdout(`Фетчим ${package} на репозитории ${repo}\n`) + + let status = await installPackage(repo+"/"+package) + + if (status == 0) { + let pkg = await getInstalledPackage(package) + await writeStdout(`Пакет ${pkg['name']}-${pkg['version']} установлен\n`) + return 0 + } else if (status == 1) { + await writeStdout("Пакет не установлен тк он уже установлен чувааак\n") + return 1 + } + } + } else if (args[1] == "u") { + let config = JSON.parse(readFile("/config/ppm.json")) + + for (const repo of config["repositories"]) { + let status = await updatePackage(package, repo+"/"+package) + + if (status == 0) { + let pkg = await getInstalledPackage(package) + await writeStdout(`Пакет ${pkg['name']}-${pkg['version']} обновлен\n`) + return 0 + } else if (status == 1) { + await writeStdout("Пакет не найден ты его установи сначала чел\n") + return 1 + } + } + + await writeStdout("обнова не прошла успешна\n") + return 1 + } else if (args[1] == "r") { + if (await removePackage(package)) { + await writeStdout(`Пакет ${package} удален\n`) + } else { + await writeStdout("Биспокойся произошла ошибко\n") + return 1 + } + } else if (args[1] == "s") { + let pkg = await getInstalledPackage(package) + for (const [key, value] of Object.entries(pkg)) { + await writeStdout(key.charAt(0).toUpperCase()+key.slice(1)+": "+value+"\n") + } + } + } else if (args.length == 2 && args[1] == "l") { + await writeStdout("ваши покеты:\n") + for (const package of (await listPackages())) { + await writeStdout("- "+package["name"]+"-"+package["version"]+"\n") + } + } else if (args.length == 2 && args[1] == "a") { + let config = JSON.parse(readFile("/config/ppm.json")) + + for (const package of (await listPackages())) { + for (const repo of config["repositories"]) { + // await writeStdout(`Фетчим ${package["name"]} на репозитории ${repo}\n`) + + let fetched = await fetchPackage(repo+"/"+package["name"]) + if (fetched != null) { + if (fetched["version"] == package["version"]) { + await writeStdout(`Пакет ${package['name']}-${package['version']} уже на последней версии\n`) + break + } + + let status = await updatePackage(package["name"], repo+"/"+package["name"]) + + if (status == 0) { + let pkg = await getInstalledPackage(package["name"]) + await writeStdout(`Пакет ${pkg['name']}-${pkg['version']} обновлен\n`) + break + } else if (status == 1) { + break + } + } + } + } + + await writeStdout("Обнова прошла успешна\n") + return 0 + } else if (args.length == 2 && args[1] == "A") { + let config = JSON.parse(readFile("/config/ppm.json")) + + for (const package of (await listPackages())) { + for (const repo of config["repositories"]) { + let status = await updatePackage(package["name"], repo+"/"+package["name"]) + + if (status == 0) { + let pkg = await getInstalledPackage(package["name"]) + await writeStdout(`Пакет ${pkg['name']}-${pkg['version']} обновлен\n`) + break + } else{ + console.log(status, repo, package) + } + } + } + + await writeStdout("Обнова прошла успешна\n") + return 0 + } else { + await writeStdout("Использование:\n") + await writeStdout(" ppm i <пакет> - установить пакет\n") + await writeStdout(" ppm u <пакет> - обновить пакет\n") + await writeStdout(" ppm r <пакет> - удалить пакет\n") + await writeStdout(" ppm s <пакет> - показать инфу о пакете\n") + await writeStdout(" ppm l - показать инфу о пакете\n") + await writeStdout(" ppm a - обновить все пакеты\n") + await writeStdout(" ppm A - обновить все пакеты принудительно\n") + } + + return 0 +} \ No newline at end of file diff --git a/app/ppm/ppm.json b/app/ppm/ppm.json new file mode 100644 index 0000000..578a53f --- /dev/null +++ b/app/ppm/ppm.json @@ -0,0 +1,5 @@ +{ + "repositories": [ + "https://poshlostios.meex.lol/app" + ] +} \ No newline at end of file diff --git a/app/putils/bump.js b/app/putils/bump.js new file mode 100644 index 0000000..536a7bc --- /dev/null +++ b/app/putils/bump.js @@ -0,0 +1,5 @@ +async function main(args) { + writeFile(args[1], "") + writeStdout(`все твой файл создан ${args[1]} такой же ты хотел жа\n`) + return 0 +} \ No newline at end of file diff --git a/app/putils/cat.js b/app/putils/cat.js new file mode 100644 index 0000000..68cd488 --- /dev/null +++ b/app/putils/cat.js @@ -0,0 +1,8 @@ +async function main(args) { + if (!hasFile(args[1])) { + writeStdout(`нет такой faila бро\n`) + return 1 + } + writeStdout(readFile(args[1])+"\n") + return 0 +} \ No newline at end of file diff --git a/app/putils/cp.js b/app/putils/cp.js new file mode 100644 index 0000000..8db38f9 --- /dev/null +++ b/app/putils/cp.js @@ -0,0 +1,55 @@ +async function main(args=["","",""]) { + let source = simplifyPath(args[1]) + let target = simplifyPath(args[2]) + + if (!hasFile(args[1])) { + writeStdout(`нет такой херни чо мне ты даешь долбан X нету :x: :regional_symbol_x: :cross:\n`) + return 1 + } + + if (isFolder(source)) { + if (!hasFile(target) || isFolder(target)) { + let create_dirs = hasFile(target) ? [] : [target] + let write_files = [] + + let recursive = (folder) => { + for (const file of listFiles(folder)) { + let path = folder+"/"+file + if (isFolder(path)) { + create_dirs.push(path) + recursive(path) + } else { + write_files.push(path) + } + } + } + + recursive(source) + + for (const dir of create_dirs) { + createFolder(target + dir.slice(source.length)) + } + + for (const file of write_files) { + let path = target + file.slice(source.length) + writeFile(path, readFile(file)) + } + } else { + writeStdout("ой сукаа я вахуе\n") + return 1 + } + } else { + if (isFolder(target)) { + target = target+"/"+source.split("/").reverse()[0] + } + + let content = readFile(source) + writeFile(target, content) + } + + removeFile(source) + + writeStdout(`все скопировано бро ${source} в ${target} скопировано спомощье команды CP (ЦП)\n`) + + return 0 +} \ No newline at end of file diff --git a/app/putils/ls.js b/app/putils/ls.js new file mode 100644 index 0000000..9a975a0 --- /dev/null +++ b/app/putils/ls.js @@ -0,0 +1,12 @@ +async function main(args) { + if (args.length < 2) { + writeStdout(listFiles(".").map(o => { + return (isFolder(o) ? "DIR " : "FILE") + " " + simplifyPath(o) + }).join("\n")+"\n") + } else { + writeStdout(listFiles(args[1]).map(o => { + return (isFolder(args[1]+"/"+o) ? "DIR " : "FILE") + " " + simplifyPath(args[1]+"/"+o) + }).join("\n")+"\n") + } + return 0 +} \ No newline at end of file diff --git a/app/putils/mkdir.js b/app/putils/mkdir.js new file mode 100644 index 0000000..00fcb3f --- /dev/null +++ b/app/putils/mkdir.js @@ -0,0 +1,5 @@ +async function main(args) { + createFolder(args[1]) + writeStdout(`папка создана ${args[1]}\n`) + return 0 +} \ No newline at end of file diff --git a/app/putils/mv.js b/app/putils/mv.js new file mode 100644 index 0000000..6f46048 --- /dev/null +++ b/app/putils/mv.js @@ -0,0 +1,55 @@ +async function main(args=["","",""]) { + let source = simplifyPath(args[1]) + let target = simplifyPath(args[2]) + + if (!hasFile(args[1])) { + writeStdout(`бро я б помог сделал что ты хочешь но сука нет такого файла бай бай\n`) + return 1 + } + + if (isFolder(source)) { + if (!hasFile(target) || isFolder(target)) { + let create_dirs = hasFile(target) ? [] : [target] + let write_files = [] + + let recursive = (folder) => { + for (const file of listFiles(folder)) { + let path = folder+"/"+file + if (isFolder(path)) { + create_dirs.push(path) + recursive(path) + } else { + write_files.push(path) + } + } + } + + recursive(source) + + for (const dir of create_dirs) { + createFolder(target + dir.slice(source.length)) + } + + for (const file of write_files) { + let path = target + file.slice(source.length) + writeFile(path, readFile(file)) + } + } else { + writeStdout("как по твоему я должен блять переместить папку в файл ты ебнутый сука\n") + return 1 + } + } else { + if (isFolder(target)) { + target = target+"/"+source.split("/").reverse()[0] + } + + let content = readFile(source) + writeFile(target, content) + } + + removeFile(source) + + writeStdout(`все перемещено чо надо то есть ${source} в ${target} ну изи получается\n`) + + return 0 +} \ No newline at end of file diff --git a/app/putils/package.json b/app/putils/package.json new file mode 100644 index 0000000..1cba82d --- /dev/null +++ b/app/putils/package.json @@ -0,0 +1,8 @@ +{ + "name": "putils", + "version": "0.1.0", + "description": "Poshliye Utiliti", + "author": "MeexReay", + "apps": [ "bump.js", "cat.js", "cp.js", "ls.js", "mkdir.js", "mv.js", "rm.js" ], + "configs": [] +} \ No newline at end of file diff --git a/app/putils/rm.js b/app/putils/rm.js new file mode 100644 index 0000000..a3b31b1 --- /dev/null +++ b/app/putils/rm.js @@ -0,0 +1,9 @@ +async function main(args) { + if (!hasFile(args[1])) { + writeStdout(`я вахуе ну ты же знаешь что такого файла нет нахуй ты это делаешь\n`) + return 1 + } + removeFile(args[1]) + writeStdout(`ну все твой файл ${args[1]} удален ДОВОЛЕН ЧТО ТЫ СДЕЛАЛ?\n`) + return 0 +} \ No newline at end of file diff --git a/app/reset/package.json b/app/reset/package.json new file mode 100644 index 0000000..ce69b49 --- /dev/null +++ b/app/reset/package.json @@ -0,0 +1,8 @@ +{ + "name": "reset", + "version": "0.1.0", + "description": "Resets system to a newborn", + "author": "MeexReay", + "apps": [ "reset.js" ], + "configs": [] +} \ No newline at end of file diff --git a/app/reset/reset.js b/app/reset/reset.js new file mode 100644 index 0000000..a02f7e4 --- /dev/null +++ b/app/reset/reset.js @@ -0,0 +1,18 @@ +async function main(args) { + await writeStdout("Ты уверен, что хочешь стереть систему, и установить снова?? (y/n) > ") + let confirm = (await readLine()).toLowerCase() == "y" + if (confirm) { + await new Promise(r => setTimeout(r, 100)); + await writeStdout("Прощай...") + await new Promise(r => setTimeout(r, 2000)); + await resetSystem() + document.location.reload() + for (const c of Array.from("\nвелосипе")) { + await writeStdout(c) + await new Promise(r => setTimeout(r, 30)); + } + await new Promise(r => setTimeout(r, 999999)); + } + + return 0 +} \ No newline at end of file diff --git a/app/vget/package.json b/app/vget/package.json new file mode 100644 index 0000000..0f2e0fe --- /dev/null +++ b/app/vget/package.json @@ -0,0 +1,8 @@ +{ + "name": "vget", + "version": "0.1.0", + "description": "Vulgar get", + "author": "MeexReay", + "apps": [ "vget.js" ], + "configs": [] +} \ No newline at end of file diff --git a/app/vget/vget.js b/app/vget/vget.js new file mode 100644 index 0000000..334bad5 --- /dev/null +++ b/app/vget/vget.js @@ -0,0 +1,17 @@ +/* + + VGet - Vulgar Get + +*/ + +async function main(args) { + if (args.length != 3) { + writeStdout("vget \n") + return 1 + } + + writeFile(args[2], await (await fetch(args[0])).text()) + writeStdout("Файл "+args[1]+" вставлен внутрь пути "+args[2]+"\n") + + return 0 +} \ No newline at end of file diff --git a/font/terminus.ttf b/font/terminus.ttf new file mode 100644 index 0000000..d125e63 Binary files /dev/null and b/font/terminus.ttf differ diff --git a/index.html b/index.html new file mode 100644 index 0000000..42d854b --- /dev/null +++ b/index.html @@ -0,0 +1,77 @@ + + + + + + PoshlostiOS + + + + + + + + + + + _ + + + + + + + diff --git a/sys/fs.js b/sys/fs.js new file mode 100644 index 0000000..c549aeb --- /dev/null +++ b/sys/fs.js @@ -0,0 +1,223 @@ +var fs_mapping = {} + +function saveMapping() { + localStorage.setItem("fs_mapping", JSON.stringify(fs_mapping)) +} + +function loadMapping() { + if (localStorage.getItem("fs_mapping") == null) { + fs_mapping = {} + } else { + fs_mapping = JSON.parse(localStorage.getItem("fs_mapping")) + } +} + +loadMapping() + +function getMappingCode(path) { + let parts = (path.startsWith("/") ? path.slice(1) : path).split("/") + let now = fs_mapping + + let index = 0 + for (const part of parts) { + if (Object.keys(now).includes(part)) { + if (index == parts.length-1) { + return "file_"+now[part] + } else { + now = now[part] + } + } else { + return null + } + index++ + } + return null +} + +function createMappingCode(path) { + if (path.length === 0) return null + + let code = 0, i, chr + + for (i = 0; i < path.length; i++) { + chr = path.charCodeAt(i) + code = ((code << 5) - code) + chr + code |= 0 + } + + let parts = (path.startsWith("/") ? path.slice(1) : path).split("/") + let now = fs_mapping + + let index = 0 + for (const part of parts) { + if (index == parts.length-1) { + now[part] = code + } else if (Object.keys(now).includes(part)) { + now = now[part] + } else { + return null + } + index++ + } + saveMapping() + + return "file_"+code +} + +function createMappingFolder(path) { + let parts = (path.startsWith("/") ? path.slice(1) : path).split("/") + let now = fs_mapping + + let index = 0 + for (const part of parts) { + if (index == parts.length-1) { + now[part] = {} + } else if (Object.keys(now).includes(part)) { + now = now[part] + } else { + return null + } + index++ + } + saveMapping() + return null +} + +function listMappingFolder(path) { + if (path == "/") return Object.keys(fs_mapping) + let parts = (path.startsWith("/") ? path.slice(1) : path).split("/") + let now = fs_mapping + + let index = 0 + for (const part of parts) { + if (Object.keys(now).includes(part)) { + if (index == parts.length-1) { + return Object.keys(now[part]) + } else { + now = now[part] + } + } else { + return null + } + index++ + } + return null +} + +function isMappingFolder(path) { + let parts = (path.startsWith("/") ? path.slice(1) : path).split("/") + let now = fs_mapping + + let index = 0 + for (const part of parts) { + if (Object.keys(now).includes(part)) { + if (index == parts.length-1) { + return typeof now[part] === "object" + } else { + now = now[part] + } + } else { + return null + } + index++ + } + return null +} + +function removeMappingEntry(path) { + let parts = (path.startsWith("/") ? path.slice(1) : path).split("/") + let now = fs_mapping + + let index = 0 + for (const part of parts) { + if (Object.keys(now).includes(part)) { + if (index == parts.length-1) { + delete now[part] + + saveMapping() + } else { + now = now[part] + } + } else { + return null + } + index++ + } + return null +} + +function readFile(path) { + path = simplifyPath(path) + return localStorage.getItem(getMappingCode(path)) +} + +function writeFile(path, content) { + path = simplifyPath(path) + let code = getMappingCode(path) + if (code == null) code = createMappingCode(path) + localStorage.setItem(code, content) +} + +function hasFile(path) { + path = simplifyPath(path) + return getMappingCode(path) != null +} + +function listFiles(path) { + path = simplifyPath(path) + return listMappingFolder(path) +} + +function createFolder(path) { + createMappingFolder(simplifyPath(path)) +} + +function removeFile(path) { + path = simplifyPath(path) + if (!isFolder(path)) { + localStorage.removeItem(getMappingCode(path)) + } + removeMappingEntry(path) +} + +function isFolder(path) { + path = simplifyPath(path) + return isMappingFolder(path) +} + +function simplifyPath(path) { + if (!path.startsWith("/")) path = cwd+"/" + path; + + const segments = path.split('/'); + const stack = []; + + for (let segment of segments) { + if (segment === '' || segment === '.') { + continue; + } else if (segment === '..') { + if (stack.length > 0) { + stack.pop(); + } + } else { + stack.push(segment); + } + } + + const simplifiedPath = '/' + stack.join('/'); + return simplifiedPath; +} + +function clearFileSystem() { + let callback = (x) => { + for (const o of Object.values(x)) { + if (typeof o === "object") { + callback(o) + } else { + localStorage.removeItem("file_"+o) + } + } + } + callback(fs_mapping) + fs_mapping = {} + saveMapping() +} \ No newline at end of file diff --git a/sys/ppm.js b/sys/ppm.js new file mode 100644 index 0000000..5453d2a --- /dev/null +++ b/sys/ppm.js @@ -0,0 +1,128 @@ +async function fetchPackage(url) { + try { + return await fetchJson(url+"/package.json") + } catch (error) { + return null + } +} + +async function getInstalledPackage(name) { + if (!hasFile("/etc/ppm/"+name)) { + return null + } + + return JSON.parse(readFile("/etc/ppm/"+name)) +} + +async function listPackages() { + let packages = [] + for (const name of listFiles("/etc/ppm")) { + packages.push(JSON.parse(readFile("/etc/ppm/"+name))) + } + return packages +} + +async function fetchJson(url) { + for (let i = 0; i < 10; i++) { + try { + return await fetch(url).then(o => o.json()) + } catch (error) { + continue + } + } + return null +} + +async function fetchText(url) { + for (let i = 0; i < 10; i++) { + try { + return await fetch(url).then(o => o.text()) + } catch (error) { + continue + } + } + return null +} + +async function installPackage(url) { + let package + try { + package = await fetch(url+"/package.json").then(o => o.json()) + } catch (error) { + return 2 + } + + if (!hasFile("/etc/ppm")) { + createFolder("/etc/ppm") + } + + if (hasFile("/etc/ppm/"+package["name"])) { + return 1 + } + + if ("apps" in package) { + for (const app of package.apps) { + writeFile("/app/"+app, await fetchText(url+"/"+app)) + } + } + + if ("configs" in package) { + for (const config of package.configs) { + writeFile("/config/"+config, await fetchText(url+"/"+config)) + } + } + + writeFile("/etc/ppm/"+package["name"], JSON.stringify(package)) + + return 0 +} + +async function removePackage(name) { + let package = await getInstalledPackage(name) + + if (package == null) return false + + if ("apps" in package) { + for (const app of package.apps) { + removeFile("/app/"+app) + } + } + + if ("configs" in package) { + for (const config of package.configs) { + removeFile("/config/"+config) + } + } + + removeFile("/etc/ppm/"+name) + + return true +} + +async function updatePackage(name, url) { + let package = await getInstalledPackage(name) + + if (package == null) return 1 + + if ("apps" in package) { + for (const app of package.apps) { + removeFile("/app/"+app) + } + } + + try { + package = await fetch(url+"/package.json").then(o => o.json()) + } catch (error) { + return 2 + } + + if ("apps" in package) { + for (const app of package.apps) { + writeFile("/app/"+app, await fetchText(url+"/"+app)) + } + } + + writeFile("/etc/ppm/"+name, JSON.stringify(package)) + + return 0 +} \ No newline at end of file diff --git a/sys/system.js b/sys/system.js new file mode 100644 index 0000000..8370012 --- /dev/null +++ b/sys/system.js @@ -0,0 +1,190 @@ +var cwd = "/home" + +var stdout = "" +var stdin = "" + +var disable_stdin = true +var silent_stdin = false + +var processes = [] + +const RENDER_STDIN = 1 +const SILENT_STDIN = 2 +const DISABLE_STDIN = 3 +const ENABLE_STDIN = 4 + +async function readLine(on_key=(key, ctrl, alt, shift, content, pos) => [content, pos]) { + setStdinFlag(ENABLE_STDIN) + + let start_cursor_pos = getCursor() + + let pos = 0 + let content = "" + while (true) { + let event = await pollStdinEvent() + + if (event.type == "key") { + if (event.key == "Backspace") { + if (content.length >= 1) { + content = content.substring(0, content.length-1) + trimTerminal(1) + } + } else if (event.key == "ArrowLeft") { + let cursor = getCursor() + if (cursor[0] > start_cursor_pos[0]) { + setCursor(cursor[0]-1, cursor[1]) + pos -= 1 + } + } else if (event.key == "ArrowRight") { + let cursor = getCursor() + if (cursor[0] < start_cursor_pos[0] + content.length) { + setCursor(cursor[0]+1, cursor[1]) + pos += 1 + } + } else { + let res = on_key(key, is_ctrl, is_alt, is_shift, content, pos) + terminal_text = terminal_text.slice(0, terminal_text.length - content.length) + res[0] + updateTerminal() + setCursor(start_cursor_pos[0] + res[1], start_cursor_pos[1]) + content = res[0] + pos = res[1] + } + + continue + } else if (event.type == "char") { + if (event.char == "\n") break + + content = content.slice(0, pos) + event.char + content.slice(pos) + pos += 1 + } + } + + setStdinFlag(DISABLE_STDIN) + + return content +} + +async function pollStdinEvent() { + let char = await readStdin() + + if (char == "\r") { + let key = "" + char = await readStdin() + while (char != "\r") { + key += char + char = await readStdin() + } + + let is_ctrl = key.charAt(0) == "1" + let is_alt = key.charAt(1) == "1" + let is_shift = key.charAt(2) == "1" + key = key.slice(3) + + return { + "type": "key", + "ctrl": is_ctrl, + "alt": is_alt, + "shift": is_shift, + "key": key + } + } + + return { + "type": "char", + "char": char + } +} + +async function readStdin() { + while (stdin.length == 0) { + await new Promise(resolve => setTimeout(resolve, 10)) + } + let was_stdin = stdin.charAt(0) + stdin = stdin.slice(1) + return was_stdin +} + +async function writeStdout(content) { + stdout += content + writeTerminal(content) +} + +function setStdinFlag(flag) { + if (flag == 1) { + silent_stdin = true + } else if (flag == 2) { + silent_stdin = false + } else if (flag == 3) { + disable_stdin = true + } else if (flag == 4) { + disable_stdin = false + } +} + +function executeCommand(args, read=readStdin, write=writeStdout) { + let id = new Date().getMilliseconds().toString()+(Math.random()*100) + let func_content = readFile(args[0]) + if (func_content == null || !func_content.includes("function main")) return + let func = new Function("args", "readStdin", "writeStdout", func_content+"\n\nreturn main(args)") + let process = { + "id": id, + "name": args.join(" "), + "promise": new Promise((resolve, reject) => { + setTimeout(() => { + try { + resolve(func(args, read, write)) + } catch (e) { + console.log(e) + reject(e) + } + }, 0) + }).then(o => { + processes = processes.filter(x => x.id != id) + return o + }) + } + processes.push(process) + return process +} + +function loadApp(name) { + fetch("app/"+name+".js") + .then(o => o.text()) + .then(o => { + writeFile("/app/"+name+".js", o) + }) +} + +function loadAppAndExecute(name) { + fetch("app/"+name+".js") + .then(o => o.text()) + .then(o => { + writeFile("/app/"+name+".js", o) + }) +} + +async function resetSystem() { + clearFileSystem() + + createFolder("/") + createFolder("/home") + createFolder("/app") + createFolder("/config") + createFolder("/temp") + createFolder("/etc") + + await installPackage("app/hello") + await installPackage("app/posh") + await installPackage("app/ppm") + await installPackage("app/putils") + await installPackage("app/reset") + await installPackage("app/vget") +} + +if (Object.keys(fs_mapping).length == 0) { + resetSystem() +} + +executeCommand(["/app/posh.js"]) + +var start_date = new Date() \ No newline at end of file diff --git a/sys/terminal.js b/sys/terminal.js new file mode 100644 index 0000000..cd30c88 --- /dev/null +++ b/sys/terminal.js @@ -0,0 +1,116 @@ +const CHAR_SIZE = [10, 22] +const CURSOR_OFFSET = [10, 10] + +var terminal = document.getElementById("terminal") +var terminal_text = "" +var last_stdin_length = 0 + +function writeTerminalAtCursor(content) { + let cursor_index = getCursorIndex() + terminal_text = terminal_text.slice(0, cursor_index) + content + terminal_text.slice(cursor_index) + setCursor(cursor_pos[0]+1, cursor_pos[1]) + last_stdin_length += 1 + updateTerminalWOCursor() +} + +function writeTerminal(content) { + terminal_text += content + last_stdin_length = 0 + updateTerminal() +} + +function replaceTerminal(content) { + terminal_text = content + updateTerminal() +} + +function trimTerminal(length) { + terminal_text = terminal_text.substring(0, terminal_text.length-length) + updateTerminal() +} + +function clearTerminal() { + terminal_text = "" + updateTerminal() +} + +function updateTerminalWOCursor() { + terminal.innerText = terminal_text +} + +function updateTerminal() { + setCursor( + terminal_text.split("\n").reverse()[0].length, + terminal_text.split("\n").length-1 + ) + updateTerminalWOCursor() +} + +var cursor = document.getElementById("cursor") +var cursor_pos = [0, 0] + +function getCursorIndex() { + let lines = terminal_text.split("\n") + let index = 0 + for (let y = 0; y < lines.length; y++) { + const line = lines[y]; + const length = line.length + + if (y == cursor_pos[1]) { + return index + cursor_pos[0] + } + + index += length + 1 + } + return 0 +} + +function getCursor() { + return cursor_pos +} + +function setCursor(x, y) { + cursor_pos = [x, y] + updateCursor() +} + +function updateCursor() { + cursor.style["top"] = cursor_pos[1] * CHAR_SIZE[1] + CURSOR_OFFSET[1] + "px" + cursor.style["left"] = cursor_pos[0] * CHAR_SIZE[0] + CURSOR_OFFSET[0] + "px" + cursor.scrollIntoView({behavior: "smooth", inline: "end"}) +} + +document.onkeydown = (e) => { + let key = e.key; + if (!disable_stdin) { + if (key == "Enter") { + stdin += "\n" + if (!silent_stdin) { + writeTerminal("\n") + } + } else if (key.length == 1) { + if (e.ctrlKey && key == "v") return + stdin += key + if (!silent_stdin) { + writeTerminalAtCursor(key) + } + } else { + stdin += "\r"+(e.ctrlKey ? "1" : "0")+(e.altKey ? "1" : "0")+(e.shiftKey ? "1" : "0")+e.key+"\r" + } + } +} + +var clipboard_collect = document.getElementById("clipboard-collect") + +setInterval(() => { + clipboard_collect.focus() +}); + +clipboard_collect.onpaste = (e) => { + let paste = (e.clipboardData || window.clipboardData).getData("text"); + + if (paste != null) { + stdin += paste.replace("\r", "") + writeTerminal(paste) + } +} \ No newline at end of file