mirror of
https://github.com/SillyTavern/SillyTavern.git
synced 2025-03-06 12:47:57 +01:00
Markdown hotkeys for textareas (#2800)
* initial commit * dont hijack all ctrl keybinds * change strikethrough bind, convert to subscribable class, target key textareas * better early return, hotkey reversiblility * possible undo solution, key checks to switch * execCommand alternate, perfect Undo * format full word when caret is in the middle of a word * double backticks do nothing, dummy. * ctrl+K for ....'K'ode snippet... * remove console logs * Add new hotkeys to help * Allow hotkeys in message edit textarea * add markdown hotkey help text * help text addition to mention hotkeys work in message edits * add markdown hotkeys to WI entry content box * disengage if alt/win pressed, universal prevent default * disengage if shiftKey pressed * re-allow shift for one special case * add MD hotkeys toggle in user settings * add markdown enabled icon on relevant textareas when appropriate * Add icon to help * Uniform formatting * Add opacity to icon --------- Co-authored-by: Cohee <18619528+Cohee1207@users.noreply.github.com>
This commit is contained in:
parent
bbb0391459
commit
b376ea884b
@ -455,3 +455,7 @@ body.expandMessageActions .mes .mes_buttons .extraMesButtonsHint {
|
|||||||
#smooth_streaming:checked~#smooth_streaming_speed_control {
|
#smooth_streaming:checked~#smooth_streaming_speed_control {
|
||||||
display: block;
|
display: block;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.mdhotkey_icon {
|
||||||
|
opacity: 0.6;
|
||||||
|
}
|
||||||
|
@ -4033,10 +4033,17 @@
|
|||||||
<input id="world_import_dialog" type="checkbox" />
|
<input id="world_import_dialog" type="checkbox" />
|
||||||
<small data-i18n="Lorebook Import Dialog">Lorebook Import Dialog</small>
|
<small data-i18n="Lorebook Import Dialog">Lorebook Import Dialog</small>
|
||||||
</label>
|
</label>
|
||||||
<label class="checkbox_label" for="enable_auto_select_input" title="Enable auto-select of input text in some text fields when clicking/selecting them. Applies to popup input textboxes, and possible other custom input fields." data-i18n="[title]Enable auto-select of input text in some text fields when clicking/selecting them. Applies to popup input textboxes, and possible other custom input fields.">
|
<label class="checkbox_label" for=" enable_auto_select_input" title="Enable auto-select of input text in some text fields when clicking/selecting them. Applies to popup input textboxes, and possible other custom input fields." data-i18n="[title]Enable auto-select of input text in some text fields when clicking/selecting them. Applies to popup input textboxes, and possible other custom input fields.">
|
||||||
<input id="enable_auto_select_input" type="checkbox" />
|
<input id="enable_auto_select_input" type="checkbox" />
|
||||||
<small data-i18n="Auto-select Input Text">Auto-select Input Text</small>
|
<small data-i18n="Auto-select Input Text">Auto-select Input Text</small>
|
||||||
</label>
|
</label>
|
||||||
|
<label class="checkbox_label alignItemsCenter" for="enable_md_hotkeys" title="Enable hotkeys for inserting markdown format characters in certain text input boxes. See '/help hotkeys'.">
|
||||||
|
<input id="enable_md_hotkeys" type="checkbox" />
|
||||||
|
<small>
|
||||||
|
<span data-i18n="Markdown Hotkeys">Markdown Hotkeys</span>
|
||||||
|
<i class="fa-brands fa-markdown"></i>
|
||||||
|
</small>
|
||||||
|
</label>
|
||||||
<label class="checkbox_label" for="restore_user_input" title="Restore unsaved user input on page refresh." data-i18n="[title]Restore unsaved user input on page refresh">
|
<label class="checkbox_label" for="restore_user_input" title="Restore unsaved user input on page refresh." data-i18n="[title]Restore unsaved user input on page refresh">
|
||||||
<input id="restore_user_input" type="checkbox" />
|
<input id="restore_user_input" type="checkbox" />
|
||||||
<small data-i18n="Restore User Input">Restore User Input</small>
|
<small data-i18n="Restore User Input">Restore User Input</small>
|
||||||
@ -4720,7 +4727,7 @@
|
|||||||
<hr>
|
<hr>
|
||||||
<div id="description_div" class="title_restorable">
|
<div id="description_div" class="title_restorable">
|
||||||
<div class="flex-container alignitemscenter">
|
<div class="flex-container alignitemscenter">
|
||||||
<span data-i18n="Character Description">Description</span>
|
<span data-i18n="Character Description" class="mdhotkey_location">Description</span>
|
||||||
<i class="editor_maximize fa-solid fa-maximize right_menu_button" data-for="description_textarea" title="Expand the editor" data-i18n="[title]Expand the editor"></i>
|
<i class="editor_maximize fa-solid fa-maximize right_menu_button" data-for="description_textarea" title="Expand the editor" data-i18n="[title]Expand the editor"></i>
|
||||||
<a href="https://docs.sillytavern.app/usage/core-concepts/characterdesign/#character-description" class="notes-link" target="_blank">
|
<a href="https://docs.sillytavern.app/usage/core-concepts/characterdesign/#character-description" class="notes-link" target="_blank">
|
||||||
<span class="fa-solid fa-circle-question note-link-span"></span>
|
<span class="fa-solid fa-circle-question note-link-span"></span>
|
||||||
@ -4734,7 +4741,7 @@
|
|||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<textarea id="description_textarea" data-i18n="[placeholder]Describe your character's physical and mental traits here." placeholder="Describe your character's physical and mental traits here." name="description" placeholder=""></textarea>
|
<textarea id="description_textarea" class="mdHotkeys" data-i18n="[placeholder]Describe your character's physical and mental traits here." placeholder="Describe your character's physical and mental traits here." name="description" placeholder=""></textarea>
|
||||||
<div class="extension_token_counter">
|
<div class="extension_token_counter">
|
||||||
<span data-i18n="extension_token_counter">Tokens:</span> <span data-token-counter="description_textarea" data-token-permanent="true">counting...</span>
|
<span data-i18n="extension_token_counter">Tokens:</span> <span data-token-counter="description_textarea" data-token-permanent="true">counting...</span>
|
||||||
</div>
|
</div>
|
||||||
@ -4742,7 +4749,7 @@
|
|||||||
<div id="firstMessageWrapper" class="flex-container flexFlowColumn flex1">
|
<div id="firstMessageWrapper" class="flex-container flexFlowColumn flex1">
|
||||||
<div id="first_message_div" class="title_restorable">
|
<div id="first_message_div" class="title_restorable">
|
||||||
<div class="flex-container alignitemscenter flex1">
|
<div class="flex-container alignitemscenter flex1">
|
||||||
<span data-i18n="First message">First message</span>
|
<span data-i18n="First message" class="mdhotkey_location">First message</span>
|
||||||
<i class="editor_maximize fa-solid fa-maximize right_menu_button" data-for="firstmessage_textarea" title="Expand the editor" data-i18n="[title]Expand the editor"></i>
|
<i class="editor_maximize fa-solid fa-maximize right_menu_button" data-for="firstmessage_textarea" title="Expand the editor" data-i18n="[title]Expand the editor"></i>
|
||||||
<a href="https://docs.sillytavern.app/usage/core-concepts/characterdesign/#first-message" class="notes-link" target="_blank">
|
<a href="https://docs.sillytavern.app/usage/core-concepts/characterdesign/#first-message" class="notes-link" target="_blank">
|
||||||
<span class="fa-solid fa-circle-question note-link-span"></span>
|
<span class="fa-solid fa-circle-question note-link-span"></span>
|
||||||
@ -4754,7 +4761,7 @@
|
|||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<textarea id="firstmessage_textarea" data-i18n="[placeholder]This will be the first message from the character that starts every chat." placeholder="This will be the first message from the character that starts every chat." name="first_mes" placeholder=""></textarea>
|
<textarea class="mdHotkeys" id="firstmessage_textarea" data-i18n="[placeholder]This will be the first message from the character that starts every chat." placeholder="This will be the first message from the character that starts every chat." name="first_mes" placeholder=""></textarea>
|
||||||
<div class="extension_token_counter">
|
<div class="extension_token_counter">
|
||||||
<span data-i18n="extension_token_counter">Tokens:</span> <span data-token-counter="firstmessage_textarea">counting...</span>
|
<span data-i18n="extension_token_counter">Tokens:</span> <span data-token-counter="firstmessage_textarea">counting...</span>
|
||||||
</div>
|
</div>
|
||||||
@ -5112,7 +5119,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<hr>
|
<hr>
|
||||||
<div id="personality_div">
|
<div id="personality_div">
|
||||||
<h4>
|
<h4 class="flex-container alignItemsBaseline">
|
||||||
<span data-i18n="Personality summary">Personality summary</span>
|
<span data-i18n="Personality summary">Personality summary</span>
|
||||||
<a href="https://docs.sillytavern.app/usage/core-concepts/characterdesign/#personality-summary" class="notes-link" target="_blank"><span class="fa-solid fa-circle-question note-link-span"></span></a>
|
<a href="https://docs.sillytavern.app/usage/core-concepts/characterdesign/#personality-summary" class="notes-link" target="_blank"><span class="fa-solid fa-circle-question note-link-span"></span></a>
|
||||||
</h4>
|
</h4>
|
||||||
@ -5122,7 +5129,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div id="scenario_div">
|
<div id="scenario_div">
|
||||||
<h4>
|
<h4 class="flex-container alignItemsBaseline">
|
||||||
<span data-i18n="Scenario">Scenario</span>
|
<span data-i18n="Scenario">Scenario</span>
|
||||||
<a href="https://docs.sillytavern.app/usage/core-concepts/characterdesign/#scenario" class="notes-link" target="_blank">
|
<a href="https://docs.sillytavern.app/usage/core-concepts/characterdesign/#scenario" class="notes-link" target="_blank">
|
||||||
<span class="fa-solid fa-circle-question note-link-span"></span>
|
<span class="fa-solid fa-circle-question note-link-span"></span>
|
||||||
@ -5179,10 +5186,10 @@
|
|||||||
<hr>
|
<hr>
|
||||||
<div id="mes_example_div" class="flex-container flexFlowColumn">
|
<div id="mes_example_div" class="flex-container flexFlowColumn">
|
||||||
<div>
|
<div>
|
||||||
<h4><span data-i18n="Examples of dialogue">Examples of dialogue</span></h4>
|
<h4 class="flex-container gap5px alignItemsBaseline"><span data-i18n="Examples of dialogue" class="mdhotkey_location">Examples of dialogue</span></h4>
|
||||||
<h5 data-i18n="Important to set the character's writing style.">Important to set the character's writing style. <a href="https://docs.sillytavern.app/usage/core-concepts/characterdesign/#examples-of-dialogue" class="notes-link" target="_blank"><span class="fa-solid fa-circle-question note-link-span"></span></a></h5>
|
<h5 data-i18n="Important to set the character's writing style.">Important to set the character's writing style. <a href="https://docs.sillytavern.app/usage/core-concepts/characterdesign/#examples-of-dialogue" class="notes-link" target="_blank"><span class="fa-solid fa-circle-question note-link-span"></span></a></h5>
|
||||||
</div>
|
</div>
|
||||||
<textarea id="mes_example_textarea" class="flexGrow" name="mes_example" data-i18n="[placeholder](Examples of chat dialog. Begin each example with START on a new line.)" placeholder="(Examples of chat dialog. Begin each example with <START> on a new line.)" form="form_create" rows="6"></textarea>
|
<textarea id="mes_example_textarea" class="flexGrow mdHotkeys" name="mes_example" data-i18n="[placeholder](Examples of chat dialog. Begin each example with START on a new line.)" placeholder="(Examples of chat dialog. Begin each example with <START> on a new line.)" form="form_create" rows="6"></textarea>
|
||||||
<div class="extension_token_counter">
|
<div class="extension_token_counter">
|
||||||
<span data-i18n="extension_token_counter">Tokens:</span> <span data-token-counter="mes_example_textarea">counting...</span>
|
<span data-i18n="extension_token_counter">Tokens:</span> <span data-token-counter="mes_example_textarea">counting...</span>
|
||||||
</div>
|
</div>
|
||||||
@ -5477,7 +5484,7 @@
|
|||||||
<label for="content ">
|
<label for="content ">
|
||||||
<small>
|
<small>
|
||||||
<span class="alignitemscenter flex-container flexnowrap wide100p justifySpaceBetween">
|
<span class="alignitemscenter flex-container flexnowrap wide100p justifySpaceBetween">
|
||||||
<span data-i18n="Content" class="alignitemscenter flex-container flexNoGap">
|
<span data-i18n="Content" class="alignitemscenter flex-container flexNoGap mdhotkey_location">
|
||||||
Content
|
Content
|
||||||
</span>
|
</span>
|
||||||
<span>
|
<span>
|
||||||
@ -5512,7 +5519,7 @@
|
|||||||
</span>
|
</span>
|
||||||
</small>
|
</small>
|
||||||
</label>
|
</label>
|
||||||
<textarea class="text_pole" name="content" rows="8" data-i18n="[placeholder]What this keyword should mean to the AI, sent verbatim" placeholder="What this keyword should mean to the AI, sent verbatim"></textarea>
|
<textarea class="text_pole mdHotkeys" name="content" rows="8" data-i18n="[placeholder]What this keyword should mean to the AI, sent verbatim" placeholder="What this keyword should mean to the AI, sent verbatim"></textarea>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="world_entry_thin_controls commentContainer">
|
<div class="world_entry_thin_controls commentContainer">
|
||||||
@ -5987,7 +5994,7 @@
|
|||||||
<div id="alternate_greetings_template" class="template_element">
|
<div id="alternate_greetings_template" class="template_element">
|
||||||
<div class="alternate_grettings flexFlowColumn flex-container">
|
<div class="alternate_grettings flexFlowColumn flex-container">
|
||||||
<div class="title_restorable">
|
<div class="title_restorable">
|
||||||
<h3><span data-i18n="Alternate Greetings">Alternate Greetings</span></h3>
|
<h3><span data-i18n="Alternate Greetings" class="mdhotkey_location">Alternate Greetings</span></h3>
|
||||||
<div title="Add" class="menu_button fa-solid fa-plus add_alternate_greeting" data-i18n="[title]Add"></div>
|
<div title="Add" class="menu_button fa-solid fa-plus add_alternate_greeting" data-i18n="[title]Add"></div>
|
||||||
</div>
|
</div>
|
||||||
<small class="justifyLeft" data-i18n="Alternate_Greetings_desc">
|
<small class="justifyLeft" data-i18n="Alternate_Greetings_desc">
|
||||||
@ -6008,7 +6015,7 @@
|
|||||||
<strong><span data-i18n="Alternate Greeting #">Alternate Greeting #</span><span class="greeting_index"></span></strong>
|
<strong><span data-i18n="Alternate Greeting #">Alternate Greeting #</span><span class="greeting_index"></span></strong>
|
||||||
<div class="menu_button fa-solid fa-trash-alt delete_alternate_greeting"></div>
|
<div class="menu_button fa-solid fa-trash-alt delete_alternate_greeting"></div>
|
||||||
</div>
|
</div>
|
||||||
<textarea name="alternate_greetings" data-i18n="[placeholder](This will be the first message from the character that starts every chat)" placeholder="(This will be the first message from the character that starts every chat)" class="text_pole textarea_compact alternate_greeting_text" value="" autocomplete="off" rows="16"></textarea>
|
<textarea name="alternate_greetings" data-i18n="[placeholder](This will be the first message from the character that starts every chat)" placeholder="(This will be the first message from the character that starts every chat)" class="text_pole textarea_compact alternate_greeting_text mdHotkeys" value="" autocomplete="off" rows="16"></textarea>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@ -6392,7 +6399,7 @@
|
|||||||
<div id="leftSendForm" class="alignContentCenter">
|
<div id="leftSendForm" class="alignContentCenter">
|
||||||
<div id="options_button" class="fa-solid fa-bars interactable"></div>
|
<div id="options_button" class="fa-solid fa-bars interactable"></div>
|
||||||
</div>
|
</div>
|
||||||
<textarea id="send_textarea" name="text" data-i18n="[no_connection_text]Not connected to API!;[connected_text]Type a message, or /? for help" placeholder="Not connected to API!" no_connection_text="Not connected to API!" connected_text="Type a message, or /? for help"></textarea>
|
<textarea id="send_textarea" name="text" class="mdHotkeys" data-i18n="[no_connection_text]Not connected to API!;[connected_text]Type a message, or /? for help" placeholder="Not connected to API!" no_connection_text="Not connected to API!" connected_text="Type a message, or /? for help"></textarea>
|
||||||
<div id="rightSendForm" class="alignContentCenter">
|
<div id="rightSendForm" class="alignContentCenter">
|
||||||
<div id="stscript_continue" title="Continue script execution" class="stscript_btn stscript_continue" data-i18n="[title]Continue script execution">
|
<div id="stscript_continue" title="Continue script execution" class="stscript_btn stscript_continue" data-i18n="[title]Continue script execution">
|
||||||
<i class="fa-solid fa-play"></i>
|
<i class="fa-solid fa-play"></i>
|
||||||
|
@ -242,6 +242,7 @@ import { INTERACTABLE_CONTROL_CLASS, initKeyboard } from './scripts/keyboard.js'
|
|||||||
import { initDynamicStyles } from './scripts/dynamic-styles.js';
|
import { initDynamicStyles } from './scripts/dynamic-styles.js';
|
||||||
import { SlashCommandEnumValue, enumTypes } from './scripts/slash-commands/SlashCommandEnumValue.js';
|
import { SlashCommandEnumValue, enumTypes } from './scripts/slash-commands/SlashCommandEnumValue.js';
|
||||||
import { commonEnumProviders, enumIcons } from './scripts/slash-commands/SlashCommandCommonEnumsProvider.js';
|
import { commonEnumProviders, enumIcons } from './scripts/slash-commands/SlashCommandCommonEnumsProvider.js';
|
||||||
|
import { initInputMarkdown } from './scripts/input-md-formatting.js';
|
||||||
import { AbortReason } from './scripts/util/AbortReason.js';
|
import { AbortReason } from './scripts/util/AbortReason.js';
|
||||||
|
|
||||||
//exporting functions and vars for mods
|
//exporting functions and vars for mods
|
||||||
@ -951,6 +952,7 @@ async function firstLoadInit() {
|
|||||||
initStats();
|
initStats();
|
||||||
initCfg();
|
initCfg();
|
||||||
initLogprobs();
|
initLogprobs();
|
||||||
|
initInputMarkdown();
|
||||||
doDailyExtensionUpdatesCheck();
|
doDailyExtensionUpdatesCheck();
|
||||||
await hideLoader();
|
await hideLoader();
|
||||||
await fixViewport();
|
await fixViewport();
|
||||||
@ -10200,7 +10202,7 @@ jQuery(async function () {
|
|||||||
.closest('.mes_block')
|
.closest('.mes_block')
|
||||||
.find('.mes_text')
|
.find('.mes_text')
|
||||||
.append(
|
.append(
|
||||||
'<textarea id=\'curEditTextarea\' class=\'edit_textarea\' style=\'max-width:auto;\'></textarea>',
|
'<textarea id=\'curEditTextarea\' class=\'edit_textarea mdHotkeys\' style=\'max-width:auto;\'></textarea>',
|
||||||
);
|
);
|
||||||
$('#curEditTextarea').val(text);
|
$('#curEditTextarea').val(text);
|
||||||
let edit_textarea = $(this)
|
let edit_textarea = $(this)
|
||||||
|
152
public/scripts/input-md-formatting.js
Normal file
152
public/scripts/input-md-formatting.js
Normal file
@ -0,0 +1,152 @@
|
|||||||
|
import { power_user } from './power-user.js';
|
||||||
|
|
||||||
|
export function initInputMarkdown() {
|
||||||
|
$(document).on('keydown', 'textarea.mdHotkeys', function (e) {
|
||||||
|
if (!power_user.enable_md_hotkeys) { return; }
|
||||||
|
|
||||||
|
// Ensure that the element is a textarea
|
||||||
|
let textarea = this;
|
||||||
|
if (!(textarea instanceof HTMLTextAreaElement)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Early return on only control or no control, alt key, and win/cmd key
|
||||||
|
if (e.key === 'Control' || !e.ctrlKey || e.altKey || e.metaKey || (e.shiftKey && !(e.ctrlKey && e.shiftKey && e.code === 'Backquote'))) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let charsToAdd = '';
|
||||||
|
let possiblePreviousFormattingMargin = 1;
|
||||||
|
|
||||||
|
switch (true) {
|
||||||
|
case e.ctrlKey && e.shiftKey && e.code === 'Backquote':
|
||||||
|
e.preventDefault();
|
||||||
|
e.stopPropagation();
|
||||||
|
charsToAdd = '~~';
|
||||||
|
possiblePreviousFormattingMargin = 2;
|
||||||
|
break;
|
||||||
|
case e.ctrlKey && e.code === 'KeyB':
|
||||||
|
e.preventDefault();
|
||||||
|
e.stopPropagation();
|
||||||
|
charsToAdd = '**';
|
||||||
|
possiblePreviousFormattingMargin = 2;
|
||||||
|
break;
|
||||||
|
case e.ctrlKey && e.code === 'KeyI':
|
||||||
|
e.preventDefault();
|
||||||
|
e.stopPropagation();
|
||||||
|
charsToAdd = '*';
|
||||||
|
break;
|
||||||
|
case e.ctrlKey && e.code === 'KeyU':
|
||||||
|
e.preventDefault();
|
||||||
|
e.stopPropagation();
|
||||||
|
charsToAdd = '__';
|
||||||
|
possiblePreviousFormattingMargin = 2;
|
||||||
|
break;
|
||||||
|
case e.ctrlKey && e.code === 'KeyK':
|
||||||
|
e.preventDefault();
|
||||||
|
e.stopPropagation();
|
||||||
|
charsToAdd = '`';
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return; // Early return if no key matches
|
||||||
|
}
|
||||||
|
|
||||||
|
let selectedText = '';
|
||||||
|
let start = textarea.selectionStart;
|
||||||
|
let end = textarea.selectionEnd;
|
||||||
|
let beforeCaret = textarea.value.substring(start - 1, start);
|
||||||
|
let afterCaret = textarea.value.substring(end, end + 1);
|
||||||
|
let isTextSelected = (start !== end);
|
||||||
|
let cursorShift = charsToAdd.length;
|
||||||
|
let selectedTextandPossibleFormatting = textarea.value.substring(start - possiblePreviousFormattingMargin, end + possiblePreviousFormattingMargin).trim();
|
||||||
|
|
||||||
|
if (isTextSelected) { //if text is selected
|
||||||
|
selectedText = textarea.value.substring(start, end);
|
||||||
|
if (selectedTextandPossibleFormatting === charsToAdd + selectedText + charsToAdd) {
|
||||||
|
// If the selected text is already formatted, remove the formatting
|
||||||
|
|
||||||
|
let expandedStart = start - charsToAdd.length;
|
||||||
|
let expandedEnd = end + charsToAdd.length;
|
||||||
|
|
||||||
|
// Ensure expanded range is within the bounds of the text
|
||||||
|
if (expandedStart < 0) expandedStart = 0;
|
||||||
|
if (expandedEnd > textarea.value.length) expandedEnd = textarea.value.length;
|
||||||
|
|
||||||
|
// Select the expanded range
|
||||||
|
textarea.setSelectionRange(expandedStart, expandedEnd);
|
||||||
|
|
||||||
|
// Replace the expanded selection with the original selected text
|
||||||
|
document.execCommand('insertText', false, selectedText);
|
||||||
|
// Adjust cursor position
|
||||||
|
cursorShift = -charsToAdd.length;
|
||||||
|
} else {
|
||||||
|
// Add formatting to the selected text
|
||||||
|
let possibleAddedSpace = '';
|
||||||
|
if (selectedText.endsWith(' ')) {
|
||||||
|
possibleAddedSpace = ' ';
|
||||||
|
selectedText = selectedText.substring(0, selectedText.length - 1);
|
||||||
|
end--; // Adjust the end index since we removed the space
|
||||||
|
}
|
||||||
|
// To add the formatting, we need to select the text first
|
||||||
|
textarea.focus();
|
||||||
|
document.execCommand('insertText', false, charsToAdd + selectedText + charsToAdd + possibleAddedSpace);
|
||||||
|
}
|
||||||
|
} else {// No text is selected
|
||||||
|
//check 1 character before and after the cursor for non-space characters
|
||||||
|
|
||||||
|
if (beforeCaret !== ' ' && afterCaret !== ' ' && afterCaret !== '' && beforeCaret !== '') { //look for caret in the middle of a word
|
||||||
|
//expand the selection range until the next space on both sides
|
||||||
|
let midCaretExpandedStart = start - 1;
|
||||||
|
let midCaretExpandedEnd = end + 1;
|
||||||
|
while (midCaretExpandedStart > 0 && textarea.value.substring(midCaretExpandedStart - 1, midCaretExpandedStart) !== ' ') {
|
||||||
|
midCaretExpandedStart--;
|
||||||
|
}
|
||||||
|
while (midCaretExpandedEnd < textarea.value.length && textarea.value.substring(midCaretExpandedEnd, midCaretExpandedEnd + 1) !== ' ') {
|
||||||
|
midCaretExpandedEnd++;
|
||||||
|
}
|
||||||
|
//make a selection of the discovered word
|
||||||
|
textarea.setSelectionRange(midCaretExpandedStart, midCaretExpandedEnd);
|
||||||
|
//set variables for comparison
|
||||||
|
let discoveredWordWithPossibleFormatting = textarea.value.substring(midCaretExpandedStart, midCaretExpandedEnd).trim();
|
||||||
|
let discoveredWord = '';
|
||||||
|
|
||||||
|
if (discoveredWordWithPossibleFormatting.endsWith(charsToAdd) && discoveredWordWithPossibleFormatting.startsWith(charsToAdd)) {
|
||||||
|
discoveredWord = textarea.value.substring(midCaretExpandedStart + charsToAdd.length, midCaretExpandedEnd - charsToAdd.length).trim();
|
||||||
|
} else {
|
||||||
|
discoveredWord = textarea.value.substring(midCaretExpandedStart, midCaretExpandedEnd).trim();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (charsToAdd + discoveredWord + charsToAdd === discoveredWordWithPossibleFormatting) {
|
||||||
|
|
||||||
|
// Replace the expanded selection with the original discovered word
|
||||||
|
textarea.focus();
|
||||||
|
document.execCommand('insertText', false, discoveredWord);
|
||||||
|
// Adjust cursor position
|
||||||
|
cursorShift = -charsToAdd.length;
|
||||||
|
} else { //format did not previously exist, so add it
|
||||||
|
textarea.focus();
|
||||||
|
document.execCommand('insertText', false, charsToAdd + discoveredWord + charsToAdd);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
} else { //caret is not inside a word, so just add the formatting
|
||||||
|
textarea.focus();
|
||||||
|
textarea.setSelectionRange(start, end);
|
||||||
|
selectedText = textarea.value.substring(start, end);
|
||||||
|
document.execCommand('insertText', false, charsToAdd + selectedText + charsToAdd);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Manually trigger the 'input' event to make undo/redo work
|
||||||
|
let event = new Event('input', { bubbles: true });
|
||||||
|
textarea.dispatchEvent(event); // This notifies the browser of a change, allowing undo/redo to function.
|
||||||
|
|
||||||
|
// Update the cursor position
|
||||||
|
if (isTextSelected) {
|
||||||
|
textarea.selectionStart = start + cursorShift;
|
||||||
|
textarea.selectionEnd = start + cursorShift + selectedText.length;
|
||||||
|
} else {
|
||||||
|
textarea.selectionStart = start + cursorShift;
|
||||||
|
textarea.selectionEnd = start + cursorShift;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
@ -200,6 +200,7 @@ let power_user = {
|
|||||||
relaxed_api_urls: false,
|
relaxed_api_urls: false,
|
||||||
world_import_dialog: true,
|
world_import_dialog: true,
|
||||||
enable_auto_select_input: false,
|
enable_auto_select_input: false,
|
||||||
|
enable_md_hotkeys: false,
|
||||||
tag_import_setting: tag_import_setting.ASK,
|
tag_import_setting: tag_import_setting.ASK,
|
||||||
disable_group_trimming: false,
|
disable_group_trimming: false,
|
||||||
single_line: false,
|
single_line: false,
|
||||||
@ -1452,6 +1453,7 @@ async function loadPowerUserSettings(settings, data) {
|
|||||||
$('#relaxed_api_urls').prop('checked', power_user.relaxed_api_urls);
|
$('#relaxed_api_urls').prop('checked', power_user.relaxed_api_urls);
|
||||||
$('#world_import_dialog').prop('checked', power_user.world_import_dialog);
|
$('#world_import_dialog').prop('checked', power_user.world_import_dialog);
|
||||||
$('#enable_auto_select_input').prop('checked', power_user.enable_auto_select_input);
|
$('#enable_auto_select_input').prop('checked', power_user.enable_auto_select_input);
|
||||||
|
$('#enable_md_hotkeys').prop('checked', power_user.enable_md_hotkeys);
|
||||||
$('#trim_spaces').prop('checked', power_user.trim_spaces);
|
$('#trim_spaces').prop('checked', power_user.trim_spaces);
|
||||||
$('#continue_on_send').prop('checked', power_user.continue_on_send);
|
$('#continue_on_send').prop('checked', power_user.continue_on_send);
|
||||||
$('#quick_continue').prop('checked', power_user.quick_continue);
|
$('#quick_continue').prop('checked', power_user.quick_continue);
|
||||||
@ -1601,6 +1603,17 @@ async function loadPowerUserSettings(settings, data) {
|
|||||||
switchSpoilerMode();
|
switchSpoilerMode();
|
||||||
loadMovingUIState();
|
loadMovingUIState();
|
||||||
loadCharListState();
|
loadCharListState();
|
||||||
|
toggleMDHotkeyIconDisplay();
|
||||||
|
}
|
||||||
|
|
||||||
|
function toggleMDHotkeyIconDisplay() {
|
||||||
|
if (power_user.enable_md_hotkeys) {
|
||||||
|
$('.mdhotkey_location').each(function () {
|
||||||
|
$(this).parent().append('<i class="fa-brands fa-markdown mdhotkey_icon"></i>');
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
$('.mdhotkey_icon').remove();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function loadCharListState() {
|
function loadCharListState() {
|
||||||
@ -3610,6 +3623,13 @@ $(document).ready(() => {
|
|||||||
saveSettingsDebounced();
|
saveSettingsDebounced();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
$('#enable_md_hotkeys').on('input', function () {
|
||||||
|
const value = !!$(this).prop('checked');
|
||||||
|
power_user.enable_md_hotkeys = value;
|
||||||
|
toggleMDHotkeyIconDisplay();
|
||||||
|
saveSettingsDebounced();
|
||||||
|
});
|
||||||
|
|
||||||
$('#spoiler_free_mode').on('input', function () {
|
$('#spoiler_free_mode').on('input', function () {
|
||||||
power_user.spoiler_free_mode = !!$(this).prop('checked');
|
power_user.spoiler_free_mode = !!$(this).prop('checked');
|
||||||
switchSpoilerMode();
|
switchSpoilerMode();
|
||||||
|
@ -1,13 +1,31 @@
|
|||||||
<span data-i18n="help_hotkeys_0">Hotkeys/Keybinds</span>:
|
<div>
|
||||||
|
<strong data-i18n="help_hotkeys_0">Chat Hotkeys</strong>
|
||||||
|
</div>
|
||||||
<ul>
|
<ul>
|
||||||
<li><tt data-i18n="help_hotkeys_1">Up</tt> = <span data-i18n="help_hotkeys_2">Edit last message in chat</span></li>
|
<li><kbd data-i18n="help_hotkeys_1">Up</kbd> = <span data-i18n="help_hotkeys_2">Edit last message in chat</span></li>
|
||||||
<li><tt data-i18n="help_hotkeys_3">Ctrl+Up</tt> = <span data-i18n="help_hotkeys_4">Edit last USER message in chat</span></li>
|
<li><kbd data-i18n="help_hotkeys_3">Ctrl+Up</kbd> = <span data-i18n="help_hotkeys_4">Edit last USER message in chat</span></li>
|
||||||
<li><tt data-i18n="help_hotkeys_5">Left</tt> = <span data-i18n="help_hotkeys_6">swipe left</span></li>
|
<li><kbd data-i18n="help_hotkeys_5">Left</kbd> = <span data-i18n="help_hotkeys_6">swipe left</span></li>
|
||||||
<li><tt data-i18n="help_hotkeys_7">Right</tt> = <span data-i18n="help_hotkeys_8">swipe right (NOTE: swipe hotkeys are disabled when chatbar has something typed into it)</span></li>
|
<li><kbd data-i18n="help_hotkeys_7">Right</kbd> = <span data-i18n="help_hotkeys_8">swipe right (NOTE: swipe hotkeys are disabled when chatbar has something typed into it)</span></li>
|
||||||
<li><tt data-i18n="help_hotkeys_9">Enter</tt> <span data-i18n="help_hotkeys_10">(with chat bar selected)</span> = <span data-i18n="help_hotkeys_10_1">send your message to AI</span></li>
|
<li><kbd data-i18n="help_hotkeys_9">Enter</kbd> <span data-i18n="help_hotkeys_10">(with chat bar selected)</span> = <span data-i18n="help_hotkeys_10_1">send your message to AI</span></li>
|
||||||
<li><tt data-i18n="help_hotkeys_11">Ctrl+Enter</tt> = <span data-i18n="help_hotkeys_12">Regenerate the last AI response</span></li>
|
<li><kbd data-i18n="help_hotkeys_11">Ctrl+Enter</kbd> = <span data-i18n="help_hotkeys_12">Regenerate the last AI response</span></li>
|
||||||
<li><tt data-i18n="help_hotkeys_13">Alt+Enter</tt> = <span data-i18n="help_hotkeys_14">Continue the last AI response</span></li>
|
<li><kbd data-i18n="help_hotkeys_13">Alt+Enter</kbd> = <span data-i18n="help_hotkeys_14">Continue the last AI response</span></li>
|
||||||
<li><tt data-i18n="help_hotkeys_15">Escape</tt> = <span data-i18n="help_hotkeys_16">stop AI response generation, close UI panels, cancel message edit</span></li>
|
<li><kbd data-i18n="help_hotkeys_15">Escape</kbd> = <span data-i18n="help_hotkeys_16">stop AI response generation, close UI panels, cancel message edit</span></li>
|
||||||
<li><tt data-i18n="help_hotkeys_17">Ctrl+Shift+Up</tt> = <span data-i18n="help_hotkeys_18">Scroll to context line</span></li>
|
<li><kbd data-i18n="help_hotkeys_17">Ctrl+Shift+Up</kbd> = <span data-i18n="help_hotkeys_18">Scroll to context line</span></li>
|
||||||
<li><tt data-i18n="help_hotkeys_19">Ctrl+Shift+Down</tt> = <span data-i18n="help_hotkeys_20">Scroll chat to bottom</span></li>
|
<li><kbd data-i18n="help_hotkeys_19">Ctrl+Shift+Down</kbd> = <span data-i18n="help_hotkeys_20">Scroll chat to bottom</span></li>
|
||||||
|
</ul>
|
||||||
|
<div>
|
||||||
|
<strong>Markdown Hotkeys</strong>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<small>
|
||||||
|
<span>Works in the chatbar and textareas marked with this icon:</span>
|
||||||
|
<code><i class="fa-brands fa-markdown"></i></code>
|
||||||
|
</small>
|
||||||
|
</div>
|
||||||
|
<ul>
|
||||||
|
<li><kbd>Ctrl+B</kbd> = <span>**bold**</span></li>
|
||||||
|
<li><kbd>Ctrl+I</kbd> = <span>*italic*</span></li>
|
||||||
|
<li><kbd>Ctrl+U</kbd> = <span>__underline__</span></li>
|
||||||
|
<li><kbd>Ctrl+K</kbd> = <span>`inline code`</span></li>
|
||||||
|
<li><kbd>Ctrl+Shift+~</kbd> = <span>~~strikethrough~~</span></li>
|
||||||
</ul>
|
</ul>
|
||||||
|
Loading…
x
Reference in New Issue
Block a user