From 4bc465a9648524407be61e711aa6994172399893 Mon Sep 17 00:00:00 2001 From: Adrian Date: Fri, 20 Sep 2024 23:43:16 -0400 Subject: [PATCH] everything working properly --- src/data/emoji.ts | 62 +++++++++++ src/ui/abstact-option-select-ui-handler.ts | 6 +- src/ui/autocomplete-ui-handler.ts | 66 +++++++----- src/ui/rename-form-ui-handler.ts | 115 ++++++++++++++------- src/ui/test-dialogue-ui-handler.ts | 60 ++++++----- 5 files changed, 218 insertions(+), 91 deletions(-) create mode 100644 src/data/emoji.ts diff --git a/src/data/emoji.ts b/src/data/emoji.ts new file mode 100644 index 00000000000..84ee4cd45dc --- /dev/null +++ b/src/data/emoji.ts @@ -0,0 +1,62 @@ +export default [ + "♪", + "★", + "♥", + "♣", + "☻", + "♀", + "♂", + // "☼", +// "ª", +// "►", +// "♫", +// "←", +// "→", +// "↩", +// "↪", +// "※", +// "⁜", +// "¶", +// "©", +// "®", +// "℗", +// "™", +// "$", +// "¢", +// "£", +// "¥", +// "↖", +// "↗", +// "↙", +// "↘", +// "↜", +// "↝", +// "↟", +// "↡", +// "↢", +// "↣", +// "↤", +// "↦", +// "↔", +// "↕", +// "↚", +// "↛", +// "↞", +// "↠", +// "↥", +// "↧", +// "↨", +// "↭", +// "↮", +// "↯", +// "↴", +// "↵", +// "↺", +// "↻", +// "↸", +// "↹", +// "↰", +// "↱" +] as string[]; + +export const emojiRegex: RegExp = /[#*0-9]\uFE0F?\u20E3|[\xA9\xAE\u203C\u2049\u2122\u2139\u2194-\u2199\u21A9\u21AA\u231A\u231B\u2328\u23CF\u23ED-\u23EF\u23F1\u23F2\u23F8-\u23FA\u24C2\u25AA\u25AB\u25B6\u25C0\u25FB\u25FC\u25FE\u2600-\u2604\u260E\u2611\u2614\u2615\u2618\u2620\u2622\u2623\u2626\u262A\u262E\u262F\u2638-\u263A\u2640\u2642\u2648-\u2653\u265F\u2660\u2663\u2665\u2666\u2668\u267B\u267E\u267F\u2692\u2694-\u2697\u2699\u269B\u269C\u26A0\u26A7\u26AA\u26B0\u26B1\u26BD\u26BE\u26C4\u26C8\u26CF\u26D1\u26E9\u26F0-\u26F5\u26F7\u26F8\u26FA\u2702\u2708\u2709\u270F\u2712\u2714\u2716\u271D\u2721\u2733\u2734\u2744\u2747\u2757\u2763\u27A1\u2934\u2935\u2B05-\u2B07\u2B1B\u2B1C\u2B55\u3030\u303D\u3297\u3299]\uFE0F?|[\u261D\u270C\u270D](?:\uD83C[\uDFFB-\uDFFF]|\uFE0F)?|[\u270A\u270B](?:\uD83C[\uDFFB-\uDFFF])?|[\u23E9-\u23EC\u23F0\u23F3\u25FD\u2693\u26A1\u26AB\u26C5\u26CE\u26D4\u26EA\u26FD\u2705\u2728\u274C\u274E\u2753-\u2755\u2795-\u2797\u27B0\u27BF\u2B50]|\u26D3\uFE0F?(?:\u200D\uD83D\uDCA5)?|\u26F9(?:\uD83C[\uDFFB-\uDFFF]|\uFE0F)?(?:\u200D[\u2640\u2642]\uFE0F?)?|\u2764\uFE0F?(?:\u200D(?:\uD83D\uDD25|\uD83E\uDE79))?|\uD83C(?:[\uDC04\uDD70\uDD71\uDD7E\uDD7F\uDE02\uDE37\uDF21\uDF24-\uDF2C\uDF36\uDF7D\uDF96\uDF97\uDF99-\uDF9B\uDF9E\uDF9F\uDFCD\uDFCE\uDFD4-\uDFDF\uDFF5\uDFF7]\uFE0F?|[\uDF85\uDFC2\uDFC7](?:\uD83C[\uDFFB-\uDFFF])?|[\uDFC4\uDFCA](?:\uD83C[\uDFFB-\uDFFF])?(?:\u200D[\u2640\u2642]\uFE0F?)?|[\uDFCB\uDFCC](?:\uD83C[\uDFFB-\uDFFF]|\uFE0F)?(?:\u200D[\u2640\u2642]\uFE0F?)?|[\uDCCF\uDD8E\uDD91-\uDD9A\uDE01\uDE1A\uDE2F\uDE32-\uDE36\uDE38-\uDE3A\uDE50\uDE51\uDF00-\uDF20\uDF2D-\uDF35\uDF37-\uDF43\uDF45-\uDF4A\uDF4C-\uDF7C\uDF7E-\uDF84\uDF86-\uDF93\uDFA0-\uDFC1\uDFC5\uDFC6\uDFC8\uDFC9\uDFCF-\uDFD3\uDFE0-\uDFF0\uDFF8-\uDFFF]|\uDDE6\uD83C[\uDDE8-\uDDEC\uDDEE\uDDF1\uDDF2\uDDF4\uDDF6-\uDDFA\uDDFC\uDDFD\uDDFF]|\uDDE7\uD83C[\uDDE6\uDDE7\uDDE9-\uDDEF\uDDF1-\uDDF4\uDDF6-\uDDF9\uDDFB\uDDFC\uDDFE\uDDFF]|\uDDE8\uD83C[\uDDE6\uDDE8\uDDE9\uDDEB-\uDDEE\uDDF0-\uDDF7\uDDFA-\uDDFF]|\uDDE9\uD83C[\uDDEA\uDDEC\uDDEF\uDDF0\uDDF2\uDDF4\uDDFF]|\uDDEA\uD83C[\uDDE6\uDDE8\uDDEA\uDDEC\uDDED\uDDF7-\uDDFA]|\uDDEB\uD83C[\uDDEE-\uDDF0\uDDF2\uDDF4\uDDF7]|\uDDEC\uD83C[\uDDE6\uDDE7\uDDE9-\uDDEE\uDDF1-\uDDF3\uDDF5-\uDDFA\uDDFC\uDDFE]|\uDDED\uD83C[\uDDF0\uDDF2\uDDF3\uDDF7\uDDF9\uDDFA]|\uDDEE\uD83C[\uDDE8-\uDDEA\uDDF1-\uDDF4\uDDF6-\uDDF9]|\uDDEF\uD83C[\uDDEA\uDDF2\uDDF4\uDDF5]|\uDDF0\uD83C[\uDDEA\uDDEC-\uDDEE\uDDF2\uDDF3\uDDF5\uDDF7\uDDFC\uDDFE\uDDFF]|\uDDF1\uD83C[\uDDE6-\uDDE8\uDDEE\uDDF0\uDDF7-\uDDFB\uDDFE]|\uDDF2\uD83C[\uDDE6\uDDE8-\uDDED\uDDF0-\uDDFF]|\uDDF3\uD83C[\uDDE6\uDDE8\uDDEA-\uDDEC\uDDEE\uDDF1\uDDF4\uDDF5\uDDF7\uDDFA\uDDFF]|\uDDF4\uD83C\uDDF2|\uDDF5\uD83C[\uDDE6\uDDEA-\uDDED\uDDF0-\uDDF3\uDDF7-\uDDF9\uDDFC\uDDFE]|\uDDF6\uD83C\uDDE6|\uDDF7\uD83C[\uDDEA\uDDF4\uDDF8\uDDFA\uDDFC]|\uDDF8\uD83C[\uDDE6-\uDDEA\uDDEC-\uDDF4\uDDF7-\uDDF9\uDDFB\uDDFD-\uDDFF]|\uDDF9\uD83C[\uDDE6\uDDE8\uDDE9\uDDEB-\uDDED\uDDEF-\uDDF4\uDDF7\uDDF9\uDDFB\uDDFC\uDDFF]|\uDDFA\uD83C[\uDDE6\uDDEC\uDDF2\uDDF3\uDDF8\uDDFE\uDDFF]|\uDDFB\uD83C[\uDDE6\uDDE8\uDDEA\uDDEC\uDDEE\uDDF3\uDDFA]|\uDDFC\uD83C[\uDDEB\uDDF8]|\uDDFD\uD83C\uDDF0|\uDDFE\uD83C[\uDDEA\uDDF9]|\uDDFF\uD83C[\uDDE6\uDDF2\uDDFC]|\uDF44(?:\u200D\uD83D\uDFEB)?|\uDF4B(?:\u200D\uD83D\uDFE9)?|\uDFC3(?:\uD83C[\uDFFB-\uDFFF])?(?:\u200D(?:[\u2640\u2642]\uFE0F?(?:\u200D\u27A1\uFE0F?)?|\u27A1\uFE0F?))?|\uDFF3\uFE0F?(?:\u200D(?:\u26A7\uFE0F?|\uD83C\uDF08))?|\uDFF4(?:\u200D\u2620\uFE0F?|\uDB40\uDC67\uDB40\uDC62\uDB40(?:\uDC65\uDB40\uDC6E\uDB40\uDC67|\uDC73\uDB40\uDC63\uDB40\uDC74|\uDC77\uDB40\uDC6C\uDB40\uDC73)\uDB40\uDC7F)?)|\uD83D(?:[\uDC3F\uDCFD\uDD49\uDD4A\uDD6F\uDD70\uDD73\uDD76-\uDD79\uDD87\uDD8A-\uDD8D\uDDA5\uDDA8\uDDB1\uDDB2\uDDBC\uDDC2-\uDDC4\uDDD1-\uDDD3\uDDDC-\uDDDE\uDDE1\uDDE3\uDDE8\uDDEF\uDDF3\uDDFA\uDECB\uDECD-\uDECF\uDEE0-\uDEE5\uDEE9\uDEF0\uDEF3]\uFE0F?|[\uDC42\uDC43\uDC46-\uDC50\uDC66\uDC67\uDC6B-\uDC6D\uDC72\uDC74-\uDC76\uDC78\uDC7C\uDC83\uDC85\uDC8F\uDC91\uDCAA\uDD7A\uDD95\uDD96\uDE4C\uDE4F\uDEC0\uDECC](?:\uD83C[\uDFFB-\uDFFF])?|[\uDC6E\uDC70\uDC71\uDC73\uDC77\uDC81\uDC82\uDC86\uDC87\uDE45-\uDE47\uDE4B\uDE4D\uDE4E\uDEA3\uDEB4\uDEB5](?:\uD83C[\uDFFB-\uDFFF])?(?:\u200D[\u2640\u2642]\uFE0F?)?|[\uDD74\uDD90](?:\uD83C[\uDFFB-\uDFFF]|\uFE0F)?|[\uDC00-\uDC07\uDC09-\uDC14\uDC16-\uDC25\uDC27-\uDC3A\uDC3C-\uDC3E\uDC40\uDC44\uDC45\uDC51-\uDC65\uDC6A\uDC79-\uDC7B\uDC7D-\uDC80\uDC84\uDC88-\uDC8E\uDC90\uDC92-\uDCA9\uDCAB-\uDCFC\uDCFF-\uDD3D\uDD4B-\uDD4E\uDD50-\uDD67\uDDA4\uDDFB-\uDE2D\uDE2F-\uDE34\uDE37-\uDE41\uDE43\uDE44\uDE48-\uDE4A\uDE80-\uDEA2\uDEA4-\uDEB3\uDEB7-\uDEBF\uDEC1-\uDEC5\uDED0-\uDED2\uDED5-\uDED7\uDEDC-\uDEDF\uDEEB\uDEEC\uDEF4-\uDEFC\uDFE0-\uDFEB\uDFF0]|\uDC08(?:\u200D\u2B1B)?|\uDC15(?:\u200D\uD83E\uDDBA)?|\uDC26(?:\u200D(?:\u2B1B|\uD83D\uDD25))?|\uDC3B(?:\u200D\u2744\uFE0F?)?|\uDC41\uFE0F?(?:\u200D\uD83D\uDDE8\uFE0F?)?|\uDC68(?:\u200D(?:[\u2695\u2696\u2708]\uFE0F?|\u2764\uFE0F?\u200D\uD83D(?:\uDC8B\u200D\uD83D)?\uDC68|\uD83C[\uDF3E\uDF73\uDF7C\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D(?:[\uDC68\uDC69]\u200D\uD83D(?:\uDC66(?:\u200D\uD83D\uDC66)?|\uDC67(?:\u200D\uD83D[\uDC66\uDC67])?)|[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uDC66(?:\u200D\uD83D\uDC66)?|\uDC67(?:\u200D\uD83D[\uDC66\uDC67])?)|\uD83E(?:[\uDDAF\uDDBC\uDDBD](?:\u200D\u27A1\uFE0F?)?|[\uDDB0-\uDDB3]))|\uD83C(?:\uDFFB(?:\u200D(?:[\u2695\u2696\u2708]\uFE0F?|\u2764\uFE0F?\u200D\uD83D(?:\uDC8B\u200D\uD83D)?\uDC68\uD83C[\uDFFB-\uDFFF]|\uD83C[\uDF3E\uDF73\uDF7C\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E(?:[\uDDAF\uDDBC\uDDBD](?:\u200D\u27A1\uFE0F?)?|[\uDDB0-\uDDB3]|\uDD1D\u200D\uD83D\uDC68\uD83C[\uDFFC-\uDFFF])))?|\uDFFC(?:\u200D(?:[\u2695\u2696\u2708]\uFE0F?|\u2764\uFE0F?\u200D\uD83D(?:\uDC8B\u200D\uD83D)?\uDC68\uD83C[\uDFFB-\uDFFF]|\uD83C[\uDF3E\uDF73\uDF7C\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E(?:[\uDDAF\uDDBC\uDDBD](?:\u200D\u27A1\uFE0F?)?|[\uDDB0-\uDDB3]|\uDD1D\u200D\uD83D\uDC68\uD83C[\uDFFB\uDFFD-\uDFFF])))?|\uDFFD(?:\u200D(?:[\u2695\u2696\u2708]\uFE0F?|\u2764\uFE0F?\u200D\uD83D(?:\uDC8B\u200D\uD83D)?\uDC68\uD83C[\uDFFB-\uDFFF]|\uD83C[\uDF3E\uDF73\uDF7C\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E(?:[\uDDAF\uDDBC\uDDBD](?:\u200D\u27A1\uFE0F?)?|[\uDDB0-\uDDB3]|\uDD1D\u200D\uD83D\uDC68\uD83C[\uDFFB\uDFFC\uDFFE\uDFFF])))?|\uDFFE(?:\u200D(?:[\u2695\u2696\u2708]\uFE0F?|\u2764\uFE0F?\u200D\uD83D(?:\uDC8B\u200D\uD83D)?\uDC68\uD83C[\uDFFB-\uDFFF]|\uD83C[\uDF3E\uDF73\uDF7C\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E(?:[\uDDAF\uDDBC\uDDBD](?:\u200D\u27A1\uFE0F?)?|[\uDDB0-\uDDB3]|\uDD1D\u200D\uD83D\uDC68\uD83C[\uDFFB-\uDFFD\uDFFF])))?|\uDFFF(?:\u200D(?:[\u2695\u2696\u2708]\uFE0F?|\u2764\uFE0F?\u200D\uD83D(?:\uDC8B\u200D\uD83D)?\uDC68\uD83C[\uDFFB-\uDFFF]|\uD83C[\uDF3E\uDF73\uDF7C\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E(?:[\uDDAF\uDDBC\uDDBD](?:\u200D\u27A1\uFE0F?)?|[\uDDB0-\uDDB3]|\uDD1D\u200D\uD83D\uDC68\uD83C[\uDFFB-\uDFFE])))?))?|\uDC69(?:\u200D(?:[\u2695\u2696\u2708]\uFE0F?|\u2764\uFE0F?\u200D\uD83D(?:\uDC8B\u200D\uD83D)?[\uDC68\uDC69]|\uD83C[\uDF3E\uDF73\uDF7C\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D(?:[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uDC66(?:\u200D\uD83D\uDC66)?|\uDC67(?:\u200D\uD83D[\uDC66\uDC67])?|\uDC69\u200D\uD83D(?:\uDC66(?:\u200D\uD83D\uDC66)?|\uDC67(?:\u200D\uD83D[\uDC66\uDC67])?))|\uD83E(?:[\uDDAF\uDDBC\uDDBD](?:\u200D\u27A1\uFE0F?)?|[\uDDB0-\uDDB3]))|\uD83C(?:\uDFFB(?:\u200D(?:[\u2695\u2696\u2708]\uFE0F?|\u2764\uFE0F?\u200D\uD83D(?:[\uDC68\uDC69]|\uDC8B\u200D\uD83D[\uDC68\uDC69])\uD83C[\uDFFB-\uDFFF]|\uD83C[\uDF3E\uDF73\uDF7C\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E(?:[\uDDAF\uDDBC\uDDBD](?:\u200D\u27A1\uFE0F?)?|[\uDDB0-\uDDB3]|\uDD1D\u200D\uD83D[\uDC68\uDC69]\uD83C[\uDFFC-\uDFFF])))?|\uDFFC(?:\u200D(?:[\u2695\u2696\u2708]\uFE0F?|\u2764\uFE0F?\u200D\uD83D(?:[\uDC68\uDC69]|\uDC8B\u200D\uD83D[\uDC68\uDC69])\uD83C[\uDFFB-\uDFFF]|\uD83C[\uDF3E\uDF73\uDF7C\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E(?:[\uDDAF\uDDBC\uDDBD](?:\u200D\u27A1\uFE0F?)?|[\uDDB0-\uDDB3]|\uDD1D\u200D\uD83D[\uDC68\uDC69]\uD83C[\uDFFB\uDFFD-\uDFFF])))?|\uDFFD(?:\u200D(?:[\u2695\u2696\u2708]\uFE0F?|\u2764\uFE0F?\u200D\uD83D(?:[\uDC68\uDC69]|\uDC8B\u200D\uD83D[\uDC68\uDC69])\uD83C[\uDFFB-\uDFFF]|\uD83C[\uDF3E\uDF73\uDF7C\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E(?:[\uDDAF\uDDBC\uDDBD](?:\u200D\u27A1\uFE0F?)?|[\uDDB0-\uDDB3]|\uDD1D\u200D\uD83D[\uDC68\uDC69]\uD83C[\uDFFB\uDFFC\uDFFE\uDFFF])))?|\uDFFE(?:\u200D(?:[\u2695\u2696\u2708]\uFE0F?|\u2764\uFE0F?\u200D\uD83D(?:[\uDC68\uDC69]|\uDC8B\u200D\uD83D[\uDC68\uDC69])\uD83C[\uDFFB-\uDFFF]|\uD83C[\uDF3E\uDF73\uDF7C\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E(?:[\uDDAF\uDDBC\uDDBD](?:\u200D\u27A1\uFE0F?)?|[\uDDB0-\uDDB3]|\uDD1D\u200D\uD83D[\uDC68\uDC69]\uD83C[\uDFFB-\uDFFD\uDFFF])))?|\uDFFF(?:\u200D(?:[\u2695\u2696\u2708]\uFE0F?|\u2764\uFE0F?\u200D\uD83D(?:[\uDC68\uDC69]|\uDC8B\u200D\uD83D[\uDC68\uDC69])\uD83C[\uDFFB-\uDFFF]|\uD83C[\uDF3E\uDF73\uDF7C\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E(?:[\uDDAF\uDDBC\uDDBD](?:\u200D\u27A1\uFE0F?)?|[\uDDB0-\uDDB3]|\uDD1D\u200D\uD83D[\uDC68\uDC69]\uD83C[\uDFFB-\uDFFE])))?))?|\uDC6F(?:\u200D[\u2640\u2642]\uFE0F?)?|\uDD75(?:\uD83C[\uDFFB-\uDFFF]|\uFE0F)?(?:\u200D[\u2640\u2642]\uFE0F?)?|\uDE2E(?:\u200D\uD83D\uDCA8)?|\uDE35(?:\u200D\uD83D\uDCAB)?|\uDE36(?:\u200D\uD83C\uDF2B\uFE0F?)?|\uDE42(?:\u200D[\u2194\u2195]\uFE0F?)?|\uDEB6(?:\uD83C[\uDFFB-\uDFFF])?(?:\u200D(?:[\u2640\u2642]\uFE0F?(?:\u200D\u27A1\uFE0F?)?|\u27A1\uFE0F?))?)|\uD83E(?:[\uDD0C\uDD0F\uDD18-\uDD1F\uDD30-\uDD34\uDD36\uDD77\uDDB5\uDDB6\uDDBB\uDDD2\uDDD3\uDDD5\uDEC3-\uDEC5\uDEF0\uDEF2-\uDEF8](?:\uD83C[\uDFFB-\uDFFF])?|[\uDD26\uDD35\uDD37-\uDD39\uDD3D\uDD3E\uDDB8\uDDB9\uDDCD\uDDCF\uDDD4\uDDD6-\uDDDD](?:\uD83C[\uDFFB-\uDFFF])?(?:\u200D[\u2640\u2642]\uFE0F?)?|[\uDDDE\uDDDF](?:\u200D[\u2640\u2642]\uFE0F?)?|[\uDD0D\uDD0E\uDD10-\uDD17\uDD20-\uDD25\uDD27-\uDD2F\uDD3A\uDD3F-\uDD45\uDD47-\uDD76\uDD78-\uDDB4\uDDB7\uDDBA\uDDBC-\uDDCC\uDDD0\uDDE0-\uDDFF\uDE70-\uDE7C\uDE80-\uDE89\uDE8F-\uDEC2\uDEC6\uDECE-\uDEDC\uDEDF-\uDEE9]|\uDD3C(?:\u200D[\u2640\u2642]\uFE0F?|\uD83C[\uDFFB-\uDFFF])?|\uDDCE(?:\uD83C[\uDFFB-\uDFFF])?(?:\u200D(?:[\u2640\u2642]\uFE0F?(?:\u200D\u27A1\uFE0F?)?|\u27A1\uFE0F?))?|\uDDD1(?:\u200D(?:[\u2695\u2696\u2708]\uFE0F?|\uD83C[\uDF3E\uDF73\uDF7C\uDF84\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E(?:[\uDDAF\uDDBC\uDDBD](?:\u200D\u27A1\uFE0F?)?|[\uDDB0-\uDDB3]|\uDD1D\u200D\uD83E\uDDD1|\uDDD1\u200D\uD83E\uDDD2(?:\u200D\uD83E\uDDD2)?|\uDDD2(?:\u200D\uD83E\uDDD2)?))|\uD83C(?:\uDFFB(?:\u200D(?:[\u2695\u2696\u2708]\uFE0F?|\u2764\uFE0F?\u200D(?:\uD83D\uDC8B\u200D)?\uD83E\uDDD1\uD83C[\uDFFC-\uDFFF]|\uD83C[\uDF3E\uDF73\uDF7C\uDF84\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E(?:[\uDDAF\uDDBC\uDDBD](?:\u200D\u27A1\uFE0F?)?|[\uDDB0-\uDDB3]|\uDD1D\u200D\uD83E\uDDD1\uD83C[\uDFFB-\uDFFF])))?|\uDFFC(?:\u200D(?:[\u2695\u2696\u2708]\uFE0F?|\u2764\uFE0F?\u200D(?:\uD83D\uDC8B\u200D)?\uD83E\uDDD1\uD83C[\uDFFB\uDFFD-\uDFFF]|\uD83C[\uDF3E\uDF73\uDF7C\uDF84\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E(?:[\uDDAF\uDDBC\uDDBD](?:\u200D\u27A1\uFE0F?)?|[\uDDB0-\uDDB3]|\uDD1D\u200D\uD83E\uDDD1\uD83C[\uDFFB-\uDFFF])))?|\uDFFD(?:\u200D(?:[\u2695\u2696\u2708]\uFE0F?|\u2764\uFE0F?\u200D(?:\uD83D\uDC8B\u200D)?\uD83E\uDDD1\uD83C[\uDFFB\uDFFC\uDFFE\uDFFF]|\uD83C[\uDF3E\uDF73\uDF7C\uDF84\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E(?:[\uDDAF\uDDBC\uDDBD](?:\u200D\u27A1\uFE0F?)?|[\uDDB0-\uDDB3]|\uDD1D\u200D\uD83E\uDDD1\uD83C[\uDFFB-\uDFFF])))?|\uDFFE(?:\u200D(?:[\u2695\u2696\u2708]\uFE0F?|\u2764\uFE0F?\u200D(?:\uD83D\uDC8B\u200D)?\uD83E\uDDD1\uD83C[\uDFFB-\uDFFD\uDFFF]|\uD83C[\uDF3E\uDF73\uDF7C\uDF84\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E(?:[\uDDAF\uDDBC\uDDBD](?:\u200D\u27A1\uFE0F?)?|[\uDDB0-\uDDB3]|\uDD1D\u200D\uD83E\uDDD1\uD83C[\uDFFB-\uDFFF])))?|\uDFFF(?:\u200D(?:[\u2695\u2696\u2708]\uFE0F?|\u2764\uFE0F?\u200D(?:\uD83D\uDC8B\u200D)?\uD83E\uDDD1\uD83C[\uDFFB-\uDFFE]|\uD83C[\uDF3E\uDF73\uDF7C\uDF84\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E(?:[\uDDAF\uDDBC\uDDBD](?:\u200D\u27A1\uFE0F?)?|[\uDDB0-\uDDB3]|\uDD1D\u200D\uD83E\uDDD1\uD83C[\uDFFB-\uDFFF])))?))?|\uDEF1(?:\uD83C(?:\uDFFB(?:\u200D\uD83E\uDEF2\uD83C[\uDFFC-\uDFFF])?|\uDFFC(?:\u200D\uD83E\uDEF2\uD83C[\uDFFB\uDFFD-\uDFFF])?|\uDFFD(?:\u200D\uD83E\uDEF2\uD83C[\uDFFB\uDFFC\uDFFE\uDFFF])?|\uDFFE(?:\u200D\uD83E\uDEF2\uD83C[\uDFFB-\uDFFD\uDFFF])?|\uDFFF(?:\u200D\uD83E\uDEF2\uD83C[\uDFFB-\uDFFE])?))?)/g; diff --git a/src/ui/abstact-option-select-ui-handler.ts b/src/ui/abstact-option-select-ui-handler.ts index c6abecda4c0..4d55ff7c29f 100644 --- a/src/ui/abstact-option-select-ui-handler.ts +++ b/src/ui/abstact-option-select-ui-handler.ts @@ -84,7 +84,7 @@ export default abstract class AbstractOptionSelectUiHandler extends UiHandler { // for performance reasons, this limits how many options we can see at once. Without this, it would try to make text options for every single options // which makes the performance take a hit. If there's not enough options to do this (set to 10 at the moment) and the ui mode !== Mode.AUTO_COMPLETE, // this is ignored and the original code is untouched, with the options array being all the options from the config - if (configOptions.length >= 10 && this.scene.ui.getMode() === Mode.AUTO_COMPLETE) { + if (configOptions.length >= 10 && this.mode === Mode.AUTO_COMPLETE) { const optionsScrollTotal = configOptions.length; const optionStartIndex = this.scrollCursor; const optionEndIndex = Math.min(optionsScrollTotal, optionStartIndex + (!optionStartIndex || this.scrollCursor + (this.config?.maxOptions! - 1) >= optionsScrollTotal ? this.config?.maxOptions! - 1 : this.config?.maxOptions! - 2)); @@ -101,7 +101,9 @@ export default abstract class AbstractOptionSelectUiHandler extends UiHandler { this.optionSelectIcons.splice(0, this.optionSelectIcons.length); } - this.optionSelectText = addTextObject(this.scene, 0, 0, options.map(o => o.item ? ` ${o.label}` : o.label).join("\n"), TextStyle.WINDOW, { maxLines: options.length }); + this.optionSelectText = addTextObject(this.scene, 0, 0, options.map(o => o.item ? ` ${o.label}` : o.label).join("\n"), TextStyle.WINDOW, { + maxLines: options.length + 1 // +1 because at the end of the options in autocomplete it hides the last option + }); this.optionSelectText.setLineSpacing(this.scale * 72); this.optionSelectText.setName("text-option-select"); this.optionSelectText.setLineSpacing(12); diff --git a/src/ui/autocomplete-ui-handler.ts b/src/ui/autocomplete-ui-handler.ts index 6cbbe6569ea..1ec65d514bc 100644 --- a/src/ui/autocomplete-ui-handler.ts +++ b/src/ui/autocomplete-ui-handler.ts @@ -8,20 +8,21 @@ export interface OptionSelectConfigAC extends OptionSelectConfig { inputContainer: Phaser.GameObjects.Container; modalContainer: Phaser.GameObjects.Container; maxOptionsReverse?: number; - reverse?: true; + reverse?: true; // So that instead of showing the options looking below the input, they are shown above } export default class AutoCompleteUiHandler extends AbstractOptionSelectUiHandler { modalContainer: Phaser.GameObjects.Container; inputContainer: Phaser.GameObjects.Container; handlerKeyDown: (inputObject: InputText, evt: KeyboardEvent) => void; + revertAutoCompleteMode: () => void; reverse?: true; constructor(scene: BattleScene, mode: Mode = Mode.AUTO_COMPLETE) { super(scene, mode); this.handlerKeyDown = (inputObject, evt) => { - // Don't move inputText cursor + // Don't move inputText cursor, cursor move fast for this if (["arrowup"].some((key) => key === (evt.code || evt.key).toLowerCase())) { evt.preventDefault(); this.processInput(Button.UP); @@ -30,9 +31,9 @@ export default class AutoCompleteUiHandler extends AbstractOptionSelectUiHandler this.processInput(Button.DOWN); } - // Revert Mode when not press... - if (!["enter", "arrowup", "arrowdown"].some((key) => (evt.code || evt.key).toLowerCase().includes(key))) { - this.scene.ui.revertMode(); + // Revert Mode if not press... + if (!["enter", "arrowup", "arrowdown", "shift", "control", "alt"].some((key) => (evt.code || evt.key).toLowerCase().includes(key))) { + this.revertAutoCompleteMode(); } // Recovery focus @@ -42,6 +43,13 @@ export default class AutoCompleteUiHandler extends AbstractOptionSelectUiHandler } }; + this.revertAutoCompleteMode = () => { + const ui = this.getUi(); + if (ui.getMode() === Mode.AUTO_COMPLETE) { + ui.revertMode(); + } + }; + } getWindowWidth(): integer { @@ -58,7 +66,7 @@ export default class AutoCompleteUiHandler extends AbstractOptionSelectUiHandler const originalHandler = args[0].options[index].handler; opt.handler = () => { if (originalHandler()) { - ui.revertMode(); + this.revertAutoCompleteMode(); return true; } return false; @@ -67,35 +75,26 @@ export default class AutoCompleteUiHandler extends AbstractOptionSelectUiHandler this.modalContainer = modalContainer; this.inputContainer = inputContainer; const input = args[0].inputContainer.list.find((el) => el instanceof InputText); - const ui = this.getUi(); const originalsEvents = input.listeners("keydown"); + // Remove the current keydown events from the input so that the one added in this mode is executed first for (let i = 0; i < originalsEvents?.length; i++) { input.off("keydown", originalsEvents[i]); } - const handlerBlur = () => { - ui.revertMode(); - input.off("blur", handlerBlur); - }; - const handlerPointerUp = () => { - ui.revertMode(); - this.modalContainer.off("pointerup", handlerPointerUp); - }; - - input.on("blur", handlerBlur); + input.on("blur", this.revertAutoCompleteMode); input.on("keydown", this.handlerKeyDown); - this.modalContainer.on("pointerup", handlerPointerUp); + this.modalContainer.on("pointerdown", this.revertAutoCompleteMode); + // After adding the event that will execute this mode first, return the ones it already had for (let i = 0; i < originalsEvents?.length; i++) { input.on("keydown", originalsEvents[i]); } - if (this.reverse && newArgs[0].maxOptionsReverse) { - newArgs[0].maxOptions = newArgs[0].maxOptionsReverse; - } - if (this.reverse) { + if (newArgs[0].maxOptionsReverse) { + newArgs[0].maxOptions = newArgs[0].maxOptionsReverse; + } newArgs[0].options.reverse(); } @@ -113,18 +112,39 @@ export default class AutoCompleteUiHandler extends AbstractOptionSelectUiHandler protected setupOptions() { super.setupOptions(); if (this.modalContainer) { - this.optionSelectContainer.setSize(this.optionSelectContainer.height, Math.max(this.optionSelectText.displayWidth + 24, this.getWindowWidth())); + // Adjust the width to the minimum of the visible options + this.optionSelectContainer.setSize(this.optionSelectContainer.height, Math.max(this.optionSelectText.displayWidth, this.getWindowWidth())); if (this.reverse) { + // Position above the input this.optionSelectContainer.setPositionRelative(this.modalContainer, this.optionSelectBg.width + this.inputContainer.x, this.inputContainer.y); return; } + // Position below the input this.optionSelectContainer.setPositionRelative(this.modalContainer, this.optionSelectBg.width + this.inputContainer.x, this.optionSelectBg.height + this.inputContainer.y + (this.inputContainer.list.find((el) => el instanceof Phaser.GameObjects.NineSlice)?.height ?? 0)); + + // If the modal goes off screen, center it + // if ((this.optionSelectContainer.getBounds().width + this.optionSelectContainer.getBounds().x) > this.scene.game.canvas.width) { + // this.optionSelectContainer.setX(this.optionSelectContainer.getBounds().x - ((this.optionSelectContainer.getBounds().width + this.optionSelectContainer.getBounds().x) - this.scene.game.canvas.width)); + // } } } + processInput(button: Button): boolean { + // the cancel and action button are here because if you're typing, x and z are used for cancel/action. This means you could be typing something and accidentally cancel/select when you don't mean to + // the submit button is therefore used to select a choice (the enter button), though this does not work on my local dev testing for phones, as for my phone/keyboard combo, the enter and z key are both + // bound to Button.ACTION, which makes this not work on mobile + if (button !== Button.CANCEL && button !== Button.ACTION) { + return super.processInput(button); + } + return false; + } + clear(): void { super.clear(); const input = this.inputContainer.list.find((el) => el instanceof InputText); input?.off("keydown", this.handlerKeyDown); + input?.off("blur", this.revertAutoCompleteMode); + this.modalContainer.off("pointerdown", this.revertAutoCompleteMode); + this.scrollCursor = 0; } } diff --git a/src/ui/rename-form-ui-handler.ts b/src/ui/rename-form-ui-handler.ts index 137c56028ee..aad5f29eea3 100644 --- a/src/ui/rename-form-ui-handler.ts +++ b/src/ui/rename-form-ui-handler.ts @@ -7,10 +7,30 @@ import { OptionSelectItem } from "./abstact-option-select-ui-handler"; import { addWindow } from "./ui-theme"; import { addTextObject, getTextStyleOptions, TextStyle } from "./text"; import InputText from "phaser3-rex-plugins/plugins/inputtext"; -import BattleScene from "#app/battle-scene.js"; +import BattleScene from "#app/battle-scene"; import AutoCompleteUiHandler, { OptionSelectConfigAC } from "./autocomplete-ui-handler"; +import emojisAvailable from "#app/data/emoji"; +import { emojiRegex } from "#app/data/emoji"; -const emojiAvailable = ["♪", "★", "♥", "♣", "☻", "ª", "☼", "►", "♫", "←", "→", "↩", "↪"]; +function textArrayWithEmojis(str: string): string[] { + const result: string[] = []; + let match; + let lastIndex = 0; + + while ((match = emojiRegex.exec(str)) !== null) { + if (match.index > lastIndex) { + result.push(...str.slice(lastIndex, match.index).split("")); + } + result.push(match[0]); + lastIndex = emojiRegex.lastIndex; + } + + if (lastIndex < str.length) { + result.push(...str.slice(lastIndex).split("")); + } + + return result; +} export default class RenameFormUiHandler extends FormModalUiHandler { protected autocomplete: AutoCompleteUiHandler; @@ -94,7 +114,7 @@ export default class RenameFormUiHandler extends FormModalUiHandler { const maxEmojis = 6; const modalOptions: OptionSelectConfigAC = { - options: emojiAvailable.map((emoji, index): OptionSelectItem => { + options: emojisAvailable.map((emoji, index): OptionSelectItem => { return { label: `${emoji} /${index + 1}`, handler: ()=> { @@ -125,62 +145,81 @@ export default class RenameFormUiHandler extends FormModalUiHandler { // If deleting and currently positioned at "/", display the list of emojis if ( !evt.data && - input.text.split("").filter((char) => emojiAvailable.some((em) => em === char)).length < maxEmojis && + input.text.split("").filter((char) => emojisAvailable.some((em) => em === char)).length < maxEmojis && input.text.split("").some((char, i) => char === "/" && i + 1 === input.cursorPosition) ) { ui.setOverlayMode(Mode.AUTO_COMPLETE, modalOptions); } - const getDisallowedEmojis = (text) => { - return text.split("").filter((char) => { - const isEmoji = !char.match(/[\u0000-\u00ff]/); - const isAllowedEmoji = emojiAvailable.includes(char); - return isEmoji && !isAllowedEmoji; - }); - }; - // Remove disallowed emojis. - while (getDisallowedEmojis(input.text).length > 0) { - const disallowedEmojis = getDisallowedEmojis(input.text); - const lastDisallowedEmojiIndex = input.text.lastIndexOf(disallowedEmojis[0]); - - if (lastDisallowedEmojiIndex !== -1) { - const textBeforeCursor = input.text.substring(0, lastDisallowedEmojiIndex); - const textAfterCursor = input.text.substring(lastDisallowedEmojiIndex + 1); - const newText = textBeforeCursor + textAfterCursor; - - if (newText !== input.text) { - input.setText(newText); - input.setCursorPosition(lastDisallowedEmojiIndex); + if (textArrayWithEmojis(input.text).filter((char) => { + // const isEmoji = !char.match(/[\u0000-\u00ff]/); // Emojis, special characters and kaomojis + const isEmoji = char.match(emojiRegex); // Only Emojis + const isAllowedEmoji = emojisAvailable.includes(char); + return isEmoji && !isAllowedEmoji; + }).length) { + const regex = emojiRegex; + let totalLength: number = 0; + const newText = input.text.replace(regex, (match) => { + if (emojisAvailable.includes(match)) { + return match; } - } + totalLength += match.length; + return ""; + }); + + const cursorPosition = input.cursorPosition; + input.setText(newText); + input.setCursorPosition(cursorPosition - totalLength); + } // If the number of available emojis exceeds the maximum allowed number of emojis.. //.. Delete any attempt to insert another one. - if (evt.data && input.text.split("").filter((char) => emojiAvailable.some((em) => em === char)).length > maxEmojis) { - const texto = input.text; - const textBeforeCursor = texto.substring(0, input.cursorPosition); - const textAfterCursor = texto.substring(input.cursorPosition); + while (textArrayWithEmojis(input.text).filter((char) => emojisAvailable.includes(char)).length > maxEmojis) { + const charactersWithEmojis = textArrayWithEmojis(input.text); + const emojis = charactersWithEmojis.filter((char, i, arr) => { + if (emojisAvailable.includes(char)) { + let totalLength = 0; + for (let j = 0; j <= i; j++) { + totalLength += arr[j].length; + } + return totalLength < input.cursorPosition + 1; + } + return false; + }); + const cursorPosition = input.cursorPosition; + const lastEmoji = emojis[emojis.length - 1]; + const lastEmojiIndex = charactersWithEmojis.filter((char, i, arr) => { + let totalLength = 0; + for (let j = 0; j <= i; j++) { + totalLength += arr[j].length; + } + return totalLength < input.cursorPosition + 1; + }).lastIndexOf(lastEmoji); - const exactlyEmoji = textBeforeCursor.lastIndexOf(evt.data); - if (exactlyEmoji !== -1) { - const textReplace = textBeforeCursor.substring(0, exactlyEmoji) + textAfterCursor; - if (textReplace !== input.text) { - input.setText(textReplace); - input.setCursorPosition(exactlyEmoji); + if (lastEmojiIndex !== -1) { + const textBeforeCursor = charactersWithEmojis.slice(0, lastEmojiIndex).join(""); + const textAfterCursor = charactersWithEmojis.slice(lastEmojiIndex + 1).join(""); + const newText = textBeforeCursor + textAfterCursor; + + if (newText !== input.text) { + input.setText(newText); + input.setCursorPosition(cursorPosition - lastEmoji.length); } } } // If the number of available emojis has been reached, do not display the list of emojis - if (evt.data && input.text.split("").filter((char) => emojiAvailable.some((em) => em === char)).length < maxEmojis) { + if (evt.data && input.text.split("").filter((char) => emojisAvailable.some((em) => em === char)).length < maxEmojis) { // Retrieve the exact command, as it can be either "/", or "/n" - const command = input.text.split("").filter((_, i, arr) => arr.includes("/") && i >= (input.text.split("").filter((_, i) => i < input.cursorPosition).lastIndexOf("/")) && i < input.cursorPosition).join(""); + const command = input.text.split("").filter((_, i, arr) => i >= (input.text.split("").filter((_, i) => i < input.cursorPosition).lastIndexOf("/")) && i < input.cursorPosition).join(""); - if (modalOptions.options.some((opt) => opt.label.includes(command))) { + if (evt.data === "/") { + ui.setOverlayMode(Mode.AUTO_COMPLETE, modalOptions); + } else if (command.includes("/") && modalOptions.options.some((opt) => opt.label.includes(command))) { const filterOptions = { ...modalOptions, options: modalOptions.options.filter((opt) => opt.label.includes(command)) diff --git a/src/ui/test-dialogue-ui-handler.ts b/src/ui/test-dialogue-ui-handler.ts index 3f353bbc461..94c6b703e8f 100644 --- a/src/ui/test-dialogue-ui-handler.ts +++ b/src/ui/test-dialogue-ui-handler.ts @@ -5,6 +5,8 @@ import { PlayerPokemon } from "#app/field/pokemon"; import { OptionSelectItem } from "./abstact-option-select-ui-handler"; import { isNullOrUndefined } from "#app/utils"; import { Mode } from "./ui"; +import { OptionSelectConfigAC } from "./autocomplete-ui-handler"; +import InputText from "phaser3-rex-plugins/plugins/inputtext"; export default class TestDialogueUiHandler extends FormModalUiHandler { @@ -23,7 +25,6 @@ export default class TestDialogueUiHandler extends FormModalUiHandler { if (typeof value === "object" && !isNullOrUndefined(value)) { // we check for not null or undefined here because if the language json file has a null key, the typeof will still be an object, but that object will be null, causing issues // If the value is an object, execute the same process - // si el valor es un objeto ejecuta el mismo proceso return flattenKeys(value, topKey ?? t, topKey ? midleKey ? [...midleKey, t] : [t] : undefined).filter((t) => t.length > 0); } else if (typeof value === "string" || isNullOrUndefined(value)) { // we check for null or undefined here as per above - the typeof is still an object but the value is null so we need to exit out of this and pass the null key @@ -73,37 +74,33 @@ export default class TestDialogueUiHandler extends FormModalUiHandler { const input = this.inputs[0]; input.setMaxLength(255); - input.on("keydown", (inputObject, evt: KeyboardEvent) => { - if (["escape", "space"].some((v) => v === evt.key.toLowerCase() || v === evt.code.toLowerCase()) && ui.getMode() === Mode.AUTO_COMPLETE) { - // Delete autocomplete list and recovery focus. - inputObject.on("blur", () => inputObject.node.focus(), { once: true }); - ui.revertMode(); - } - }); - - input.on("textchange", (inputObject, evt: InputEvent) => { - // Delete autocomplete. - if (ui.getMode() === Mode.AUTO_COMPLETE) { - ui.revertMode(); - } - + input.on("textchange", (inputObject: InputText, evt: InputEvent) => { let options: OptionSelectItem[] = []; const splitArr = inputObject.text.split(" "); - const filteredKeys = this.keys.filter((command) => command.toLowerCase().includes(splitArr[splitArr.length - 1].toLowerCase())); + + // At what index of splitArr is the input cursor located + let indexInOriginalArray = 0; + let cumulativeLength = 0; + for (let i = 0; i < splitArr.length; i++) { + const spaceAndWordLength = splitArr[i] === "" ? 2 : 1; + cumulativeLength += (splitArr[i].length + spaceAndWordLength); + if (cumulativeLength > input.cursorPosition) { + indexInOriginalArray = i; + break; + } + } + + const filteredKeys = this.keys.filter((command) => command.toLowerCase().includes(splitArr[indexInOriginalArray].toLowerCase())); if (inputObject.text !== "" && filteredKeys.length > 0) { - // if performance is required, you could reduce the number of total results by changing the slice below to not have all ~8000 inputs going - options = filteredKeys.slice(0).map((value) => { + options = filteredKeys.map((value) => { return { label: value, handler: () => { - // this is here to make sure that if you try to backspace then enter, the last known evt.data (backspace) is picked up - // this is because evt.data is null for backspace, so without this, the autocomplete windows just closes - if (!isNullOrUndefined(evt.data) || evt.inputType?.toLowerCase() === "deletecontentbackward") { - const separatedArray = inputObject.text.split(" "); - separatedArray[separatedArray.length - 1] = value; - inputObject.setText(separatedArray.join(" ")); - } - ui.revertMode(); + const separatedArray = inputObject.text.split(" "); + separatedArray[indexInOriginalArray] = value; + const cursorPosition = inputObject.cursorPosition - splitArr[indexInOriginalArray].length + separatedArray[indexInOriginalArray].length; + inputObject.setText(separatedArray.join(" ")); + inputObject.setCursorPosition(cursorPosition); return true; } }; @@ -111,10 +108,11 @@ export default class TestDialogueUiHandler extends FormModalUiHandler { } if (options.length > 0) { - const modalOpts = { + const modalOpts: OptionSelectConfigAC = { options: options, maxOptions: 5, - modalContainer: this.modalContainer + modalContainer: this.modalContainer, + inputContainer: this.inputContainers[0] }; ui.setOverlayMode(Mode.AUTO_COMPLETE, modalOpts); } @@ -140,6 +138,12 @@ export default class TestDialogueUiHandler extends FormModalUiHandler { } return false; }; + const originalCancel = config.buttonActions[1]; + config.buttonActions[1] = ()=>{ + if (ui.getMode() === Mode.TEST_DIALOGUE) { + originalCancel(); + } + }; return true; } return false;