feat: Emoji-Picker Button im Chat-Input (rechts neben Senden)
- #emojiBtn links neben #send platziert - Fluent Emoji 3D Rendering via parseFluentEmojis() - Kein Tooltip auf den Emoji-Buttons - Picker öffnet sich rechts-ausgerichtet über dem Button - emoji.userdata.js geladen (Kategorien + Mappings) - Schließt beim Klick außerhalb oder beim destroy()
This commit is contained in:
1 parent
cdd3414771
commit
2673aecc5b
4 files changed
+163
No files matched your search
@@ -2,6 +2,97 @@
|
||||
|
||||
let _streamingPollInterval = null;
|
||||
let _recoveryChainId = null;
|
||||
let _emojiPickerEl = null;
|
||||
let _emojiPickerCloseHandler = null;
|
||||
|
||||
function _buildEmojiPicker() {
|
||||
const picker = document.createElement('div');
|
||||
picker.className = 'emoji-picker';
|
||||
|
||||
for (const [category, emojis] of Object.entries(EMOJI_USERDATA)) {
|
||||
const section = document.createElement('div');
|
||||
section.className = 'emoji-picker-section';
|
||||
|
||||
const label = document.createElement('div');
|
||||
label.className = 'emoji-picker-label';
|
||||
label.textContent = category;
|
||||
section.appendChild(label);
|
||||
|
||||
const grid = document.createElement('div');
|
||||
grid.className = 'emoji-picker-grid';
|
||||
|
||||
for (const emoji of emojis) {
|
||||
const btn = document.createElement('button');
|
||||
btn.className = 'emoji-picker-btn';
|
||||
btn.textContent = emoji;
|
||||
btn.addEventListener('click', (e) => {
|
||||
e.stopPropagation();
|
||||
const input = document.getElementById('input');
|
||||
if (input) {
|
||||
const start = input.selectionStart;
|
||||
const end = input.selectionEnd;
|
||||
const val = input.value;
|
||||
input.value = val.slice(0, start) + emoji + val.slice(end);
|
||||
const pos = start + emoji.length;
|
||||
input.setSelectionRange(pos, pos);
|
||||
input.dispatchEvent(new Event('input'));
|
||||
input.focus();
|
||||
}
|
||||
closeEmojiPicker();
|
||||
});
|
||||
// Fluent Emoji im Button rendern
|
||||
parseFluentEmojis(btn);
|
||||
grid.appendChild(btn);
|
||||
}
|
||||
|
||||
section.appendChild(grid);
|
||||
picker.appendChild(section);
|
||||
}
|
||||
|
||||
return picker;
|
||||
}
|
||||
|
||||
function toggleEmojiPicker(anchor) {
|
||||
if (_emojiPickerEl) {
|
||||
closeEmojiPicker();
|
||||
return;
|
||||
}
|
||||
|
||||
const picker = _buildEmojiPicker();
|
||||
document.body.appendChild(picker);
|
||||
_emojiPickerEl = picker;
|
||||
|
||||
// Positionierung: über dem Button, rechts ausgerichtet
|
||||
const rect = anchor.getBoundingClientRect();
|
||||
const pickerHeight = 320;
|
||||
const pickerWidth = 280;
|
||||
let top = rect.top - pickerHeight - 6;
|
||||
let left = rect.right - pickerWidth;
|
||||
|
||||
// Falls kein Platz oben → unter dem Button
|
||||
if (top < 8) top = rect.bottom + 6;
|
||||
// Falls links abgeschnitten → linksbündig zum Button
|
||||
if (left < 8) left = 8;
|
||||
|
||||
picker.style.top = top + 'px';
|
||||
picker.style.left = left + 'px';
|
||||
|
||||
_emojiPickerCloseHandler = (e) => {
|
||||
if (!picker.contains(e.target)) closeEmojiPicker();
|
||||
};
|
||||
setTimeout(() => document.addEventListener('click', _emojiPickerCloseHandler), 0);
|
||||
}
|
||||
|
||||
function closeEmojiPicker() {
|
||||
if (_emojiPickerEl) {
|
||||
_emojiPickerEl.remove();
|
||||
_emojiPickerEl = null;
|
||||
}
|
||||
if (_emojiPickerCloseHandler) {
|
||||
document.removeEventListener('click', _emojiPickerCloseHandler);
|
||||
_emojiPickerCloseHandler = null;
|
||||
}
|
||||
}
|
||||
|
||||
function startStreamingPoll(sessionId, sendBtn, chainId) {
|
||||
_recoveryChainId = chainId;
|
||||
@@ -190,6 +281,15 @@ registerPage('chat', {
|
||||
inputEl.dispatchEvent(new Event('input'))
|
||||
}
|
||||
|
||||
// Emoji Picker
|
||||
const emojiBtn = document.getElementById('emojiBtn');
|
||||
if (emojiBtn) {
|
||||
emojiBtn.addEventListener('click', (e) => {
|
||||
e.stopPropagation();
|
||||
toggleEmojiPicker(emojiBtn);
|
||||
});
|
||||
}
|
||||
|
||||
// Workspace Explorer
|
||||
const explorerBtn = document.getElementById('explorerBtn');
|
||||
if (explorerBtn) {
|
||||
@@ -275,6 +375,7 @@ registerPage('chat', {
|
||||
// Stream nicht abbrechen (Backend soll weiterlaufen),
|
||||
// aber Frontend-Processing stoppen
|
||||
_chatAbandoned = true;
|
||||
closeEmojiPicker();
|
||||
if (this._closeMenu) {
|
||||
document.removeEventListener('click', this._closeMenu);
|
||||
this._closeMenu = null;
|
||||
|
||||
Reference in new issue
Block a user