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"...