aboutsummaryrefslogtreecommitdiffstats
path: root/src/client/ChatInput.hx
diff options
context:
space:
mode:
authorPinapelz <yukais@pinapelz.com>2026-10-19 15:32:53 -0700
committerPinapelz <yukais@pinapelz.com>2026-10-19 15:32:53 -0700
commit304bd1e77a5442f614aacd2b35f330777118b2cd (patch)
tree970e7da13bb1bd8edf1f9b453af4d0994c665e94 /src/client/ChatInput.hx
parenta4f3064542ddad6939598e7e60ca0090be07df60 (diff)
merge with upstream https://github.com/RblSb/SyncTubeHEADmaster
Diffstat (limited to 'src/client/ChatInput.hx')
-rw-r--r--src/client/ChatInput.hx188
1 files changed, 188 insertions, 0 deletions
diff --git a/src/client/ChatInput.hx b/src/client/ChatInput.hx
new file mode 100644
index 0000000..61fb95e
--- /dev/null
+++ b/src/client/ChatInput.hx
@@ -0,0 +1,188 @@
+package client;
+
+import client.Main.getEl;
+import haxe.Timer;
+import js.Browser.document;
+import js.Browser.window;
+import js.html.Element;
+import js.html.Event;
+import js.html.InputElement;
+import js.html.KeyboardEvent;
+
+class ChatInput {
+ public final chatline:InputElement;
+ public final commandsWrap:Element;
+ public final commandsList:Element;
+ public final inputWithHistory:InputWithHistory;
+
+ final main:Main;
+ var filteredCommands:Array<Commands.CommandInfo> = [];
+ var selectedCommandIndex = -1;
+
+ public function new(main:Main) {
+ this.main = main;
+ chatline = getEl("#chatline");
+ commandsWrap = getEl("#chat-commands-wrap");
+ commandsList = getEl("#chat-commands-list");
+
+ setupFocusHandler();
+ setupAutocomplete();
+
+ inputWithHistory = new InputWithHistory(chatline, 50, onEnter, onInterceptKeyDown);
+ }
+
+ function setupFocusHandler():Void {
+ chatline.onfocus = e -> {
+ if (Utils.isIOS()) {
+ final startY = 0;
+ Timer.delay(() -> {
+ window.scrollBy(0, -(window.scrollY - startY));
+ getEl("#video").scrollTop = 0;
+ main.scrollChatToEnd();
+ }, 100);
+ } else if (Utils.isTouch()) {
+ main.scrollChatToEnd();
+ }
+ };
+ }
+
+ function setupAutocomplete():Void {
+ commandsList.addEventListener("mousemove", e -> {
+ commandsList.classList.remove("keyboard-nav");
+ });
+
+ chatline.addEventListener("input", e -> {
+ final val = chatline.value;
+ if (val.startsWith("/")) {
+ filteredCommands = Commands.filterForAutocomplete(val);
+ selectedCommandIndex = filteredCommands.length > 0 ? 0 : -1;
+ renderCommands();
+ } else {
+ commandsWrap.style.display = "none";
+ selectedCommandIndex = -1;
+ }
+ });
+
+ chatline.addEventListener("blur", e -> {
+ commandsWrap.style.display = "none";
+ });
+ }
+
+ function renderCommands():Void {
+ commandsList.innerHTML = "";
+ if (filteredCommands.length == 0) {
+ commandsWrap.style.display = "none";
+ return;
+ }
+
+ final parsed = Commands.parse(chatline.value);
+ final currentArgIndex = parsed.currentArgIndex;
+
+ for (i in 0...filteredCommands.length) {
+ final cmd = filteredCommands[i];
+ final item = document.createDivElement();
+ item.className = "chat-command-item" + (i == selectedCommandIndex ? " active" : "");
+ item.onclick = e -> {
+ chatline.value = cmd.name + (cmd.args > 0 ? " " : "");
+ chatline.focus();
+ if (cmd.args == 0) {
+ commandsWrap.style.display = "none";
+ selectedCommandIndex = -1;
+ } else {
+ chatline.dispatchEvent(new Event("input"));
+ }
+ return;
+ };
+ item.onmouseenter = e -> {
+ if (!commandsList.classList.contains("keyboard-nav")) {
+ selectedCommandIndex = i;
+ for (j in 0...commandsList.children.length) {
+ final child = commandsList.children[j];
+ if (j == i) child.classList.add("active");
+ else child.classList.remove("active");
+ }
+ }
+ };
+
+ final nameDiv = document.createDivElement();
+ nameDiv.className = "chat-command-item-name";
+ nameDiv.innerText = cmd.name.toString();
+
+ final descDiv = document.createDivElement();
+ descDiv.className = "chat-command-item-desc";
+ descDiv.innerHTML = Commands.formatDescWithHighlight(Lang.get(cmd.lang), currentArgIndex);
+
+ item.appendChild(nameDiv);
+ item.appendChild(descDiv);
+ commandsList.appendChild(item);
+ }
+ commandsWrap.style.display = "";
+ if (selectedCommandIndex >= 0 && commandsList.children.length > selectedCommandIndex) {
+ final selectedEl:Element = cast commandsList.children[selectedCommandIndex];
+ untyped selectedEl.scrollIntoView({block: "center"});
+ }
+ }
+
+ function onEnter(value:String):Bool {
+ if (main.handleCommands(value)) return true;
+ main.send({
+ type: Message,
+ message: {
+ clientName: "",
+ text: value
+ }
+ });
+ if (Utils.isTouch()) chatline.blur();
+ commandsWrap.style.display = "none";
+ return true;
+ }
+
+ function onInterceptKeyDown(e:KeyboardEvent):Bool {
+ if (commandsWrap.style.display == "none") return false;
+ final key:KeyCode = cast e.keyCode;
+ switch key {
+ case Up:
+ commandsList.classList.add("keyboard-nav");
+ if (selectedCommandIndex > 0) selectedCommandIndex--;
+ else selectedCommandIndex = filteredCommands.length - 1;
+ renderCommands();
+ e.preventDefault();
+ return true;
+ case Down:
+ commandsList.classList.add("keyboard-nav");
+ if (selectedCommandIndex < filteredCommands.length - 1) selectedCommandIndex++;
+ else selectedCommandIndex = 0;
+ renderCommands();
+ e.preventDefault();
+ return true;
+ case Return, Tab:
+ if (selectedCommandIndex < 0 || selectedCommandIndex >= filteredCommands.length) {
+ return false;
+ }
+ final selectedCmd = filteredCommands[selectedCommandIndex];
+ final val = chatline.value;
+ final hasSpace = val.indexOf(" ") >= 0;
+
+ if (key == Return) {
+ if (hasSpace
+ || (selectedCmd.args == 0
+ && val.trim().toLowerCase() == selectedCmd.name.toString())) {
+ return false;
+ }
+ }
+
+ if (!hasSpace) {
+ chatline.value = selectedCmd.name + (selectedCmd.args > 0 ? " " : "");
+ }
+
+ e.preventDefault();
+ chatline.dispatchEvent(new Event("input"));
+ return true;
+ case Escape:
+ commandsWrap.style.display = "none";
+ return false;
+ case _:
+ return false;
+ }
+ }
+}
send patches to the email below
yukais@pinapelz.com
include the subject [PATCH repo_name]
pinapelz.com
homepage