Add bad coders webring, update kumaos.
This commit is contained in:
parent
770369c84b
commit
dc0b9b14cb
17 changed files with 401 additions and 155 deletions
|
|
@ -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);
|
||||
export default app;
|
||||
|
|
@ -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);
|
||||
export default app;
|
||||
|
|
@ -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 = `
|
||||
<script src="https://iframe.chat/scripts/main.min.js"></script>
|
||||
|
|
@ -35,4 +35,4 @@ function main(p) {
|
|||
}
|
||||
|
||||
const app = new Application('guestbook', main);
|
||||
registerApplication(app);
|
||||
export default app;
|
||||
5
static/kumos/apps/pkg.mjs
Normal file
5
static/kumos/apps/pkg.mjs
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
import "./apps.mjs";
|
||||
import "./browser.mjs";
|
||||
import "./guestbook.mjs";
|
||||
import "./taskmanager.mjs";
|
||||
import "./shell.mjs";
|
||||
61
static/kumos/apps/shell.mjs
Normal file
61
static/kumos/apps/shell.mjs
Normal file
|
|
@ -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;
|
||||
|
|
@ -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);
|
||||
export default app;
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
["log","warn", "error", "info"].forEach(method => {
|
||||
const original = console[method];
|
||||
|
||||
console[method] = (...args) => {
|
||||
original(...args);
|
||||
bus.dispatchEvent(new CustomEvent("system-console", {
|
||||
detail: args
|
||||
}));
|
||||
};
|
||||
})
|
||||
138
static/kumos/kumos.css
Normal file
138
static/kumos/kumos.css
Normal file
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
10
static/kumos/system/init.mjs
Normal file
10
static/kumos/system/init.mjs
Normal file
|
|
@ -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);
|
||||
43
static/kumos/system/login.mjs
Normal file
43
static/kumos/system/login.mjs
Normal file
|
|
@ -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);
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
5
static/kumos/system/targets/interactive-login.mjs
Normal file
5
static/kumos/system/targets/interactive-login.mjs
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
import login from "../login.mjs";
|
||||
|
||||
export default (p) => {
|
||||
p.child("login", login);
|
||||
}
|
||||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
Loading…
Reference in a new issue