Autosave: 20260216-130929

This commit is contained in:
Flatlogic Bot 2026-02-16 13:09:29 +00:00
parent 77269fa65c
commit e6233598d6
4 changed files with 289 additions and 208 deletions

View File

@ -652,6 +652,22 @@ body {
.dm-status-online { background-color: #23a559; } .dm-status-online { background-color: #23a559; }
.dm-status-offline { background-color: #80848e; } .dm-status-offline { background-color: #80848e; }
/* Custom Scrollbar */
.custom-scrollbar::-webkit-scrollbar {
width: 8px;
height: 8px;
}
.custom-scrollbar::-webkit-scrollbar-track {
background: transparent;
}
.custom-scrollbar::-webkit-scrollbar-thumb {
background: #1e1f22;
border-radius: 4px;
}
.custom-scrollbar::-webkit-scrollbar-thumb:hover {
background: #1a1b1e;
}
.emoji-picker { .emoji-picker {
position: fixed; position: fixed;
background-color: #1e1f22; background-color: #1e1f22;

View File

@ -27,103 +27,221 @@ document.addEventListener('DOMContentLoaded', () => {
} }
} }
// Emoji list categorized // Unified Emoji Categories - Expanded for "Complete" feel
const EMOJI_CATEGORIES = { const EMOJI_CATEGORIES = {
'Smileys': ['😀', '😃', '😄', '😁', '😆', '😅', '🤣', '😂', '🙂', '🙃', '😉', '😊', '😇', '🥰', '😍', '🤩', '😘', '😗', '😚', '😙', '😋', '😛', '😜', '🤪', '😝', '🤑', '🤗', '🤭', '🤫', '🤔', '🤐', '🤨', '😐', '😑', '😶', '😏', '😒', '🙄', '😬', '🤥', '😌', '😔', '😪', '🤤', '😴', '😷', '🤒', '🤕', '🤢', '🤮', '🤧', '🥵', '🥶', '🥴', '😵', '🤯', '🤠', '🥳', '😎', '🤓', '🧐', '😕', '😟', '🙁', '☹️', '😮', '😯', '😲', '😳', '🥺', '😦', '😧', '😨', '😰', '😥', '😢', '😭', '😱', '😖', '😣', '😞', '😓', '😩', '😫', '🥱', '😤', '😡', '😠', '🤬', '😈', '👿', '👹', '👺', '💀', '☠️', '💩', '🤡', '👹', '👺', '👻', '👽', '👾', '🤖', '😺', '😸', '😻', '😼', '😽', '🙀', '😿', '😾'], 'Smileys': ['😀', '😃', '😄', '😁', '😆', '😅', '🤣', '😂', '🙂', '🙃', '😉', '😊', '😇', '🥰', '😍', '🤩', '😘', '😗', '😚', '😙', '😋', '😛', '😜', '🤪', '😝', '🤑', '🤗', '🤭', '🤫', '🤔', '🤐', '🤨', '😐', '😑', '😶', '😏', '😒', '🙄', '😬', '🤥', '😌', '😔', '😪', '🤤', '😴', '😷', '🤒', '🤕', '🤢', '🤮', '🤧', '🥵', '🥶', '🥴', '😵', '🤯', '🤠', '🥳', '😎', '🤓', '🧐', '😕', '😟', '🙁', '☹️', '😮', '😯', '😲', '😳', '🥺', '😦', '😧', '😨', '😰', '😥', '😢', '😭', '😱', '😖', '😣', '😞', '😓', '😩', '😫', '🥱', '😤', '😡', '😠', '🤬', '😈', '👿', '👹', '👺', '💀', '☠️', '💩', '🤡', '👻', '👽', '👾', '🤖', '😺', '😸', '😻', '😼', '😽', '🙀', '😿', '😾', '🙈', '🙉', '🙊', '💋', '💌', '💘', '💝', '💖', '💗', '💓', '💞', '💕', '💟', '❣️', '💔', '❤️', '🧡', '💛', '💚', '💙', '💜', '🖤', '🤍', '🤎', '💯', '💢', '💥', '💫', '💦', '💨', '🕳️', '💣', '💬', '👁️‍🗨️', '🗨️', '🗯️', '💭', '💤', '🪐', '🌠', '🎇', '🎆', '🌇', '🌆', '🏙️', '🌃', '🌌'],
'People': ['👋', '🤚', '🖐️', '✋', '🖖', '👌', '🤏', '✌️', '🤞', '🤟', '🤘', '🤙', '👈', '👉', '👆', '🖕', '👇', '☝️', '👍', '👎', '✊', '👊', '🤛', '🤜', '👏', '🙌', '👐', '🤲', '🤝', '🙏', '✍️', '💅', '🤳', '💪', '🦾', '🦵', '🦿', '🦶', '👂', '🦻', '👃', '🧠', '🦷', '🦴', '👀', '👁️', '👅', '👄', '👶', '🧒', '👦', '👧', '🧑', '👱', '👨', '🧔', '👩', '🧓', '👴', '👵', '👮', '🕵️', '💂', '👷', '🤴', '👸', '👳', '👲', '🧕', '🤵', '👰', '🤰', '🤱', '👼', '🎅', '🤶', '🦸', '🦹', '🧙', '🧚', '🧛', '🧜', '🧝', '🧞', '🧟', '💆', '💇', '🚶', '🏃', '💃', '🕺', '🕴️', '👯', '🧖', '🧗'], 'Gestures': ['👋', '🤚', '🖐️', '✋', '🖖', '👌', '🤏', '✌️', '🤞', '🤟', '🤘', '🤙', '👈', '👉', '👆', '🖕', '👇', '☝️', '👍', '👎', '✊', '👊', '🤛', '🤜', '👏', '🙌', '👐', '🤲', '🤝', '🙏', '✍️', '💅', '🤳', '💪', '🦾', '🦵', '🦿', '🦶', '👂', '🦻', '👃', '🧠', '🦷', '🦴', '👀', '👁️', '👅', '👄', '🖖', '🤘', '🤙', '🖐️', '🖕', '🖖', '✍️', '🤳', '💪', '🦾'],
'Animals': ['🐶', '🐱', '🐭', '🐹', '🐰', '🦊', '🐻', '🐼', '🐨', '🐯', '🦁', '🐮', '🐷', '🐽', '🐸', '🐵', '🙈', '🙉', '🙊', '🐒', '🐔', '🐧', '🐦', '🐤', '🐣', '🐥', '🦆', '🦅', '🦉', '🦇', '🐺', '🐗', '🐴', '🦄', '🐝', '🐛', '🦋', '🐌', '🐞', '🐜', '🦟', '🦗', '🕷️', '🕸️', '蠍', '🐢', '🐍', '🦎', '🦖', '🦕', '🐙', '🦑', '🦐', '🦞', '🦀', '🐡', '🐠', '🐟', '🐬', '🐳', '🐋', '🦈', '🐊', '🐅', '🐆', '🦓', '🦍', '🦧', '🐘', '🦛', '🦏', '🐪', '🐫', '🦒', '🦘', '🐃', '🐂', '🐄', '🐎', '🐖', '🐏', '🐑', '🐐', '🦌', '🐕', '🐩', '🦮', '🐈', '🐓', '🦃', '🦚', '🦜', '🦢', '🦩', '🕊️', '🐇', '🦝', '🦨', '🦡', '🦦', '🦥', '🐁', '🐀', '🐿️', '🦔'], 'People': ['👶', '🧒', '👦', '👧', '🧑', '👱', '👨', '👩', '🧓', '👴', '👵', '👮', '🕵️', '💂', '👷', '🤴', '👸', '👳', '👲', '🧕', '🤵', '👰', '🤰', '🤱', '👼', '🎅', '🤶', '🦸', '🦹', '🧙', '🧚', '🧛', '🧜', '🧝', '🧞', '🧟', '💆', '💇', '🚶', '🏃', '💃', '🕺', '🕴️', '👯', '🧖', '🧗', '🤺', '🏇', '⛷️', '🏂', '🏌️', '🏄', '🚣', '🏊', '⛹️', '🏋️', '🚴', '🚵', '🤸', '🤼', '🤽', '🤾', '🤹', '🧘', '🛀', '🛌'],
'Nature': ['🌵', '🎄', '🌲', '🌳', '🌴', '🌱', '🌿', '☘️', '🍀', '🎍', '🎋', '🍃', '🍂', '🍁', '🍄', '🐚', '🌾', '💐', '🌷', '🌹', '🥀', '🌺', '🌸', '🌼', '🌻', '🌞', '🌝', '🌛', '🌜', '🌚', '🌕', '🌖', '🌗', '🌘', '🌑', '🌒', '🌓', '🌔', '🌙', '🌎', '🌍', '🌏', '🪐', '💫', '⭐️', '🌟', '✨', '⚡️', '☄️', '💥', '🔥', '🌪️', '🌈', '☀️', '🌤️', '⛅️', '🌥️', '☁️', '🌦️', '🌧️', '🌨️', '🌩️', '❄️', '☃️', '⛄️', '🌬️', '💨', '💧', '💦', '☔️', '☂️', '🌊', '🌫️'], 'Animals': ['🐶', '🐱', '🐭', '🐹', '🐰', '🦊', '🐻', '🐼', '🐨', '🐯', '🦁', '🐮', '🐷', '🐽', '🐸', '🐵', '🙈', '🙉', '🙊', '🐒', '🐔', '🐧', '🐦', '🐤', '🐣', '🐥', '🦆', '🦅', '🦉', '🦇', '🐺', '🐗', '🐴', '🦄', '🐝', '🐛', '🦋', '🐌', '🐞', '🐜', '🦟', '🦗', '🕷️', '🕸️', '🦂', '🐢', '🐍', '🦎', '🦖', '🦕', '🐙', '🦑', '🦐', '🦞', '🦀', '🐡', '🐠', '🐟', '🐬', '🐳', '🐋', '🦈', '🐊', '🐅', '🐆', '🦓', '🦍', '🦧', '🐘', '🦛', '🦏', '🐪', '🐫', '🦒', '🦘', '🦬', '🐃', '🐂', '🐄', '🐎', '🐖', '🐏', '🐑', '🐐', '🦌', '🐕', '🐩', '🦮', '🐕‍🦺', '🐈', '🐈‍⬛', '🐓', '🦃', '🦚', '🦜', '🦢', '🦩', '🕊️', '🐇', '🦝', '🦨', '🦡', '🦦', '🦥', '🐁', '🐀', '🐿️', '🦔', '🐾', '🐉', '🐲', '🌵', '🎄', '🌲', '🌳', '🌴', '🌱', '🌿', '☘️', '🍀', '🎍', '🎋', '🍃', '🍂', '🍁', '🍄', '🐚', '🌾'],
'Food': ['🍏', '🍎', '🍐', '🍊', '🍋', ' BANANA', '🍉', '🍇', '🍓', '🍈', '🍒', '🍑', '🥭', '🍍', '🥥', '🥝', '🍅', '🍆', '🥑', '🥦', '🥬', '🥒', '🌽', '🥕', '🧄', '🥔', '🍠', '🥐', '🥯', '🍞', '🥖', '🥨', '🧀', '🥚', '🍳', '🧈', '🥞', ' waffle', '🥓', '🥩', '🍗', '🍖', '🦴', '🌭', '🍔', '🍟', '🍕', '🥪', '🥙', '🧆', '🌮', '🌯', '🥗', '🥘', '🍝', '🍜', '🍲', '🍛', ' sushi', ' Bento', ' Dumpling', ' Oyster', '🍤', ' Rice Ball', ' Rice', ' Rice Cracker', '🍥', '🥠', '🥮', '🍢', '🍡', '🍧', '🍨', '🍦', '🥧', '🧁', '🍰', '🎂', '🍮', '🍭', '🍬', '🍫', ' popcorn', '🍩', '🍪', '🌰', ' peanuts', '🍯', '🥛', '☕️', '🍵', '🥤', '🍶', '🍺', '🍻', '🥂', '🍷', '🥃', '🍸', '🍹', '🧉', '🍾', '🧊', '🥄', '🍴', '🍽️', '🥣', '🥡'], 'Nature': ['💐', '🌷', '🌹', '🥀', '🌺', '🌸', '🌼', '🌻', '🌞', '🌝', '🌛', '🌜', '🌚', '🌕', '🌖', '🌗', '🌘', '🌑', '🌒', '🌓', '🌔', '🌙', '🌎', '🌍', '🌏', '🪐', '💫', '⭐️', '🌟', '✨', '⚡️', '☄️', '💥', '🔥', '🌪️', '🌈', '☀️', '🌤️', '⛅️', '🌥️', '☁️', '🌦️', '🌧️', '🌨️', '🌩️', '❄️', '☃️', '⛄️', '🌬️', '💨', '💧', '💦', '☔️', '☂️', '🌊', '🌫️', '⛰️', '🏔️', '🗻', '🌋', '🏜️', '🏖️', '🏝️', '🏕️', '⛺️'],
'Travel': ['🚗', '🚕', '🚙', '🚌', '🚎', '🏎️', '🚓', '🚑', '🚒', '🚐', '🚚', '🚛', '🚜', '🛵', '🚲', '🛴', '🚏', '🛣️', '🛤️', '⛽️', '🚨', '🚥', '🚦', '🚧', '⚓️', '⛵️', '🚤', '🛳️', '⛴️', '🚢', '✈️', '🛫', '🛬', '💺', '🚁', '🚟', '🚠', '🚡', '🚀', '🛸', '🛰️', '⌛️', '⏳', '⌚️', '⏰', '⏱️', '⏲️', '🕰️', '🌡️', '☀️', '🪐', '🌟', '☁️', '⛅️', '⛈️', '🌈', '⛰️', '🏔️', '🗻', '🌋', '🏜️', '🏕️', '⛺️', '🏠', '🏡', '🏢', '🏣', '🏤', '🏥', '🏦', '🏨', '🏩', '🏪', '🏫', '🏬', '🏭', '🏯', '🏰', '💒', '🗼', '🗽', '⛪️', '🕌', '🕍', '⛩️', '🕋', '⛲️', '🌁', '🌃', '🏙️', '🌄', '🌅', '🌆', '🌇', '🌉', '🎠', '🎡', '🎢', '🚂', '🚃', '🚄', '🚅', '🚆', '🚇', '🚈', '🚉', '🚊', '🚝', '🚞', '🚋'], 'Food': ['🍏', '🍎', '🍐', '🍊', '🍋', '🍌', '🍉', '🍇', '🍓', '🍈', '🍒', '🍑', '🥭', '🍍', '🥥', '🥝', '🍅', '🍆', '🥑', '🥦', '🥬', '🥒', '🌽', '🥕', '🧄', '🧅', '🍄', '🥜', '🌰', '🍞', '🥐', '🥖', '🥨', '🥯', '🥞', '🧇', '🧀', '🍖', '🍗', '🥩', '🥓', '🍔', '🍟', '🍕', '🌭', '🥪', '🌮', '🌯', '🥙', '🧆', '🍳', '🥘', '🍲', '🥣', '🥗', '🍿', '🧈', '🧂', '🥫', '🍱', '🍘', '🍙', '🍚', '🍛', '🍜', '🍝', '🍠', '🍢', '🍣', '🍤', '🍥', '🥮', '🍡', '🥟', '🥠', '🥡', '🍦', '🍧', '🍨', '🍩', '🍪', '🎂', '🍰', '🧁', '🥧', '🍫', '🍬', '🍭', '🍮', '🍯', '🍼', '🥛', '☕️', '🍵', '🧉', '🥤', '🧃', '🍺', '🍻', '🥂', '🍷', '🥃', '🍸', '🍹', '🍾', '🧊', '🥄', '🍴', '🍽️'],
'Activities': ['⚽️', '🏀', '🏈', '⚾️', '🥎', '🎾', '🏐', '🏉', '🎱', '🏓', '🏸', '🥅', ' hockey', ' field hockey', ' cricket', '⛳️', '🏹', '🎣', ' boxing', '🥋', ' skateboard', '🛷', '⛸️', '🥌', '🎿', '⛷️', '🏂', '🏋️', ' fencing', '🤼', ' gymnastics', ' basketball player', '🤽', ' handball', ' juggle', '🧘', '🏇', ' rowing', ' swimming', '🚴', '🚵', '🧗', '🎖️', '🏆', '🏅', '🥇', '🥈', '🥉', '🎫', '🎟️', '🎭', '🎨', '🎬', '🎤', '🎧', '🎼', '🎹', '🥁', '🎷', '🎺', '🎸', '🪕', '🎻', '🎲', '♟️', '🎯', '🎳', '🎮', '🎰', '🧩'], 'Activities': ['⚽️', '🏀', '🏈', '⚾️', '🥎', '🎾', '🏐', '🏉', '🎱', '🏓', '🏸', '🥅', '🏒', '🏑', '🏏', '⛳️', '🏹', '🎣', '🥊', '🥋', '🛹', '🛷', '⛸️', '🥌', '🎿', '⛷️', '🏂', '🏋️', '🤺', '🤼', '🤸', '⛹️', '🤽', '🤾', '🤹', '🧘', '🏇', '🚣', '🏊', '🚴', '🚵', '🧗', '🎖️', '🏆', '🏅', '🥇', '🥈', '🥉', '🎫', '🎟️', '🎭', '🎨', '🎬', '🎤', '🎧', '🎼', '🎹', '🥁', '🎷', '🎺', '🎸', '🪕', '🎻', '🎲', '♟️', '🎯', '🎳', '🎮', '🎰', '🧩'],
'Objects': ['⌚️', '📱', '📲', '💻', '⌨️', '🖱️', '🖲️', '🕹️', '🗜️', '💽', '💾', '💿', '🎞️', '📷', '📸', '📹', '📼', '🔍', '🔎', '🕯️', '💡', '🔦', '🏮', '🪔', '📔', '📕', '📖', '📗', '📘', '📙', '📚', '📓', '📒', '📃', '📜', '📄', '📰', '🗞️', '📑', '🔖', '🏷️', '💰', '💴', '💵', '💶', '💷', '💸', '💳', '🧾', '💹', '✉️', '📧', '📨', '📩', '📤', '📥', '📦', '📫', '📩', '📬', '📭', '📮', '🗳️', '✏️', '✒️', '🖋️', '🖊️', '🖌️', '🖍️', '📝', '💼', '📁', '📂', '🗂️', '📅', '📆', '🗒️', '🗓️', '📇', '📈', '📉', '📊', '📋', '📌', '📍', '📎', '🖇️', '📏', '📐', '✂️', '🗃️', '🗄️', '🗑️', '🔒', '🔓', '🔏', '🔐', '🔑', '🗝️', '🔨', '🪓', '⛏️', '⚒️', '🛠️', '🗡️', '⚔️', '🔫', '🛡️', '🔧', '🔩', '⚙️', '🗜️', '⚖️', '🦯', '🔗', '⛓️', '🧰', '🧲', '⚗️', '🧪', '🧫', '🧬', '🔬', '🔭', '📡', '💉', '🩸', '💊', '🩹', '🩺', '🚪', '🛏️', '🛋️', '🪑', '🚽', '🚿', '🛀', '🪒', '🧴', '🧷', '🧹', '🧺', '🧻', '🧼', '🧯', '🛒', '🚬', '⚰️', '⚱️', '🗿'], 'Travel': ['🚗', '🚕', '🚙', '🚌', '🚎', '🏎️', '🚓', '🚑', '🚒', '🚐', '🚚', '🚛', '🚜', '🛵', '🚲', '🛴', '🚏', '🛣️', '🛤️', '⛽️', '🚨', '🚥', '🚦', '🚧', '⚓️', '⛵️', '🚤', '🛳️', '⛴️', '🚢', '✈️', '🛫', '🛬', '💺', '🚁', '🚟', '🚠', '🚡', '🚀', '🛸', '🛰️', '⌛️', '⏳', '⌚️', '⏰', '⏱️', '⏲️', '🕰️', '🌡️', '🌃', '🏙️', '🌄', '🌅', '🌆', '🌇', '🌉', '🎠', '🎡', '🎢', '🚂', '🚃', '🚄', '🚅', '🚆', '🚇', '🚈', '🚉', '🚊', '🚝', '🚞', '🚋'],
'Symbols': ['💘', '💝', '💖', '💗', '💓', '💞', '💕', '💟', '❣️', '💔', '❤️', '🧡', '💛', '💚', '💙', '💜', '🖤', '🤍', '🤎', '💯', '💢', '💥', '💫', '💦', '💨', '🕳️', '💣', '💬', '👁️‍🗨️', '🗨️', '🗯️', '💭', '💤', '🌐', '♠️', '♥️', '♦️', '♣️', '🃏', '🀄️', '🎴', '🎭', '🔇', '🔈', '🔉', '🔊', '📢', '📣', '📯', '🔔', '🔕', '🎼', '🎵', '🎶', '💹', '🏧', '🚮', '🚰', '♿️', '🚹', '🚺', '🚻', '🚼', '🚾', '🛂', '🛃', '🛄', '🛅', '⚠️', '🚸', '⛔️', '🚫', '🚳', '🚭', '🚯', '🚱', '🚷', '📵', '🔞', '☢️', '☣️', '⬆️', '↗️', '➡️', '↘️', '⬇️', '↙️', '⬅️', '↖️', '↕️', '↔️', '↩️', '↪️', '⤴️', '⤵️', '🔃', '🔄', '🔙', '🔚', '🔛', '🔜', '🔝', '🛐', '⚛️', '🕉️', '✡️', '☸️', '☯️', '✝️', '☦️', '☪️', '☮️', '🕎', '🔯', '♈️', '♉️', '♊️', '♋️', '♌️', '♍️', '♎️', '♏️', '♐️', '♑️', '♒️', '♓️', '⛎', '🔀', '🔁', '🔂', '▶️', '⏩', '⏭️', '⏯️', '◀️', '⏪', '⏮️', '🔼', '⏫', '🔽', '⏬', '⏸️', '⏹️', '⏺️', '⏏️', '🎦', '🔅', '🔆', '📶', '📳', '📴', '', '', '➗', '✖️', '♾️', '‼️', '⁉️', '❓', '❔', '❕', '❗️', '〰️', '💱', '💲', '⚕️', '♻️', '⚜️', '🔱', '📛', '🔰', '⭕️', '✅', '☑️', '✔️', '✖️', '❌', '❎', '➰', '➿', '〽️', '✳️', '✴️', '❇️', '‼️', '🈁', '🈂️', '🈷️', '🈶', '🈯️', '🉐', '🈹', '🈚️', '🈲', '🉑', '🈸', '🈴', '🈳', '㊗️', '㊙️', '🈺', '🈵', '🔴', '🟠', '🟡', '🟢', '🔵', '🟣', '🟤', '⚫️', '⚪️', '🟥', '🟧', '🟨', '🟩', '🟦', '🟪', '🟫', '⬛️', '⬜️'], 'Objects': ['⌚️', '📱', '📲', '💻', '⌨️', '🖱️', '🖲️', '🕹️', '🗜️', '💽', '💾', '💿', '📀', '📼', '📷', '📸', '📹', '🎥', '📽️', '🎞️', '📞', '📠', '📺', '📻', '🎙️', '🎚️', '🎛️', '🧭', '⏱️', '⏲️', '⏰', '🕰️', '⌛️', '⏳', '📡', '🔋', '🔌', '💡', '🔦', '🕯️', '🪔', '🧯', '🛢️', '💸', '💵', '💴', '💶', '💷', '💰', '💳', '💎', '⚖️', '🧰', '🔧', '🔨', '⚒️', '🛠️', '⛏️', '🔩', '⚙️', '🧱', '⛓️', '🧲', '🔫', '💣', '🧨', '🪓', '🔪', '🗡️', '⚔️', '🛡️', '🚬', '⚰️', '⚱️', '🏺', '🔮', '🧿', '📿', '💈', '⚗️', '🔭', '🔬', '🕳️', '💊', '💉', '🩸', '🧬', '🦠', '🧫', '🧪', '🌡️', '🧹', '🧺', '🧻', '🧼', '🧽', '🪒', '🧴', '🛎️', '🔑', '🗝️', '🚪', '🪑', '🛋️', '🛏️', '🛌', '🧸', '🖼️', '🛍️', '🛒', '🎁', '🎈', '🎏', '🎀', '🎊', '🎉', '🎎', '🏮', '🎐', '🧧', '✉️', '📩', '📨', '📧', '💌', '📥', '📤', '📦', '🏷️', '📁', '📂', '📅', '📆', '🗒️', '🗓️', '📇', '📈', '📉', '📊', '📋', '📌', '📍', '📎', '🖇️', '📏', '📐', '✂️', '🗃️', '🗄️', '🗑️', '🔒', '🔓', '🔏', '🔐', '🔑', '🗝️'],
'Flags': ['🏁', '🚩', '🎌', '🏴', '🏳️', '🏳️‍🌈', '🏳️‍⚧️', '🏴‍☠️', '🇦🇫', '🇦🇽', '🇦🇱', '🇩🇿', '🇦🇲', '🇦🇺', '🇦🇹', '🇦🇿', '🇧🇪', '🇧🇷', '🇨🇦', '🇨🇱', '🇨🇳', '🇨🇴', '🇨🇿', '🇩🇰', '🇪🇬', '🇫🇮', '🇫🇷', '🇩🇪', '🇬🇷', '🇭🇰', '🇮🇳', '🇮🇩', '🇮🇪', '🇮🇱', '🇮🇹', '🇯🇵', '🇰🇷', '🇲🇽', '🇳🇱', '🇳🇿', '🇳🇴', '🇵🇰', '🇵🇭', '🇵🇱', '🇵🇹', '🇷🇺', '🇸🇦', '🇸🇬', '🇿🇦', '🇪🇸', '🇸🇪', '🇨🇭', '🇹🇭', '🇹🇷', '🇺🇦', '🇦🇪', '🇬🇧', '🇺🇸', '🇻🇳'] 'Symbols': ['💘', '💝', '💖', '💗', '💓', '💞', '💕', '💟', '❣️', '💔', '❤️', '🧡', '💛', '💚', '💙', '💜', '🖤', '🤍', '🤎', '💯', '💢', '💥', '💫', '💦', '💨', '🕳️', '💣', '💬', '👁️‍🗨️', '🗨️', '🗯️', '💭', '💤', '🌐', '♠️', '♥️', '♦️', '♣️', '🃏', '🀄️', '🎴', '🔇', '🔈', '🔉', '🔊', '📢', '📣', '📯', '🔔', '🔕', '🎼', '🎵', '🎶', '💹', '🏧', '🚮', '🚰', '♿️', '🚹', '🚺', '🚻', '🚼', '🚾', '🛂', '🛃', '🛄', '🛅', '⚠️', '🚸', '⛔️', '🚫', '🚳', '🚭', '🚯', '🚱', '🚷', '📵', '🔞', '☢️', '☣️', '⬆️', '↗️', '➡️', '↘️', '⬇️', '↙️', '⬅️', '↖️', '↕️', '↔️', '↩️', '↪️', '⤴️', '⤵️', '🔃', '🔄', '🔙', '🔚', '🔛', '🔜', '🔝', '🛐', '⚛️', '🕉️', '✡️', '☸️', '☯️', '✝️', '☦️', '☪️', '☮️', '🕎', '🔯', '♈️', '♉️', '♊️', '♋️', '♌️', '♍️', '♎️', '♏️', '♐️', '♑️', '♒️', '♓️', '⛎', '🔀', '🔁', '🔂', '▶️', '⏩', '⏭️', '⏯️', '◀️', '⏪', '⏮️', '🔼', '⏫', '🔽', '⏬', '⏸️', '⏹️', '⏺️', '⏏️', '🎦', '🔅', '🔆', '📶', '📳', '📴', '', '', '➗', '✖️', '♾️', '‼️', '⁉️', '❓', '❔', '❕', '❗️', '〰️', '💱', '💲', '⚕️', '♻️', '⚜️', '🔱', '📛', '🔰', '⭕️', '✅', '☑️', '✔️', '✖️', '❌', '❎', '➰', '➿', '〽️', '✳️', '✴️', '❇️', '‼️', '🈁', '🈂️', '🈷️', '🈶', '🈯️', '🉐', '🈹', '🈚️', '🈲', '🉑', '🈸', '🈴', '🈳', '㊗️', '㊙️', '🈺', '🈵', '🔴', '🟠', '🟡', '🟢', '🔵', '🟣', '🟤', '⚫️', '⚪️', '🟥', '🟧', '🟨', '🟩', '🟦', '🟪', '🟫', '⬛️', '⬜️', '♈', '♉', '♊', '♋', '♌', '♍', '♎', '♏', '♐', '♑', '♒', '♓', '⛎', '☸', '☦', '☦', '☪', '☮', '☯', '♈', '♉', '♊', '♋', '♌', '♍', '♎', '♏', '♐', '♑', '♒', '♓', '⛎', '♀', '♂', '⚕', '♾', '⚓', '⚔', '⚖', '⚗', '⚙', '⚖', '⚓', '⚔'],
'Flags': ['🏁', '🚩', '🎌', '🏴', '🏳️', '🏳️‍🌈', '🏳️‍⚧️', '🏴‍☠️', '🇦🇫', '🇦🇽', '🇦🇱', '🇩🇿', '🇦🇲', '🇦🇺', '🇦🇹', '🇦🇿', '🇧🇪', '🇧🇷', '🇨🇦', '🇨🇱', '🇨🇳', '🇨🇴', '🇨🇿', '🇩🇰', '🇪🇬', '🇫🇮', '🇫🇷', '🇩🇪', '🇬🇷', '🇭🇰', '🇮🇳', '🇮🇩', '🇮🇪', '🇮🇱', '🇮🇹', '🇯🇵', '🇰🇷', '🇲🇽', '🇳🇱', '🇳🇿', '🇳🇴', '🇵🇰', '🇵🇭', '🇵🇱', '🇵🇹', '🇷🇺', '🇸🇦', '🇸🇬', '🇿🇦', '🇪🇸', '🇸🇪', '🇨🇭', '🇹🇭', '🇹🇷', '🇺🇦', '🇦🇪', '🇬🇧', '🇺🇸', '🇻🇳', '🇦🇷', '🇧🇩', '🇧🇪', '🇧🇴', '🇮🇩', '🇮🇷', '🇮🇶', '🇯🇲', '🇰🇿', '🇰🇪', '🇲🇾', '🇲🇦', '🇳🇬', '🇵🇪', '🇷🇴', '🇷🇸', '🇸🇰', '🇺🇾', '🇿🇼']
}; };
// Global EMOJIS list for reactions (flattened) const ALL_EMOJIS = Object.values(EMOJI_CATEGORIES).flat();
const EMOJIS = Object.values(EMOJI_CATEGORIES).flat();
function populateEmojiGrid(category = null, searchTerm = '') { /**
const roleEmojiGrid = document.getElementById('role-emoji-grid'); * Centralized Emoji Picker Component
if (!roleEmojiGrid) return; */
const UniversalEmojiPicker = {
roleEmojiGrid.innerHTML = ''; currentPicker: null,
let emojis = [];
if (searchTerm) {
emojis = EMOJIS.filter(e => e.includes(searchTerm) || searchTerm === '');
// For simple search by name, we'd need names, but since we only have the emoji characters,
// the user might search by the emoji itself or we could just show all if search is empty.
// Actually, without a mapping of emoji to names, searching is limited.
// But I'll implement it anyway so if they paste an emoji it works,
// or I can add common names if I had a mapping.
// Given the constraint, I'll just filter by character for now,
// or maybe the user just wants a way to filter the massive list.
} else {
emojis = EMOJI_CATEGORIES[category] || [];
}
emojis.forEach(emoji => {
const span = document.createElement('span');
span.textContent = emoji;
span.style.cursor = 'pointer';
span.style.fontSize = '20px';
span.style.padding = '5px';
span.style.textAlign = 'center';
span.className = 'rounded role-emoji-item';
span.onclick = () => {
document.getElementById('edit-role-icon').value = emoji;
document.getElementById('selected-role-emoji-preview').textContent = emoji;
};
roleEmojiGrid.appendChild(span);
});
}
function initEmojiCategories() { show(anchor, callback, options = {}) {
const categoriesContainer = document.getElementById('role-emoji-categories'); this.hide();
const searchInput = document.getElementById('role-emoji-search');
if (!categoriesContainer) return;
categoriesContainer.innerHTML = '';
Object.keys(EMOJI_CATEGORIES).forEach((cat, index) => {
const btn = document.createElement('button');
btn.type = 'button';
btn.className = 'btn btn-sm btn-dark text-nowrap px-2 py-1';
btn.style.fontSize = '0.75em';
btn.textContent = cat;
if (index === 0) btn.classList.add('active', 'btn-primary');
btn.onclick = () => { const picker = document.createElement('div');
if (searchInput) searchInput.value = ''; picker.className = 'emoji-picker p-0 overflow-hidden d-flex flex-column';
categoriesContainer.querySelectorAll('button').forEach(b => b.classList.remove('active', 'btn-primary')); picker.style.width = options.width || '450px';
btn.classList.add('active', 'btn-primary'); picker.style.height = options.height || '450px';
populateEmojiGrid(cat); picker.style.backgroundColor = '#313338';
}; picker.style.border = '1px solid #1e1f22';
categoriesContainer.appendChild(btn); picker.style.borderRadius = '8px';
}); picker.style.boxShadow = '0 8px 24px rgba(0,0,0,0.5)';
picker.style.zIndex = '4000';
picker.style.position = 'fixed';
// Tab Navigation
const tabs = document.createElement('div');
tabs.className = 'd-flex overflow-auto border-bottom border-secondary p-1 custom-scrollbar';
tabs.style.gap = '2px';
tabs.style.backgroundColor = '#2b2d31';
// Search Container
const searchContainer = document.createElement('div');
searchContainer.className = 'p-2 border-bottom border-secondary';
searchContainer.style.backgroundColor = '#313338';
const searchInput = document.createElement('input');
searchInput.type = 'text';
searchInput.placeholder = 'Chercher un emoji...';
searchInput.className = 'form-control form-control-sm bg-dark border-secondary text-white';
searchContainer.appendChild(searchInput);
// Grid Container
const grid = document.createElement('div');
grid.className = 'flex-grow-1 overflow-auto p-2 custom-scrollbar';
grid.style.display = 'grid';
grid.style.gridTemplateColumns = 'repeat(8, 1fr)';
grid.style.gap = '5px';
grid.style.backgroundColor = '#313338';
const renderGrid = (cat = null, term = '') => {
grid.innerHTML = '';
let list = term ? ALL_EMOJIS.filter(e => e.includes(term)) : EMOJI_CATEGORIES[cat];
(list || []).forEach(emoji => {
const span = document.createElement('span');
span.textContent = emoji;
span.className = 'emoji-item rounded d-flex align-items-center justify-content-center';
span.style.cursor = 'pointer';
span.style.fontSize = '24px';
span.style.padding = '8px';
span.style.aspectRatio = '1/1';
span.onclick = (e) => {
e.stopPropagation();
callback(emoji);
if (!options.keepOpen) this.hide();
};
grid.appendChild(span);
});
};
// Init Tabs
const categoryIcons = {
'Smileys': '😀',
'Gestures': '👌',
'People': '👶',
'Animals': '🐶',
'Nature': '🌵',
'Food': '🍏',
'Activities': '⚽️',
'Travel': '🚗',
'Objects': '⌚️',
'Symbols': '❤️',
'Flags': '🏁'
};
Object.keys(EMOJI_CATEGORIES).forEach((cat, idx) => {
const btn = document.createElement('button');
btn.className = `btn btn-sm text-nowrap px-2 py-2 border-0 ${idx === 0 ? 'btn-primary' : 'btn-dark'}`;
btn.style.fontSize = '1.2em';
btn.title = cat;
btn.textContent = categoryIcons[cat] || '❓';
btn.onclick = (e) => {
e.stopPropagation();
searchInput.value = '';
tabs.querySelectorAll('button').forEach(b => {
b.classList.remove('btn-primary');
b.classList.add('btn-dark');
});
btn.classList.add('btn-primary');
btn.classList.remove('btn-dark');
renderGrid(cat);
};
tabs.appendChild(btn);
});
btn.classList.add('btn-primary');
btn.classList.remove('btn-dark');
renderGrid(cat);
};
tabs.appendChild(btn);
});
if (searchInput) {
searchInput.oninput = () => { searchInput.oninput = () => {
const term = searchInput.value.trim(); const term = searchInput.value.trim();
if (term) { if (term) {
categoriesContainer.querySelectorAll('button').forEach(b => b.classList.remove('active', 'btn-primary')); tabs.querySelectorAll('button').forEach(b => b.classList.replace('btn-primary', 'btn-dark'));
populateEmojiGrid(null, term); renderGrid(null, term);
} else { } else {
const activeCat = categoriesContainer.querySelector('button.active')?.textContent || Object.keys(EMOJI_CATEGORIES)[0]; const activeCat = tabs.querySelector('button.btn-primary')?.textContent || Object.keys(EMOJI_CATEGORIES)[0];
populateEmojiGrid(activeCat); renderGrid(activeCat);
} }
}; };
picker.appendChild(tabs);
picker.appendChild(searchContainer);
picker.appendChild(grid);
document.body.appendChild(picker);
this.currentPicker = picker;
// Positioning
const rect = anchor.getBoundingClientRect();
let top = rect.top - picker.offsetHeight - 10;
if (top < 0) top = rect.bottom + 10;
let left = rect.left;
if (left + picker.offsetWidth > window.innerWidth) left = window.innerWidth - picker.offsetWidth - 20;
if (left < 10) left = 10;
picker.style.top = `${top}px`;
picker.style.left = `${left}px`;
renderGrid(Object.keys(EMOJI_CATEGORIES)[0]);
// Handle outside click
const outsideClick = (e) => {
if (!picker.contains(e.target) && e.target !== anchor && !anchor.contains(e.target)) {
this.hide();
document.removeEventListener('click', outsideClick);
}
};
setTimeout(() => document.addEventListener('click', outsideClick), 10);
},
hide() {
if (this.currentPicker) {
this.currentPicker.remove();
this.currentPicker = null;
}
} }
};
// Initial load // Replace old showEmojiPicker and role grid logic
populateEmojiGrid(Object.keys(EMOJI_CATEGORIES)[0]); window.showEmojiPicker = (anchor, callback) => UniversalEmojiPicker.show(anchor, callback, { width: '350px', height: '400px' });
// Update role editor emoji triggers
function setupRoleEmojiTriggers() {
const triggers = [
{ btn: 'role-emoji-select-btn', target: 'edit-role-icon', preview: 'selected-role-emoji-preview' },
{ btn: 'add-autorole-emoji-btn', target: 'add-autorole-icon', preview: 'add-autorole-emoji-preview' },
{ btn: 'edit-autorole-emoji-btn', target: 'edit-autorole-icon', preview: 'edit-autorole-emoji-preview' }
];
triggers.forEach(t => {
const btn = document.getElementById(t.btn);
if (btn) {
btn.onclick = (e) => {
e.preventDefault();
UniversalEmojiPicker.show(btn, (emoji) => {
const input = document.getElementById(t.target);
const preview = document.getElementById(t.preview);
if (input) input.value = emoji;
if (preview) preview.textContent = emoji;
}, { width: '350px', height: '400px' });
};
}
});
// Chat Emoji Picker
const chatEmojiBtn = document.getElementById('chat-emoji-btn');
const chatInput = document.getElementById('chat-input');
if (chatEmojiBtn && chatInput) {
chatEmojiBtn.onclick = (e) => {
e.preventDefault();
UniversalEmojiPicker.show(chatEmojiBtn, (emoji) => {
chatInput.value += emoji;
chatInput.focus();
}, { keepOpen: true, width: '350px', height: '400px' });
};
}
} }
// Call init if elements exist // Call setup
if (document.getElementById('role-emoji-grid')) { setupRoleEmojiTriggers();
initEmojiCategories();
}
// Scroll to bottom // Scroll to bottom
scrollToBottom(true); scrollToBottom(true);
@ -332,12 +450,6 @@ document.addEventListener('DOMContentLoaded', () => {
showEmojiPicker(addBtn, (emoji) => toggleReaction(msgId, emoji)); showEmojiPicker(addBtn, (emoji) => toggleReaction(msgId, emoji));
return; return;
} }
// Close picker if click outside
if (!e.target.closest('.emoji-picker')) {
const picker = document.querySelector('.emoji-picker');
if (picker) picker.remove();
}
}); });
async function toggleReaction(messageId, emoji) { async function toggleReaction(messageId, emoji) {
@ -361,105 +473,6 @@ document.addEventListener('DOMContentLoaded', () => {
} catch (e) { console.error(e); } } catch (e) { console.error(e); }
} }
function showEmojiPicker(anchor, callback) {
document.querySelector('.emoji-picker')?.remove();
const picker = document.createElement('div');
picker.className = 'emoji-picker p-0 overflow-hidden d-flex flex-column';
picker.style.width = '280px';
picker.style.height = '320px';
picker.style.backgroundColor = 'var(--bg-secondary)';
picker.style.border = '1px solid var(--bg-tertiary)';
picker.style.borderRadius = '8px';
picker.style.boxShadow = '0 8px 24px rgba(0,0,0,0.5)';
picker.style.zIndex = '2000';
const tabs = document.createElement('div');
tabs.className = 'd-flex overflow-auto border-bottom border-secondary p-1 bg-dark';
tabs.style.gap = '2px';
const searchContainer = document.createElement('div');
searchContainer.className = 'p-2 border-bottom border-secondary';
const searchInput = document.createElement('input');
searchInput.type = 'text';
searchInput.placeholder = 'Search emoji...';
searchInput.className = 'form-control form-control-sm bg-dark border-secondary text-white';
searchContainer.appendChild(searchInput);
const grid = document.createElement('div');
grid.className = 'flex-grow-1 overflow-auto p-2';
grid.style.display = 'grid';
grid.style.gridTemplateColumns = 'repeat(7, 1fr)';
grid.style.gap = '2px';
const renderGrid = (cat = null, term = '') => {
grid.innerHTML = '';
let emojis = [];
if (term) {
emojis = EMOJIS.filter(e => e.includes(term));
} else {
emojis = EMOJI_CATEGORIES[cat] || [];
}
emojis.forEach(emoji => {
const span = document.createElement('span');
span.textContent = emoji;
span.style.cursor = 'pointer';
span.style.fontSize = '20px';
span.style.padding = '5px';
span.style.textAlign = 'center';
span.className = 'rounded role-emoji-item';
span.onclick = () => {
callback(emoji);
picker.remove();
};
grid.appendChild(span);
});
};
searchInput.oninput = () => {
const term = searchInput.value.trim();
if (term) {
tabs.querySelectorAll('button').forEach(b => b.classList.remove('text-primary'));
renderGrid(null, term);
} else {
const activeCat = tabs.querySelector('button.text-primary')?.textContent || Object.keys(EMOJI_CATEGORIES)[0];
renderGrid(activeCat);
}
};
Object.keys(EMOJI_CATEGORIES).forEach((cat, index) => {
const btn = document.createElement('button');
btn.type = 'button';
btn.className = 'btn btn-sm btn-dark text-nowrap px-2 py-1 border-0';
btn.style.fontSize = '0.7em';
btn.textContent = cat;
if (index === 0) btn.classList.add('text-primary');
btn.onclick = (e) => {
e.stopPropagation();
searchInput.value = '';
tabs.querySelectorAll('button').forEach(b => b.classList.remove('text-primary'));
btn.classList.add('text-primary');
renderGrid(cat);
};
tabs.appendChild(btn);
});
picker.appendChild(tabs);
picker.appendChild(searchContainer);
picker.appendChild(grid);
document.body.appendChild(picker);
renderGrid(Object.keys(EMOJI_CATEGORIES)[0]);
const rect = anchor.getBoundingClientRect();
let top = rect.top - picker.offsetHeight - 5;
if (top < 0) top = rect.bottom + 5;
picker.style.position = 'fixed';
picker.style.top = `${top}px`;
picker.style.left = `${Math.min(rect.left, window.innerWidth - 300)}px`;
}
function updateReactionUI(messageId, reactions) { function updateReactionUI(messageId, reactions) {
const container = document.querySelector(`.message-reactions[data-message-id="${messageId}"]`); const container = document.querySelector(`.message-reactions[data-message-id="${messageId}"]`);
if (!container) return; if (!container) return;
@ -1027,19 +1040,23 @@ document.addEventListener('DOMContentLoaded', () => {
modal.querySelector('#edit-channel-category-id').value = btn.dataset.category || ''; modal.querySelector('#edit-channel-category-id').value = btn.dataset.category || '';
modal.querySelector('#delete-channel-id').value = channelId; modal.querySelector('#delete-channel-id').value = channelId;
// Check if channel is named "rôle" or "role"
const isRoleChannel = channelName.toLowerCase().normalize("NFD").replace(/[\u0300-\u036f]/g, "") === "role";
// Toggle rules role visibility // Toggle rules role visibility
const rulesRoleContainer = document.getElementById('edit-channel-rules-role-container'); const rulesRoleContainer = document.getElementById('edit-channel-rules-role-container');
if (rulesRoleContainer) { if (rulesRoleContainer) {
rulesRoleContainer.style.display = (channelType === 'rules') ? 'block' : 'none'; rulesRoleContainer.style.display = (channelType === 'rules') ? 'block' : 'none';
} }
// Hide limit, files and clear chat for rules // Hide limit, files and clear chat for rules, autorole, and role channels
const editLimitContainer = document.getElementById('edit-channel-limit-container'); const editLimitContainer = document.getElementById('edit-channel-limit-container');
const editFilesContainer = document.getElementById('edit-channel-files-container'); const editFilesContainer = document.getElementById('edit-channel-files-container');
const clearChatBtn = document.getElementById('clear-channel-history-btn'); const clearChatBtn = document.getElementById('clear-channel-history-btn');
if (editLimitContainer) editLimitContainer.style.display = (channelType === 'rules') ? 'none' : 'block'; const hideExtra = (channelType === 'rules' || channelType === 'autorole' || isRoleChannel);
if (editFilesContainer) editFilesContainer.style.display = (channelType === 'rules') ? 'none' : 'block'; if (editLimitContainer) editLimitContainer.style.display = hideExtra ? 'none' : 'block';
if (editFilesContainer) editFilesContainer.style.display = hideExtra ? 'none' : 'block';
if (clearChatBtn) clearChatBtn.style.display = (channelType === 'rules') ? 'none' : 'inline-block'; if (clearChatBtn) clearChatBtn.style.display = (channelType === 'rules') ? 'none' : 'inline-block';
// Reset delete zone // Reset delete zone
@ -1078,6 +1095,8 @@ document.addEventListener('DOMContentLoaded', () => {
const type = editChannelType.value; const type = editChannelType.value;
const rssTabNav = document.getElementById('rss-tab-nav'); const rssTabNav = document.getElementById('rss-tab-nav');
const statusContainer = document.getElementById('edit-channel-status-container'); const statusContainer = document.getElementById('edit-channel-status-container');
const channelName = document.getElementById('edit-channel-name').value;
const isRoleChannel = channelName.toLowerCase().normalize("NFD").replace(/[\u0300-\u036f]/g, "") === "role";
rssTabNav.style.display = (type === 'announcement') ? 'block' : 'none'; rssTabNav.style.display = (type === 'announcement') ? 'block' : 'none';
statusContainer.style.display = (type === 'voice') ? 'block' : 'none'; statusContainer.style.display = (type === 'voice') ? 'block' : 'none';
@ -1092,9 +1111,10 @@ document.addEventListener('DOMContentLoaded', () => {
const editFilesContainer = document.getElementById('edit-channel-files-container'); const editFilesContainer = document.getElementById('edit-channel-files-container');
const clearChatBtn = document.getElementById('clear-channel-history-btn'); const clearChatBtn = document.getElementById('clear-channel-history-btn');
if (editLimitContainer) editLimitContainer.style.display = (type === 'rules' || type === 'autorole') ? 'none' : 'block'; const hideExtra = (type === 'rules' || type === 'autorole' || isRoleChannel);
if (editFilesContainer) editFilesContainer.style.display = (type === 'rules' || type === 'autorole') ? 'none' : 'block'; if (editLimitContainer) editLimitContainer.style.display = hideExtra ? 'none' : 'block';
if (clearChatBtn) clearChatBtn.style.display = (type === 'rules' || type === 'autorole') ? 'none' : 'inline-block'; if (editFilesContainer) editFilesContainer.style.display = hideExtra ? 'none' : 'block';
if (clearChatBtn) clearChatBtn.style.display = (type === 'rules') ? 'none' : 'inline-block';
}); });
// RSS Management // RSS Management
@ -2346,6 +2366,10 @@ document.addEventListener('DOMContentLoaded', () => {
document.getElementById('edit-autorole-icon').value = icon; document.getElementById('edit-autorole-icon').value = icon;
document.getElementById('edit-autorole-title').value = title; document.getElementById('edit-autorole-title').value = title;
document.getElementById('edit-autorole-role-id').value = roleId; document.getElementById('edit-autorole-role-id').value = roleId;
// Update preview
const preview = document.getElementById('edit-autorole-emoji-preview');
if (preview) preview.textContent = icon;
}); });
// Universal Emoji Picker Trigger // Universal Emoji Picker Trigger

View File

@ -915,6 +915,9 @@ $projectImageUrl = $_SERVER['PROJECT_IMAGE_URL'] ?? '';
</div> </div>
<?php endif; ?> <?php endif; ?>
<input type="text" id="chat-input" class="chat-input" placeholder="Message #<?php echo htmlspecialchars($current_channel_name); ?>" autocomplete="off"> <input type="text" id="chat-input" class="chat-input" placeholder="Message #<?php echo htmlspecialchars($current_channel_name); ?>" autocomplete="off">
<button type="button" class="btn border-0 text-muted p-2" id="chat-emoji-btn" title="Emoji Picker">
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"></circle><path d="M8 14s1.5 2 4 2 4-2 4-2"></path><line x1="9" y1="9" x2="9.01" y2="9"></line><line x1="15" y1="9" x2="15.01" y2="9"></line></svg>
</button>
</div> </div>
</form> </form>
<?php endif; ?> <?php endif; ?>
@ -1275,7 +1278,7 @@ $projectImageUrl = $_SERVER['PROJECT_IMAGE_URL'] ?? '';
<!-- Add Autorole Modal --> <!-- Add Autorole Modal -->
<div class="modal fade" id="addAutoroleModal" tabindex="-1"> <div class="modal fade" id="addAutoroleModal" tabindex="-1">
<div class="modal-dialog"> <div class="modal-dialog modal-lg">
<div class="modal-content"> <div class="modal-content">
<div class="modal-header"> <div class="modal-header">
<h5 class="modal-title">Ajouter un Autorole</h5> <h5 class="modal-title">Ajouter un Autorole</h5>
@ -1289,8 +1292,9 @@ $projectImageUrl = $_SERVER['PROJECT_IMAGE_URL'] ?? '';
<div class="mb-3"> <div class="mb-3">
<label class="form-label text-uppercase fw-bold" style="font-size: 0.7em; color: var(--text-muted);">Icône (Emoji)</label> <label class="form-label text-uppercase fw-bold" style="font-size: 0.7em; color: var(--text-muted);">Icône (Emoji)</label>
<div class="d-flex align-items-center mb-2"> <div class="d-flex align-items-center mb-2">
<input type="text" name="icon" id="add-autorole-icon" class="form-control" style="width: 60px; text-align: center; font-size: 1.5em;" placeholder="🚀" required readonly> <div id="add-autorole-emoji-preview" class="d-flex align-items-center justify-content-center border rounded me-2" style="width: 48px; height: 48px; font-size: 24px; background: #1e1f22;">🚀</div>
<button type="button" class="btn btn-outline-secondary ms-2 open-emoji-picker" data-target="#add-autorole-icon">Choisir...</button> <input type="hidden" name="icon" id="add-autorole-icon" value="🚀">
<button type="button" class="btn btn-outline-secondary" id="add-autorole-emoji-btn">Choisir un emoji...</button>
</div> </div>
</div> </div>
<div class="mb-3"> <div class="mb-3">
@ -1318,7 +1322,7 @@ $projectImageUrl = $_SERVER['PROJECT_IMAGE_URL'] ?? '';
<!-- Edit Autorole Modal --> <!-- Edit Autorole Modal -->
<div class="modal fade" id="editAutoroleModal" tabindex="-1"> <div class="modal fade" id="editAutoroleModal" tabindex="-1">
<div class="modal-dialog"> <div class="modal-dialog modal-lg">
<div class="modal-content"> <div class="modal-content">
<div class="modal-header"> <div class="modal-header">
<h5 class="modal-title">Modifier l'Autorole</h5> <h5 class="modal-title">Modifier l'Autorole</h5>
@ -1333,8 +1337,9 @@ $projectImageUrl = $_SERVER['PROJECT_IMAGE_URL'] ?? '';
<div class="mb-3"> <div class="mb-3">
<label class="form-label text-uppercase fw-bold" style="font-size: 0.7em; color: var(--text-muted);">Icône (Emoji)</label> <label class="form-label text-uppercase fw-bold" style="font-size: 0.7em; color: var(--text-muted);">Icône (Emoji)</label>
<div class="d-flex align-items-center mb-2"> <div class="d-flex align-items-center mb-2">
<input type="text" name="icon" id="edit-autorole-icon" class="form-control" style="width: 60px; text-align: center; font-size: 1.5em;" required readonly> <div id="edit-autorole-emoji-preview" class="d-flex align-items-center justify-content-center border rounded me-2" style="width: 48px; height: 48px; font-size: 24px; background: #1e1f22;"></div>
<button type="button" class="btn btn-outline-secondary ms-2 open-emoji-picker" data-target="#edit-autorole-icon">Modifier...</button> <input type="hidden" name="icon" id="edit-autorole-icon">
<button type="button" class="btn btn-outline-secondary" id="edit-autorole-emoji-btn">Changer l'emoji...</button>
</div> </div>
</div> </div>
<div class="mb-3"> <div class="mb-3">
@ -1605,7 +1610,7 @@ $projectImageUrl = $_SERVER['PROJECT_IMAGE_URL'] ?? '';
<!-- Role Editor Modal --> <!-- Role Editor Modal -->
<div class="modal fade" id="roleEditorModal" tabindex="-1"> <div class="modal fade" id="roleEditorModal" tabindex="-1">
<div class="modal-dialog"> <div class="modal-dialog modal-xl">
<div class="modal-content"> <div class="modal-content">
<div class="modal-header"> <div class="modal-header">
<h5 class="modal-title">Edit Role</h5> <h5 class="modal-title">Edit Role</h5>
@ -1624,19 +1629,11 @@ $projectImageUrl = $_SERVER['PROJECT_IMAGE_URL'] ?? '';
<div class="mb-3"> <div class="mb-3">
<label class="form-label text-uppercase fw-bold" style="font-size: 0.7em; color: var(--text-muted);">Role Icon</label> <label class="form-label text-uppercase fw-bold" style="font-size: 0.7em; color: var(--text-muted);">Role Icon</label>
<div class="d-flex align-items-center mb-2"> <div class="d-flex align-items-center mb-2">
<div id="selected-role-emoji-preview" class="d-flex align-items-center justify-content-center border rounded" style="width: 40px; height: 40px; font-size: 24px; background: rgba(0,0,0,0.2);"></div> <div id="selected-role-emoji-preview" class="d-flex align-items-center justify-content-center border rounded" style="width: 48px; height: 48px; font-size: 24px; background: #1e1f22;"></div>
<input type="hidden" id="edit-role-icon"> <input type="hidden" id="edit-role-icon">
<button type="button" class="btn btn-sm btn-outline-primary ms-2" id="role-emoji-select-btn">Choisir un Emoji</button>
<button type="button" class="btn btn-sm btn-outline-danger ms-2" onclick="document.getElementById('edit-role-icon').value=''; document.getElementById('selected-role-emoji-preview').textContent='';">Remove Icon</button> <button type="button" class="btn btn-sm btn-outline-danger ms-2" onclick="document.getElementById('edit-role-icon').value=''; document.getElementById('selected-role-emoji-preview').textContent='';">Remove Icon</button>
</div> </div>
<div id="role-emoji-categories" class="d-flex overflow-auto mb-2 p-1" style="background: rgba(0,0,0,0.1); border-radius: 4px; gap: 5px;">
<!-- Categories populated by JS -->
</div>
<div class="mb-2">
<input type="text" id="role-emoji-search" class="form-control form-control-sm bg-dark border-secondary text-white" placeholder="Search emoji...">
</div>
<div id="role-emoji-grid" class="border rounded p-2" style="max-height: 150px; overflow-y: auto; display: grid; grid-template-columns: repeat(7, 1fr); gap: 5px; background: rgba(0,0,0,0.2);">
<!-- Emojis populated by JS -->
</div>
</div> </div>
<div class="mb-3"> <div class="mb-3">
<label class="form-label text-uppercase fw-bold" style="font-size: 0.7em; color: var(--text-muted);">Permissions</label> <label class="form-label text-uppercase fw-bold" style="font-size: 0.7em; color: var(--text-muted);">Permissions</label>

View File

@ -165,3 +165,47 @@
2026-02-16 03:21:33 - GET /index.php?server_id=1&channel_id=17 - POST: [] 2026-02-16 03:21:33 - GET /index.php?server_id=1&channel_id=17 - POST: []
2026-02-16 03:21:35 - GET /index.php?server_id=1&channel_id=17 - POST: [] 2026-02-16 03:21:35 - GET /index.php?server_id=1&channel_id=17 - POST: []
2026-02-16 03:21:38 - GET /index.php?server_id=1&channel_id=17 - POST: [] 2026-02-16 03:21:38 - GET /index.php?server_id=1&channel_id=17 - POST: []
2026-02-16 03:26:28 - GET /?fl_project=38443 - POST: []
2026-02-16 03:26:35 - GET /index.php?server_id=1&channel_id=17 - POST: []
2026-02-16 03:28:36 - GET /index.php?server_id=1&channel_id=17 - POST: []
2026-02-16 03:30:31 - GET /index.php?server_id=1&channel_id=12 - POST: []
2026-02-16 03:30:36 - GET /index.php?server_id=1&channel_id=11 - POST: []
2026-02-16 03:30:41 - GET /index.php?server_id=1&channel_id=17 - POST: []
2026-02-16 03:32:55 - GET / - POST: []
2026-02-16 03:32:59 - GET /index.php?server_id=1&channel_id=17 - POST: []
2026-02-16 03:33:09 - GET /index.php?server_id=1&channel_id=17 - POST: []
2026-02-16 03:33:11 - GET /?fl_project=38443 - POST: []
2026-02-16 03:33:12 - GET /index.php?server_id=1&channel_id=17 - POST: []
2026-02-16 03:33:14 - GET /index.php?server_id=1&channel_id=17 - POST: []
2026-02-16 03:33:34 - GET /index.php?server_id=1&channel_id=17 - POST: []
2026-02-16 03:33:37 - GET /index.php?server_id=1&channel_id=17 - POST: []
2026-02-16 03:35:52 - GET /?fl_project=38443 - POST: []
2026-02-16 08:35:40 - GET /?fl_project=38443 - POST: []
2026-02-16 08:37:05 - GET /index.php?server_id=1&channel_id=6 - POST: []
2026-02-16 08:54:29 - GET / - POST: []
2026-02-16 08:54:39 - GET /?fl_project=38443 - POST: []
2026-02-16 12:42:27 - GET /?fl_project=38443 - POST: []
2026-02-16 12:47:00 - GET /index.php?server_id=1&channel_id=6 - POST: []
2026-02-16 12:47:29 - GET /index.php?server_id=1&channel_id=17 - POST: []
2026-02-16 12:47:34 - GET /index.php?server_id=1&channel_id=17 - POST: []
2026-02-16 12:48:16 - GET / - POST: []
2026-02-16 12:48:18 - HEAD / - POST: []
2026-02-16 12:48:24 - GET /?fl_project=38443 - POST: []
2026-02-16 12:48:45 - GET /?fl_project=38443 - POST: []
2026-02-16 12:51:34 - GET /?fl_project=38443 - POST: []
2026-02-16 12:53:59 - GET / - POST: []
2026-02-16 12:54:11 - GET /?fl_project=38443 - POST: []
2026-02-16 12:54:33 - GET /index.php?server_id=1&channel_id=17 - POST: []
2026-02-16 12:54:52 - GET /index.php?server_id=1&channel_id=17 - POST: []
2026-02-16 12:56:37 - GET /index.php?server_id=1&channel_id=11 - POST: []
2026-02-16 12:56:38 - GET /index.php?server_id=1&channel_id=12 - POST: []
2026-02-16 13:01:21 - GET / - POST: []
2026-02-16 13:01:28 - GET /?fl_project=38443 - POST: []
2026-02-16 13:03:56 - GET /index.php?server_id=1&channel_id=12 - POST: []
2026-02-16 13:06:06 - GET /index.php?server_id=1&channel_id=17 - POST: []
2026-02-16 13:06:15 - GET /index.php?server_id=1&channel_id=17 - POST: []
2026-02-16 13:06:20 - GET /index.php?server_id=1&channel_id=17 - POST: []
2026-02-16 13:06:25 - GET /index.php?server_id=1&channel_id=1 - POST: []
2026-02-16 13:06:41 - GET /index.php?server_id=1&channel_id=1 - POST: []
2026-02-16 13:06:43 - GET /index.php?server_id=1&channel_id=1 - POST: []
2026-02-16 13:06:45 - GET /index.php?server_id=1&channel_id=17 - POST: []