Terminal Web Component

:) WIP

by Nicholas Berlette

HTML

<div class="outer" data-theme="dark">
  <section class="inner">
    <div class="relative">

      <v-terminal id="terminal" title="/usr/bin/bash" class="dark terminal">
        <ul aria-label="Code Tabs" class="hidden" role="tablist"><li role="presentation"><button class="lume-code-tab is-active" aria-controls="panel-4-1" aria-selected="true" id="tab-4-1" role="tab" tabindex="0">custom.tsx</button></li></ul>
        <div class="w-full absolute top-12 left-0 right-0 bottom-0 line-numbers-bg">
        
        <!-- start user code -->
          <pre class="code-outer overflow-x-auto !leading-5.5 h-full w-full block"><code class="code"><span class="line"><span style="color: #db889a;">is</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">string</span><span style="color: #D8DEE9FF">(</span><span style="color: #A3BE8C">value</span><span style="color: #ECEFF499">: unknown</span><span style="color: #D8DEE9FF">)</span> <span style="color: #81A1C1">=&gt;</span> <span style="color: #A3BE8C">value</span> <span style="color: #ECEFF499">is</span> <span style="color: #d4976c">string</span><span style="color:#ECEFF499">;</span></span><span class="line"><span style="color: #db889a;">is</span><span style="color: #ECEFF4">.</span><span style="color: #d4976c;">assert</span><span style="color: #ECEFF4">.</span><span style="color: #88C0D0">string</span><span style="color: #D8DEE9FF">(</span><span style="color: #A3BE8C">value</span><span style="color: #ECEFF499">: unknown</span><span style="color: #D8DEE9FF">)</span> <span style="color: #81A1C1">=&gt;</span> <span style="color: #d4976c">asserts</span> <span style="color: #A3BE8C">value</span> <span style="color: #ECEFF499">is</span> <span style="color: #d4976c">string</span><span style="color:#ECEFF499">;</span></span></code></pre>
          <!-- end user code -->
        </div>
      </v-terminal>
      
            <v-terminal id="terminal2" title="/usr/bin/bash" class="dark terminal">
        <ul...

SCSS

:root {
  --light-bg-1: #f0f0f0;
  --light-bg-2: #cfcfcf;
  --light-bg-3: #ececec;
  --light-bg-4: #9ab;
  --light-bg-alpha-1: #fff6;
  --light-text-1: #000;
  --light-text-2: #123;
  --light-text-3: #000a;
  --light-text-4: #456;
  --light-border-1: #0004;
  --light-border-2: #0003;
  --light-shadow-4: #0004;
  --light-btn-shadow: #00000019;

  /* backgrounds (dark) */
  --dark-bg-1: #111518;
  --dark-bg-2: #111827;
  --dark-bg-3: #20262e;
  --dark-bg-4: #234;
  --dark-bg-alpha-1: #ffffff05;
  --dark-text-1: #fff;
  --dark-text-2: #ddd;
  --dark-text-3: #fffa;
  --dark-text-4: #789;
  --dark-border-1: #000a;
  --dark-border-2: #fff3;
  --dark-shadow-4: #000;
  
  /* shadows */
  --shadow-1: #0004;
  --shadow-2: #0003;
  --shadow-3: #0002;
  --shadow-4: #0004;
}

:root:not(.light) {
  /* defaults */
  --bg-1: var(--dark-bg-1);
  --bg-2: var(--dark-bg-2);
  --bg-3: var(--dark-bg-3);
  --bg-4: var(--dark-bg-4);
  --bg-alpha-1: var(--dark-bg-alpha-1);
  --text-1: var(--dark-text-1);
  --text-2: var(--dark-text-2);
  --text-3: var(--dark-text-3);
  --text-4: var(--dark-text-4);
  --border-1: var(--dark-border-1);
  --border-2: var(--dark-border-2);
}
.dark {
  /* backgrounds (dark) */
  --bg-1: #111518;
  --bg-2: #111827;
  --bg-3: #20262e;
  --bg-4: #234;
  --bg-alpha-1: #ffffff05;
  --text-1: #fff;
  --text-2: #ddd;
  --text-3: #fffa;
  --text-4: #789;
  --border-1: #000a;
  --border-2: #fff3;
  --shadow-4: #000;
}
.light {
  --bg-1: #f0f0f0;
  --bg-2: #cfcfcf;
  --bg-3: #ececec;
  --bg-4: #9ab;
  --bg-alpha-1: #fff6;
  --text-1: #000;
  --text-2: #123;
  --text-3: #000a;
  --text-4: #456;
  --border-1: #0004;
  --border-2: #0003;
  --shadow-4: #0004;
  --btn-shadow: #00000019;
}

@keyframes morph {
  50% {
    --transform: scale(1.2) translateZ(150%);
  }
}
@keyframes wobble {
  50% {
    --transform: var(--transform) rotate3d(0.2turn, -0.3turn, 1.5turn);
  }
}
.bg-container {
  --transform: scale(1);
  animation: morph 10s ease-in infinite 2s both alternate,...

JavaScript

const $ctor = function (expressionSyntax) {
	try {
		return $Function('"use strict"; return (' + expressionSyntax + ').constructor;')();
	} catch (e) {}
};



const sugar = {
  on: function(names, fn) {
    names
      .split(' ')
      .forEach(name =>
        this.addEventListener(name, fn));
    return this;
  },
  off: function(names, fn) {
    names
      .split(' ')
      .forEach(name =>
        this.removeEventListener(name, fn));
    return this;
  },
  attr: function(attr, val) {
    if (val === undefined) return this.getAttribute(attr);

    val == null ?
      this.removeAttribute(attr) :
      this.setAttribute(attr, val || '')

    return this;
  }
}

window.$ = function $(query, $context = document) {
  let $nodes = query instanceof NodeList || Array.isArray(query) ?
    query :
    query instanceof HTMLElement || query instanceof SVGElement ?
    [query] :
    $context.querySelectorAll(query);

  if (!$nodes.length) $nodes = [];

  const azucar = Array.from($nodes, $el => Object.assign($el, sugar));
  return Object.assign(azucar, {
    on: function(names, fn) {
      return (this.forEach($el => $el.on(names, fn)), this);
    },
    off: function(names, fn) {
      return (this.forEach($el => $el.off(names, fn)), this);
    },
    attr: function(attrs, val) {
      if (typeof attrs === 'string' && val === undefined) {
        return this[0].attr(attrs);
      } else if (typeof attrs === 'object') {
        this.forEach($el =>
          Object.entries(attrs)
          .forEach(([key, val]) =>
            $el.attr(key, val)));
      } else if (
        typeof attrs == 'string' &&
        (val || val == null || val == '')
      ) {
        this.forEach($el => $el.attr(attrs, val));
      }
      return this;
    }
  })
}

class Terminal extends HTMLElement {
  constructor() {
    super();
    /*     const template = document
          .getElementById('element-details-template')
          .content;
        const shadowRoot = this.attachShadow({mode:...