diff --git a/site/_includes/iframe.njk b/site/_includes/iframe.njk index 235b6fc..9bf32a4 100644 --- a/site/_includes/iframe.njk +++ b/site/_includes/iframe.njk @@ -15,11 +15,6 @@ - - - - - @@ -31,21 +26,10 @@ - {% poison %} - - -
- {{ content | safe }} -
+ {{ content | safe }} \ No newline at end of file diff --git a/site/index.njk b/site/index.njk index 4041002..8c9527c 100644 --- a/site/index.njk +++ b/site/index.njk @@ -241,6 +241,12 @@ headerLink: home image: "/static/images/metro-flowers.jpg" }) }} + +
+ + +
+ {% include 'widgets/droidring.html' %} diff --git a/site/misc/kumaos/index.njk b/site/misc/kumaos/index.njk index 9fb1032..07bcd46 100644 --- a/site/misc/kumaos/index.njk +++ b/site/misc/kumaos/index.njk @@ -1,34 +1,34 @@ --- -layout: text.njk +pageTitle: kumaOS pagination: data: languages size: 1 alias: langKey -pageTitle: KumaOS -background: bliss-dark.jpg --- -{% from "macros.njk" import i88x31 with context %} - - - - - - -
-

Welcome to KumOS

-

There's not much to do right now as the system is being developed... Try running apps to see what's available.

-
- \ No newline at end of file + + + + + Document + + + + + +
+

kumaOS

+

demo - not for production

