diff --git a/static/koboldai.css b/static/koboldai.css
index e384bc6b..50dbf990 100644
--- a/static/koboldai.css
+++ b/static/koboldai.css
@@ -1199,6 +1199,187 @@ td.server_vars {
margin-right: 5px;
}
+#screenshot-wizard {
+ width: 50%;
+ background-color: var(--background);
+}
+
+#screenshot-wizard > .popup-header {
+ padding-top: 5px;
+ padding-left: 5px;
+ margin-bottom: 6px;
+}
+
+#screenshot-wizard > .help_text {
+ margin-top: 0.7em;
+}
+
+#screenshot-wizard > .popup-header > h1 {
+ margin-top: 0px;
+ margin-bottom: 0px;
+}
+
+#screenshot-target-container {
+ position: relative;
+ margin: 20px;
+ margin-top: 10px;
+ max-height: 25vh;
+ overflow-y: auto;
+}
+
+#screenshot-target {
+ min-height: 130px;
+ background-color: var(--gamescreen_background);
+ padding: 20px;
+ /* width: 1080px; */
+ display: flex;
+ overflow: hidden;
+}
+
+#screenshot-target > #screenshot-left {
+ display: flex;
+ flex-direction: column;
+ justify-content: space-between;
+ flex-grow: 1;
+}
+
+#screenshot-target > #screenshot-images {
+ border-left: 1px solid var(--background);
+ flex-shrink: 0;
+ max-width: 25%;
+ padding-left: 20px;
+ margin-left: 20px;
+ position: relative;
+ overflow: hidden;
+}
+
+#screenshot-target > #screenshot-images > img {
+ display: block;
+ width: 100%; /* the buggy line */
+ margin-bottom: 12px;
+}
+
+#screenshot-target .action-text {
+ font-size: 1.1em;
+ font-weight: bold;
+}
+
+.robot-attribution {
+ margin-top: 30px;
+}
+
+#screenshot-target .human-text,
+.robot-attribution > .human-attribution {
+ color: var(--text_edit);
+}
+
+#screenshot-target .ai-text,
+.robot-attribution > .model-name {
+ color: var(--text);
+}
+
+.robot-attribution > .model-name {
+ font-family: monospace;
+}
+
+.robot-attribution > .story-attribution {
+ font-style: italic;
+}
+
+.robot-attribution > .human-attribution,
+.robot-attribution > .model-name,
+.robot-attribution > .story-attribution {
+ font-weight: bold;
+}
+
+.robot-attribution > .boring-part-of-madlibs {
+ opacity: 0.9;
+}
+
+#screenshot-options {
+ display: flex;
+ flex-direction: column;
+ margin-left: 8px;
+ margin-bottom: 8px;
+}
+
+#screenshot-wizard .header {
+ font-weight: bold;
+ opacity: 0.6;
+}
+
+#screenshot-wizard .option {
+ margin-bottom: 3px;
+}
+
+#screenshot-wizard .distribution-bar {
+ display: flex;
+ justify-content: space-between;
+ padding-left: 5px;
+ padding-right: 5px;
+}
+
+#screenshot-options .indent {
+ margin-left: 15px;
+}
+
+#screenshot-options .warning {
+ color: rgb(185, 70, 70);
+ font-style: italic;
+}
+
+/* Screenshot Image Picker */
+#screenshot-image-picker {
+ display: grid;
+ grid-template-columns: auto auto auto auto auto;
+ max-height: 200px;
+ overflow-y: auto;
+ overflow-x: hidden;
+ /* TODO: We REALLY need to figure out a better system for colors and themeing */
+ background-color: var(--flyout_background_pinned);
+}
+
+#screenshot-image-picker > .img-container {
+ display: inline-block;
+ position: relative;
+ cursor: pointer;
+ border: 2px solid var(----enabled_button_background_color);
+}
+
+#screenshot-image-picker > .img-container > img {
+ height: 100%;
+ width: 100%;
+ object-fit: cover;
+ overflow: hidden;
+ aspect-ratio: 1/1;
+}
+
+#screenshot-image-picker > .img-container > input {
+ position: absolute;
+ bottom: 5px;
+ right: 5px;
+ z-index: 2;
+}
+
+#screenshot-image-picker > .img-container > input:checked ~ img {
+ opacity: 0.6;
+}
+
+#sw-download {
+ margin-top: 12px;
+ margin: 3px;
+ padding: 12px;
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ background-color: var(--button_background);
+ font-size: 1.2em;
+ cursor: pointer;
+}
+
+#screenshot-text-container {
+ line-height: 2;
+}
/* ---------------------------- OVERALL PAGE CONFIG ------------------------------*/
body {
diff --git a/static/koboldai.js b/static/koboldai.js
index e9d7df56..70d730bd 100644
--- a/static/koboldai.js
+++ b/static/koboldai.js
@@ -6929,4 +6929,77 @@ $el(".gametext").addEventListener("keydown", function(event) {
// contentEditable="plaintext-only" we're just gonna have to roll with it
document.execCommand("insertLineBreak");
event.preventDefault();
-});
\ No newline at end of file
+});
+
+/* Screenshot */
+(function() {
+ const screenshotTarget = $el("#screenshot-target");
+ const screenshotImagePicker = $el("#screenshot-image-picker");
+ const genImgToggle = $el("#sw-gen-img");
+ const attributionToggle = $el("#sw-attribution");
+ const screenshotImageContainer = $el("#screenshot-images");
+
+ // JQuery required for bootstrap toggle events
+ $(genImgToggle).change(function() {
+ screenshotImagePicker.classList.toggle("disabled", !genImgToggle.checked);
+ updateShownImages();
+ });
+
+ $(genImgToggle).bootstrapToggle();
+
+ function showScreenshotWizard() {
+ let imageUrls = [];
+
+ for (let i=1;i<17;i++) {
+ let s = i.toString();
+ if (s.length === 1) s = "0"+s;
+ imageUrls.push(`/static/test/${s}.jpg`);
+ }
+
+ for (const imageSrc of imageUrls) {
+ const imgContainer = $e("div", screenshotImagePicker, {classes: ["img-container"]});
+ const checkbox = $e("input", imgContainer, {type: "checkbox"});
+ const image = $e("img", imgContainer, {src: imageSrc, draggable: false});
+
+ imgContainer.addEventListener("click", function(event) {
+ // TODO: Preventdefault if too many images selected and checked is false
+ checkbox.click();
+ });
+
+ checkbox.addEventListener("click", function(event) {
+ event.stopPropagation();
+ updateShownImages();
+ });
+ }
+ openPopup("screenshot-wizard");
+ }
+
+ function updateShownImages() {
+ screenshotImageContainer.innerHTML = "";
+ if (!genImgToggle.checked) return;
+
+ for (const imgCont of screenshotImagePicker.children) {
+ const checked = imgCont.querySelector("input").checked;
+ if (!checked) continue;
+ const src = imgCont.querySelector("img").src;
+ $e("img", screenshotImageContainer, {src: src});
+ }
+ }
+
+ async function downloadScreenshot() {
+ // TODO: Upscale (eg transform with given ratio like 1.42 to make image
+ // bigger via screenshotTarget cloning)
+ const canvas = await html2canvas(screenshotTarget, {
+ width: screenshotTarget.clientWidth,
+ height: screenshotTarget.clientHeight - 1
+ });
+
+ canvas.style.display = "none";
+ document.body.appendChild(canvas);
+ $e("a", null, {download: "screenshot.png", href: canvas.toDataURL("image/png")}).click();
+ canvas.remove();
+ }
+ $el("#sw-download").addEventListener("click", downloadScreenshot);
+
+ showScreenshotWizard();
+})();
\ No newline at end of file
diff --git a/templates/popups.html b/templates/popups.html
index a12b1fcb..9a368fc1 100644
--- a/templates/popups.html
+++ b/templates/popups.html
@@ -320,6 +320,83 @@
+
+
+