~roxwize/ambient

04c438fe10fb33f753d455bf2440b0fa5622bb14 — roxwize a month ago cf3b068
more apps, de functionality+

Signed-off-by: roxwize <rx@roxwize.xyz>
7 files changed, 145 insertions(+), 18 deletions(-)

A README.md
A assets/chevron.png
M src/app.ts
M src/apps/desktop.ts
A src/apps/terminal.ts
M src/main.ts
M src/system.ts
A README.md => README.md +0 -0
A assets/chevron.png => assets/chevron.png +0 -0
M src/app.ts => src/app.ts +2 -1
@@ 28,7 28,8 @@ export default abstract class App implements Process {

    this.pid = sys.pid_or(pid, override);
    this.name = name;
    this.logger = sys.logger;
    // this.logger = sys.logger;
    this.logger = new Logger(`App::${this.name}`);
    this.logger.info(`Created process with PID ${this.pid} and display name "${this.name}".`);
  }
}

M src/apps/desktop.ts => src/apps/desktop.ts +51 -9
@@ 43,26 43,34 @@ export default class DefaultDesktop extends App {
  private container: HTMLDivElement;
  private header: HTMLDivElement;
  private content: HTMLDivElement;
  private clock: HTMLSpanElement;
  private calendar: HTMLDivElement;

  private clock_open: boolean = false;

  constructor() {
    super(10, "desktop_default", true);
  }

  private setupDOM(main: HTMLElement) {
    System.get().win = new DefaultWindowManager(main);

    if (document.getElementById("de-style")) {
      document.getElementById("de-style").remove();
    }
    const style = document.createElement("style");
    style.innerHTML = `\
.de-container { position:absolute; width:100%; height:100%; top:0; left:0; display:flex; flex-direction:column; background-image:url("/assets/wallpaper.png"); background-size:cover; background-position:center center; }\
.de-header { width:100%; height:6%; display:flex; justify-content:space-between; align-items:center; padding:6px; box-sizing:border-box; backdrop-filter:blur(3px); -webkit-backdrop-filter:blur(3px); color:white; background-color:rgba(0, 0, 0, 0.3); box-shadow:0 2px 3px rgba(0, 0, 0, 0.4); }\
@import url('https://fonts.googleapis.com/css2?family=DM+Mono:ital,wght@0,300;0,400;0,500;1,300;1,400;1,500&display=swap');\
* { --fg:#343a40; --bg:#f1f3f5; }\
.de-container { position:absolute; width:100%; height:100%; top:0; left:0; display:flex; flex-direction:column; background-image:url("/assets/wallpaper.png"); background-size:cover; background-position:center center; overflow:hidden; font-family:"DM Mono", monospace; }\
.de-header { width:100%; height:6%; display:flex; justify-content:space-between; align-items:center; padding:6px; box-sizing:border-box; backdrop-filter:blur(3px); -webkit-backdrop-filter:blur(3px); color:var(--bg); background-color:rgba(0, 0, 0, 0.3); box-shadow:0 2px 3px rgba(0, 0, 0, 0.4); cursor:default; }\
.de-header span { text-align:center; margin:auto; }
.de-content { width:100%; height:auto; box-sizing:border-box; flex-grow:1; }\
.de-window { position:absolute; min-width:50px; min-height:50px; border-radius:4px; overflow:hidden; }\
.de-window-header { display:flex; justify-content:space-around; width:100%; padding:2px; text-align:center; box-sizing:border-box; backdrop-filter:blur(3px); -webkit-backdrop-filter:blur(3px); color:white; background-color:rgba(0, 0, 0, 0.3); cursor:move; gap:10px; }\
.de-window-content { background-color:white; padding:6px; }\
.de-window { position:absolute; min-width:50px; min-height:50px; border-radius:4px; overflow:hidden; max-width:100%; max-height:100%; resize:both; }\
.de-window-header { display:flex; justify-content:space-between; width:100%; padding:2px; text-align:center; box-sizing:border-box; backdrop-filter:blur(3px); -webkit-backdrop-filter:blur(3px); color:var(--bg); background-color:rgba(0, 0, 0, 0.3); cursor:move; gap:10px; }\
.de-window-content { background-color:var(--bg); padding:6px; padding-bottom:10%; color:var(--fg); overflow:auto; width:100%; height:100%; box-sizing:border-box; }\
.de-window-close { cursor:pointer; }\
.de-popover::before { content:""; background-image:url('/assets/chevron.png'); position:absolute; width:60px; height:9px; top:-12%; left:50%; transform:translateX(-50%); }\
.de-popover { background-color:var(--bg); color:var(--fg); position:absolute; border-radius:4px; box-shadow:0 2px 3px rgba(0, 0, 0, 0.4); padding:6px; }\
.de-calendar { left:75%; top:8%; transform:translateX(-50%); text-align:center; }\
`;
    document.head.appendChild(style);



@@ 74,18 82,48 @@ export default class DefaultDesktop extends App {
    this.header.classList.add("de-header");
    this.header.innerHTML = `\
<span>Applications</span>
<span>0:00</span>
<span>ambient</span>\
<span class="de-clock">0:00</span>\
    `;
    this.container.appendChild(this.header);

    this.clock = this.header.querySelector(".de-clock");
    this.clock.addEventListener("click", () => {
      this.display_calendar();
    });
    this.update_clock();

    this.content = document.createElement("div");
    this.content.classList.add("de-content");
    this.container.appendChild(this.content);
    
    System.get().win = new DefaultWindowManager(this.container);

    this.logger.info("Setup DOM successfully!");
  }

  private update_clock() {
    this.clock.innerHTML = `${new Date(Date.now()).toLocaleTimeString()}`;
  }
  private update_calendar() {
    const now = new Date(Date.now());
    this.calendar.innerHTML = `
      <span style="font-size:larger;">${now.toLocaleTimeString()}</span><br>
      <span>${now.toLocaleDateString()}</span>
    `;
  }
  private display_calendar() {
    if (this.clock_open) {
      this.calendar.remove();
      this.clock_open = false;
      return;
    }
    this.calendar = document.createElement("div");
    this.calendar.classList.add("de-calendar", "de-popover");
    this.update_calendar();
    this.container.appendChild(this.calendar);
    this.clock_open = true;
  }

  public run = (args: string[]): Promise<number> => {
    return new Promise((res) => {
      this.logger.info("Starting Desktop...");


@@ 97,6 135,10 @@ export default class DefaultDesktop extends App {
      }

      this.setupDOM(main);
      setInterval(() => {
        this.update_clock();
        if (this.calendar) this.update_calendar();
      }, 1000);
    });
  }
  public exit = () => {

A src/apps/terminal.ts => src/apps/terminal.ts +39 -0
@@ 0,0 1,39 @@
import { WindowApp } from "../app";
import { Log } from "../global";
import { System } from "../system";

export class Terminal extends WindowApp {
  private frame: HTMLDivElement;

  public log_index: number = 0;

  constructor() {
    super(30, "terminal", "Terminal");
  }

  private add_line(l: Log) {
    const el = document.createElement("span");
    el.style.display = "block";
    el.textContent = l.message;
    this.frame.appendChild(el);
    this.frame.scrollTop = this.frame.scrollHeight;
  }

  public run = (args: string[]): Promise<number> => {
    return new Promise((res) => {
      const sys = System.get();
      this.frame = document.createElement("div");
      this.frame.setAttribute("style", `overflow-y:auto;font-size:smaller;`);

      for (let log of sys.global_logs[this.log_index]) {
        this.add_line(log);
      }

      System.get().e_msglogged.add_listener_id((log) => {
        this.add_line(log);
      }, "term_watcher");
      this.content.appendChild(this.frame);
    });
  };
  public exit: () => void;
}

M src/main.ts => src/main.ts +4 -1
@@ 1,14 1,17 @@
import DefaultDesktop from "./apps/desktop";
import { TaskManager } from "./apps/taskmgr";
import { Terminal } from "./apps/terminal";
import { System } from "./system";

const system = System.get();
const DEBUG = false;

function boot() {
  system.logger.info("Booting...");
  system.run(new DefaultDesktop());
  system.run(new Terminal());
  system.run(new TaskManager());
  console.log(system);
  if (DEBUG) console.log(system);
}

boot();

M src/system.ts => src/system.ts +49 -7
@@ 1,21 1,58 @@
import App from "./app";
import { Log, Process } from "./global";

export class Event<T> {
  private listeners: ((v: T) => void)[];
  private ids: {[id: string]: number};

  constructor() {
    this.listeners = [];
    this.ids = {};
  }
  public add_listener(listener: (v: T) => void) {
    return this.listeners.push(listener);
  }
  public add_listener_id(listener: (v: T) => void, id: string) {
    this.ids[id] = this.add_listener(listener) - 1;
  }
  public remove_listener(id: string) {
    this.listeners.splice(this.ids[id], 1);
  }
  public fire(v: T) {
    for (let listener of this.listeners) {
      listener(v);
    }
  }
}

export class Logger {
  static INFO = 1;
  static WARN = 2;
  static ERROR = 4;

  private namespace: string;
  private id: number;
  log: Log[];

  constructor() {
  /**
   * @param namespace Will be used to identify log messages
   * @param id An ID of 1 of 6 global logs to insert messages into
   */
  constructor(namespace = "", id = 0) {
    this.namespace = namespace;
    this.log = [];
    this.id = id;
  }
  private _log(m: string, t: number) {
    this.log.push({
    const sys = System.get();

    const log: Log = {
      message: m,
      type: t
    });
    }
    this.log.push(log);
    sys.global_logs[this.id].push(log);
    sys.e_msglogged.fire(log);
    // log to dev console
    switch (t) {
      case Logger.INFO:


@@ 32,13 69,13 @@ export class Logger {
    }
  }
  public info(t: string) {
    this._log(`[INFO] ${t}`, Logger.INFO);
    this._log(`[INFO] ${this.namespace}: ${t}`, Logger.INFO);
  }
  public warn(t: string) {
    this._log(`[WARN] ${t}`, Logger.WARN);
    this._log(`[WARN] ${this.namespace}: ${t}`, Logger.WARN);
  }
  public error(t: string) {
    this._log(`[ERROR] ${t}`, Logger.ERROR);
    this._log(`[ERROR] ${this.namespace}: ${t}`, Logger.ERROR);
  }
  public from_error(t: Error) {
    this.error(`${t.name}: ${t.message}`);


@@ 68,11 105,16 @@ export class System {
  private static instance?: System;

  public logger: Logger;
  public global_logs: Log[][];
  public e_msglogged: Event<Log>;

  public win: WindowManager;

  private constructor() {
    this.processes = {};
    this.logger = new Logger();
    this.logger = new Logger("System");
    this.global_logs = [[], [], [], [], [], []];
    this.e_msglogged = new Event();
  }

  public static get() {