Matching
@@ -4171,14 +4191,14 @@
STRICT_ESCAPING
-
+ REPLACE_GETVAR
-
+
diff --git a/public/lib/eventemitter.js b/public/lib/eventemitter.js
index a1b40e38c..3177518de 100644
--- a/public/lib/eventemitter.js
+++ b/public/lib/eventemitter.js
@@ -42,6 +42,46 @@ EventEmitter.prototype.on = function (event, listener) {
this.events[event].push(listener);
};
+/**
+ * Makes the listener the last to be called when the event is emitted
+ * @param {string} event Event name
+ * @param {function} listener Event listener
+ */
+EventEmitter.prototype.makeLast = function (event, listener) {
+ if (typeof this.events[event] !== 'object') {
+ this.events[event] = [];
+ }
+
+ const events = this.events[event];
+ const idx = events.indexOf(listener);
+
+ if (idx > -1) {
+ events.splice(idx, 1);
+ }
+
+ events.push(listener);
+}
+
+/**
+ * Makes the listener the first to be called when the event is emitted
+ * @param {string} event Event name
+ * @param {function} listener Event listener
+ */
+EventEmitter.prototype.makeFirst = function (event, listener) {
+ if (typeof this.events[event] !== 'object') {
+ this.events[event] = [];
+ }
+
+ const events = this.events[event];
+ const idx = events.indexOf(listener);
+
+ if (idx > -1) {
+ events.splice(idx, 1);
+ }
+
+ events.unshift(listener);
+}
+
EventEmitter.prototype.removeListener = function (event, listener) {
var idx;
diff --git a/public/locales/ko-kr.json b/public/locales/ko-kr.json
index 03fb8c8f1..1bda6fb97 100644
--- a/public/locales/ko-kr.json
+++ b/public/locales/ko-kr.json
@@ -425,7 +425,7 @@
"Start new chat": "새로운 채팅 시작",
"View past chats": "과거 채팅 보기",
"Delete messages": "메시지 삭제",
- "Impersonate": "사칭",
+ "Impersonate": "대신 말하기",
"Regenerate": "재생성",
"PNG": "PNG",
"JSON": "JSON",
@@ -914,7 +914,30 @@
"Learn how to contribute your idle GPU cycles to the Horde": "여유로운 GPU 주기를 호드에 기여하는 방법 배우기",
"Use the appropriate tokenizer for Google models via their API. Slower prompt processing, but offers much more accurate token counting.": "Google 모델용 적절한 토크나이저를 사용하여 API를 통해 제공됩니다. 더 느린 프롬프트 처리지만 훨씬 정확한 토큰 계산을 제공합니다.",
"Load koboldcpp order": "코볼드 CPP 순서로 로드",
- "Use Google Tokenizer": "Google 토크나이저 사용"
-
+ "Use Google Tokenizer": "구글 토크나이저 사용",
+ "Hide Chat Avatars": "채팅 아바타 숨기기",
+ "Hide avatars in chat messages.": "채팅 메시지에서 아바타 숨김.",
+ "Avatar Hover Magnification": "아바타 마우스오버 시 확대",
+ "Enable magnification for zoomed avatar display.": "마우스 오버 시 아바타가 커지도록 설정하세요.",
+ "AutoComplete Settings": "자동 완성 설정",
+ "Autocomplete Matching": "자동 완성 매칭",
+ "Starts with": "시작하는 단어로",
+ "Autocomplete Style": "자동 완성 스타일",
+ "Includes": "포함하는",
+ "Fuzzy": "퍼지 매칭",
+ "Follow Theme": "테마 적용",
+ "Dark": "다크 모드",
+ "Sets the font size of the autocomplete.": "자동 완성 글꼴 크기 설정",
+ "Autocomplete Width": "자동 완성 너비 조절",
+ "Parser Flags": "파서 플래그 설정",
+ "Sets default flags for the STscript parser.": "STscript 파서 기본 플래그 설정",
+ "Switch to stricter escaping, allowing all delimiting characters to be escaped with a backslash, and backslashes to be escaped as well.": "모든 구분자를 백슬래시로 이스케이핑하고, 백슬래시 자체도 이스케이프할 수 있도록 엄격한 방식으로 전환합니다.",
+ "STscript Settings": "STscript 설정",
+ "Smooth Streaming": "부드러운 스트리밍",
+ "Experimental feature. May not work for all backends.": "실험적인 기능으로, 모든 백엔드에서 작동이 보장되지는 않을 수 있습니다.",
+ "Char List Subheader": "문자 목록 하위 제목",
+ "Account": "계정",
+ "Theme Colors": "테마 색상",
+ "# Messages to Load": "로딩할 메시지 수"
}
\ No newline at end of file
diff --git a/public/scripts/RossAscends-mods.js b/public/scripts/RossAscends-mods.js
index 5239b71c3..826f968ea 100644
--- a/public/scripts/RossAscends-mods.js
+++ b/public/scripts/RossAscends-mods.js
@@ -702,7 +702,7 @@ const isFirefox = navigator.userAgent.toLowerCase().indexOf('firefox') > -1;
*/
function autoFitSendTextArea() {
const originalScrollBottom = chatBlock.scrollHeight - (chatBlock.scrollTop + chatBlock.offsetHeight);
- if (sendTextArea.scrollHeight + 3 == sendTextArea.offsetHeight) {
+ if (Math.ceil(sendTextArea.scrollHeight + 3) >= Math.floor(sendTextArea.offsetHeight)) {
// Needs to be pulled dynamically because it is affected by font size changes
const sendTextAreaMinHeight = window.getComputedStyle(sendTextArea).getPropertyValue('min-height');
sendTextArea.style.height = sendTextAreaMinHeight;
diff --git a/public/scripts/autocomplete/AutoComplete.js b/public/scripts/autocomplete/AutoComplete.js
index d1205f6e8..dc000f732 100644
--- a/public/scripts/autocomplete/AutoComplete.js
+++ b/public/scripts/autocomplete/AutoComplete.js
@@ -25,6 +25,8 @@ export class AutoComplete {
/**@type {boolean}*/ isReplaceable = false;
/**@type {boolean}*/ isShowingDetails = false;
/**@type {boolean}*/ wasForced = false;
+ /**@type {boolean}*/ isForceHidden = false;
+ /**@type {boolean}*/ canBeAutoHidden = false;
/**@type {string}*/ text;
/**@type {AutoCompleteNameResult}*/ parserResult;
@@ -57,6 +59,10 @@ export class AutoComplete {
return power_user.stscript.matching ?? 'fuzzy';
}
+ get autoHide() {
+ return power_user.stscript.autocomplete.autoHide ?? false;
+ }
+
@@ -224,6 +230,16 @@ export class AutoComplete {
return a.name.localeCompare(b.name);
}
+ basicAutoHideCheck() {
+ // auto hide only if at least one char has been typed after the name + space
+ return this.textarea.selectionStart > this.parserResult.start
+ + this.parserResult.name.length
+ + (this.startQuote ? 1 : 0)
+ + (this.endQuote ? 1 : 0)
+ + 1
+ ;
+ }
+
/**
* Show the autocomplete.
* @param {boolean} isInput Whether triggered by input.
@@ -244,6 +260,9 @@ export class AutoComplete {
return this.hide();
}
+ // disable force-hide if trigger was forced
+ if (isForced) this.isForceHidden = false;
+
// request provider to get name result (potentially "incomplete", i.e. not an actual existing name) for
// cursor position
this.parserResult = await this.getNameAt(this.text, this.textarea.selectionStart);
@@ -275,12 +294,16 @@ export class AutoComplete {
this.name = this.name.slice(0, this.textarea.selectionStart - (this.parserResult.start) - (this.startQuote ? 1 : 0));
this.parserResult.name = this.name;
this.isReplaceable = true;
+ this.isForceHidden = false;
+ this.canBeAutoHidden = false;
} else {
this.isReplaceable = false;
+ this.canBeAutoHidden = this.basicAutoHideCheck();
}
} else {
// if not forced and no user input -> just show details
this.isReplaceable = false;
+ this.canBeAutoHidden = this.basicAutoHideCheck();
}
if (isForced || isInput || isSelect) {
@@ -292,8 +315,11 @@ export class AutoComplete {
this.secondaryParserResult = result;
this.name = this.secondaryParserResult.name;
this.isReplaceable = isForced || this.secondaryParserResult.isRequired;
+ this.isForceHidden = false;
+ this.canBeAutoHidden = false;
} else {
this.isReplaceable = false;
+ this.canBeAutoHidden = this.basicAutoHideCheck();
}
}
}
@@ -314,7 +340,17 @@ export class AutoComplete {
// filter the list of options by the partial name according to the matching type
.filter(it => this.isReplaceable || it.name == '' ? matchers[this.matchType](it.name) : it.name.toLowerCase() == this.name)
// remove aliases
- .filter((it,idx,list) => list.findIndex(opt=>opt.value == it.value) == idx)
+ .filter((it,idx,list) => list.findIndex(opt=>opt.value == it.value) == idx);
+
+ if (this.result.length == 0 && this.effectiveParserResult != this.parserResult && isForced) {
+ // no matching secondary results and forced trigger -> show current command details
+ this.secondaryParserResult = null;
+ this.result = [this.effectiveParserResult.optionList.find(it=>it.name == this.effectiveParserResult.name)];
+ this.name = this.effectiveParserResult.name;
+ this.fuzzyRegex = /(.*)(.*)(.*)/;
+ }
+
+ this.result = this.result
// update remaining options
.map(option => {
// build element
@@ -336,6 +372,15 @@ export class AutoComplete {
;
+
+ if (this.isForceHidden) {
+ // hidden with escape
+ return this.hide();
+ }
+ if (this.autoHide && this.canBeAutoHidden && !isForced && this.effectiveParserResult == this.parserResult && this.result.length == 1) {
+ // auto hide user setting enabled and somewhere after name part and would usually show command details
+ return this.hide();
+ }
if (this.result.length == 0) {
if (!isInput) {
// no result and no input? hide autocomplete
@@ -683,6 +728,8 @@ export class AutoComplete {
if (evt.ctrlKey || evt.altKey || evt.shiftKey) return;
evt.preventDefault();
evt.stopPropagation();
+ this.isForceHidden = true;
+ this.wasForced = false;
this.hide();
return;
}
diff --git a/public/scripts/extensions/assets/index.js b/public/scripts/extensions/assets/index.js
index 50bad2479..2d96fa875 100644
--- a/public/scripts/extensions/assets/index.js
+++ b/public/scripts/extensions/assets/index.js
@@ -109,7 +109,7 @@ function downloadAssetsList(url) {
`);
}
- for (const i in availableAssets[assetType]) {
+ for (const i in availableAssets[assetType].sort((a, b) => a?.name && b?.name && a['name'].localeCompare(b['name']))) {
const asset = availableAssets[assetType][i];
const elemId = `assets_install_${assetType}_${i}`;
let element = $('', { id: elemId, class: 'asset-download-button right_menu_button' });
@@ -200,6 +200,9 @@ function downloadAssetsList(url) {
`);
if (assetType === 'character') {
+ if (asset.highlight) {
+ assetBlock.find('.asset-name').append('');
+ }
assetBlock.find('.asset-name').prepend(`