/**
 * Copyright 2024 haxtheweb
 * @license Apache-2.0, see LICENSE for full text.
 */
import{LitElement as e,html as t,css as i}from"../../lit/index.js";import{DDDSuper as r}from"../d-d-d/d-d-d.js";import{WebContainer as n}from"../../@webcontainer/api/dist/index.js";import{Terminal as o}from"../../@xterm/xterm/lib/xterm.mjs";import{FitAddon as a}from"../../@xterm/addon-fit/lib/addon-fit.mjs";export class WebContainerEl extends(r(e)){static get tag(){return"web-container"}constructor(){super(),this.filesShown=[],this.status="Loading",this.fname=null,this.hideEditor=!1,this.hideTerminal=!1,this.hideWindow=!1,this.webcontainerInstance=null,this.files={"index.js":{file:{contents:" "}},"package.json":{file:{contents:'\n            {\n              "name": "hax-webcontainer",\n              "type": "module",\n              "dependencies": {\n              }\n            }'}}},this.commands=[]}async installDependencies(){const e=await this.webcontainerInstance.spawn("npm",["install"]);return e.output.pipeTo(new WritableStream({write(e){console.log(e)}})),e.exit}async startDevServer(){await this.webcontainerInstance.spawn("npm",["run","start"])}async startShell(e){const t=await this.webcontainerInstance.spawn("jsh",{terminal:{cols:e.cols,rows:e.rows}});t.output.pipeTo(new WritableStream({write(t){e.write(t)}}));const i=t.input.getWriter();return e.onData((e=>{i.write(e)})),t}async writeFile(e,t){await this.webcontainerInstance.fs.writeFile(`/${e}`,t)}async readFile(e){return await this.webcontainerInstance.fs.readFile(`/${e}`,"utf-8")}static get properties(){return{...super.properties,fname:{type:String,reflect:!0},status:{type:String,reflect:!0},files:{type:Object},filesShown:{type:Array},hideTerminal:{type:Boolean,reflect:!0,attribute:"hide-terminal"},hideEditor:{type:Boolean,reflect:!0,attribute:"hide-editor"},hideWindow:{type:Boolean,reflect:!0,attribute:"hide-window"}}}firstUpdated(e){if(super.firstUpdated(e),this.querySelector("template")){let e=this.querySelector("template").content.textContent.trim().split("\n");if(e.length>0)for(let t=0;t<e.length;t++)e[t]=e[t].trim(),this.commands.push(e[t].split(" "))}this.hideEditor||import("../code-editor/code-editor.js").then((e=>{setTimeout((()=>{this.files["index.js"]?this.fname="index.js":this.files["index.html"]&&(this.fname="index.html"),this.fname&&this.setCodeEditor(this.files[this.fname].file.contents,this.getLanguageFromFileEnding(this.fname))}),100)})),this.setupWebContainers()}getLanguageFromFileEnding(e){return e.endsWith(".js")?"javascript":e.endsWith(".json")?"json":e.endsWith(".html")?"html":e.endsWith(".yaml")?"yaml":"javascript"}setCodeEditor(e,t="javascript"){this.shadowRoot&&this.shadowRoot.querySelector("code-editor")&&(this.shadowRoot.querySelector("code-editor").innerHTML="html"===t?`<template><iframe>${e}</iframe></template>`:`<template>${e}</template>`,this.shadowRoot.querySelector("code-editor").language=t)}async setupWebContainers(){const e=new a,t=new o({convertEol:!0});t.loadAddon(e),t.open(this.shadowRoot.querySelector(".terminal")),e.fit(),this.status="Setting up container",this.webcontainerInstance=await globalThis.WebContainerManager.requestAvailability(),await this.webcontainerInstance.mount(this.files);const i=await this.startShell(t);this.commands.length>0?(this.status="Running commands",await this.runCommands(this.commands)):(this.status="Installing..",this.dispatchEvent(new CustomEvent("web-container-dependencies-installing",{bubbles:!0,compose:!0,cancelable:!1,detail:!0})),await this.installDependencies(),this.dispatchEvent(new CustomEvent("web-container-dependencies-installed",{bubbles:!0,compose:!0,cancelable:!1,detail:!0})),this.status="Running Start..",await this.startDevServer(),this.dispatchEvent(new CustomEvent("web-container-npm-start",{bubbles:!0,compose:!0,cancelable:!1,detail:!0}))),this.webcontainerInstance.on("server-ready",((e,t)=>{this.dispatchEvent(new CustomEvent("web-container-server-ready",{bubbles:!0,compose:!0,cancelable:!1,detail:{port:e,url:t}})),this.shadowRoot.querySelector("iframe")&&(this.shadowRoot.querySelector("iframe").src=t),this.status=""})),globalThis.addEventListener("resize",(()=>{e.fit(),i.resize({cols:t.cols,rows:t.rows})}))}async runCommands(e){var t;for(let i=0;i<e.length;i++){if(this.dispatchEvent(new CustomEvent("web-container-command-start",{bubbles:!0,compose:!0,cancelable:!1,detail:{command:e[i]}})),Array.isArray(e[i])){const r=Object.assign([],e[i]);let n=r.length<=1?r[0]:r.shift();this.status=`Running command (${i}/${e.length}): ${n}`,(t=await this.webcontainerInstance.spawn(n,r)).output.pipeTo(new WritableStream({write(e){console.log(e)}}))}else this.status=`Running command (${i}/${e.length}): ${e[i]}`,(t=await this.webcontainerInstance.spawn(e[i])).output.pipeTo(new WritableStream({write(e){console.log(e)}}));this.dispatchEvent(new CustomEvent("web-container-command-finished",{bubbles:!0,compose:!0,cancelable:!1,detail:{command:e[i]}}))}return t.exit}static get styles(){return[super.styles,i`
      :host {
        display: block;
        color: var(--ddd-theme-primary);
        background-color: var(--ddd-theme-accent);
        font-family: var(--ddd-font-navigation);
      }
      :host([hide-terminal]) .terminal {
        display: none;
      }
      :host([hide-editor]) .editor {
        display: none;
      }
      :host([hide-window]) iframe {
        display: none;
      }

      :host([hide-editor]) .container,
      :host([hide-window]) .container {
        display: block;
      }
      
      .status {
        background-color: var(--web-container-status-bg-color, var(--ddd-theme-default-infoLight, lightblue));
        color: var(--web-container-status-color, var(--ddd-theme-default-info, navy));
        font-size: var(--ddd-font-size-xxs);
        font-family: var(--ddd-font-navigation);
        padding: 4px 8px;
      }
      iframe {
        width: 100%;
        height: var(--web-container-iframe-height, 500px);
        border: none;
        background-color: transparent;
      }

      .terminal {
        padding: 0;
        margin: 0;
        height: var(--web-container-terminal-height, 200px);
        overflow: hidden;
      }
      .container {
        display: grid; 
        grid-template-columns: auto auto; 
        grid-template-rows: auto auto; 
        gap: 0px 0px; 
        grid-template-areas: 
          "editor preview"
          "terminal terminal"; 
      }
      .editor {
        grid-area: editor;
        height: 100%;
        display: contents;
      }
      code-editor {
        margin: 0;
      }
      .editor {
        display: grid; 
        gap: 0px 0px; 
        grid-template-areas: 
          "files"
          "codeeditor"; 
        grid-area: editor; 
      }
      .files {
        grid-area: files;
        width: 100%;
      }
      .files button {
        opacity: .9;
        background-color: #333333;
        color: white;
        font-size: var(--ddd-font-size-4xs);
        padding: 4px 16px;
      }
      .files button:hover,
      .files button:focus {
        opacity: 1;
      }
      .files button[active] {
        opacity: 1;
        background-color: black;
        border-color: var(--ddd-primary-1);
      }
      code-editor {
        grid-area: codeeditor;
        height: var(--web-container-iframe-height, 500px);
      }
      .terminal {
        grid-area: terminal;
        max-height: 200px;
      }
      .preview {
        grid-area: preview;
        height: 100%;
        display: grid; 
        grid-template-columns:auto; 
        grid-template-rows: auto; 
        gap: 0px 0px; 
        grid-template-areas: 
          "status"
          "iframe"; 
        grid-area: preview; 
      }
      .iframe { grid-area: iframe; }
      .status { grid-area: status; }
      /**
      * Copyright (c) 2014 The xterm.js authors. All rights reserved.
      * Copyright (c) 2012-2013, Christopher Jeffrey (MIT License)
      * https://github.com/chjj/term.js
      * @license MIT
      *
      * Permission is hereby granted, free of charge, to any person obtaining a copy
      * of this software and associated documentation files (the "Software"), to deal
      * in the Software without restriction, including without limitation the rights
      * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
      * copies of the Software, and to permit persons to whom the Software is
      * furnished to do so, subject to the following conditions:
      *
      * The above copyright notice and this permission notice shall be included in
      * all copies or substantial portions of the Software.
      *
      * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
      * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
      * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
      * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
      * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
      * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
      * THE SOFTWARE.
      *
      * Originally forked from (with the author's permission):
      *   Fabrice Bellard's javascript vt100 for jslinux:
      *   http://bellard.org/jslinux/
      *   Copyright (c) 2011 Fabrice Bellard
      *   The original design remains. The terminal itself
      *   has been extended to include xterm CSI codes, among
      *   other features.
      */

      /**
      *  Default styles for xterm.js
      */

      .xterm {
          cursor: text;
          position: relative;
          user-select: none;
          -ms-user-select: none;
          -webkit-user-select: none;
      }

      .xterm.focus,
      .xterm:focus {
          outline: none;
      }

      .xterm .xterm-helpers {
          position: absolute;
          top: 0;
          /**
          * The z-index of the helpers must be higher than the canvases in order for
          * IMEs to appear on top.
          */
          z-index: 5;
      }

      .xterm .xterm-helper-textarea {
          padding: 0;
          border: 0;
          margin: 0;
          /* Move textarea out of the screen to the far left, so that the cursor is not visible */
          position: absolute;
          opacity: 0;
          left: -9999em;
          top: 0;
          width: 0;
          height: 0;
          z-index: -5;
          /** Prevent wrapping so the IME appears against the textarea at the correct position */
          white-space: nowrap;
          overflow: hidden;
          resize: none;
      }

      .xterm .composition-view {
          /* TODO: Composition position got messed up somewhere */
          background: #000;
          color: #FFF;
          display: none;
          position: absolute;
          white-space: nowrap;
          z-index: 1;
      }

      .xterm .composition-view.active {
          display: block;
      }

      .xterm .xterm-viewport {
          /* On OS X this is required in order for the scroll bar to appear fully opaque */
          background-color: #000;
          overflow-y: scroll;
          cursor: default;
          position: absolute;
          right: 0;
          left: 0;
          top: 0;
          bottom: 0;
      }

      .xterm .xterm-screen {
          position: relative;
      }

      .xterm .xterm-screen canvas {
          position: absolute;
          left: 0;
          top: 0;
      }

      .xterm .xterm-scroll-area {
          visibility: hidden;
      }

      .xterm-char-measure-element {
          display: inline-block;
          visibility: hidden;
          position: absolute;
          top: 0;
          left: -9999em;
          line-height: normal;
      }

      .xterm.enable-mouse-events {
          /* When mouse events are enabled (eg. tmux), revert to the standard pointer cursor */
          cursor: default;
      }

      .xterm.xterm-cursor-pointer,
      .xterm .xterm-cursor-pointer {
          cursor: pointer;
      }

      .xterm.column-select.focus {
          /* Column selection mode */
          cursor: crosshair;
      }

      .xterm .xterm-accessibility:not(.debug),
      .xterm .xterm-message {
          position: absolute;
          left: 0;
          top: 0;
          bottom: 0;
          right: 0;
          z-index: 10;
          color: transparent;
          pointer-events: none;
      }

      .xterm .xterm-accessibility-tree:not(.debug) *::selection {
        color: transparent;
      }

      .xterm .xterm-accessibility-tree {
        user-select: text;
        white-space: pre;
      }

      .xterm .live-region {
          position: absolute;
          left: -9999px;
          width: 1px;
          height: 1px;
          overflow: hidden;
      }

      .xterm-dim {
          /* Dim should not apply to background, so the opacity of the foreground color is applied
          * explicitly in the generated class and reset to 1 here */
          opacity: 1 !important;
      }

      .xterm-underline-1 { text-decoration: underline; }
      .xterm-underline-2 { text-decoration: double underline; }
      .xterm-underline-3 { text-decoration: wavy underline; }
      .xterm-underline-4 { text-decoration: dotted underline; }
      .xterm-underline-5 { text-decoration: dashed underline; }

      .xterm-overline {
          text-decoration: overline;
      }

      .xterm-overline.xterm-underline-1 { text-decoration: overline underline; }
      .xterm-overline.xterm-underline-2 { text-decoration: overline double underline; }
      .xterm-overline.xterm-underline-3 { text-decoration: overline wavy underline; }
      .xterm-overline.xterm-underline-4 { text-decoration: overline dotted underline; }
      .xterm-overline.xterm-underline-5 { text-decoration: overline dashed underline; }

      .xterm-strikethrough {
          text-decoration: line-through;
      }

      .xterm-screen .xterm-decoration-container .xterm-decoration {
        z-index: 6;
        position: absolute;
      }

      .xterm-screen .xterm-decoration-container .xterm-decoration.xterm-decoration-top-layer {
        z-index: 7;
      }

      .xterm-decoration-overview-ruler {
          z-index: 8;
          position: absolute;
          top: 0;
          right: 0;
          pointer-events: none;
      }

      .xterm-decoration-top {
          z-index: 2;
          position: relative;
      }
    `]}editorValueChanged(e){this.writeFile(this.fname,e.detail.value)}async updateFile(e){this.fname=e.target.getAttribute("data-fname");let t=await this.readFile(this.fname);this.setCodeEditor(t,this.getLanguageFromFileEnding(this.fname))}render(){return t`
    <div class="container" part="container">
      ${this.hideEditor?"":t`
        <div class="editor" part="editor">
          <div class="files" part="files">
            ${this.filesShown.map((e=>t`<button @click="${this.updateFile}" data-fname="${e.file}" ?active="${e.file===this.fname}">${e.label}</button>`))}
          </div>
          <code-editor part="code-editor" @value-changed="${this.editorValueChanged}"></code-editor>
        </div>`}
      <div class="preview" part="preview">
        ${this.hideWindow?"":t`<div class="status" part="status">${this.status}</div><iframe part="iframe" src="${new URL("./lib/loading.html",import.meta.url).href}"></iframe>`}
      </div>
    </div>
    <div class="terminal" part="terminal"></div>`}static get haxProperties(){return new URL(`./lib/${this.tag}.haxProperties.json`,import.meta.url).href}}globalThis.customElements.define(WebContainerEl.tag,WebContainerEl),globalThis.WebContainerManager=globalThis.WebContainerManager||{},globalThis.WebContainerManager.requestAvailability=async()=>(!globalThis.WebContainerManager.instance&&globalThis.document&&(globalThis.WebContainerManager.instance=await n.boot()),globalThis.WebContainerManager.instance);