Yet another terminal window

by Nicholas Berlette

HTML

<script type="importmap">
  {
    "imports": {
      "uno.css": "https://unpkg.com/@unocss/[email protected]/full.global.js",
      "@unocss/": "https://unpkg.com/@unocss/"
    },
    "scopes": {}
  }
</script>

<script type="module">
  const safelist = [
    "opacity-0",
    "display-none",
    "opacity-100",
    "display-inline",
    "display-inline-block",
    "display-block",
    "bg-red-400",
    "bg-blue-400",
    "bg-red-800",
    "window-buttons",
    "panel-container",
    "tab-container",
    "a-tab",
    "a-panel",
    "window",
    "terminal",
    "terminal-window",
    "bg-btn-close",
    "bg-btn-minimize",
    "bg-btn-maximize",
    "maximize",
    "close",
    "minimize",
    "blurred",
    "focused",
    "fullscreen",
    "tab-active",
    "tab-hidden",
    "tab-button",
    "subtitle",
    "avatar",
  ]

  globalThis.__unocss = {
    get safelist() {
      return [
        ...new Set([
          ...safelist,
          ...Object.keys(this.shortcuts),
          ...Object.values(this.shortcuts).flatMap((v) => v.split(/\s+/)),
        ]),
      ]
    },
    shortcuts: {
      invisible: "opacity-0",
      visible: "!opacity-100 inline-block",
      "visible-block": "!opacity-100 block",
      "btn":
        "size-2.5 md:size-3 xl:size-3.5 rounded-full block bg-transparent border-current border-2 cursor-pointer shadow-sm hover:shadow hover:saturate-115 hover:bg-current group-hover:bg-current transition-all duration-300 ease-in",
      "close": "!bg-current !color-btn-close",
      "minimize": "!bg-current !color-btn-minimize",
      "maximize": "!bg-current !color-btn-maximize",
      "btn-create-window":
        "text-3xl font-black font-sans lowercase tracking-tight rounded-full px-5 py-4 absolute right-6 top-6 border-none text-white bg-gradient-to-tr via-indigo-800/10 from-cyan-600/30 ring-4 ring-offset-2 ring-offset-white hover:ring-green-200/80 cursor-pointer transition-all duration-300 ease-out shadow-lg hover:ring-6 ring-orange-300/80...

CSS

[un-cloak],.un-cloak {
  opacity: 0;
  transition: opacity 1s 0.5s ease-in;
}

html,body{
  color-scheme: light dark;
  font-family: var(--font-sans, sans-serif);
  font-size: 0.875rem;
}

:root, html, body {
  background-color: light-dark(rgb(226 232 240 / var(--tw-bg-opacity, 1)), rgb(11 18 36 / var(--tw-bg-opacity, 1))) !important;
}

:root {
  color-scheme: light dark;
  --base00: light-dark(var(--override-base00, #fff), var(--override-base00, #232a2f));
  --base01: light-dark(var(--override-base01, #eaeaeb), var(--override-base01, #444267));
  --base02: light-dark(var(--override-base02, #dbdbdc), var(--override-base02, #32374d));
  --base03: light-dark(var(--override-base03, #a0a1a7), var(--override-base03, #707a84));
  --base04: light-dark(var(--override-base04, #232324), var(--override-base04, #8796b0));
  --base05: light-dark(var(--override-base05, #232324), var(--override-base05, #939da5));
  --base06: light-dark(var(--override-base06, #8c98a2), var(--override-base06, #535d75));
  --base07: light-dark(var(--override-base07, #000), var(--override-base07, #0184bc));
  --base08: light-dark(var(--override-base08, #0184bc), var(--override-base08, #ff6a80));
  --base09: light-dark(var(--override-base09, #986801), var(--override-base09, #ffa763));
  --base0A: light-dark(var(--override-base0A, #4078f2), var(--override-base0A, #ffea6b));
  --base0B: light-dark(var(--override-base0B, #50a14e), var(--override-base0B, #5bec95));
  --base0C: light-dark(var(--override-base0C, #383a42), var(--override-base0C, #89ddff));
  --base0D: light-dark(var(--override-base0D, #4078f2), var(--override-base0D, #82aaff));
  --base0E: light-dark(var(--override-base0E, #a626a4), var(--override-base0E, #ba8ef7));
  --base0F: light-dark(var(--override-base0F, #dbdbdc), var(--override-base0F, #ff8ad1));
  --base0G: light-dark(var(--override-base0G, #f7f9fc), var(--override-base0G, #1a2023));
  
  --snippet-bg: light-dark(var(--override-snippet-bg, #fff), var(--override-snippet-bg,...

JavaScript

class BaseWindowButtonElement extends HTMLElement {
  #window = null;
	#type = null;
  #types =  ["close", "minimize", "maximize"];
  
  constructor(type = "close", window = null) {
    super();
    if (new.target === BaseWindowButtonElement) {
    	throw new TypeError("Illegal constructor");
    }

		this.$shadowRoot = this.attachShadow({ mode: 'closed' });
    this.$subtype = new.target || this.constructor;

		if (type != null) this.type = type;
    if (window != null) this.window = window;
  }

  get type() {
  	return this.#type ??= this.#types[0];
  }
  
  set type(value) {
   	if (this.#types.includes(value)) this.#type = value;
  }
  
  get window() {
  	if (!this.#window) {
      if (this.hasAttribute("window_id")) {
      	this.window = this.getAttribute("window_id");
      }
    }
  	return typeof this.#window === "string" ? document.querySelector(this.#window) : this.#window;
  }
  
  set window(value) {
    if (typeof value === "string") {
    	this.#window = document.querySelector(value) ?? this.#window;
    } else if (value instanceof HTMLElement) {
    	this.#window = value;
    }
  }
  
  set window_id(value) {
  	this.window = value;
  }
  
  clickHandler = (e) => {
  	switch (this.type) {
    	case "close": {
      	this.window?.close();
      } break;
      case "minimize": {
        this.window?.minimize();
/*         if (document.exitFullscreen) document.exitFullscreen();
        const bounding = this.window.getBoundingClientRect();
        const panelsBBox = this.window.panelContainer.getBoundingClientRect();
              
        if (panels) {
          if (this.$isMinimized) {
            panels.animate?.({ height: (this.$panelsBBox ??= panelsBBox).height, opacity: 1 }, { duration: 600, easing: "ease-in-out", fill: "both", delay: 50 });
            this.$panelsBBox = null;
            this.$isMinimized = false;
          } else { 
            panels.animate?.({ height: 15, opacity: 0 }, { duration: 800, easing: "ease-in", fill: "both"...