Merge branch 'staging' into connection-profile-omit
| @@ -6090,7 +6090,10 @@ | ||
| 6090 | 6090 | <div class="alternate_grettings flexFlowColumn flex-container"> |
| 6091 | 6091 | <div class="title_restorable"> |
| 6092 | 6092 | <h3><span data-i18n="Alternate Greetings" class="mdhotkey_location">Alternate Greetings</span></h3> |
| 6093 | - <div title="Add" class="menu_button fa-solid fa-plus add_alternate_greeting" data-i18n="[title]Add"></div> | |
| 6093 | + <div class="menu_button menu_button_icon add_alternate_greeting"> | |
| 6094 | + <i class="fa-solid fa-plus"></i> | |
| 6095 | + <span data-i18n="Add">Add</span> | |
| 6096 | + </div> | |
| 6094 | 6097 | </div> |
| 6095 | 6098 | <small class="justifyLeft" data-i18n="Alternate_Greetings_desc"> |
| 6096 | 6099 | These will be displayed as swipes on the first message when starting a new chat. |
| @@ -6106,11 +6109,18 @@ | ||
| 6106 | 6109 | </div> |
| 6107 | 6110 | <div id="alternate_greeting_form_template" class="template_element"> |
| 6108 | 6111 | <div class="alternate_greeting"> |
| 6109 | 6112 | <divdetails class="title_restorable"open> |
| 6110 | - <strong><span data-i18n="Alternate Greeting #">Alternate Greeting #</span><span class="greeting_index"></span></strong> | |
| 6113 | + <summary> | |
| 6111 | - <div class="menu_button fa-solid fa-trash-alt delete_alternate_greeting"></div> | |
| 6114 | + <div class="title_restorable"> | |
| 6112 | - </div> | |
| 6115 | + <strong><span data-i18n="Alternate Greeting #">Alternate Greeting #</span><span class="greeting_index"></span></strong> | |
| 6113 | - <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> | |
| 6116 | + <div class="menu_button menu_button_icon delete_alternate_greeting"> | |
| 6117 | + <i class="fa-solid fa-trash-alt"></i> | |
| 6118 | + <span data-i18n="Delete">Delete</span> | |
| 6119 | + </div> | |
| 6120 | + </div> | |
| 6121 | + </summary> | |
| 6122 | + <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="12"></textarea> | |
| 6123 | + </details> | |
| 6114 | 6124 | </div> |
| 6115 | 6125 | </div> |
| 6116 | 6126 | |
| @@ -7390,7 +7390,7 @@ function onScenarioOverrideRemoveClick() { | ||
| 7390 | 7390 | */ |
| 7391 | 7391 | export function callPopup(text, type, inputValue = '', { okButton, rows, wide, wider, large, allowHorizontalScrolling, allowVerticalScrolling, cropAspect } = {}) { |
| 7392 | 7392 | function getOkButtonText() { |
| 7393 | 7393 | if (['text', 'alternate_greeting', 'char_not_selected'].includes(popup_type)) { |
| 7394 | 7394 | $dialoguePopupCancel.css('display', 'none'); |
| 7395 | 7395 | return okButton ?? 'Ok'; |
| 7396 | 7396 | } else if (['delete_extension'].includes(popup_type)) { |
| @@ -7827,24 +7827,42 @@ function openAlternateGreetings() { | ||
| 7827 | 7827 | |
| 7828 | 7828 | const template = $('#alternate_greetings_template .alternate_grettings').clone(); |
| 7829 | 7829 | const getArray = () => menu_type == 'create' ? create_save.alternate_greetings : characters[chid].data.alternate_greetings; |
| 7830 | + const popup = new Popup(template, POPUP_TYPE.TEXT, '', { | |
| 7831 | + wide: true, | |
| 7832 | + large: true, | |
| 7833 | + allowVerticalScrolling: true, | |
| 7834 | + onClose: async () => { | |
| 7835 | + if (menu_type !== 'create') { | |
| 7836 | + await createOrEditCharacter(); | |
| 7837 | + } | |
| 7838 | + } | |
| 7839 | + }); | |
| 7830 | 7840 | |
| 7831 | 7841 | for (let index = 0; index < getArray().length; index++) { |
| 7832 | 7842 | addAlternateGreeting(template, getArray()[index], index, getArray, popup); |
| 7833 | 7843 | } |
| 7834 | 7844 | |
| 7835 | 7845 | template.find('.add_alternate_greeting').on('click', function () { |
| 7836 | 7846 | const array = getArray(); |
| 7837 | 7847 | const index = array.length; |
| 7838 | 7848 | array.push(''); |
| 7839 | 7849 | addAlternateGreeting(template, '', index, getArray, popup); |
| 7840 | 7850 | updateAlternateGreetingsHintVisibility(template); |
| 7841 | 7851 | }); |
| 7842 | 7852 | |
| 7843 | 7853 | updateAlternateGreetingsHintVisibility(template); |
| 7844 | - callPopup(template, 'alternate_greeting', '', { wide: true, large: true }); | |
| 7854 | + popup.show(); | |
| 7845 | 7855 | } |
| 7846 | 7856 | |
| 7847 | -function addAlternateGreeting(template, greeting, index, getArray) { | |
| 7857 | +/** | |
| 7858 | + * Adds an alternate greeting to the template. | |
| 7859 | + * @param {JQuery<HTMLElement>} template | |
| 7860 | + * @param {string} greeting | |
| 7861 | + * @param {number} index | |
| 7862 | + * @param {() => any[]} getArray | |
| 7863 | + * @param {Popup} popup | |
| 7864 | + */ | |
| 7865 | +function addAlternateGreeting(template, greeting, index, getArray, popup) { | |
| 7848 | 7866 | const greetingBlock = $('#alternate_greeting_form_template .alternate_greeting').clone(); |
| 7849 | 7867 | greetingBlock.find('.alternate_greeting_text').on('input', async function () { |
| 7850 | 7868 | const value = $(this).val(); |
| @@ -7852,11 +7870,16 @@ function addAlternateGreeting(template, greeting, index, getArray) { | ||
| 7852 | 7870 | array[index] = value; |
| 7853 | 7871 | }).val(greeting); |
| 7854 | 7872 | greetingBlock.find('.greeting_index').text(index + 1); |
| 7855 | 7873 | greetingBlock.find('.delete_alternate_greeting').on('click', async function (event) { |
| 7874 | + event.preventDefault(); | |
| 7875 | + event.stopPropagation(); | |
| 7876 | + | |
| 7856 | 7877 | if (confirm('Are you sure you want to delete this alternate greeting?')) { |
| 7857 | 7878 | const array = getArray(); |
| 7858 | 7879 | array.splice(index, 1); |
| 7880 | + | |
| 7859 | 7881 | // We need to reopen the popup to update the index numbers |
| 7882 | + await popup.complete(POPUP_RESULT.AFFIRMATIVE); | |
| 7860 | 7883 | openAlternateGreetings(); |
| 7861 | 7884 | } |
| 7862 | 7885 | }); |
| @@ -9574,9 +9597,6 @@ jQuery(async function () { | ||
| 9574 | 9597 | }, 2000); |
| 9575 | 9598 | } |
| 9576 | 9599 | } |
| 9577 | - if (popup_type == 'alternate_greeting' && menu_type !== 'create') { | |
| 9578 | - createOrEditCharacter(); | |
| 9579 | - } | |
| 9580 | 9600 | |
| 9581 | 9601 | if (dialogueResolve) { |
| 9582 | 9602 | if (popup_type == 'input') { |
| @@ -3469,7 +3469,7 @@ function getModelOptions(quiet) { | ||
| 3469 | 3469 | case 'openai': |
| 3470 | 3470 | return oai_settings.chat_completion_source; |
| 3471 | 3471 | default: |
| 3472 | 3472 | return nullResultnull; |
| 3473 | 3473 | } |
| 3474 | 3474 | } |
| 3475 | 3475 | |
| @@ -12,7 +12,7 @@ import { commonEnumProviders, enumIcons } from './slash-commands/SlashCommandCom | ||
| 12 | 12 | import { SlashCommandEnumValue, enumTypes } from './slash-commands/SlashCommandEnumValue.js'; |
| 13 | 13 | import { PARSER_FLAG, SlashCommandParser } from './slash-commands/SlashCommandParser.js'; |
| 14 | 14 | import { SlashCommandScope } from './slash-commands/SlashCommandScope.js'; |
| 15 | 15 | import { isFalseBoolean, convertValueType, isTrueBoolean } from './utils.js'; |
| 16 | 16 | |
| 17 | 17 | /** @typedef {import('./slash-commands/SlashCommandParser.js').NamedArguments} NamedArguments */ |
| 18 | 18 | /** @typedef {import('./slash-commands/SlashCommand.js').UnnamedArguments} UnnamedArguments */ |
| @@ -488,7 +488,7 @@ function existsGlobalVariable(name) { | ||
| 488 | 488 | /** |
| 489 | 489 | * Parses boolean operands from command arguments. |
| 490 | 490 | * @param {object} args Command arguments |
| 491 | 491 | * @returns {{a: string | number, b: string | number?, rule: string}} Boolean operands |
| 492 | 492 | */ |
| 493 | 493 | export function parseBooleanOperands(args) { |
| 494 | 494 | // Resolution order: numeric literal, local variable, global variable, string literal |
| @@ -497,6 +497,9 @@ export function parseBooleanOperands(args) { | ||
| 497 | 497 | */ |
| 498 | 498 | function getOperand(operand) { |
| 499 | 499 | if (operand === undefined) { |
| 500 | + return undefined; | |
| 501 | + } | |
| 502 | + if (operand === '') { | |
| 500 | 503 | return ''; |
| 501 | 504 | } |
| 502 | 505 | |
| @@ -525,8 +528,8 @@ export function parseBooleanOperands(args) { | ||
| 525 | 528 | return stringLiteral || ''; |
| 526 | 529 | } |
| 527 | 530 | |
| 528 | 531 | const left = getOperand(args.a ||?? args.left ||?? args.first ||?? args.x); |
| 529 | 532 | const right = getOperand(args.b ||?? args.right ||?? args.second ||?? args.y); |
| 530 | 533 | const rule = args.rule; |
| 531 | 534 | |
| 532 | 535 | return { a: left, b: right, rule }; |
| @@ -534,84 +537,79 @@ export function parseBooleanOperands(args) { | ||
| 534 | 537 | |
| 535 | 538 | /** |
| 536 | 539 | * Evaluates a boolean comparison rule. |
| 537 | - * @param {string} rule Boolean comparison rule | |
| 540 | + * | |
| 541 | + * @param {string?} rule Boolean comparison rule | |
| 538 | 542 | * @param {string|number} a The left operand |
| 539 | 543 | * @param {string|number?} b The right operand |
| 540 | 544 | * @returns {boolean} True if the rule yields true, false otherwise |
| 541 | 545 | */ |
| 542 | 546 | export function evalBoolean(rule, a, b) { |
| 543 | 547 | if (!rulea === undefined) { |
| 544 | - toastr.warning('The rule must be specified for the boolean comparison.', 'Invalid command'); | |
| 548 | + throw new Error('Left operand is not provided'); | |
| 545 | - throw new Error('Invalid command.'); | |
| 549 | + } | |
| 550 | + | |
| 551 | + // If right-hand side was not provided, whe just check if the left side is truthy | |
| 552 | + if (b === undefined) { | |
| 553 | + switch (rule) { | |
| 554 | + case undefined: | |
| 555 | + case 'not': { | |
| 556 | + const resultOnTruthy = rule !== 'not'; | |
| 557 | + if (isTrueBoolean(String(a))) return resultOnTruthy; | |
| 558 | + if (isFalseBoolean(String(a))) return !resultOnTruthy; | |
| 559 | + return a ? resultOnTruthy : !resultOnTruthy; | |
| 560 | + } | |
| 561 | + default: | |
| 562 | + throw new Error(`Unknown boolean comparison rule for truthy check. If right operand is not provided, the rule must not provided or be 'not'. Provided: ${rule}`); | |
| 563 | + } | |
| 546 | 564 | } |
| 547 | 565 | |
| 548 | - let result = false; | |
| 566 | + // If no rule was provided, we are implicitly using 'eq', as defined for the slash commands | |
| 567 | + rule ??= 'eq'; | |
| 568 | + | |
| 549 | 569 | if (typeof a === 'number' && typeof b === 'number') { |
| 550 | 570 | // only do numeric comparison if both operands are numbers |
| 551 | 571 | const aNumber = Number(a); |
| 552 | 572 | const bNumber = Number(b); |
| 553 | 573 | |
| 554 | 574 | switch (rule) { |
| 555 | - case 'not': | |
| 556 | - result = !aNumber; | |
| 557 | - break; | |
| 558 | 575 | case 'gt': |
| 559 | 576 | result =return aNumber > bNumber; |
| 560 | - break; | |
| 561 | 577 | case 'gte': |
| 562 | 578 | result =return aNumber >= bNumber; |
| 563 | - break; | |
| 564 | 579 | case 'lt': |
| 565 | 580 | result =return aNumber < bNumber; |
| 566 | - break; | |
| 567 | 581 | case 'lte': |
| 568 | 582 | result =return aNumber <= bNumber; |
| 569 | - break; | |
| 570 | 583 | case 'eq': |
| 571 | 584 | result =return aNumber === bNumber; |
| 572 | - break; | |
| 573 | 585 | case 'neq': |
| 574 | 586 | result =return aNumber !== bNumber; |
| 575 | - break; | |
| 576 | - default: | |
| 577 | - toastr.error('Unknown boolean comparison rule for type number.', 'Invalid command'); | |
| 578 | - throw new Error('Invalid command.'); | |
| 579 | - } | |
| 580 | - } else { | |
| 581 | - // otherwise do case-insensitive string comparsion, stringify non-strings | |
| 582 | - let aString; | |
| 583 | - let bString; | |
| 584 | - if (typeof a == 'string') { | |
| 585 | - aString = a.toLowerCase(); | |
| 586 | - } else { | |
| 587 | - aString = JSON.stringify(a).toLowerCase(); | |
| 588 | - } | |
| 589 | - if (typeof b == 'string') { | |
| 590 | - bString = b.toLowerCase(); | |
| 591 | - } else { | |
| 592 | - bString = JSON.stringify(b).toLowerCase(); | |
| 593 | - } | |
| 594 | - | |
| 595 | - switch (rule) { | |
| 596 | 587 | case 'in': |
| 597 | - result = aString.includes(bString); | |
| 598 | - break; | |
| 599 | 588 | case 'nin': |
| 600 | - result = !aString.includes(bString); | |
| 589 | + // Fall through to string comparison. Otherwise you could not check if 12345 contains 45 for example. | |
| 601 | - break; | |
| 590 | + console.debug(`Boolean comparison rule '${rule}' is not supported for type number. Falling back to string comparison.`); | |
| 602 | - case 'eq': | |
| 603 | - result = aString === bString; | |
| 604 | - break; | |
| 605 | - case 'neq': | |
| 606 | - result = aString !== bString; | |
| 607 | 591 | break; |
| 608 | 592 | default: |
| 609 | - toastr.error('Unknown boolean comparison rule for type string.', 'Invalid /if command'); | |
| 593 | + throw new Error(`Unknown boolean comparison rule for type number. Accepted: gt, gte, lt, lte, eq, neq. Provided: ${rule}`); | |
| 610 | - throw new Error('Invalid command.'); | |
| 611 | 594 | } |
| 612 | 595 | } |
| 613 | 596 | |
| 614 | - return result; | |
| 597 | + // otherwise do case-insensitive string comparsion, stringify non-strings | |
| 598 | + let aString = (typeof a === 'string') ? a.toLowerCase() : JSON.stringify(a).toLowerCase(); | |
| 599 | + let bString = (typeof b === 'string') ? b.toLowerCase() : JSON.stringify(b).toLowerCase(); | |
| 600 | + | |
| 601 | + switch (rule) { | |
| 602 | + case 'in': | |
| 603 | + return aString.includes(bString); | |
| 604 | + case 'nin': | |
| 605 | + return !aString.includes(bString); | |
| 606 | + case 'eq': | |
| 607 | + return aString === bString; | |
| 608 | + case 'neq': | |
| 609 | + return aString !== bString; | |
| 610 | + default: | |
| 611 | + throw new Error(`Unknown boolean comparison rule for type number. Accepted: in, nin, eq, neq. Provided: ${rule}`); | |
| 612 | + } | |
| 615 | 613 | } |
| 616 | 614 | |
| 617 | 615 | /** |
| @@ -1317,32 +1315,36 @@ export function registerVariableCommands() { | ||
| 1317 | 1315 | typeList: [ARGUMENT_TYPE.VARIABLE_NAME, ARGUMENT_TYPE.STRING, ARGUMENT_TYPE.NUMBER], |
| 1318 | 1316 | isRequired: true, |
| 1319 | 1317 | enumProvider: commonEnumProviders.variables('all'), |
| 1320 | - forceEnum: false, | |
| 1321 | 1318 | }), |
| 1322 | 1319 | SlashCommandNamedArgument.fromProps({ |
| 1323 | 1320 | name: 'right', |
| 1324 | 1321 | description: 'right operand', |
| 1325 | 1322 | typeList: [ARGUMENT_TYPE.VARIABLE_NAME, ARGUMENT_TYPE.STRING, ARGUMENT_TYPE.NUMBER], |
| 1326 | - isRequired: true, | |
| 1327 | 1323 | enumProvider: commonEnumProviders.variables('all'), |
| 1328 | - forceEnum: false, | |
| 1329 | 1324 | }), |
| 1330 | 1325 | new SlashCommandNamedArgument.fromProps({ |
| 1331 | - 'rule', 'comparison rule', [ARGUMENT_TYPE.STRING], true, false, null, [ | |
| 1326 | + name: 'rule', | |
| 1332 | - new SlashCommandEnumValue('gt', 'a > b'), | |
| 1327 | + description: 'comparison rule', | |
| 1333 | - new SlashCommandEnumValue('gte', 'a >= b'), | |
| 1328 | + typeList: [ARGUMENT_TYPE.STRING], | |
| 1334 | - new SlashCommandEnumValue('lt', 'a < b'), | |
| 1329 | + defaultValue: 'eq', | |
| 1335 | - new SlashCommandEnumValue('lte', 'a <= b'), | |
| 1330 | + enumList: [ | |
| 1336 | 1331 | new SlashCommandEnumValue('eq', 'a == b (strings & numbers)'), |
| 1337 | 1332 | new SlashCommandEnumValue('neq', 'a !== b (strings & numbers)'), |
| 1338 | 1333 | new SlashCommandEnumValue('notin', '!a includes b (strings & numbers as strings)'), |
| 1339 | 1334 | new SlashCommandEnumValue('innin', 'a not includes b (strings & numbers as strings)'), |
| 1340 | 1335 | new SlashCommandEnumValue('ningt', 'a not includes> b (numbers)'), |
| 1341 | - ], | |
| 1336 | + new SlashCommandEnumValue('gte', 'a >= b (numbers)'), | |
| 1342 | - ), | |
| 1337 | + new SlashCommandEnumValue('lt', 'a < b (numbers)'), | |
| 1343 | - new SlashCommandNamedArgument( | |
| 1338 | + new SlashCommandEnumValue('lte', 'a <= b (numbers)'), | |
| 1344 | - 'else', 'command to execute if not true', [ARGUMENT_TYPE.CLOSURE, ARGUMENT_TYPE.SUBCOMMAND], false, | |
| 1339 | + new SlashCommandEnumValue('not', '!a (truthy)'), | |
| 1345 | - ), | |
| 1340 | + ], | |
| 1341 | + forceEnum: true, | |
| 1342 | + }), | |
| 1343 | + SlashCommandNamedArgument.fromProps({ | |
| 1344 | + name: 'else', | |
| 1345 | + description: 'command to execute if not true', | |
| 1346 | + typeList: [ARGUMENT_TYPE.CLOSURE, ARGUMENT_TYPE.SUBCOMMAND], | |
| 1347 | + }), | |
| 1346 | 1348 | ], |
| 1347 | 1349 | unnamedArgumentList: [ |
| 1348 | 1350 | new SlashCommandArgument( |
| @@ -1360,17 +1362,25 @@ export function registerVariableCommands() { | ||
| 1360 | 1362 | Numeric values and string literals for left and right operands supported. |
| 1361 | 1363 | </div> |
| 1362 | 1364 | <div> |
| 1365 | + If the rule is not provided, it defaults to <code>eq</code>. | |
| 1366 | + </div> | |
| 1367 | + <div> | |
| 1368 | + If no right operand is provided, it defaults to checking the <code>left</code> value to be truthy. | |
| 1369 | + A non-empty string or non-zero number is considered truthy, as is the value <code>true</code> or <code>on</code>.<br /> | |
| 1370 | + Only acceptable rules for no provided right operand are <code>not</code>, and no provided rule - which default to returning whether it is not or is truthy. | |
| 1371 | + </div> | |
| 1372 | + <div> | |
| 1363 | 1373 | <strong>Available rules:</strong> |
| 1364 | 1374 | <ul> |
| 1365 | 1375 | <li>gt<code>eq</code> => a >== b <small>(strings & numbers)</small></li> |
| 1366 | 1376 | <li>gte<code>neq</code> => a >!== b <small>(strings & numbers)</small></li> |
| 1367 | 1377 | <li>lt<code>in</code> => a <includes b <small>(strings & numbers as strings)</small></li> |
| 1368 | - <li>lte => a <= b</li> | |
| 1378 | + <li><code>nin</code> => a not includes b <small>(strings & numbers as strings)</small></li> | |
| 1369 | 1379 | <li>eq<code>gt</code> => a ==> b <small>(numbers)</small></li> |
| 1370 | 1380 | <li>neq<code>gte</code> => a !>= b <small>(numbers)</small></li> |
| 1371 | 1381 | <li>not<code>lt</code> => !a < b <small>(numbers)</small></li> |
| 1372 | 1382 | <li>in (strings)<code>lte</code> => a includes<= b <small>(numbers)</small></li> |
| 1373 | - <li>nin (strings) => a not includes b</li> | |
| 1383 | + <li><code>not</code> => !a <small>(truthy)</small></li> | |
| 1374 | 1384 | </ul> |
| 1375 | 1385 | </div> |
| 1376 | 1386 | <div> |
| @@ -1380,6 +1390,17 @@ export function registerVariableCommands() { | ||
| 1380 | 1390 | <pre><code class="language-stscript">/if left=score right=10 rule=gte "/speak You win"</code></pre> |
| 1381 | 1391 | triggers a /speak command if the value of "score" is greater or equals 10. |
| 1382 | 1392 | </li> |
| 1393 | + <li> | |
| 1394 | + <pre><code class="language-stscript">/if left={{lastMessage}} rule=in right=surprise {: /echo SURPISE! :}</code></pre> | |
| 1395 | + executes a subcommand defined as a closure if the given value contains a specified word. | |
| 1396 | + <li> | |
| 1397 | + <pre><code class="language-stscript">/if left=myContent {: /echo My content had some content. :}</code></pre> | |
| 1398 | + executes the defined subcommand, if the provided value of left is truthy (contains some kind of contant that is not empty or false) | |
| 1399 | + </li> | |
| 1400 | + <li> | |
| 1401 | + <pre><code class="language-stscript">/if left=tree right={{getvar::object}} {: /echo The object is a tree! :}</code></pre> | |
| 1402 | + executes the defined subcommand, if the left and right values are equals. | |
| 1403 | + </li> | |
| 1383 | 1404 | </ul> |
| 1384 | 1405 | </div> |
| 1385 | 1406 | `, |
| @@ -1395,32 +1416,38 @@ export function registerVariableCommands() { | ||
| 1395 | 1416 | typeList: [ARGUMENT_TYPE.VARIABLE_NAME, ARGUMENT_TYPE.STRING, ARGUMENT_TYPE.NUMBER], |
| 1396 | 1417 | isRequired: true, |
| 1397 | 1418 | enumProvider: commonEnumProviders.variables('all'), |
| 1398 | - forceEnum: false, | |
| 1399 | 1419 | }), |
| 1400 | 1420 | SlashCommandNamedArgument.fromProps({ |
| 1401 | 1421 | name: 'right', |
| 1402 | 1422 | description: 'right operand', |
| 1403 | 1423 | typeList: [ARGUMENT_TYPE.VARIABLE_NAME, ARGUMENT_TYPE.STRING, ARGUMENT_TYPE.NUMBER], |
| 1404 | - isRequired: true, | |
| 1405 | 1424 | enumProvider: commonEnumProviders.variables('all'), |
| 1406 | - forceEnum: false, | |
| 1407 | 1425 | }), |
| 1408 | 1426 | new SlashCommandNamedArgument.fromProps({ |
| 1409 | - 'rule', 'comparison rule', [ARGUMENT_TYPE.STRING], true, false, null, [ | |
| 1427 | + name: 'rule', | |
| 1410 | - new SlashCommandEnumValue('gt', 'a > b'), | |
| 1428 | + description: 'comparison rule', | |
| 1411 | - new SlashCommandEnumValue('gte', 'a >= b'), | |
| 1429 | + typeList: [ARGUMENT_TYPE.STRING], | |
| 1412 | - new SlashCommandEnumValue('lt', 'a < b'), | |
| 1430 | + defaultValue: 'eq', | |
| 1413 | - new SlashCommandEnumValue('lte', 'a <= b'), | |
| 1431 | + enumList: [ | |
| 1414 | 1432 | new SlashCommandEnumValue('eq', 'a == b (strings & numbers)'), |
| 1415 | 1433 | new SlashCommandEnumValue('neq', 'a !== b (strings & numbers)'), |
| 1416 | 1434 | new SlashCommandEnumValue('notin', '!a includes b (strings & numbers as strings)'), |
| 1417 | 1435 | new SlashCommandEnumValue('innin', 'a not includes b (strings & numbers as strings)'), |
| 1418 | 1436 | new SlashCommandEnumValue('ningt', 'a not includes> b (numbers)'), |
| 1419 | - ], | |
| 1437 | + new SlashCommandEnumValue('gte', 'a >= b (numbers)'), | |
| 1420 | - ), | |
| 1438 | + new SlashCommandEnumValue('lt', 'a < b (numbers)'), | |
| 1421 | - new SlashCommandNamedArgument( | |
| 1439 | + new SlashCommandEnumValue('lte', 'a <= b (numbers)'), | |
| 1422 | - 'guard', 'disable loop iteration limit', [ARGUMENT_TYPE.STRING], false, false, null, commonEnumProviders.boolean('onOff')(), | |
| 1440 | + new SlashCommandEnumValue('not', '!a (truthy)'), | |
| 1423 | - ), | |
| 1441 | + ], | |
| 1442 | + forceEnum: true, | |
| 1443 | + }), | |
| 1444 | + SlashCommandNamedArgument.fromProps({ | |
| 1445 | + name: 'guard', | |
| 1446 | + description: 'disable loop iteration limit', | |
| 1447 | + typeList: [ARGUMENT_TYPE.STRING], | |
| 1448 | + defaultValue: 'off', | |
| 1449 | + enumList: commonEnumProviders.boolean('onOff')(), | |
| 1450 | + }), | |
| 1424 | 1451 | ], |
| 1425 | 1452 | unnamedArgumentList: [ |
| 1426 | 1453 | new SlashCommandArgument( |
| @@ -1439,15 +1466,15 @@ export function registerVariableCommands() { | ||
| 1439 | 1466 | <div> |
| 1440 | 1467 | <strong>Available rules:</strong> |
| 1441 | 1468 | <ul> |
| 1442 | 1469 | <li>gt<code>eq</code> => a >== b <small>(strings & numbers)</small></li> |
| 1443 | 1470 | <li>gte<code>neq</code> => a >!== b <small>(strings & numbers)</small></li> |
| 1444 | 1471 | <li>lt<code>in</code> => a <includes b <small>(strings & numbers as strings)</small></li> |
| 1445 | - <li>lte => a <= b</li> | |
| 1472 | + <li><code>nin</code> => a not includes b <small>(strings & numbers as strings)</small></li> | |
| 1446 | 1473 | <li>eq<code>gt</code> => a ==> b <small>(numbers)</small></li> |
| 1447 | 1474 | <li>neq<code>gte</code> => a !>= b <small>(numbers)</small></li> |
| 1448 | 1475 | <li>not<code>lt</code> => !a < b <small>(numbers)</small></li> |
| 1449 | 1476 | <li>in (strings)<code>lte</code> => a includes<= b <small>(numbers)</small></li> |
| 1450 | - <li>nin (strings) => a not includes b</li> | |
| 1477 | + <li><code>not</code> => !a <small>(truthy)</small></li> | |
| 1451 | 1478 | </ul> |
| 1452 | 1479 | </div> |
| 1453 | 1480 | <div> |
| @@ -1457,7 +1484,11 @@ export function registerVariableCommands() { | ||
| 1457 | 1484 | <pre><code class="language-stscript">/setvar key=i 0 | /while left=i right=10 rule=lte "/addvar key=i 1"</code></pre> |
| 1458 | 1485 | adds 1 to the value of "i" until it reaches 10. |
| 1459 | 1486 | </li> |
| 1460 | - </ul> | |
| 1487 | + <li> | |
| 1488 | + <pre><code class="language-stscript">/while left={{getvar::currentword}} {: /setvar key=currentword {: /do-something-and-return :}() | /echo The current work is "{{getvar::currentword}}" :}</code></pre> | |
| 1489 | + executes the defined subcommand as long as the "currentword" variable is truthy (has any content that is not false/empty) | |
| 1490 | + </ul> | |
| 1491 | + </li> | |
| 1461 | 1492 | </div> |
| 1462 | 1493 | <div> |
| 1463 | 1494 | Loops are limited to 100 iterations by default, pass <code>guard=off</code> to disable. |
| @@ -1572,7 +1603,7 @@ export function registerVariableCommands() { | ||
| 1572 | 1603 | typeList: [ARGUMENT_TYPE.NUMBER, ARGUMENT_TYPE.VARIABLE_NAME], |
| 1573 | 1604 | isRequired: true, |
| 1574 | 1605 | acceptsMultiple: true, |
| 1575 | 1606 | enumProvider: (executor, scope) => { |
| 1576 | 1607 | const vars = commonEnumProviders.variables('all')(executor, scope); |
| 1577 | 1608 | vars.push( |
| 1578 | 1609 | new SlashCommandEnumValue( |
| @@ -1580,16 +1611,16 @@ export function registerVariableCommands() { | ||
| 1580 | 1611 | null, |
| 1581 | 1612 | enumTypes.variable, |
| 1582 | 1613 | enumIcons.variable, |
| 1583 | 1614 | (input) => /^\w*$/.test(input), |
| 1584 | 1615 | (input) => input, |
| 1585 | 1616 | ), |
| 1586 | 1617 | new SlashCommandEnumValue( |
| 1587 | 1618 | 'any number', |
| 1588 | 1619 | null, |
| 1589 | 1620 | enumTypes.number, |
| 1590 | 1621 | enumIcons.number, |
| 1591 | 1622 | (input) => input == '' || !Number.isNaN(Number(input)), |
| 1592 | 1623 | (input) => input, |
| 1593 | 1624 | ), |
| 1594 | 1625 | ); |
| 1595 | 1626 | return vars; |
| @@ -3154,6 +3154,26 @@ grammarly-extension { | ||
| 3154 | 3154 | align-items: center; |
| 3155 | 3155 | } |
| 3156 | 3156 | |
| 3157 | +.alternate_greeting details { | |
| 3158 | + padding: 2px; | |
| 3159 | +} | |
| 3160 | + | |
| 3161 | +.alternate_greeting summary { | |
| 3162 | + list-style-position: outside; | |
| 3163 | + margin-left: 1em; | |
| 3164 | + padding-left: 1em; | |
| 3165 | +} | |
| 3166 | + | |
| 3167 | +.alternate_greeting textarea { | |
| 3168 | + field-sizing: content; | |
| 3169 | + max-height: 50dvh; | |
| 3170 | +} | |
| 3171 | + | |
| 3172 | +.alternate_greeting summary::marker, | |
| 3173 | +.alternate_greeting summary strong { | |
| 3174 | + cursor: pointer; | |
| 3175 | +} | |
| 3176 | + | |
| 3157 | 3177 | #rm_characters_block .form_create_bottom_buttons_block { |
| 3158 | 3178 | justify-content: space-evenly !important; |
| 3159 | 3179 | flex-grow: 0; |