// ==UserScript== // @name BIC EDIT - https://rentry.org/bicedit // @version 1.6 // @match https://copilot.microsoft.com/images/create* // @match https://www.bing.com/images/create* // @grant GM.setValue // @grant GM.getValue // @grant unsafeWindow // @grant GM.xmlHttpRequest // @run-at document-idle // ==/UserScript== // Experimental: Increases prompt max length from 480 characters to 850 but things might break // Check rentry page for more info var useExperimentalLargerPrompts = false; // Whether script should automatically write your last submitted prompt to the prompt input // Prompts are saved when you click create button // If you are viewing a generation, script will write the prompt that was used for the current generation instead // Requires useExperimentalLargerPrompts to be enabled var rememberMyLastSubmittedPrompt = true; // Whether script should force low-priority mode for every generation. See: rentry.co/bicedit#about-saving-credits var saveCredits = true; // Hide Related content from the web panel from the image viewer var hideInsightsPanel = true; // Suggestions for the style input, edit these to match your preferences. This is controlled by the browser and depending on your environment (E.G: phone) this might not work as intended // "item1", // "item2", // Set enableSuggestions to 'false' to not display them var enableSuggestions = true; var styleSuggestions = [ "(Open this userscript source code to edit these presets)", "monochrome dot-printed manga-style", "watercolor", "mmd 3d render", "fluorescent fiery colors" ]; var refreshIterationsNeeded = 7; var styleTb = null; var aspectRatioCb = null; var lastRegen = 0; var createBtnText = null; var newPromptInput = null; var mainInterval = null; var generationInfo = []; (function () { if (!shouldExecuteScript()) { return; } if (useExperimentalLargerPrompts) { enableLargePrompts(); } else if (saveCredits) { saveCreditsDefaultInput(); } if (isCustomGen()) { customGenerationInProgress(); } if (checkShouldRedirect()) { return; } thumbnailStuff(false); mainInterval = setInterval(update, 1000); window.addEventListener('popstate', function () { if (mainInterval == null) { console.log("started interval"); mainInterval = setInterval(update, 1000); } }); })(); // Regeneration in progress function customGenerationInProgress() { const urlParams = new URLSearchParams(window.location.search); const errorMsg = document.getElementsByClassName("gil_err_mt"); const errorDesc = document.getElementsByClassName("gil_err_sbt"); const errorImg = document.getElementsByClassName("gil_err_img rms_img"); const notFound = errorMsg.length > 0 && errorImg[0].src.includes("dtahmJyCX5kbcYNFtsv00qhgfAc.jpg"); if (!notFound) { thumbnailStuff(true); return true; } let useOldBehavior = false; let refreshInterval = null; errorImg[0].src = "https://r.bing.com/rp/TX9QuO3WzcCJz1uaaSwQAz39Kb0.jpg"; const updateText = function () { errorMsg[0].innerText = "Image generation in progress"; errorDesc[0].innerText = useOldBehavior ? "This page will refresh automatically every 7 seconds. If the image doesn't appears within 40 seconds, it was most likely filtered and you can close this tab. Refreshing in " + refreshIterationsNeeded + "s..." : "Periodically checking this image generation status..."; } const oldBehavior = function () { if (--refreshIterationsNeeded <= 0) { location.reload(); clearInterval(refreshInterval); } else { updateText(); } }; const ontick = function() { if (useOldBehavior) { oldBehavior(); return; } checkEditStatus(skey, genId, onsuccess, onblocked, onfailed); }; const restartInterval = function () { if(refreshInterval != null) { clearInterval(refreshInterval); } refreshInterval = setInterval(ontick, useOldBehavior ? 1000 : 2500); }; const genId = getGenId(); const skey = urlParams.get("skey"); const onsuccess = function () { location.reload(); clearInterval(refreshInterval); }; const onblocked = function () { clearInterval(refreshInterval); errorImg[0].src = "https://r.bing.com/rp/in-2zU3AJUdkgFe7ZKv19yPBHVs.png"; errorMsg[0].innerText = "Image was blocked"; errorDesc[0].innerText = ""; document.title = "(Blocked) " + document.title; }; const onfailed = function () { useOldBehavior = true; restartInterval(); }; if (skey == null) { useOldBehavior = true; } updateText(); restartInterval(); return true; } // Editing feature function update() { const frameElm = getDocument(); const detailsElm = frameElm.getElementById("detailMeta"); const imgd = frameElm.getElementById("msz"); if (detailsElm != null && imgd != null && imgd.innerText != null) { clearInterval(mainInterval); mainInterval = null; console.log("stopped interval"); hideInsights(); if (!isCustomGen()) { checkEditedGenerations(getGenId(), checkEditedGenerationsCallback); } } } function checkEditedGenerationsCallback(result) { generationInfo = result; addContent(getDocument().getElementById("detailMeta")); } function prepare() { const imageWinElm = getDocument().getElementById("mainImageWindow"); const meta = JSON.parse(imageWinElm.getAttribute("data-m").replaceAll(""", "\"")); let thumb = meta.ClickThroughUrl.match(/(OIG.+?)(&|$)/)[1]; if (thumb.includes("%2e")) { thumb = decodeURI(thumb); } const imgInfo = getImgInfo(thumb); if (imgInfo == null) { alert("Image mismatch. Refresh the page"); return; } if (imgInfo.isEdited) { alert("BIC doesn't support editing an already edited image"); return; } const imgId = imgInfo.imgid; const querystrings = getWindow().ImageDetailReducers.g_PageConfig.persistedQueryStrings; const genId = getGenId(); const skey = /skey=(.+?)&/.exec(querystrings)[1]; const query = getDocument().getElementsByClassName("ptitle nolink")[0].innerText; if (imgId == null || imgId.length < 5 || skey == null || skey.length < 5) { alert("Something went wrong"); return; } sendResizeRequest(encodeURIComponent(genId), encodeURIComponent(imgId), encodeURIComponent(skey), encodeURIComponent(query)); } function addContent(detailsElm) { const main = detailsElm.getElementsByClassName("actn_container")[0]; const container = document.createElement("div"); const generateBtn = createButton("generatebtn", "Generate", onGenerateBtnClick, {"width": "150px"}); const randomizeBtn = createButton("randomizeBtn", "Random", onRandomizeBtnClick, {"width": "95px"}); aspectRatioCb = createCombobox("aspectrationcb", ["Square (1024x1024)", "Landscape (1792x1024)", "Portrait (1024x1792)"]); container.setAttribute("style", "margin-top:15px;"); addPromptTextbox(container); container.append(randomizeBtn, document.createElement("br"), aspectRatioCb, generateBtn); main.appendChild(container); detailsElm.firstChild.setAttribute("style", "line-height: 20px;font-size:16px; width: 90%;"); (async () => { await updateEditElements(); })(); } async function updateEditElements() { const lastUsedStyle = await GM.getValue("style", "original"); const lastUsedAspectRatio = await GM.getValue("aspectratio", 1); if (lastUsedStyle !== undefined && lastUsedStyle.toLowerCase() !== "original") { styleTb.value = lastUsedStyle; } if (lastUsedAspectRatio >= 0 && lastUsedAspectRatio <= 2) { aspectRatioCb.children[lastUsedAspectRatio].setAttribute("selected", "selected"); } } // Large prompts feature function enableLargePrompts() { const form = document.getElementById("sb_form"); const container = document.getElementById("b_searchboxForm"); const promptInput = document.getElementById("sb_form_q"); const submitButton = document.getElementById("create_btn_c"); const clearButton = document.getElementById("gi_clear"); newPromptInput = document.createElement("textarea"); const surpriseMeButton = document.getElementById("surprise-me"); if (form === null || promptInput === null) { console.log("form or input is null"); return; } const oldValue = promptInput.value; container.removeChild(promptInput); container.setAttribute("style", "border-radius: unset; height:125px; margin-bottom: 45px;"); clearButton.setAttribute("style", "display:none;"); surpriseMeButton.setAttribute("style", "display:none;"); document.getElementById("b_footer").setAttribute("style", "display:none;"); document.getElementById("fbpgbt").setAttribute("style", "display:none;"); form.setAttribute("method", "get"); form.setAttribute("action", "/images/create?q=%QUERY%&rt=%RequestTier%"); //remove FORM=GENCRE form.appendChild(createHiddenInput("partner", "sydney")); form.appendChild(createHiddenInput("gptexp", "designer")); form.appendChild(createHiddenInput("showselective", "1")); form.appendChild(createHiddenInput("rt", getRequestTier())); newPromptInput.value = oldValue; newPromptInput.setAttribute("style", "background-color:#625f5f; color:#f7f1de;"); newPromptInput.setAttribute("name", "q"); newPromptInput.setAttribute("class", "b_searchbox gi_sb"); newPromptInput.setAttribute("id", "sb_form_q"); newPromptInput.setAttribute("title", ""); newPromptInput.setAttribute("placeholder", "Type anything"); newPromptInput.setAttribute("autocapitalize", "none"); newPromptInput.setAttribute("autocorrect", "true"); newPromptInput.setAttribute("autocomplete", "off"); newPromptInput.setAttribute("spellcheck", "true"); newPromptInput.setAttribute("aria-live", "polite"); newPromptInput.setAttribute("maxlength", "850"); newPromptInput.setAttribute("data-maxlength", "850"); newPromptInput.addEventListener("input", () => { updateCreateButtonText(); }); container.insertBefore(newPromptInput, container.firstChild); submitButton.addEventListener("click", () => { if (/\S/.test(newPromptInput.value)) { GM.setValue("lastUsedPrompt", newPromptInput.value); } form.submit(); }); submitButton.classList.remove("disabled"); updateCreateButtonText(); if (/^\s*$/.test(newPromptInput.value) && rememberMyLastSubmittedPrompt) { (async () => { const lastUsedPrompt = await GM.getValue("lastUsedPrompt"); if (lastUsedPrompt !== undefined) { newPromptInput.value = lastUsedPrompt; updateCreateButtonText(); } })(); } } function updateCreateButtonText() { const buttonText = document.getElementById("create_btn"); if (createBtnText === null) { createBtnText = buttonText.innerText; } buttonText.innerHTML = `${createBtnText}
(${newPromptInput.value.length}/850)`; } // misc function getImgInfo(thumb) { for (let i = 0; i < generationInfo.length; i++) { if (generationInfo[i].thumb.includes(thumb)) { return generationInfo[i]; } } return null; } function saveCreditsDefaultInput() { const formElm = document.getElementById("sb_form"); if (formElm === null) { return; } const attr = formElm.getAttribute("data-actn"); if (attr.includes("%RequestTier%")) { formElm.setAttribute("data-actn", attr.replace("%RequestTier%", "3")); editCreditsText(); } } function editCreditsText() { const creditsElm = document.getElementById("token_bal"); if (creditsElm === null) { return; } creditsElm.setAttribute("style", "color:#35f96e;font-weight:bold;") } function hideInsights() { if (!hideInsightsPanel) { return; } const imageDiv = getDocument().getElementsByClassName("idp-refresh-page-main-line"); if (imageDiv.length > 0) { imageDiv[0].setAttribute("style", "height: 100%;"); } const insightsDiv = getDocument().getElementsByClassName("idp-refresh-page-bottom-page"); if (insightsDiv.length > 0) { insightsDiv[0].remove(); } } function thumbnailStuff(addQueryParam) { let results = document.getElementsByClassName("single-img-link"); for (var i = 0; i < results.length; i++) { var element = results[i]; if (addQueryParam) { element.href = element.href + "&_dbe=1"; } const src = element.firstChild.getAttribute("src"); if (location.hostname === "copilot.microsoft.com" && src.startsWith("/th/")) { element.firstChild.src = "https://www.bing.com" + src; } } } function shouldExecuteScript() { return window.top === window.self; } function checkShouldRedirect() { const errDiv = document.getElementById("girer"); if (errDiv !== null) { // hide report button if there is one const reportButton = document.getElementsByClassName("gie_btns"); for (const button of reportButton) { button.setAttribute("style", "display:none;"); } // There is an error message (You can't submit any more prompts, unsafe prompt, etc) // do not redirect so the user can see it but return true to not start the update interval as it is not needed return true; } const url = new URL(window.location.href); const params = new URLSearchParams(window.location.search); if (params.get("showselective") === "1") { params.delete("showselective"); url.search = params.toString(); window.location.href = url.toString(); return true; } return false; } function getDocument() { return unsafeWindow.document.getElementById("OverlayIFrame")?.contentWindow?.document ?? unsafeWindow.document; } function getWindow() { return unsafeWindow.document.getElementById("OverlayIFrame")?.contentWindow?.window ?? unsafeWindow; } function getGenId() { const pathSplit = window.location.pathname.split('/'); return pathSplit[pathSplit.length - 1]; } function isCustomGen() { const params = new URLSearchParams(window.location.search); return params.get("_dbe") === "1"; } function getRequestTier() { if (saveCredits) { editCreditsText(); return "3"; } const element = document.getElementById("token_bal"); if (element === null) { return "3"; } return parseInt(element.innerText) > 0 ? "4" : "3"; } function createButton(id, text, onclick, additionalCSSDiv = null, additionalCSSSpan = null) { const defaultCSSDiv = { "background-color": "#656565", "display": "inline-block", "margin-left": "20px", "text-align": "center" }; const defaultCSSSpan = { "color": "#fff", "font-size": "17px", "font-weight": "bold" }; const newButton = document.createElement("div"); const newButtonText = document.createElement("span"); newButtonText.setAttribute("id", `${id}_text`); newButtonText.innerText = text; newButton.setAttribute("class", "action nofocus"); newButton.setAttribute("id", id); newButton.onclick = onclick; stylize(newButton, defaultCSSDiv, additionalCSSDiv); stylize(newButtonText, defaultCSSSpan, additionalCSSSpan); newButton.appendChild(newButtonText); return newButton; } function createTextbox(id, placeholder, maxlength = 100, spellcheck = true, additionalCSS = null) { const defaultCSS = { "background-color": "#f7e6d2", "color": "#535151", "font-size": "15px", "font-weight": "bold" }; const newTextbox = document.createElement("input"); newTextbox.setAttribute("type", "text"); newTextbox.setAttribute("placeholder", placeholder); newTextbox.setAttribute("id", id); newTextbox.setAttribute("maxlength", maxlength); newTextbox.setAttribute("spellcheck", spellcheck); stylize(newTextbox, defaultCSS, additionalCSS); return newTextbox; } function createCombobox(id, items, additionalCSS = null) { const defaultCSS = { "background-color": "#f7e6d2", "color": "#535151", "font-size": "13px", "font-weight": "bold" }; const newCombobox = document.createElement("select"); newCombobox.setAttribute("id", id); for (let i = 0; i < items.length; i++) { var option = document.createElement("option"); option.setAttribute("value", i.toString()); option.innerText = items[i]; newCombobox.appendChild(option); } stylize(newCombobox, defaultCSS, additionalCSS) return newCombobox; } function createHiddenInput(name, value) { const newInput = document.createElement("input"); newInput.setAttribute("type", "hidden"); newInput.setAttribute("name", name); newInput.setAttribute("value", value); return newInput; } function addPromptTextbox(container) { const additionalCSS = { "width": "310px", "text-align": "center", "margin-bottom": "14px" } styleTb = createTextbox("bicprompt", "Style prompt. See rentry page for more info", 270, true, additionalCSS); container.appendChild(styleTb); if (enableSuggestions) { styleTb.setAttribute("list", "styleSuggestions"); const dl = document.createElement("datalist"); dl.setAttribute("id", "styleSuggestions"); for (let i = 0; i < styleSuggestions.length; i++) { const opt = document.createElement("option"); opt.setAttribute("value", styleSuggestions[i]); dl.appendChild(opt); } container.appendChild(dl); } } function stylize(element, defaultStyle, newStyle) { let style = ""; if (defaultStyle != null) { for (let i = 0; i < Object.keys(defaultStyle).length; i++) { const key = Object.keys(defaultStyle)[i]; if (newStyle != null && newStyle[key] != null) { continue; } style += `${key}:${defaultStyle[key]};`; } } if (newStyle != null) { for (let i = 0; i < Object.keys(newStyle).length; i++) { const key = Object.keys(newStyle)[i]; style += `${key}:${newStyle[key]};`; } } if (style.length > 0) { element.setAttribute("style", style); } } // requests function checkEditedGenerations(genid, callback) { GM.xmlHttpRequest({ method: "GET", url: `/images/create/detail/async/${genid}?imageId=qrvSx5UnVFBE3jM3T1uD5g%3D%3D`, onload: function (response) { checkEditedGenerationsResponse(response, callback); }, error: function () { console.log("Failed generation metadata request"); } }); } function sendResizeRequest(genid, imgid, skey, query) { query = "-"; let style = "original"; let aspectratio = 1; if (styleTb != null && /\S/.test(styleTb.value)) { style = styleTb.value; } if (aspectRatioCb != null && aspectRatioCb.selectedIndex !== -1) { aspectratio = aspectRatioCb.value; } console.log("Generating this image with an aspect ratio value of '" + aspectratio + "' with the style '" + style + "'."); GM.setValue("style", style); GM.setValue("aspectratio", aspectratio); GM.xmlHttpRequest({ method: "POST", url: "/images/edit/resize?iid=" + imgid + "&skey=" + skey + "&id=" + genid + "&q=" + query + "&partner=Sydney&ar=" + aspectratio + "&s=" + style + "&rt=3", onload: function (response) { sendResizeRequestResponse(response, skey); }, error: function () { alert("Couldn't send request."); } }); } function checkEditStatus(skey, genid, onsuccess, onblocked, onfailed) { GM.xmlHttpRequest({ method: "GET", url: "/images/edit/poll?iid=ey%3D%3D&skey=" + skey + "&id=" + genid + "&q=-&partner=Sydney", onload: function (response) { checkEditStatusResponse(response, onsuccess, onblocked, onfailed); }, error: function () { onfailed(); } }); } // events function checkEditedGenerationsResponse(response, callback) { if (response.status === 200) { const jsonRes = JSON.parse(response.responseText); if (jsonRes.totalEstimatedMatches === 0) { callback([]); return; } const generationInfo = []; for (const img of jsonRes.value) { generationInfo.push({ "imgid": img.imageId, "thumb": img.thumbnailUrl, "isEdited": img.style !== "" }); } callback(generationInfo); } } function sendResizeRequestResponse(response, skey) { var jsonRes = JSON.parse(response.responseText); if (response.status === 200) { const urlSplit = jsonRes.webSearchUrl.split('/'); window.open("/images/create/-/" + urlSplit[urlSplit.length - 1] + "?_dbe=1&skey=" + skey, '_blank'); } else if (jsonRes.errors[0].message.includes("InvalidOwner")) { alert("Request was unsuccessful. You are trying to edit an image that doesn't belongs to the current account you are using."); } else if (jsonRes.errors[0].code === ("RateLimitExceeded")) { alert("Request was unsuccessful. Your account exceeded rate limit."); } else { alert("Request was unsuccessful. Unknown error."); } } function checkEditStatusResponse(response, onsuccess, onblocked, onfailed) { if (response.status === 404) { onfailed(); return; } const jsonRes = JSON.parse(response.responseText); if (jsonRes._type === "RetrieveImagesResponse" && jsonRes.images != null && jsonRes.images.length > 0) { onsuccess(); } else if (jsonRes._type === "ErrorResponse") { for (const error of jsonRes.errors) { if (error.subCode === "ContentPolicy" && error.message === "Blocked by DallE") { onblocked(); } } } } function onGenerateBtnClick() { const now = performance.now(); if (lastRegen + 450 < now) { lastRegen = now; prepare(); } } function onRandomizeBtnClick() { styleTb.value = crypto.randomUUID(); }