From 3c3c6796169e3cb1e19dfa0229be8694411cfb04 Mon Sep 17 00:00:00 2001 From: somebody Date: Wed, 21 Sep 2022 16:31:05 -0500 Subject: [PATCH 1/2] Init context menu --- static/koboldai.css | 36 ++++++++++++++++++++++++++++ static/koboldai.js | 27 +++++++++++++++++++++ templates/index_new.html | 51 +++++++++++++++++++++++++++++++++++++++- 3 files changed, 113 insertions(+), 1 deletion(-) diff --git a/static/koboldai.css b/static/koboldai.css index 2a4805bd..ab5cd720 100644 --- a/static/koboldai.css +++ b/static/koboldai.css @@ -2148,6 +2148,42 @@ body { border-radius: var(--radius_settings_button); } +/* Context Menu */ +#context-menu { + position: absolute; + cursor: default; + + /* Nothing should be above the context menu (that I can think of) */ + z-index: 9999999; + + /* TODO: Theme */ + background-color: #222f3a; + border: 1px solid #485e6d; +} + +#context-menu > hr { + /* TODO: Theme */ + border-top: 2px solid #485e6d; + margin: 5px 5px; +} + +.context-menu-item { + padding: 5px; + padding-right: 25px; + min-width: 100px; +} + +.context-menu-item:hover { + /* TODO: Theme */ + background-color: #273b48; +} + +.context-menu-item > .material-icons-outlined { + position: relative; + top: 2px; + font-size: 15px; +} + /*---------------------------------- Global ------------------------------------------------*/ .hidden { display: none; diff --git a/static/koboldai.js b/static/koboldai.js index 470805bf..7f0b520f 100644 --- a/static/koboldai.js +++ b/static/koboldai.js @@ -4290,6 +4290,33 @@ $(document).ready(function(){ debugContainer.addEventListener("click", function(e) { debugContainer.classList.add("hidden"); }); + + // Context menu + const contextMenu = document.getElementById("context-menu"); + + $("#gamescreen").contextmenu(function(event) { + contextMenu.classList.remove("hidden"); + + // Set position to click position + contextMenu.style.left = `${event.originalEvent.x}px`; + contextMenu.style.top = `${event.originalEvent.y}px`; + + // Don't open browser context menu + event.preventDefault(); + + // Don't let the document contextmenu catch us and close our context menu + event.stopPropagation(); + }); + + // When we make a browser context menu, close ours. + $(document).contextmenu(function(event) { + contextMenu.classList.add("hidden"); + }); + + // When we click outside of our context menu, close ours. + $(document).click(function(event) { + contextMenu.classList.add("hidden"); + }); }); document.addEventListener("keydown", function(event) { diff --git a/templates/index_new.html b/templates/index_new.html index 07899e78..4db73e98 100644 --- a/templates/index_new.html +++ b/templates/index_new.html @@ -138,6 +138,55 @@ upload_file - +
+
+ content_cut + Cut +
+ +
+ content_copy + Copy +
+ +
+ content_paste + Paste +
+ +
+ +
+ assignment + Add to Memory +
+ +
+ auto_stories + Add to World Info Entry +
+ +
+ insights + Add as Bias +
+ +
+ refresh + Retry from here +
+ +
+ +
+ assessment + View Token Probabilities +
+ +
+ account_tree + View Token Probabilities +
+
\ No newline at end of file From bfb9b65680613f5be2f3df8ee9bfc96190d143c4 Mon Sep 17 00:00:00 2001 From: somebody Date: Wed, 21 Sep 2022 18:28:52 -0500 Subject: [PATCH 2/2] Make the context menu no longer for decoration --- static/koboldai.css | 1 + static/koboldai.js | 120 +++++++++++++++++++++++++++++++++++---- templates/index_new.html | 51 ----------------- 3 files changed, 111 insertions(+), 61 deletions(-) diff --git a/static/koboldai.css b/static/koboldai.css index ab5cd720..e0be7328 100644 --- a/static/koboldai.css +++ b/static/koboldai.css @@ -2182,6 +2182,7 @@ body { position: relative; top: 2px; font-size: 15px; + margin-right: 5px; } /*---------------------------------- Global ------------------------------------------------*/ diff --git a/static/koboldai.js b/static/koboldai.js index 7f0b520f..a7b5006c 100644 --- a/static/koboldai.js +++ b/static/koboldai.js @@ -73,6 +73,22 @@ const finder_actions = [ // {name: "", icon: "palette", func: function() { highlightEl("#biasing") }}, ]; +const context_menu_actions = [ + {label: "Cut", icon: "content_cut", visibilityCondition: "SELECTION", click: cut}, + {label: "Copy", icon: "content_copy", visibilityCondition: "SELECTION", click: copy}, + {label: "Paste", icon: "content_paste", visibilityCondition: "SELECTION", click: paste}, + // Null makes a seperation bar + null, + {label: "Add to Memory", icon: "assignment", visibilityCondition: "SELECTION", click: push_selection_to_memory}, + {label: "Add to World Info Entry", icon: "auto_stories", visibilityCondition: "SELECTION", click: push_selection_to_world_info}, + {label: "Add as Bias", icon: "insights", visibilityCondition: "SELECTION", click: push_selection_to_phrase_bias}, + {label: "Retry from here", icon: "refresh", visibilityCondition: "CARET", click: retry_from_here}, + // Not implemented! See view_selection_probabiltiies + // null, + // {label: "View Token Probabilities", icon: "assessment", visibilityCondition: "SELECTION", click: view_selection_probabilities}, + // {label: "View Token Probabilities", icon: "account_tree", visibilityCondition: "SELECTION", click: view_selection_probabilities}, +]; + const map1 = new Map() map1.set('Top K Sampling', 0) @@ -2310,6 +2326,9 @@ function push_selection_to_phrase_bias() { } function retry_from_here() { + // TODO: Make this from the caret position (get_caret_position()) instead + // of per action. Actions may start out well, but go off the rails later, so + // we should be able to retry from any position. let chunk = null; for (element of document.getElementsByClassName("editing")) { if (element.id == 'story_prompt') { @@ -2331,16 +2350,25 @@ function retry_from_here() { } } +function view_selection_probabilities() { + // Not quite sure how this should work yet. Probabilities are obviously on + // the token level, which we have no UI representation of. There are other + // token-level visualization features I'd like to implement (like something + // for self-attention), so if that works out it might be best to have a + // modifier key (i.e. alt) enter a "token selection mode" when held. + console.log("Not implemented! :("); +} + function copy() { - document.execCommand("copy") + document.execCommand("copy"); } function paste() { - document.execCommand("paste") + document.execCommand("paste"); } function cut() { - document.execCommand("cut") + document.execCommand("cut"); } function getSelectionText() { @@ -2359,6 +2387,15 @@ function getSelectionText() { return text; } +function get_caret_position(target) { + if ( + document.activeElement !== target && + !$.contains(target, document.activeElement) + ) return null; + + return getSelection().focusOffset; +} + function show_save_preset() { document.getElementById("save_preset").classList.remove("hidden"); } @@ -3806,7 +3843,6 @@ function updateStandardSearchListings(query) { function $e(tag, parent, attributes) { // Small helper function for dynamic UI creation - // TODO: Support nested attributed with "." syntax. let element = document.createElement(tag); @@ -4127,6 +4163,34 @@ function process_cookies() { load_tweaks(); } +function position_context_menu(contextMenu, x, y) { + // Calculate where to position context menu based on window confines and + // menu size. + + let height = contextMenu.clientHeight; + let width = contextMenu.clientWidth; + + let bounds = { + top: 0, + bottom: window.innerHeight, + left: 0, + right: window.innerWidth, + }; + + let farMenuBounds = { + top: y, + bottom: y + height, + left: x, + right: x + width, + }; + + if (farMenuBounds.right > bounds.right) x -= farMenuBounds.right - bounds.right; + if (farMenuBounds.bottom > bounds.bottom) y -= farMenuBounds.bottom - bounds.bottom; + + contextMenu.style.left = `${x}px`; + contextMenu.style.top = `${y}px`; +} + $(document).ready(function(){ on_colab = document.getElementById("on_colab").textContent == "true"; @@ -4292,17 +4356,49 @@ $(document).ready(function(){ }); // Context menu - const contextMenu = document.getElementById("context-menu"); + const contextMenu = $e("div", document.body, {id: "context-menu"}); + + for (const action of context_menu_actions) { + // Null adds horizontal rule + if (!action) { + $e("hr", contextMenu); + continue; + } + + let item = $e("div", contextMenu, { + classes: ["context-menu-item", "noselect"], + "visibility-condition": action.visibilityCondition + }); + let icon = $e("span", item, {classes: ["material-icons-outlined"], innerText: action.icon}); + item.append(action.label); + + item.addEventListener("mousedown", (e) => (e.preventDefault())); + item.addEventListener("click", action.click); + } $("#gamescreen").contextmenu(function(event) { + // Don't open browser context menu + event.preventDefault(); + + // Close if open + if (!contextMenu.classList.contains("hidden")) { + contextMenu.classList.add("hidden"); + return; + } + + // Disable non-applicable items + $(".context-menu-item").addClass("disabled"); + + // A selection is made + if (getSelectionText()) $(".context-menu-item[visibility-condition=SELECTION]").removeClass("disabled"); + + // The caret is placed + if (get_caret_position($("#gamescreen")[0]) !== null) $(".context-menu-item[visibility-condition=CARET]").removeClass("disabled"); + contextMenu.classList.remove("hidden"); // Set position to click position - contextMenu.style.left = `${event.originalEvent.x}px`; - contextMenu.style.top = `${event.originalEvent.y}px`; - - // Don't open browser context menu - event.preventDefault(); + position_context_menu(contextMenu, event.originalEvent.x, event.originalEvent.y); // Don't let the document contextmenu catch us and close our context menu event.stopPropagation(); @@ -4317,6 +4413,10 @@ $(document).ready(function(){ $(document).click(function(event) { contextMenu.classList.add("hidden"); }); + + window.addEventListener("blur", function(event) { + contextMenu.classList.add("hidden"); + }); }); document.addEventListener("keydown", function(event) { diff --git a/templates/index_new.html b/templates/index_new.html index 4db73e98..143a2802 100644 --- a/templates/index_new.html +++ b/templates/index_new.html @@ -137,56 +137,5 @@ - -
-
- content_cut - Cut -
- -
- content_copy - Copy -
- -
- content_paste - Paste -
- -
- -
- assignment - Add to Memory -
- -
- auto_stories - Add to World Info Entry -
- -
- insights - Add as Bias -
- -
- refresh - Retry from here -
- -
- -
- assessment - View Token Probabilities -
- -
- account_tree - View Token Probabilities -
-
\ No newline at end of file