<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Stable Diffusion UI</title>
  </head>
  <body>
    <!-- The react app entry point. Currently no ui just poc importing and logging -->
    <div id="root"></div>
    <script type="module" src="/src/main.tsx"></script>

    <!-- ORIGINAL CODE BELOW FOR REFENCE -->

    <!--  KEEP FOR NOW -->
    <!-- THE STYLES ARE BEING USED IN THE REACT APP  -->
    <!-- WE NEED TO PORT OVER THE STYLES OVER TO THE REACT COMPONENTS -->
    <style>
      body {
        font-family: Arial, Helvetica, sans-serif;
        font-size: 11pt;
        background-color: rgb(32, 33, 36);
        color: #eee;
      }
      a {
        color: rgb(0, 102, 204);
      }
      a:visited {
        color: rgb(0, 102, 204);
      }
      label {
        font-size: 10pt;
      }
      #prompt {
        width: 100%;
        height: 50pt;
      }
      @media screen and (max-width: 600px) {
        #prompt {
          width: 95%;
        }
      }
      .image_preview_container {
        display: none;
        margin-top: 10pt;
      }
      .image_clear_btn {
        position: absolute;
        transform: translateX(-50%) translateY(-35%);
        background: black;
        color: white;
        border: 2pt solid #ccc;
        padding: 0;
        cursor: pointer;
        outline: inherit;
        border-radius: 8pt;
        width: 16pt;
        height: 16pt;
        font-family: Verdana;
        font-size: 8pt;
      }
      #editor-settings-entries {
        font-size: 9pt;
        margin-bottom: 5px;
        padding-left: 10px;
        list-style-type: none;
      }
      #editor-settings-entries li {
        padding-bottom: 3pt;
      }
      #guidance_scale {
        transform: translateY(30%);
      }
      #outputMsg {
        font-size: small;
      }
      #footer {
        font-size: small;
        padding-left: 10pt;
        background: none;
      }
      #footer-legal {
        font-size: 8pt;
      }
      .imgSeedLabel {
        position: absolute;
        transform: translateX(-100%);
        margin-top: 5pt;
        margin-left: -5pt;
        font-size: 10pt;

        background-color: #333;
        opacity: 0.8;
        color: #ddd;
        border-radius: 3pt;
        padding: 1pt 3pt;
      }
      .imgUseBtn {
        position: absolute;
        transform: translateX(-100%);
        margin-top: 30pt;
        margin-left: -5pt;
      }
      .imgSaveBtn {
        position: absolute;
        transform: translateX(-100%);
        margin-top: 55pt;
        margin-left: -5pt;
      }
      .imgItem {
        display: inline;
        padding-right: 10px;
      }
      .imgItemInfo {
        opacity: 0.5;
      }

      #container {
        width: 75%;
        margin-left: auto;
        margin-right: auto;
      }
      @media screen and (max-width: 1400px) {
        #container {
          width: 100%;
        }
      }
      #meta small {
        font-size: 11pt;
      }
      #editor {
        padding: 5px;
      }
      #editor label {
        font-weight: bold;
      }
      #preview {
        padding: 5px;
      }
      #editor-inputs {
        margin-bottom: 20px;
      }
      #editor-inputs-prompt {
        flex: 1;
      }
      #editor-inputs .row {
        padding-bottom: 10px;
      }
      #makeImage {
        border-radius: 6px;
      }
      #editor-modifiers h5 {
        padding: 5pt 0;
        margin: 0;
      }
      #makeImage {
        flex: 0 0 70px;
        background: rgb(80, 0, 185);
        border: 2px solid rgb(40, 0, 78);
        color: rgb(255, 221, 255);
        width: 100%;
        height: 30pt;
      }
      #makeImage:hover {
        background: rgb(93, 0, 214);
      }
      .flex-container {
        display: flex;
      }
      .col-50 {
        flex: 50%;
      }
      .col-free {
        flex: 1;
      }
      .collapsible {
        cursor: pointer;
      }
      .collapsible-content {
        display: none;
        padding-left: 15px;
      }
      .collapsible-content h5 {
        padding: 5pt 0pt;
        margin: 0;
        font-size: 10pt;
      }
      .collapsible-handle {
        color: white;
        padding-right: 5px;
      }
      .panel-box {
        background: rgb(44, 45, 48);
        border: 1px solid rgb(47, 49, 53);
        border-radius: 7px;
        padding: 5px;
        margin-bottom: 15px;
        box-shadow: 0 4px 8px 0 rgba(0, 0, 0, 0.15),
          0 6px 20px 0 rgba(0, 0, 0, 0.15);
      }
      .panel-box h4 {
        margin: 0;
        padding: 2px 0;
      }
      .prompt-modifier-tag {
        border: 1px solid rgb(10, 0, 24);
        border-radius: 4px;
        padding: 0pt 3pt;
        margin-right: 2pt;
        cursor: pointer;
        display: inline;
        background: rgb(163, 163, 163);
        color: black;
        line-height: 25pt;
        float: left;
        font-size: 9pt;
      }
      .prompt-modifier-tag:hover {
        background: black;
        color: white;
      }
      #editor-modifiers-entries .prompt-modifier-tag {
        background: #110f0f;
        color: rgb(212, 212, 212);
        margin-bottom: 4pt;
        font-size: 10pt;
      }
      #editor-modifiers-entries .prompt-modifier-tag:hover {
        background: rgb(163, 163, 163);
        color: black;
      }
      #editor-modifiers .editor-modifiers-leaf {
        padding-top: 10pt;
        padding-bottom: 10pt;
      }
      #preview {
        margin-left: 20pt;
      }
      img {
        box-shadow: 0 4px 8px 0 rgba(0, 0, 0, 0.15),
          0 6px 20px 0 rgba(0, 0, 0, 0.15);
      }
      .line-separator {
        background: rgb(56, 56, 56);
        height: 1pt;
        margin: 15pt 0;
      }
      #editor-inputs-tags-container {
        margin-top: 5pt;
        display: none;
      }
      #server-status {
        float: right;
      }
      #server-status-color {
        width: 8pt;
        height: 8pt;
        border-radius: 4pt;
        background-color: rgb(128, 87, 0);
        /* background-color: rgb(197, 1, 1); */
        float: left;
        transform: translateY(15%);
      }
      #server-status-msg {
        color: rgb(128, 87, 0);
        padding-left: 2pt;
        font-size: 10pt;
      }
      #preview-prompt {
        font-size: 16pt;
        margin-bottom: 10pt;
      }
      #coffeeButton {
        height: 23px;
        transform: translateY(25%);
      }
    </style>

    <!-- ALL OF THIS IS HERE FOR REFERNCE FOR THE TIME BEING BUT CAN BE SAFLEY REMOVED -->

    <!-- All of the original DOM -->
    <div id="container" style="display: none">
      <div class="flex-container">
        <div id="editor" class="col-50">
          <div id="meta">
            <div id="server-status">
              <div id="server-status-color">&nbsp;</div>
              <span id="server-status-msg">Stable Diffusion is starting..</span>
            </div>
            <h1>
              Stable Diffusion UI
              <small>v2.1 <span id="updateBranchLabel"></span></small>
            </h1>
          </div>
          <div id="editor-inputs">
            <div id="editor-inputs-prompt" class="row">
              <label for="prompt">Prompt</label>
              <textarea id="prompt" class="col-free">