+
+ + + \ No newline at end of file diff --git a/static/kumos/apps.mjs b/static/kumos/apps/apps.mjs similarity index 79% rename from static/kumos/apps.mjs rename to static/kumos/apps/apps.mjs index c8fedd7..37e81b5 100644 --- a/static/kumos/apps.mjs +++ b/static/kumos/apps/apps.mjs @@ -1,7 +1,7 @@ -import { Application, Window, promoteWindow, registerApplication, getScreenRoot, getApplications } from "./kernel.mjs"; +import { Application, Window, promoteWindow, registerApplication, getScreenRoot, getApplications } from "../kernel.mjs"; const app = new Application('apps', (p) => { - const w = new Window(); + const w = new Window(p); w.setTitle("App list"); w.content.style.padding = "1rem"; @@ -10,7 +10,7 @@ const app = new Application('apps', (p) => { const item = document.createElement("li"); const link = document.createElement("a"); link.innerText = app.name; - link.addEventListener('click', () => app.fn()); + link.addEventListener('click', () => app.bootstrap()); item.appendChild(link); list.appendChild(item); }) @@ -26,4 +26,4 @@ const app = new Application('apps', (p) => { promoteWindow(w); }) -registerApplication(app); \ No newline at end of file +export default app; \ No newline at end of file diff --git a/static/kumos/browser.mjs b/static/kumos/apps/browser.mjs similarity index 91% rename from static/kumos/browser.mjs rename to static/kumos/apps/browser.mjs index 976cd40..08997d6 100644 --- a/static/kumos/browser.mjs +++ b/static/kumos/apps/browser.mjs @@ -1,7 +1,7 @@ -import { Application, Window, promoteWindow, registerApplication, getScreenRoot } from "./kernel.mjs"; +import { Application, Window, promoteWindow, registerApplication, getScreenRoot } from "../kernel.mjs"; const app = new Application('browser', (p) => { - const w = new Window(); + const w = new Window(p); w.setTitle("Web browser"); const iframe = document.createElement("iframe"); iframe.src = "https://adrianvic.github.io"; @@ -29,4 +29,4 @@ const app = new Application('browser', (p) => { }) }) -registerApplication(app); \ No newline at end of file +export default app; \ No newline at end of file diff --git a/static/kumos/guestbook.mjs b/static/kumos/apps/guestbook.mjs similarity index 86% rename from static/kumos/guestbook.mjs rename to static/kumos/apps/guestbook.mjs index a18adf3..cd547a8 100644 --- a/static/kumos/guestbook.mjs +++ b/static/kumos/apps/guestbook.mjs @@ -1,8 +1,8 @@ -import { Application, Window, promoteWindow, registerApplication, getScreenRoot, getProcList } from "./kernel.mjs"; -import { notify } from "./shell.mjs"; +import { Application, Window, promoteWindow, registerApplication, getScreenRoot, getProcList } from "../kernel.mjs"; +import { notify } from "../system/shell.mjs"; function draw(p) { - const w = new Window(); + const w = new Window(p); w.setTitle("Guestbook"); w.content.innerHTML = ` @@ -35,4 +35,4 @@ function main(p) { } const app = new Application('guestbook', main); -registerApplication(app); \ No newline at end of file +export default app; \ No newline at end of file diff --git a/static/kumos/apps/pkg.mjs b/static/kumos/apps/pkg.mjs new file mode 100644 index 0000000..064718f --- /dev/null +++ b/static/kumos/apps/pkg.mjs @@ -0,0 +1,5 @@ +import "./apps.mjs"; +import "./browser.mjs"; +import "./guestbook.mjs"; +import "./taskmanager.mjs"; +import "./shell.mjs"; \ No newline at end of file diff --git a/static/kumos/apps/shell.mjs b/static/kumos/apps/shell.mjs new file mode 100644 index 0000000..2be512c --- /dev/null +++ b/static/kumos/apps/shell.mjs @@ -0,0 +1,61 @@ +import { Application, Window, getScreenRoot, bus, promoteWindow } from "../kernel.mjs"; + +function draw(p) { + const w = new Window(p); + w.setTitle("Shell"); + w.content.style.display = "flex"; + w.content.style.flexDirection = "column"; + + const textarea = document.createElement("textarea"); + textarea.style.width = "100%"; + textarea.style.height = "100%"; + bus.addEventListener("system-console", (e) => { + textarea.value += e.detail + .map(arg => typeof arg === "object" ? JSON.stringify(arg) : String(arg)) + .join(" ") + + "\n"; + }) + + const div = document.createElement("div"); + div.style.width = "100%"; + div.style.display = "flex"; + + const input = document.createElement("input"); + input.style.width = "100%"; + + const button = document.createElement("button"); + button.textContent = "go"; + button.addEventListener('click', () => { + eval(input.value); + }); + + div.appendChild(input); + div.appendChild(button); + + w.content.appendChild(textarea); + w.content.appendChild(div); + getScreenRoot().appendChild(w.window); + w.maximize(); + + function title(title) { + w.setTitle(title); + } + + function exit() { + p.detach(); + w.destroy(); + } + + w.close.addEventListener('click', () => { + exit(); + }) + + promoteWindow(w); +} + +function main(p) { + draw(p); +} + +const app = new Application('shell', main); +export default app; \ No newline at end of file diff --git a/static/kumos/taskmanager.mjs b/static/kumos/apps/taskmanager.mjs similarity index 78% rename from static/kumos/taskmanager.mjs rename to static/kumos/apps/taskmanager.mjs index 1d93b3c..3d643c7 100644 --- a/static/kumos/taskmanager.mjs +++ b/static/kumos/apps/taskmanager.mjs @@ -1,4 +1,4 @@ -import { Application, Window, promoteWindow, registerApplication, getScreenRoot, getProcList } from "./kernel.mjs"; +import { Application, Window, promoteWindow, getScreenRoot, getProcList } from "../kernel.mjs"; const app = new Application('task', (p) => { function update() { @@ -7,7 +7,7 @@ const app = new Application('task', (p) => { getProcList().forEach(proc => { const item = document.createElement("li"); const link = document.createElement("a"); - link.innerText = `${proc.pid} -> ${proc.name}`; + link.innerText = `${proc.pid} -> ${proc.parent.length === 0 ? `` : proc.parent} ${proc.name}`; item.appendChild(link); list.appendChild(item); }) @@ -31,4 +31,4 @@ const app = new Application('task', (p) => { promoteWindow(w); }) -registerApplication(app); \ No newline at end of file +export default app; \ No newline at end of file diff --git a/static/kumos/kernel.mjs b/static/kumos/kernel.mjs index 6f2a831..c726f55 100644 --- a/static/kumos/kernel.mjs +++ b/static/kumos/kernel.mjs @@ -1,33 +1,61 @@ const language = "en"; const procList = []; +const applications = []; +const screenRoot = document.querySelector('body'); +screenRoot.classList.add("kumosroot"); + +export const bus = new EventTarget(); + +class SecurityError extends Error { + constructor(message) { + super(message); + this.name = "SercurityError" + } +}; export class Process { - constructor(app) { + constructor(name, fn, parent = []) { this.pid = procList.length == 0 ? 0 : procList[procList.length - 1].pid + 1; - this.name = app.name; + this.name = name; + this.parent = parent; procList.push(this); - app.fn(this); + fn(this); } - + detach() { procList.splice(procList.indexOf(this), 1); } + + child(name, fn) { + return new Process(name, fn, [...this.parent, this.pid]); + } + + isPrivileged() { + return this.parent.indexOf(0) !== -1 || this.pid == 0; + } } export class Application { constructor(name, fn) { this.fn = fn; this.name = name; + registerApplication(this); + } + + bootstrap() { + new Process(this.name, this.fn); } } export class Window { - constructor() { + constructor(p) { + this.process = p; + // Window const w = document.createElement("div"); w.classList.add("window"); this.window = w; - + // Header const h = document.createElement("div"); h.classList.add("windowHeader"); @@ -37,13 +65,23 @@ export class Window { const title = document.createElement("p"); title.classList.add("windowHeaderTitle"); this.title = title; + + // Buttons container + const container = document.createElement("div"); + container.classList.add("buttonContainer"); // Close button const close = document.createElement("button"); - close.innerText = "close"; close.classList.add("windowHeaderButton"); - close.classList.add("windowHeaderButtonClose"); + close.classList.add("close"); this.close = close; + + // Maximize button + const maximize = document.createElement("button"); + maximize.classList.add("windowHeaderButton"); + maximize.classList.add("maximize"); + this.maximizeButton = maximize; + maximize.addEventListener("click", () => { this.maximize() }); // Content const c = document.createElement("div"); @@ -51,50 +89,58 @@ export class Window { this.content = c; // Building + container.appendChild(maximize); + container.appendChild(close); h.appendChild(title); - h.appendChild(close); + h.appendChild(container); w.appendChild(h); w.appendChild(c); - } + this.state = "floating"; + } + setTitle(title) { this.title.innerText = title; } - + destroy() { this.window.remove(); } + + maximize() { + this.window.classList.toggle("maximized"); + } } export function promoteWindow(window) { const header = window.header; const elmnt = window.window; - + header.style.cursor = 'move'; - + header.onmousedown = (e) => { e.preventDefault(); - + let pos3 = e.clientX; let pos4 = e.clientY; - + const startTop = elmnt.offsetTop; const startLeft = elmnt.offsetLeft; - + document.onmouseup = closeDragElement; document.onmousemove = elementDrag; - + function elementDrag(e) { e.preventDefault(); const pos1 = pos3 - e.clientX; const pos2 = pos4 - e.clientY; pos3 = e.clientX; pos4 = e.clientY; - + elmnt.style.top = (elmnt.offsetTop - pos2) + "px"; elmnt.style.left = (elmnt.offsetLeft - pos1) + "px"; } - + function closeDragElement() { document.onmouseup = null; document.onmousemove = null; @@ -103,11 +149,9 @@ export function promoteWindow(window) { } export function getScreenRoot() { - return document.querySelector('body'); + return screenRoot; } -const applications = []; - export function getApplications() { return applications; } @@ -122,4 +166,15 @@ export function getLanguage() { export function getProcList() { return procList; -} \ No newline at end of file +} + +["log","warn", "error", "info"].forEach(method => { + const original = console[method]; + + console[method] = (...args) => { + original(...args); + bus.dispatchEvent(new CustomEvent("system-console", { + detail: args + })); + }; +}) \ No newline at end of file diff --git a/static/kumos/kumos.css b/static/kumos/kumos.css new file mode 100644 index 0000000..557f15e --- /dev/null +++ b/static/kumos/kumos.css @@ -0,0 +1,138 @@ +.kumosroot { + --theme-color: #4c6d6e; + --theme-color-lighter: #84b9bb; + --theme-color-variation: #568b8d; + position: relative; + width: 100vw; + height: 100vh; + margin: 0; + padding: 0; + box-sizing: border-box; + overflow: hidden; + color: white; + background-color: black; + font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; + + * { + box-sizing: border-box; + padding: 0; + margin: 0; + } + + .window { + position: absolute; + z-index: 9; + width: 300px; + height: 300px; + display: flex; + flex-direction: column; + resize: both; + overflow: hidden; + } + + .window.maximized { + left: 0; + top: 0; + right: 0; + bottom: 0; + width: auto; + height: auto; + } + + .windowHeader { + padding: 10px; + cursor: move; + z-index: 10; + background-color: rgba(255, 255, 255, 0.6); + color: #000000; + flex: 0 0 auto; + } + + .windowHeader .buttonContainer { + display: flex; + gap: .4em; + } + + .windowContent { + width: 100%; + height: 100%; + flex: 1 1 auto; + border: 0; + } + + .windowContent { + overflow: hidden; + display: flex; + flex-direction: column; + background-color: black; + } + + .windowHeader { + display: flex; + } + + .windowHeaderTitle { + margin: 0px; + margin-right: auto; + } + + .kumosBar { + position: absolute; + left: 0; + bottom: 0; + width: 100%; + background-color: var(--theme-color); + padding: .4em; + display: flex; + gap: 1em; + } + + .kumosBar * { + height: 100%; + } + + .windowHeaderButton { + height: 1em; + width: 1em; + padding: 0; + box-sizing: border-box; + border: none; + display: inline-flex; + align-items: center; + justify-content: center; + font-size: 1em; + line-height: 1; + cursor: pointer; + } + + .windowHeaderButton.close::before { + content: "×"; + } + + .windowHeaderButton.close { + background-color: rgba(255, 0, 0, 0.6); + color: white; + } + + .windowHeaderButton.maximize::before { + content: "□"; + } + + .windowHeaderButton.maximize { + background-color: rgba(255, 255, 0, 0.6); + color: black; + } + + button, input, textarea { + padding: .2em; + border: none; + } + + textarea { + resize: none; + } + + li { + margin-inline-start: 1em; + } +} \ No newline at end of file diff --git a/static/kumos/system/init.mjs b/static/kumos/system/init.mjs new file mode 100644 index 0000000..69543a8 --- /dev/null +++ b/static/kumos/system/init.mjs @@ -0,0 +1,10 @@ +import "../apps/pkg.mjs"; +import { Process } from "../kernel.mjs"; +import interactiveLogin from "./targets/interactive-login.mjs"; + +function init(p) { + console.log("Welcome to KumOS!"); + interactiveLogin(p); +} + +new Process("init", init); \ No newline at end of file diff --git a/static/kumos/system/login.mjs b/static/kumos/system/login.mjs new file mode 100644 index 0000000..327bf85 --- /dev/null +++ b/static/kumos/system/login.mjs @@ -0,0 +1,43 @@ +import { Process, Window, getScreenRoot, promoteWindow } from "../kernel.mjs"; +import createShell from "./shell.mjs"; + +export default function init(p) { + function login(password) { + if (password == "demo") { + p.detach(); + new Process("shell", createShell); + w.destroy(); + } else { + console.log("Invalid password.") + } + } + + console.log("Please log in with login(password)!"); + window.login = login; + const w = new Window(p); + w.setTitle("Login"); + w.window.style.height = "fit-content"; + w.window.style.width = "fit-content"; + w.window.style.resize = "none"; + + const loginForm = document.createElement("div"); + loginForm.style.display = "flex"; + + const passwordInput = document.createElement("input"); + passwordInput.type = "password"; + passwordInput.placeholder = "Password is 'demo'"; + + const loginButton = document.createElement("button"); + loginButton.innerText = "login"; + loginButton.addEventListener('click', () => { + login(passwordInput.value); + }) + + loginForm.appendChild(passwordInput); + loginForm.appendChild(loginButton); + w.content.appendChild(loginForm); + + w.close.remove(); + getScreenRoot().appendChild(w.window); + promoteWindow(w); +} \ No newline at end of file diff --git a/static/kumos/shell.mjs b/static/kumos/system/shell.mjs similarity index 68% rename from static/kumos/shell.mjs rename to static/kumos/system/shell.mjs index 4058469..973c343 100644 --- a/static/kumos/shell.mjs +++ b/static/kumos/system/shell.mjs @@ -1,5 +1,5 @@ -import { getScreenRoot, getApplications, Process } from "./kernel.mjs"; -import { showNotification } from "../scripts/notification.js"; +import { getScreenRoot, getApplications, Process } from "../kernel.mjs"; +import { showNotification } from "../../scripts/notification.js"; export function notify(title, text, time) { showNotification(title, text, time); @@ -9,14 +9,14 @@ function runApp(name) { let launched = false; getApplications().forEach(app => { if (app.name == name) { - new Process(app); + app.bootstrap(); launched = true; } }) if (!launched) alert("Not found."); } -function createShell() { +export default function createShell(p) { const bar = document.createElement('div'); bar.classList.add("kumosBar"); @@ -30,7 +30,4 @@ function createShell() { bar.appendChild(runButton); getScreenRoot().appendChild(bar); -} - -createShell(); -showNotification("KumOS Shell", "Welcome to KumOS, everything is work-in-progress! Try running apps to see available packages.", 10000); +} \ No newline at end of file diff --git a/static/kumos/system/targets/interactive-login.mjs b/static/kumos/system/targets/interactive-login.mjs new file mode 100644 index 0000000..fb75178 --- /dev/null +++ b/static/kumos/system/targets/interactive-login.mjs @@ -0,0 +1,5 @@ +import login from "../login.mjs"; + +export default (p) => { + p.child("login", login); +} \ No newline at end of file diff --git a/static/main.css b/static/main.css index 9de1c52..e423005 100644 --- a/static/main.css +++ b/static/main.css @@ -1172,67 +1172,6 @@ header div:first-child { font-size: medium; } -/* KumOS */ - -.window { - position: absolute; - z-index: 9; - /* background-color: #f1f1f1; */ - width: 300px; - height: 300px; - display: flex; - flex-direction: column; - resize: both; - overflow: hidden; -} - -.windowHeader { - padding: 10px; - cursor: move; - z-index: 10; - background-color: rgba(0, 0, 0, 0.6); - color: #fff; - flex: 0 0 auto; -} - -.windowContent { - width: 100%; - height: 100%; - flex: 1 1 auto; - border: 0; -} - -.windowContent { - overflow: hidden; - display: flex; - flex-direction: column; - background-color: black; -} - -.windowHeader { - display: flex; -} - -.windowHeaderTitle { - margin: 0px; - margin-right: auto; -} - -.kumosBar { - position: fixed; - left: 0; - bottom: 0; - width: 100%; - background-color: var(--theme-color); - padding: .4em; - display: flex; - gap: 1em; -} - -.kumosBar * { - height: 100%; -} - /* Animations */ @keyframes ellipsis-loader { diff --git a/static/scripts/tips.js b/static/scripts/tips.js index 7e72b43..cad5831 100644 --- a/static/scripts/tips.js +++ b/static/scripts/tips.js @@ -1,16 +1,19 @@ const isMobile = /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent); const body = document.querySelector('body'); -const elements = document.querySelectorAll('[data-tip]'); const hint = document.querySelector("#headerSubtitle"); -const hintPanelDefaultText = hint.innerHTML; +const hintPanelDefaultText = hint ? hint.innerHTML : null; let fixedHint; let currentObserver; -elements.forEach(el => { - registerElementHint(el); -}) +if (hint) { + const elements = document.querySelectorAll('[data-tip]'); + elements.forEach(el => { + registerElementHint(el); + }); +} function cleanup() { + if (!hint) return; hint.innerHTML = hintPanelDefaultText; hint.classList.remove('fixed'); if (fixedHint) {