Code viewer for World: Monkey Library
// Use JS to write whatever HTML and data you want to the page 

// One way of using JS to write HTML and data is with a multi-line string
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Template_literals

// UI components
class TextBox extends HTMLElement {
  static observedAttributes = ["id", "handler", "label"];

  constructor() {
    super();
    this._shadowRoot = this.attachShadow({ mode: "open" });
  }

  onEvent(fn) {
    return (event) => {
      event.preventDefault();
      fn(event);
    };
  }

  connectedCallback() {
    const form = document.createElement("form");

    form.addEventListener("submit", this.onEvent(eval(this.handler)));
    form.innerHTML = `
      <label for="'${this.id}'">${this.label}</label>
      <input type="text" id="'${this.id}'" name="'${this.id}'"/>
      `

    this._shadowRoot.appendChild(form);
  }

  attributeChangedCallback(name, oldVal, newVal) {
    if (oldVal == newVal) return;
    this[name] = newVal;
  }
}

customElements.define("text-box", TextBox);

class ImageGenerator {
  constructor() {
    this.apiKey = "";
  }

  async inference(prompt) {
    if (this.apiKey === "") {
      throw {
        name: 'ApiKeyNotSet',
        message: 'Api key not set !'
      };
    }

    const response = await fetch(
      "https://api-inference.huggingface.co/models/runwayml/stable-diffusion-v1-5",
      {
        headers: { Authorization: `Bearer ${this.apiKey}` },
        method: "POST",
        body: JSON.stringify(prompt),
      }
    );

    const result = await response.blob();
    return result;
  }

  set setApiKey(apiKey) {
    document.getElementById("api-error").innerHTML = "";
    this.apiKey = apiKey;
  }
}

// Singleton ImageGenerator
let txtToImg = new ImageGenerator();

function handleApiQuery(ev) {
  ev.preventDefault();

  const inputs = ev.target.elements[0].value.trim();

  const img = txtToImg.inference({"inputs": inputs}).then(blob => {
    const imageUrl = URL.createObjectURL(blob);
    const imgElement = document.createElement('img');
    imgElement.src = imageUrl;

    return imgElement;
  }).then(img => {
    let area = document.getElementById("api-result");
    area.innerHTML = "";
    area.appendChild(img);
  }).catch(err => {
    if (err.name == "ApiKeyNotSet") {
      document.getElementById("api-error").innerHTML = err.message;
    } else throw err;
  });

  ev.target.elements[0].value = "";
}

function handleApiKeyInput(ev) {
  ev.preventDefault();
  txtToImg.setApiKey = ev.target.elements[0].value.trim();
  ev.target.elements[0].value = "";
}

function html() {
  document.write ( `<text-box label="Api Key here:" id="apiKey" handler="handleApiKeyInput"></text-box>

<h1> Inference API </h1>

<text-box label="Prompt:" id="apiQuery" handler="handleApiQuery"></text-box>

<p id="api-error"></p>

<div id="api-result">
</div>
` )
}

function main() {
  html();
}

main();