// 履歴の最大保持数
const MaxHistory = 10;
// プロンプト履歴
let historyBox = (function () {
  let _historyBox = [];

  return {
    push: function (prompt) {
      if (prompt == _historyBox[_historyBox.length - 1]) return;
      _historyBox.push(prompt);
      if (MaxHistory < _historyBox.length) {
        _historyBox.shift();
      }
    },
    pop: function () {
      let prePrompt = _historyBox.pop();
      return prePrompt;
    },
  };
})();
// ネガティブプロンプト履歴
let nhistoryBox = (function () {
  let _historyBox = [];

  return {
    push: function (prompt) {
      if (prompt == _historyBox[_historyBox.length - 1]) return;
      _historyBox.push(prompt);
      if (MaxHistory < _historyBox.length) {
        _historyBox.shift();
      }
    },
    pop: function () {
      let prePrompt = _historyBox.pop();
      return prePrompt;
    },
  };
})();

function convert(input) {
    const re_attention = /\{|\[|\}|\]|[^\{\}\[\]]+/gmu;
    let text = input.replaceAll("(", "\\(").replaceAll(")", "\\)");

    let res = [];

    let curley_brackets = [];
    let square_brackets = [];

    const curley_bracket_multiplier = 1.05;
    const square_bracket_multiplier = 1 / 1.05;

    function multiply_range(start_position, multiplier) {
        for (let pos = start_position; pos < res.length; pos++) {
            res[pos][1] *= multiplier;
        }
    }

    for (const match of text.matchAll(re_attention)) {
        let word = match[0];

        if (word == '{') {
            curley_brackets.push(res.length);
        } else if (word == '[') {
            square_brackets.push(res.length);
        } else if (word == '}' && curley_brackets.length > 0) {
            multiply_range(curley_brackets.pop(), curley_bracket_multiplier);
        } else if (word == ']' && square_brackets.length > 0) {
            multiply_range(square_brackets.pop(), square_bracket_multiplier);
        } else {
            res.push([word, 1.0]);
        }
    }

    for (const pos of curley_brackets) {
        multiply_range(pos, curley_bracket_multiplier);
    }

    for (const pos of square_brackets) {
        multiply_range(pos, square_bracket_multiplier);
    }

    if (res.length == 0) {
        res = [["", 1.0]];
    }

    // console.log(res);
    // merge runs of identical weights
    let i = 0;
    while (i+1 < res.length) {
        // console.log("test:" + res[i] + " : " + res[i+1])
        if (res[i][1] == res[i+1][1]) {
            res[i][0] = res[i][0] + res[i+1][0];
            // console.log("splicing:" + res[i+1]);
            res.splice(i+1, 1);
        } else {
            i += 1;
        }
    }
    // console.log(res);

    let result = "";
    for (let i = 0; i < res.length; i++) {
        if (res[i][1] == 1.0) {
            result += res[i][0];
        } else {
            result += "(" + res[i][0] + ":" + res[i][1].toString() + ")";
        }
    }
    return result;
}

function onClickConvert() {
  const default_prompt = "masterpiece, best quality,\n";
  const default_negative = "lowres, bad anatomy, bad hands, text, error, missing fingers, extra digit, fewer digits, cropped, worst quality, low quality, normal quality, jpeg artifacts, signature, watermark, username, blurry, artist name";

  let result = "";
  let prompt = gradioApp().querySelector("#txt2img_prompt > label > textarea");
  historyBox.push(prompt.value);
  result = convert(prompt.value);
  if (result.length != 0) {
    if (result.match(/^masterpiece, best quality,/) == null) {
        result = default_prompt + result;
    }
  }
  prompt.value = result;
  prompt.dispatchEvent(new Event("input", { bubbles: true }));

  result = "";
  let negativep = gradioApp().querySelector("#txt2img_neg_prompt > label > textarea");
  nhistoryBox.push(negativep.value);
  result = convert(negativep.value);
  if (result.length != 0) {
    if (result.match(/^lowres,/) == null) {
        result = default_negative + ",\n" + result;
    }
  } else {
    result = default_negative;
  }
  negativep.value = result;
  negativep.dispatchEvent(new Event("input", { bubbles: true }));
}

function onClickGenerate() {
  let prompt = gradioApp().querySelector("#txt2img_prompt > label > textarea");
  historyBox.push(prompt.value);
  let negativep = gradioApp().querySelector("#txt2img_neg_prompt > label > textarea");
  nhistoryBox.push(negativep.value);
}

function onClickUndo() {
  let prompt = gradioApp().querySelector("#txt2img_prompt > label > textarea");
  let prePrompt = historyBox.pop();

  if (!prePrompt) {
    prompt.value = "";
  } else {
    prompt.value = prePrompt;
  }
  prompt.dispatchEvent(new Event("input", { bubbles: true }));

  let negativep = gradioApp().querySelector("#txt2img_neg_prompt > label > textarea");
  let preNegativep = nhistoryBox.pop();

  if (!preNegativep) {
    negativep.value = "";
  } else {
    negativep.value = preNegativep;
  }
  negativep.dispatchEvent(new Event("input", { bubbles: true }));
}

onUiUpdate(function () {
  let parentArea = gradioApp().querySelector("#toprow");
  let generateBtn = gradioApp().querySelector("#txt2img_generate");
  let beforeElement = gradioApp().querySelector("#roll_col");

  if (parentArea == null || generateBtn == null || beforeElement == null) return;
  if (gradioApp().querySelector("#nai2local") != null) return;

  generateBtn.addEventListener("click", onClickGenerate);

  let nai2LocalArea = document.createElement("div");
  nai2LocalArea.id = "nai2local";
  nai2LocalArea.className = "overflow-hidden flex flex-col relative col gap-4";
  nai2LocalArea.style =
    "min-width: min(110px, 100%); max-width: 120px; flex-grow: 1; padding: 0.4em; padding-bottom: 0.8em;";

  let convertBtn = document.createElement("button");
  convertBtn.id = "nai2localconvert";
  convertBtn.type = "button";
  convertBtn.innerHTML = "変換";
  convertBtn.className = "gr-button gr-button-lg gr-button-secondary";
  convertBtn.style =
    "padding-left: 0.25em; padding-right: 0.25em; margin: 0.1em 0;max-height: 2em; max-width: 3em";
  convertBtn.addEventListener("click", onClickConvert);
  nai2LocalArea.appendChild(convertBtn);

  let undoBtn = document.createElement("button");
  undoBtn.id = "nai2localUndo";
  undoBtn.type = "button";
  undoBtn.innerHTML = "←";
  undoBtn.className = "gr-button gr-button-lg gr-button-secondary";
  undoBtn.style =
    "padding-left: 0.25em; padding-right: 0.25em; margin: 0.1em 0;max-height: 2em; max-width: 3em";
  undoBtn.addEventListener("click", onClickUndo);
  nai2LocalArea.appendChild(undoBtn);

  parentArea.insertBefore(nai2LocalArea, beforeElement.nextSibling);
});
Edit
Pub: 23 Oct 2022 04:09 UTC
Edit: 23 Oct 2022 04:40 UTC
Views: 1882