From c36d2c2cd4042839f8eb02ccdc537bf3e1fae3e8 Mon Sep 17 00:00:00 2001 From: MathMan05 Date: Mon, 27 Oct 2025 11:23:38 -0500 Subject: [PATCH] string input choices --- src/webpage/interactions/commands.ts | 79 +++++++++++++++++++++++++++- src/webpage/localuser.ts | 5 +- translations/en.json | 3 ++ 3 files changed, 84 insertions(+), 3 deletions(-) diff --git a/src/webpage/interactions/commands.ts b/src/webpage/interactions/commands.ts index 3aa1822..d2628e8 100644 --- a/src/webpage/interactions/commands.ts +++ b/src/webpage/interactions/commands.ts @@ -192,6 +192,7 @@ export class Command extends SnowFlake { ); } render(html: HTMLElement, channel: Channel) { + console.warn(this.rawJson); html.innerHTML = ""; let state = this.state.get(channel); if (!state) { @@ -232,6 +233,15 @@ export class Command extends SnowFlake { stateObj.state = state; } } + getState(option: Option, channel: Channel) { + const states = this.state.get(channel); + if (!states) return; + const stateObj = states.find((_) => _ instanceof Object && _.option === option); + if (stateObj && stateObj instanceof Object) { + return stateObj.state; + } + return; + } get info() { return this.owner.info; } @@ -333,11 +343,14 @@ abstract class Option { } toJson(state: string) { return { - value: state, + value: this.getValue(state), type: this.type, name: this.name, }; } + getValue(state: string): string | number { + return state; + } } class ErrorOption extends Option { constructor(optionjson: commandOptionJson) { @@ -392,10 +405,74 @@ class StringOption extends Option { if (input.selectionStart === input.value.length && e.key === "ArrowRight") { focusElm(div, false); } + const last = this.owner.getState(this, channel); this.owner.stateChange(this, channel, input.value); + if (this.choices?.length && last !== input.value) { + this.displayChoices(input, channel); + } }; div.append(label, input); return div; } + displayChoices(input: HTMLInputElement, channel: Channel) { + const value = input.value; + if (!this.choices) return; + const similar = (str?: string | null) => { + if (str === null || str === undefined) return 0; + if (str.includes(value)) { + return value.length / str.length; + } else if (str.toLowerCase().includes(value.toLowerCase())) { + return str.length / str.length / 1.4; + } else { + return 0; + } + }; + + const options = ( + value + ? this.choices + .map( + (_) => + [_, Math.max(similar(_.name), similar(_.name_localizations?.[I18n.lang]))] as const, + ) + .filter((_) => _[1] !== 0) + .sort((a, b) => a[1] - b[1]) + .map((_) => _[0]) + : this.choices + ).slice(0, 10); + + this.owner.localuser.MDSearchOptions( + options.map((elm) => { + return [ + `${elm.name_localizations?.[I18n.lang] || elm.name}`, + "", + undefined, + () => { + input.value = elm.name_localizations?.[I18n.lang] || elm.name; + this.owner.stateChange(this, channel, input.value); + return true; + }, + ] as const; + }), + "", + ); + } + getValue(state: string) { + if (this.choices?.length) { + const choice = this.choices.find((choice) => { + if (choice.name === state) { + return true; + } else if (choice.name_localizations?.[I18n.lang] === state) { + return true; + } + return false; + }); + if (choice) { + return choice.value; + } + throw new Error(I18n.commands.errorNotValid(state, this.localizedName)); + } + return state; + } } diff --git a/src/webpage/localuser.ts b/src/webpage/localuser.ts index e759bd2..b05c27b 100644 --- a/src/webpage/localuser.ts +++ b/src/webpage/localuser.ts @@ -3392,7 +3392,7 @@ class Localuser { | [string, string, void | HTMLElement, () => void | boolean] )[], original: string, - div: HTMLDivElement, + div: HTMLDivElement = document.getElementById("searchOptions") as HTMLDivElement, typebox?: MarkDown, ) { if (!div) return; @@ -3456,6 +3456,7 @@ class Localuser { const cancel = new Set(["ArrowUp", "ArrowDown", "Enter", "Tab"]); this.keyup = (event) => { if (remove()) return false; + if (cancel.has(event.key)) { switch (event.key) { case "ArrowUp": @@ -3882,7 +3883,7 @@ class Localuser { ["NotoColorEmoji-Regular.ttf", "Noto Color Emoji"], ["OpenMoji-color-glyf_colr_0.woff2", "OpenMoji"], ["Twemoji-16.0.1.ttf", "Twemoji"], - ["BlobmojiCompat.ttf", "Blobmoji"] + ["BlobmojiCompat.ttf", "Blobmoji"], ] as const; } async resolvemember(id: string, guildid: string): Promise { diff --git a/translations/en.json b/translations/en.json index b4100ba..f875f91 100644 --- a/translations/en.json +++ b/translations/en.json @@ -682,6 +682,9 @@ "ban": "Ban $1 from $2", "nick:": "Nickname:" }, + "commands":{ + "errorNotValid":"$1 is not a valid choice for $2" + }, "badge": { "staff": "Instance staff", "partner": "Instance partner",