diff --git a/chat.html b/chat.html
index 04d682e..b441847 100644
--- a/chat.html
+++ b/chat.html
@@ -50,7 +50,11 @@
+
+
diff --git a/css/app.css b/css/app.css
index 657db03..c0be1bb 100644
--- a/css/app.css
+++ b/css/app.css
@@ -683,6 +683,65 @@ body {
}
+
+
+#chat-autocomplete-list {
+ position: absolute;
+ bottom: 65px;
+ left: 10px;
+ width: calc(100% - 40px);
+ border-radius: 10px;
+ background: #222;
+ color: #FFF;
+
+ max-height: 300px;
+ overflow: hidden;
+ overflow-y: visible;
+
+ font-size: 14px;;
+
+ color: #696969;
+}
+
+#chat-autocomplete-list::-webkit-scrollbar { width: 10px; }
+#chat-autocomplete-list::-webkit-scrollbar-track { background: #1e1e1e; }
+#chat-autocomplete-list::-webkit-scrollbar-thumb { background-color: #555; border-radius: 10px; border: 2px solid #1e1e1e; }
+#chat-autocomplete-list::-webkit-scrollbar-thumb:hover { background-color: #777; }
+
+#chat-autocomplete-list div {
+ margin: 5px 10px;
+ padding: 5px;
+ border-radius: 5px;
+ cursor: pointer;
+}
+
+#chat-autocomplete-list div.autocomplete-item {
+ cursor: pointer;
+ transition: all ease-in-out 300ms;
+}
+
+#chat-autocomplete-list div:not(.autocomplete-item) {
+ background: #191919;
+ color: #FFF;
+ padding: 10px;
+ margin: 10px;
+}
+
+#chat-autocomplete-list .autocomplete-item strong {
+ display: block;
+}
+
+#chat-autocomplete-list .autocomplete-item:hover,
+#chat-autocomplete-list .autocomplete-item.active {
+ color: #FFF;
+ background: #292929;
+}
+
+#chat-autocomplete-list .autocomplete-item small {
+ color: #666;
+}
+
+
#statistics {
position: fixed;
z-index: 11;
diff --git a/js/app-mockup.js b/js/app-mockup.js
index 5e7a0d6..49e258e 100644
--- a/js/app-mockup.js
+++ b/js/app-mockup.js
@@ -1014,4 +1014,4 @@ function randomString(length) {
function randomColor() {
const randomColor = "hsl(" + Math.random() * 360 + ", 100%, 75%)";
return randomColor;
-}
+}
\ No newline at end of file
diff --git a/js/app.js b/js/app.js
index 6ec1187..9f1d8f2 100644
--- a/js/app.js
+++ b/js/app.js
@@ -404,6 +404,7 @@ function hexToRGBA(hexadecimal,opacity) {
const chatInputConfig = document.getElementById("chat-input-config");
const chatInputSend = document.getElementById("chat-input-send");
const chatInputForm = document.querySelector("#chat-input form");
+const chatInput = chatInputForm.querySelector("input[type=text]")
const settings = document.getElementById("chat-input-settings");
chatInputForm.addEventListener("submit", function(event) {
@@ -500,7 +501,6 @@ async function executeModCommand(event, command) {
event.preventDefault();
if (streamerBotConnected == true) {
- const chatInput = chatInputForm.querySelector("input[type=text]")
chatInput.value = command;
chatInputForm.requestSubmit();
@@ -537,3 +537,89 @@ document.addEventListener("DOMContentLoaded", () => {
});
});
});
+
+
+
+
+
+
+let chatcommands = {};
+let chatcurrentFocus = -1;
+
+fetch('js/commands.json')
+.then(response => response.json())
+.then(data => {
+ chatcommands = data;
+});
+
+const chatcommandslist = document.getElementById('chat-autocomplete-list');
+
+chatInput.addEventListener('input', function () {
+ const value = this.value.trim();
+ chatcommandslist.innerHTML = '';
+ chatcurrentFocus = -1;
+ if (!value.startsWith('/')) return;
+ Object.entries(chatcommands).forEach(([groupName, commands]) => {
+
+ const filtered = commands.filter(cmd => cmd.name.startsWith(value));
+
+ if (filtered.length === 0) return;
+
+ const groupTitle = document.createElement('div');
+ groupTitle.textContent = groupName;
+ chatcommandslist.appendChild(groupTitle);
+ filtered.forEach(cmd => {
+ const item = document.createElement('div');
+ item.classList.add('autocomplete-item');
+ item.innerHTML = `
${cmd.name} ${cmd.usage}`;
+ item.addEventListener('click', () => {
+ chatInput.value = cmd.name + ' ';
+ chatcommandslist.innerHTML = '';
+ });
+ chatcommandslist.appendChild(item);
+ });
+ });
+});
+
+
+chatInput.addEventListener('keydown', function (e) {
+ const items = chatcommandslist.querySelectorAll('.autocomplete-item');
+
+ if (items.length === 0) return;
+
+ if (e.key === 'ArrowDown') {
+ chatcurrentFocus++;
+ highlightItem(items);
+ }
+ else if (e.key === 'ArrowUp') {
+ chatcurrentFocus--;
+ highlightItem(items);
+ }
+
+ else if (e.key === 'Enter') {
+ e.preventDefault();
+ if (chatcurrentFocus > -1 && items[chatcurrentFocus]) {
+ items[chatcurrentFocus].click();
+ }
+ }
+});
+
+
+function highlightItem(items) {
+ if (!items) return;
+
+ items.forEach(item => item.classList.remove('active'));
+
+ if (chatcurrentFocus >= items.length) chatcurrentFocus = 0;
+ if (chatcurrentFocus < 0) chatcurrentFocus = items.length - 1;
+
+ items[chatcurrentFocus].classList.add('active');
+ items[chatcurrentFocus].scrollIntoView({ block: "nearest" });
+}
+
+
+document.addEventListener('click', function (e) {
+ if (e.target !== chatcommandslist) {
+ chatcommandslist.innerHTML = '';
+ }
+});
\ No newline at end of file
diff --git a/js/commands.json b/js/commands.json
new file mode 100644
index 0000000..4b689eb
--- /dev/null
+++ b/js/commands.json
@@ -0,0 +1,29 @@
+{
+ "Twitch" : [
+ { "name" : "/me", "usage" : "[message]" },
+ { "name" : "/announce", "usage" : "[message]" },
+ { "name" : "/clear", "usage" : "" },
+ { "name" : "/slow", "usage" : "[duration]" },
+ { "name" : "/slowoff", "usage" : "" },
+ { "name" : "/subscribers", "usage" : "" },
+ { "name" : "/subscribersoff", "usage" : "" },
+ { "name" : "/commercial", "usage" : "[duration]" },
+ { "name" : "/timeout", "usage" : "[user] [duration] [reason]" },
+ { "name" : "/untimeout", "usage" : "" },
+ { "name" : "/ban", "usage" : "[user] [duration] [reason]" },
+ { "name" : "/unban", "usage" : "" },
+ { "name" : "/mod", "usage" : "[user]" },
+ { "name" : "/unmod", "usage" : "[user]" },
+ { "name" : "/vip", "usage" : "[user]" },
+ { "name" : "/unvip", "usage" : "[user]" },
+ { "name" : "/shoutout", "usage" : "[user]" },
+ { "name" : "/raid", "usage" : "[user]" },
+ { "name" : "/unraid", "usage" : "" }
+ ],
+ "YouTube" : [
+ { "name" : "/yt/title", "usage" : "[title]" },
+ { "name" : "/yt/description", "usage" : "[description]" },
+ { "name" : "/yt/timeout", "usage" : "[user] [duration]" },
+ { "name" : "/yt/ban", "usage" : "[user]" }
+ ]
+}
\ No newline at end of file