a photograph of an astronaut riding a horse</textarea
              >
            </div>

            <div id="editor-inputs-init-image" class="row">
              <label for="init_image"><b>Initial Image:</b> (optional) </label>
              <input id="init_image" name="init_image" type="file" /><br />
              <div
                id="init_image_preview_container"
                class="image_preview_container"
              >
                <img id="init_image_preview" src="" width="100" height="100" />
                <button id="init_image_clear" class="image_clear_btn">X</button>
              </div>
            </div>

            <div id="editor-inputs-tags-container" class="row">
              <label>Tags: <small>(click a tag to remove it)</small></label>
              <div id="editor-inputs-tags-list"></div>
            </div>

            <button id="makeImage">Make Image</button>
          </div>

          <div class="line-separator">&nbsp;</div>

          <div id="editor-settings" class="panel-box">
            <h4 class="collapsible">Advanced Settings</h4>
            <ul id="editor-settings-entries" class="collapsible-content">
              <li>
                <input
                  id="use_face_correction"
                  name="use_face_correction"
                  type="checkbox"
                  checked
                />
                <label for="use_face_correction"
                  >Fix incorrect faces and eyes (uses GFPGAN)</label
                >
              </li>
              <li>
                <input id="use_upscale" name="use_upscale" type="checkbox" />
                <label for="use_upscale"
                  >Upscale the image to 4x resolution using
                </label>
                <select id="upscale_model" name="upscale_model">
                  <option value="RealESRGAN_x4plus" selected>
                    RealESRGAN_x4plus
                  </option>
                  <option value="RealESRGAN_x4plus_anime_6B">
                    RealESRGAN_x4plus_anime_6B
                  </option>
                </select>
              </li>
              <li>
                <input
                  id="show_only_filtered_image"
                  name="show_only_filtered_image"
                  type="checkbox"
                  checked
                />
                <label for="show_only_filtered_image"
                  >Show only the corrected/upscaled image</label
                >
              </li>
              <br />
              <li>
                <label for="seed">Seed:</label>
                <input id="seed" name="seed" size="10" value="30000" />
                <input
                  id="random_seed"
                  name="random_seed"
                  type="checkbox"
                  checked
                />
                <label for="random_seed">Random Image</label>
              </li>
              <li>
                <label for="num_outputs_total">Number of images to make:</label>
                <input
                  id="num_outputs_total"
                  name="num_outputs_total"
                  value="1"
                  size="4"
                />
                <label for="num_outputs_parallel">Generate in parallel:</label>
                <input
                  id="num_outputs_parallel"
                  name="num_outputs_parallel"
                  value="1"
                  size="4"
                />
                (images at once)
              </li>
              <li>
                <label for="width">Width:</label>
                <select id="width" name="width" value="512">
                  <option value="128">128 (*)</option>
                  <option value="192">192</option>
                  <option value="256">256 (*)</option>
                  <option value="320">320</option>
                  <option value="384">384</option>
                  <option value="448">448</option>
                  <option value="512" selected>512 (*)</option>
                  <option value="576">576</option>
                  <option value="640">640</option>
                  <option value="704">704</option>
                  <option value="768">768 (*)</option>
                  <option value="832">832</option>
                  <option value="896">896</option>
                  <option value="960">960</option>
                  <option value="1024">1024 (*)</option>
                </select>
              </li>
              <li>
                <label for="height">Height:</label>
                <select id="height" name="height" value="512">
                  <option value="128">128 (*)</option>
                  <option value="192">192</option>
                  <option value="256">256 (*)</option>
                  <option value="320">320</option>
                  <option value="384">384</option>
                  <option value="448">448</option>
                  <option value="512" selected>512 (*)</option>
                  <option value="576">576</option>
                  <option value="640">640</option>
                  <option value="704">704</option>
                  <option value="768">768 (*)</option>
                  <option value="832">832</option>
                  <option value="896">896</option>
                  <option value="960">960</option>
                  <option value="1024">1024 (*)</option>
                </select>
              </li>
              <li>
                <label for="num_inference_steps"
                  >Number of inference steps:</label
                >
                <input
                  id="num_inference_steps"
                  name="num_inference_steps"
                  size="4"
                  value="50"
                />
              </li>
              <li>
                <label for="guidance_scale">Guidance Scale:</label>
                <input
                  id="guidance_scale"
                  name="guidance_scale"
                  value="75"
                  type="range"
                  min="10"
                  max="200"
                />
                <span id="guidance_scale_value"></span>
              </li>
              <li>
                <span id="prompt_strength_container"
                  ><label for="prompt_strength">Prompt Strength:</label>
                  <input
                    id="prompt_strength"
                    name="prompt_strength"
                    value="8"
                    type="range"
                    min="0"
                    max="10" />
                  <span id="prompt_strength_value"></span><br
                /></span>
              </li>
              <li>&nbsp;</li>
              <li>
                <input id="save_to_disk" name="save_to_disk" type="checkbox" />
                <label for="save_to_disk"
                  >Automatically save to
                  <input id="diskPath" name="diskPath" size="40" disabled
                /></label>
              </li>
              <li>
                <input
                  id="sound_toggle"
                  name="sound_toggle"
                  type="checkbox"
                  checked
                />
                <label for="sound_toggle">Play sound on task completion</label>
              </li>
              <li>
                <input id="turbo" name="turbo" type="checkbox" checked />
                <label for="turbo"
                  >Turbo mode (generates images faster, but uses an additional 1
                  GB of GPU memory)</label
                >
              </li>
              <li>
                <input id="use_cpu" name="use_cpu" type="checkbox" />
                <label for="use_cpu"
                  >Use CPU instead of GPU (warning: this will be *very*
                  slow)</label
                >
              </li>
              <li>
                <input
                  id="use_full_precision"
                  name="use_full_precision"
                  type="checkbox"
                />
                <label for="use_full_precision"
                  >Use full precision (for GPU-only. warning: this will consume
                  more VRAM)</label
                >
              </li>
              <!-- <li><input id="allow_nsfw" name="allow_nsfw" type="checkbox"> <label for="allow_nsfw">Allow NSFW Content (You confirm you are above 18 years of age)</label></li> -->
              <br />
              <li>
                <input
                  id="use_beta_channel"
                  name="use_beta_channel"
                  type="checkbox"
                />
                <label for="use_beta_channel"
                  >🔥Beta channel. Get the latest features immediately (but
                  could be less stable). Please restart the program after
                  changing this.</label
                >
              </li>
            </ul>
          </div>

          <div id="editor-modifiers" class="panel-box">
            <h4 class="collapsible">Image Modifiers (art styles, tags etc)</h4>
            <div
              id="editor-modifiers-entries"
              class="collapsible-content"
            ></div>
          </div>
        </div>

        <div id="preview" class="col-50">
          <div id="preview-prompt">
            Type a prompt and press the "Make Image" button.<br /><br />You can
            set an "Initial Image" if you want to guide the AI.<br /><br />You
            can also add modifiers like "Realistic", "Pencil Sketch",
            "ArtStation" etc by browsing through the "Image Modifiers" section
            and selecting the desired modifiers.<br /><br />Click "Advanced
            Settings" for additional settings like seed, image size, number of
            images to generate etc.<br /><br />Enjoy! :)
          </div>

          <div id="outputMsg"></div>
          <div id="current-images" class="img-preview"></div>
        </div>
      </div>

      <div class="line-separator">&nbsp;</div>

      <div id="footer" class="panel-box">
        <p>
          If you found this project useful and want to help keep it alive,
          please
          <a href="https://ko-fi.com/cmdr2_stablediffusion_ui" target="_blank"
            ><img src="media/kofi.png" id="coffeeButton"
          /></a>
          to help cover the cost of development and maintenance! Thank you for
          your support!
        </p>
        <p>
          Please feel free to join the
          <a href="https://discord.com/invite/u9yhsFmEkB" target="_blank"
            >discord community</a
          >
          or
          <a
            href="https://github.com/cmdr2/stable-diffusion-ui/issues"
            target="_blank"
            >file an issue</a
          >
          if you have any problems or suggestions in using this interface.
        </p>
        <div id="footer-legal">
          <p>
            <b>Disclaimer:</b> The authors of this project are not responsible
            for any content generated using this interface.
          </p>
          <p>
            This license of this software forbids you from sharing any content
            that violates any laws, produce any harm to a person, disseminate
            any personal information that would be meant for harm, <br />spread
            misinformation and target vulnerable groups. For the full list of
            restrictions please read
            <a
              href="https://github.com/cmdr2/stable-diffusion-ui/blob/main/LICENSE"
              target="_blank"
              >the license</a
            >.
          </p>
          <p>
            By using this software, you consent to the terms and conditions of
            the license.
          </p>
        </div>
      </div>
    </div>

    <!-- All of the original script -->
    <script>
      const SOUND_ENABLED_KEY = "soundEnabled";
      const SAVE_TO_DISK_KEY = "saveToDisk";
      const USE_CPU_KEY = "useCPU";
      const USE_FULL_PRECISION_KEY = "useFullPrecision";
      const USE_TURBO_MODE_KEY = "useTurboMode";
      const DISK_PATH_KEY = "diskPath";
      const ADVANCED_PANEL_OPEN_KEY = "advancedPanelOpen";
      const MODIFIERS_PANEL_OPEN_KEY = "modifiersPanelOpen";
      const USE_FACE_CORRECTION_KEY = "useFaceCorrection";
      const USE_UPSCALING_KEY = "useUpscaling";
      const SHOW_ONLY_FILTERED_IMAGE_KEY = "showOnlyFilteredImage";
      const HEALTH_PING_INTERVAL = 5; // seconds
      const MAX_INIT_IMAGE_DIMENSION = 768;

      const IMAGE_REGEX = new RegExp("data:image/[A-Za-z]+;base64");

      let promptField = document.querySelector("#prompt");
      let numOutputsTotalField = document.querySelector("#num_outputs_total");
      let numOutputsParallelField = document.querySelector(
        "#num_outputs_parallel"
      );
      let numInferenceStepsField = document.querySelector(
        "#num_inference_steps"
      );
      let guidanceScaleField = document.querySelector("#guidance_scale");
      let guidanceScaleValueLabel = document.querySelector(
        "#guidance_scale_value"
      );
      let randomSeedField = document.querySelector("#random_seed");
      let seedField = document.querySelector("#seed");
      let widthField = document.querySelector("#width");
      let heightField = document.querySelector("#height");
      let initImageSelector = document.querySelector("#init_image");
      let initImagePreview = document.querySelector("#init_image_preview");
      // let maskImageSelector = document.querySelector("#mask")
      // let maskImagePreview = document.querySelector("#mask_preview")
      let turboField = document.querySelector("#turbo");
      let useCPUField = document.querySelector("#use_cpu");
      let useFullPrecisionField = document.querySelector("#use_full_precision");
      let saveToDiskField = document.querySelector("#save_to_disk");
      let diskPathField = document.querySelector("#diskPath");
      // let allowNSFWField = document.querySelector("#allow_nsfw")
      let useBetaChannelField = document.querySelector("#use_beta_channel");
      let promptStrengthField = document.querySelector("#prompt_strength");
      let promptStrengthValueLabel = document.querySelector(
        "#prompt_strength_value"
      );
      let useFaceCorrectionField = document.querySelector(
        "#use_face_correction"
      );
      let useUpscalingField = document.querySelector("#use_upscale");
      let upscaleModelField = document.querySelector("#upscale_model");
      let showOnlyFilteredImageField = document.querySelector(
        "#show_only_filtered_image"
      );
      let updateBranchLabel = document.querySelector("#updateBranchLabel");

      let makeImageBtn = document.querySelector("#makeImage");

      let imagesContainer = document.querySelector("#current-images");
      let initImagePreviewContainer = document.querySelector(
        "#init_image_preview_container"
      );
      let initImageClearBtn = document.querySelector("#init_image_clear");
      let promptStrengthContainer = document.querySelector(
        "#prompt_strength_container"
      );

      // let maskSetting = document.querySelector('#mask_setting')
      // let maskImagePreviewContainer = document.querySelector('#mask_preview_container')
      // let maskImageClearBtn = document.querySelector('#mask_clear')

      let editorModifierEntries = document.querySelector(
        "#editor-modifiers-entries"
      );
      let editorModifierTagsList = document.querySelector(
        "#editor-inputs-tags-list"
      );
      let editorTagsContainer = document.querySelector(
        "#editor-inputs-tags-container"
      );

      let previewPrompt = document.querySelector("#preview-prompt");

      let showConfigToggle = document.querySelector("#configToggleBtn");
      // let configBox = document.querySelector('#config')
      let outputMsg = document.querySelector("#outputMsg");

      let soundToggle = document.querySelector("#sound_toggle");

      let serverStatusColor = document.querySelector("#server-status-color");
      let serverStatusMsg = document.querySelector("#server-status-msg");

      let advancedPanelHandle = document.querySelector(
        "#editor-settings .collapsible"
      );
      let modifiersPanelHandle = document.querySelector(
        "#editor-modifiers .collapsible"
      );

      let serverStatus = "offline";
      let activeTags = [];
      let lastPromptUsed = "";

      function getLocalStorageItem(key, fallback) {
        let item = localStorage.getItem(key);
        if (item === null) {
          return fallback;
        }

        return item;
      }

      function getLocalStorageBoolItem(key, fallback) {
        let item = localStorage.getItem(key);
        if (item === null) {
          return fallback;
        }

        return item === "true" ? true : false;
      }

      function handleBoolSettingChange(key) {
        return function (e) {
          localStorage.setItem(key, e.target.checked.toString());
        };
      }

      function handleStringSettingChange(key) {
        return function (e) {
          localStorage.setItem(key, e.target.value.toString());
        };
      }

      function isSoundEnabled() {
        return getLocalStorageBoolItem(SOUND_ENABLED_KEY, true);
      }

      function isFaceCorrectionEnabled() {
        return getLocalStorageBoolItem(USE_FACE_CORRECTION_KEY, false);
      }

      function isUpscalingEnabled() {
        return getLocalStorageBoolItem(USE_UPSCALING_KEY, false);
      }

      function isShowOnlyFilteredImageEnabled() {
        return getLocalStorageBoolItem(SHOW_ONLY_FILTERED_IMAGE_KEY, true);
      }

      function isSaveToDiskEnabled() {
        return getLocalStorageBoolItem(SAVE_TO_DISK_KEY, false);
      }

      function isUseCPUEnabled() {
        return getLocalStorageBoolItem(USE_CPU_KEY, false);
      }

      function isUseFullPrecisionEnabled() {
        return getLocalStorageBoolItem(USE_FULL_PRECISION_KEY, false);
      }

      function isUseTurboModeEnabled() {
        return getLocalStorageBoolItem(USE_TURBO_MODE_KEY, true);
      }

      function getSavedDiskPath() {
        return getLocalStorageItem(DISK_PATH_KEY, "");
      }

      function isAdvancedPanelOpenEnabled() {
        return getLocalStorageBoolItem(ADVANCED_PANEL_OPEN_KEY, false);
      }

      function isModifiersPanelOpenEnabled() {
        return getLocalStorageBoolItem(MODIFIERS_PANEL_OPEN_KEY, false);
      }

      function setStatus(statusType, msg, msgType) {
        if (statusType !== "server") {
          return;
        }

        if (msgType == "error") {
          // msg = '<span style="color: red">' + msg + '<span>'
          serverStatusColor.style.backgroundColor = "red";
          serverStatusMsg.style.color = "red";
          serverStatusMsg.innerHTML = "Stable Diffusion has stopped";
        } else if (msgType == "success") {
          // msg = '<span style="color: green">' + msg + '<span>'
          serverStatusColor.style.backgroundColor = "green";
          serverStatusMsg.style.color = "green";
          serverStatusMsg.innerHTML = "Stable Diffusion is ready";
          serverStatus = "online";
        }
      }

      function logMsg(msg, level) {
        if (level === "error") {
          outputMsg.innerHTML =
            '<span style="color: red">Error: ' + msg + "</span>";
        } else if (level === "warn") {
          outputMsg.innerHTML =
            '<span style="color: orange">Warning: ' + msg + "</span>";
        } else {
          outputMsg.innerHTML = msg;
        }

        console.log(level, msg);
      }

      function logError(msg, res) {
        logMsg(msg, "error");

        console.log("request error", res);
        setStatus("request", "error", "error");
      }

      function playSound() {
        const audio = new Audio("/media/ding.mp3");
        audio.volume = 0.2;
        audio.play();
      }

      async function healthCheck() {
        try {
          let res = await fetch("/ping");
          res = await res.json();

          if (res[0] == "OK") {
            setStatus("server", "online", "success");
          } else {
            setStatus("server", "offline", "error");
          }
        } catch (e) {
          setStatus("server", "offline", "error");
        }
      }

      // makes a single image. don't call this directly, use makeImage() instead
      async function doMakeImage(reqBody) {
        let res = "";
        let seed = reqBody["seed"];

        try {
          res = await fetch("/image", {
            method: "POST",
            headers: {
              "Content-Type": "application/json",
            },
            body: JSON.stringify(reqBody),
          });

          if (res.status != 200) {
            if (serverStatus === "online") {
              logError(
                "Stable Diffusion had an error: " +
                  (await res.text()) +
                  ". This happens sometimes. Maybe modify the prompt or seed a little bit?",
                res
              );
            } else {
              logError(
                "Stable Diffusion is still starting up, please wait. If this goes on beyond a few minutes, Stable Diffusion has probably crashed.",
                res
              );
            }
            res = undefined;
          } else {
            res = await res.json();

            if (res.status !== "succeeded") {
              let msg = "";
              if (res.detail !== undefined) {
                msg = res.detail;

                if (msg.toLowerCase().includes("out of memory")) {
                  msg += `<br/><br/>
                                            <b>Suggestions</b>:
                                            <br/>
                                            1. If you have set an initial image, please try reducing its dimension to ${MAX_INIT_IMAGE_DIMENSION}x${MAX_INIT_IMAGE_DIMENSION} or smaller.<br/>
                                            2. Try disabling the '<em>Turbo mode</em>' under '<em>Advanced Settings</em>'.<br/>
                                            3. Try generating a smaller image.<br/>`;
                }
              } else {
                msg = res;
              }
              logError(msg, res);
              res = undefined;
            }
          }
        } catch (e) {
          console.log("request error", e);
          setStatus("request", "error", "error");
        }

        if (!res) {
          return false;
        }

        lastPromptUsed = reqBody["prompt"];

        for (let idx in res.output) {
          let imgBody = "";

          try {
            let imgData = res.output[idx];
            imgBody = imgData.data;
          } catch (e) {
            console.log(imgBody);
            setStatus("request", "invalid image", "error");
            continue;
          }

          let imgItem = document.createElement("div");
          imgItem.className = "imgItem";

          let img = document.createElement("img");
          img.width = parseInt(reqBody.width);
          img.height = parseInt(reqBody.height);
          img.src = imgBody;

          let imgItemInfo = document.createElement("span");
          imgItemInfo.className = "imgItemInfo";

          let imgSeedLabel = document.createElement("span");
          imgSeedLabel.className = "imgSeedLabel";
          imgSeedLabel.innerHTML = "Seed: " + seed;

          let imgUseBtn = document.createElement("button");
          imgUseBtn.className = "imgUseBtn";
          imgUseBtn.innerHTML = "Use as Input";

          let imgSaveBtn = document.createElement("button");
          imgSaveBtn.className = "imgSaveBtn";
          imgSaveBtn.innerHTML = "Download";

          imgItem.appendChild(img);
          imgItem.appendChild(imgItemInfo);
          imgItemInfo.appendChild(imgSeedLabel);
          imgItemInfo.appendChild(imgUseBtn);
          imgItemInfo.appendChild(imgSaveBtn);
          imagesContainer.appendChild(imgItem);

          imgUseBtn.addEventListener("click", function () {
            initImageSelector.value = null;
            initImagePreview.src = imgBody;

            initImagePreviewContainer.style.display = "block";
            promptStrengthContainer.style.display = "block";

            // maskSetting.style.display = 'block'

            randomSeedField.checked = false;
            seedField.value = seed;
            seedField.disabled = false;
          });

          imgSaveBtn.addEventListener("click", function () {
            let imgDownload = document.createElement("a");
            imgDownload.download = createFileName();
            imgDownload.href = imgBody;
            imgDownload.click();
          });

          imgItem.addEventListener("mouseenter", function () {
            imgItemInfo.style.opacity = 1;
          });

          imgItem.addEventListener("mouseleave", function () {
            imgItemInfo.style.opacity = 0.5;
          });
        }

        return true;
      }

      function validateInput() {
        let width = parseInt(widthField.value);
        let height = parseInt(heightField.value);

        if (IMAGE_REGEX.test(initImagePreview.src)) {
          if (
            initImagePreview.naturalWidth > MAX_INIT_IMAGE_DIMENSION ||
            initImagePreview.naturalHeight > MAX_INIT_IMAGE_DIMENSION
          ) {
            return {
              isValid: false,
              warning: `The dimensions of your initial image are very large, and can cause 'Out of Memory' errors! Please ensure that its dimensions are equal (or smaller) than the desired output image.
                                <br/><br/>
                                Your initial image size is ${initImagePreview.naturalWidth}x${initImagePreview.naturalHeight} pixels. Please try to keep it smaller than ${MAX_INIT_IMAGE_DIMENSION}x${MAX_INIT_IMAGE_DIMENSION}.`,
            };
          }
        }

        return { isValid: true };
      }

      async function makeImage() {
        if (serverStatus !== "online") {
          logError("The server is still starting up..");
          return;
        }

        let validation = validateInput();
        if (validation["isValid"]) {
          outputMsg.innerHTML = "Fetching..";
        } else {
          if (validation["error"]) {
            logError(validation["error"]);
            return;
          } else if (validation["warning"]) {
            logMsg(validation["warning"], "warn");
          }
        }

        setStatus("request", "fetching..");

        makeImageBtn.innerHTML = "Processing..";
        makeImageBtn.disabled = true;

        let seed = randomSeedField.checked
          ? Math.floor(Math.random() * 10000000)
          : parseInt(seedField.value);
        let numOutputsTotal = parseInt(numOutputsTotalField.value);
        let numOutputsParallel = parseInt(numOutputsParallelField.value);
        let batchCount = Math.ceil(numOutputsTotal / numOutputsParallel);
        let batchSize = numOutputsParallel;

        let prompt = promptField.value;
        if (activeTags.length > 0) {
          let promptTags = activeTags.join(", ");
          prompt += ", " + promptTags;
        }

        previewPrompt.innerHTML = prompt;

        let reqBody = {
          prompt: prompt,
          num_outputs: batchSize,
          num_inference_steps: numInferenceStepsField.value,
          guidance_scale: parseInt(guidanceScaleField.value) / 10,
          width: widthField.value,
          height: heightField.value,
          // allow_nsfw: allowNSFWField.checked,
          turbo: turboField.checked,
          use_cpu: useCPUField.checked,
          use_full_precision: useFullPrecisionField.checked,
        };

        if (IMAGE_REGEX.test(initImagePreview.src)) {
          reqBody["init_image"] = initImagePreview.src;
          reqBody["prompt_strength"] = parseInt(promptStrengthField.value) / 10;

          // if (IMAGE_REGEX.test(maskImagePreview.src)) {
          //     reqBody['mask'] = maskImagePreview.src
          // }
        }

        if (saveToDiskField.checked && diskPathField.value.trim() !== "") {
          reqBody["save_to_disk_path"] = diskPathField.value.trim();
        }

        if (useFaceCorrectionField.checked) {
          reqBody["use_face_correction"] = "GFPGANv1.3";
        }

        if (useUpscalingField.checked) {
          reqBody["use_upscale"] = upscaleModelField.value;
        }

        if (
          showOnlyFilteredImageField.checked &&
          (useUpscalingField.checked || useFaceCorrectionField.checked)
        ) {
          reqBody["show_only_filtered_image"] =
            showOnlyFilteredImageField.checked;
        }

        let time = new Date().getTime();
        imagesContainer.innerHTML = "";

        let successCount = 0;

        for (let i = 0; i < batchCount; i++) {
          reqBody["seed"] = seed + i * batchSize;

          let success = await doMakeImage(reqBody);

          if (success) {
            outputMsg.innerHTML =
              "Processed batch " + (i + 1) + "/" + batchCount;
            successCount++;
          }
        }

        makeImageBtn.innerHTML = "Make Image";
        makeImageBtn.disabled = false;

        if (isSoundEnabled()) {
          playSound();
        }

        time = new Date().getTime() - time;
        time /= 1000;

        if (successCount === batchCount) {
          outputMsg.innerHTML =
            "Processed " + numOutputsTotal + " images in " + time + " seconds";

          setStatus("request", "done", "success");
        }

        if (randomSeedField.checked) {
          seedField.value = seed;
        }
      }

      // create a file name with embedded prompt and metadata
      // for easier cateloging and comparison
      function createFileName() {
        // Most important information is the prompt
        let underscoreName = lastPromptUsed.replace(/[^a-zA-Z0-9]/g, "_");
        underscoreName = underscoreName.substring(0, 100);
        const seed = seedField.value;
        const steps = numInferenceStepsField.value;
        const guidance = guidanceScaleField.value;

        // name and the top level metadata
        let fileName = `${underscoreName}_Seed-${seed}_Steps-${steps}_Guidance-${guidance}`;

        // add the tags
        // let tags = [];
        // let tagString = '';
        // document.querySelectorAll(modifyTagsSelector).forEach(function(tag) {
        //     tags.push(tag.innerHTML);
        // })

        // join the tags with a pipe
        // if (activeTags.length > 0) {
        //     tagString = '_Tags-';
        //     tagString += tags.join('|');
        // }

        // // append empty or populated tags
        // fileName += `${tagString}`;

        // add the file extension
        fileName += `.png`;

        return fileName;
      }

      soundToggle.addEventListener(
        "click",
        handleBoolSettingChange(SOUND_ENABLED_KEY)
      );
      soundToggle.checked = isSoundEnabled();

      saveToDiskField.checked = isSaveToDiskEnabled();
      diskPathField.disabled = !saveToDiskField.checked;

      useFaceCorrectionField.addEventListener(
        "click",
        handleBoolSettingChange(USE_FACE_CORRECTION_KEY)
      );
      useFaceCorrectionField.checked = isFaceCorrectionEnabled();

      useUpscalingField.checked = isUpscalingEnabled();
      upscaleModelField.disabled = !useUpscalingField.checked;

      showOnlyFilteredImageField.addEventListener(
        "click",
        handleBoolSettingChange(SHOW_ONLY_FILTERED_IMAGE_KEY)
      );
      showOnlyFilteredImageField.checked = isShowOnlyFilteredImageEnabled();

      useCPUField.addEventListener(
        "click",
        handleBoolSettingChange(USE_CPU_KEY)
      );
      useCPUField.checked = isUseCPUEnabled();

      useFullPrecisionField.addEventListener(
        "click",
        handleBoolSettingChange(USE_FULL_PRECISION_KEY)
      );
      useFullPrecisionField.checked = isUseFullPrecisionEnabled();

      turboField.addEventListener(
        "click",
        handleBoolSettingChange(USE_TURBO_MODE_KEY)
      );
      turboField.checked = isUseTurboModeEnabled();

      diskPathField.addEventListener(
        "change",
        handleStringSettingChange(DISK_PATH_KEY)
      );

      saveToDiskField.addEventListener("click", function (e) {
        diskPathField.disabled = !this.checked;
        handleBoolSettingChange(SAVE_TO_DISK_KEY)(e);
      });

      useUpscalingField.addEventListener("click", function (e) {
        upscaleModelField.disabled = !this.checked;
        handleBoolSettingChange(USE_UPSCALING_KEY)(e);
      });

      function setPanelOpen(panelHandle) {
        let panelContents = panelHandle.nextElementSibling;
        panelHandle.classList.add("active");
        panelContents.style.display = "block";
      }

      if (isAdvancedPanelOpenEnabled()) {
        setPanelOpen(advancedPanelHandle);
      }

      if (isModifiersPanelOpenEnabled()) {
        setPanelOpen(modifiersPanelHandle);
      }

      makeImageBtn.addEventListener("click", makeImage);

      function updateGuidanceScale() {
        guidanceScaleValueLabel.innerHTML = guidanceScaleField.value / 10;
      }

      guidanceScaleField.addEventListener("input", updateGuidanceScale);
      updateGuidanceScale();

      function updatePromptStrength() {
        promptStrengthValueLabel.innerHTML = promptStrengthField.value / 10;
      }

      promptStrengthField.addEventListener("input", updatePromptStrength);
      updatePromptStrength();

      useBetaChannelField.addEventListener("click", async function (e) {
        if (serverStatus !== "online") {
          logError("The server is still starting up..");
          alert("The server is still starting up..");
          e.preventDefault();
          return false;
        }

        let updateBranch = this.checked ? "beta" : "main";

        try {
          let res = await fetch("/app_config", {
            method: "POST",
            headers: {
              "Content-Type": "application/json",
            },
            body: JSON.stringify({
              update_branch: updateBranch,
            }),
          });
          res = await res.json();

          console.log("set config status response", res);
        } catch (e) {
          console.log("set config status error", e);
        }
      });

      async function getAppConfig() {
        try {
          let res = await fetch("/app_config");
          config = await res.json();

          if (config.update_branch === "beta") {
            useBetaChannelField.checked = true;
            updateBranchLabel.innerHTML = "(beta)";
          }

          console.log("get config status response", config);
        } catch (e) {
          console.log("get config status error", e);
        }
      }

      function checkRandomSeed() {
        if (randomSeedField.checked) {
          seedField.disabled = true;
          seedField.value = "0";
        } else {
          seedField.disabled = false;
        }
      }
      randomSeedField.addEventListener("input", checkRandomSeed);
      checkRandomSeed();

      function showInitImagePreview() {
        if (initImageSelector.files.length === 0) {
          initImagePreviewContainer.style.display = "none";
          promptStrengthContainer.style.display = "none";
          // maskSetting.style.display = 'none'
          return;
        }

        let reader = new FileReader();
        let file = initImageSelector.files[0];

        reader.addEventListener("load", function () {
          // console.log(file.name, reader.result)
          initImagePreview.src = reader.result;
          initImagePreviewContainer.style.display = "block";
          promptStrengthContainer.style.display = "block";

          // maskSetting.style.display = 'block'
        });

        if (file) {
          reader.readAsDataURL(file);
        }
      }
      initImageSelector.addEventListener("change", showInitImagePreview);
      showInitImagePreview();

      initImageClearBtn.addEventListener("click", function () {
        initImageSelector.value = null;
        // maskImageSelector.value = null

        initImagePreview.src = "";
        // maskImagePreview.src = ''

        initImagePreviewContainer.style.display = "none";
        // maskImagePreviewContainer.style.display = 'none'

        // maskSetting.style.display = 'none'

        promptStrengthContainer.style.display = "none";
      });

      // function showMaskImagePreview() {
      //     if (maskImageSelector.files.length === 0) {
      //         maskImagePreviewContainer.style.display = 'none'
      //         return
      //     }

      //     let reader = new FileReader()
      //     let file = maskImageSelector.files[0]

      //     reader.addEventListener('load', function() {
      //         maskImagePreview.src = reader.result
      //         maskImagePreviewContainer.style.display = 'block'
      //     })

      //     if (file) {
      //         reader.readAsDataURL(file)
      //     }
      // }
      // maskImageSelector.addEventListener('change', showMaskImagePreview)
      // showMaskImagePreview()

      // maskImageClearBtn.addEventListener('click', function() {
      //     maskImageSelector.value = null
      //     maskImagePreview.src = ''
      //     maskImagePreviewContainer.style.display = 'none'
      // })
    </script>
    <script>
      function createCollapsibles(node) {
        if (!node) {
          node = document;
        }

        let collapsibles = node.querySelectorAll(".collapsible");
        collapsibles.forEach(function (c) {
          let handle = document.createElement("span");
          handle.className = "collapsible-handle";

          if (c.className.indexOf("active") !== -1) {
            handle.innerHTML = "&#x2796;"; // minus
          } else {
            handle.innerHTML = "&#x2795;"; // plus
          }
          c.insertBefore(handle, c.firstChild);

          c.addEventListener("click", function () {
            this.classList.toggle("active");
            let content = this.nextElementSibling;
            if (content.style.display === "block") {
              content.style.display = "none";
              handle.innerHTML = "&#x2795;"; // plus
            } else {
              content.style.display = "block";
              handle.innerHTML = "&#x2796;"; // minus
            }

            if (this == advancedPanelHandle) {
              let state = content.style.display === "block" ? "true" : "false";
              localStorage.setItem(ADVANCED_PANEL_OPEN_KEY, state);
            } else if (this == modifiersPanelHandle) {
              let state = content.style.display === "block" ? "true" : "false";
              localStorage.setItem(MODIFIERS_PANEL_OPEN_KEY, state);
            }
          });
        });
      }
      createCollapsibles();

      function refreshTagsList() {
        editorModifierTagsList.innerHTML = "";

        if (activeTags.length == 0) {
          editorTagsContainer.style.display = "none";
          return;
        } else {
          editorTagsContainer.style.display = "block";
        }

        activeTags.forEach(function (tag) {
          let el = document.createElement("div");
          el.className = "prompt-modifier-tag";
          el.innerHTML = tag;

          editorModifierTagsList.appendChild(el);

          el.addEventListener("click", function () {
            let idx = activeTags.indexOf(tag);
            if (idx !== -1) {
              activeTags.splice(idx, 1);
              refreshTagsList();
            }
          });
        });

        let brk = document.createElement("br");
        brk.style.clear = "both";
        editorModifierTagsList.appendChild(brk);
      }

      async function getDiskPath() {
        try {
          let diskPath = getSavedDiskPath();

          if (diskPath !== "") {
            diskPathField.value = diskPath;
            return;
          }

          let res = await fetch("/output_dir");
          if (res.status === 200) {
            res = await res.json();
            res = res[0];

            document.querySelector("#diskPath").value = res;
          }
        } catch (e) {
          console.log("error fetching output dir path", e);
        }
      }

      async function loadModifiers() {
        try {
          let res = await fetch("/modifiers.json");
          if (res.status === 200) {
            res = await res.json();

            res.forEach(function (m) {
              let title = m[0];
              let modifiers = m[1];

              let titleEl = document.createElement("h5");
              titleEl.className = "collapsible";
              titleEl.innerHTML = title;

              let modifiersEl = document.createElement("div");
              modifiersEl.classList.add(
                "collapsible-content",
                "editor-modifiers-leaf"
              );

              modifiers.forEach(function (modifier) {
                let tagEl = document.createElement("div");
                tagEl.className = "prompt-modifier-tag";
                tagEl.innerHTML = modifier;

                modifiersEl.appendChild(tagEl);

                tagEl.addEventListener("click", function () {
                  if (activeTags.includes(modifier)) {
                    return;
                  }

                  activeTags.push(modifier);
                  refreshTagsList();
                });
              });
              let brk = document.createElement("br");
              brk.style.clear = "both";
              modifiersEl.appendChild(brk);

              let e = document.createElement("div");
              e.appendChild(titleEl);
              e.appendChild(modifiersEl);

              editorModifierEntries.appendChild(e);
            });

            createCollapsibles(editorModifierEntries);
          }
        } catch (e) {
          console.log("error fetching modifiers", e);
        }
      }

      /* TURNED OFF */
      async function init() {
        // await loadModifiers()
        // await getDiskPath()
        // await getAppConfig()
        // setInterval(healthCheck, HEALTH_PING_INTERVAL * 1000)
        // healthCheck()
        // playSound()
      }

      init();
    </script>
  </body>
</html>