Merge pull request #3036 from procule/update-token-probabilities Enhancement: Token Probabilities viewer with dynamic prefix adjustment via Ctrl-click

cfd1545b8a0f25c070bccfbaf2946660512029b6

Cohee <18619528+Cohee1207@users.noreply.github.com>

Signed
1 files changed, +50 -2Showing whitespace changes
public/scripts/logprobs.js+50 -2
@@ -88,7 +88,7 @@ function renderAlternativeTokensView() {
8888 prefixSpan.text(prefix);
8989 prefixSpan.html(prefixSpan.html().replace(/\n/g, '<br>'));
9090 prefixSpan.addClass('logprobs_output_prefix');
9191 prefixSpan.attr('title', 'Select to reroll the last \'Continue\' generation.\nHold the CTRL key when clicking to reroll from before that word.');
9292 prefixSpan.click(onPrefixClicked);
9393 addKeyboardProps(prefixSpan);
9494 tokenSpans.push(...withVirtualWhitespace(prefix, prefixSpan));
@@ -243,10 +243,50 @@ function onAlternativeClicked(tokenLogprobs, alternative) {
243243}
244244
245245/**
246+ * getTextBeforeClickedWord retrieves the portion of text within a span
247+ * that appears before the word clicked by the user. Using the x and y
248+ * coordinates from a PointerEvent, this function identifies the exact
249+ * word clicked and returns the text preceding it within the span.
250+ *
251+ * If the clicked position does not resolve to a valid word or text node,
252+ * the entire span text is returned as a fallback.
253+ *
254+ * @param {PointerEvent} event - The click event containing the x and y coordinates.
255+ * @param {string} spanText - The full text content of the span element.
256+ * @returns {string} The text before the clicked word, or the entire span text as fallback.
257+ */
258+function getTextBeforeClickedWord(event, spanText) {
259+ const x = event.clientX;
260+ const y = event.clientY;
261+ const range = document.caretRangeFromPoint(x, y);
262+
263+ if (range && range.startContainer.nodeType === Node.TEXT_NODE) {
264+ const textNode = range.startContainer;
265+ const offset = range.startOffset;
266+
267+ // Get the full text content of the text node
268+ const text = textNode.nodeValue;
269+
270+ // Find the boundaries of the clicked word
271+ const start = text.lastIndexOf(" ", offset - 1) + 1;
272+
273+ // Return the text before the clicked word
274+ return text.slice(0, start);
275+ }
276+
277+ // If we can't determine the exact word, return the full span text as a fallback
278+ return spanText;
279+}
280+
281+
282+/**
246283 * onPrefixClicked is called when the user clicks on the carried-over prefix
247284 * in the token output view. It allows them to reroll the last 'continue'
248285 * completion with none of the output generated from it, in case they don't
249286 * like the results.
287+ *
288+ * If the user holds the Ctrl key while clicking, only the portion of text
289+ * before the clicked word is retained as the prefix for rerolling
250290 */
251291function onPrefixClicked() {
252292 if (!checkGenerateReady()) {
@@ -255,7 +295,15 @@ function onPrefixClicked() {
255295
256296 const { continueFrom } = getActiveMessageLogprobData();
257297 const messageId = chat.length - 1;
258- const prefix = continueFrom || '';
298+
299+ // Check if Ctrl key is pressed during the click
300+ let prefix = continueFrom || '';
301+ if (event.ctrlKey) {
302+ // Ctrl is pressed - use the text before the clicked word
303+ prefix = getTextBeforeClickedWord(event, continueFrom);
304+ }
305+
306+ // Use the determined `prefix`
259307 createSwipe(messageId, prefix);
260308 $('.swipe_right:last').click();
261309 Generate('continue').then(_ => void _);