@devchitchat/chat 4.0.5 → 4.0.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json
CHANGED
|
@@ -141,9 +141,7 @@ export default function CallIsland(root) {
|
|
|
141
141
|
data-emoji="${escHtml(r.emoji)}" data-msg-id="${escHtml(msgId)}"
|
|
142
142
|
type="button" title="${r.count} reaction${r.count !== 1 ? 's' : ''}">
|
|
143
143
|
${r.emoji} <span class="reaction-count">${r.count}</span>
|
|
144
|
-
</button>`).join('')
|
|
145
|
-
`<button class="reaction-add" data-msg-id="${escHtml(msgId)}" type="button"
|
|
146
|
-
title="Add reaction" aria-label="Add reaction">+</button>`
|
|
144
|
+
</button>`).join('')
|
|
147
145
|
}
|
|
148
146
|
|
|
149
147
|
// ── Hydrate seed message attachments ──────────────────────────────────────
|
|
@@ -157,18 +155,32 @@ export default function CallIsland(root) {
|
|
|
157
155
|
if (article.dataset.hydrated) continue
|
|
158
156
|
article.dataset.hydrated = '1'
|
|
159
157
|
|
|
160
|
-
// Add dm-trigger to non-self sender handles
|
|
158
|
+
// Add dm-trigger to non-self sender handles
|
|
161
159
|
const handle = article.querySelector('.message-handle[data-user-id]')
|
|
162
160
|
if (handle && handle.dataset.userId !== userId) {
|
|
163
161
|
handle.classList.add('dm-trigger')
|
|
164
162
|
handle.title = 'Send a direct message'
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
163
|
+
}
|
|
164
|
+
// Add hover action toolbar (react for all messages; … only for own)
|
|
165
|
+
if (!article.querySelector('.message-hover-actions')) {
|
|
166
|
+
const toolbar = document.createElement('div')
|
|
167
|
+
toolbar.className = 'message-hover-actions'
|
|
168
|
+
const reactBtn = document.createElement('button')
|
|
169
|
+
reactBtn.className = 'btn-react btn-icon'
|
|
170
|
+
reactBtn.type = 'button'
|
|
171
|
+
reactBtn.title = 'Add reaction'
|
|
172
|
+
reactBtn.setAttribute('aria-label', 'Add reaction')
|
|
173
|
+
reactBtn.textContent = '🙂'
|
|
174
|
+
toolbar.appendChild(reactBtn)
|
|
175
|
+
if (article.dataset.userId === userId) {
|
|
176
|
+
const actionsBtn = document.createElement('button')
|
|
177
|
+
actionsBtn.className = 'btn-msg-actions btn-icon'
|
|
178
|
+
actionsBtn.type = 'button'
|
|
179
|
+
actionsBtn.title = 'Message actions'
|
|
180
|
+
actionsBtn.textContent = '…'
|
|
181
|
+
toolbar.appendChild(actionsBtn)
|
|
182
|
+
}
|
|
183
|
+
article.appendChild(toolbar)
|
|
172
184
|
}
|
|
173
185
|
|
|
174
186
|
// Apply inline rendering (URLs, @mentions) to server-rendered message text.
|
|
@@ -906,7 +918,7 @@ export default function CallIsland(root) {
|
|
|
906
918
|
if (activeReactionContextMenu && !activeReactionContextMenu.contains(e.target)) closeReactionContextMenu()
|
|
907
919
|
// Close emoji picker on click-outside
|
|
908
920
|
if (emojiPickerEl && emojiPickerEl.parentNode && !emojiPickerEl.contains(e.target)) {
|
|
909
|
-
const isReactionAddBtn = e.target.closest('.reaction-add')
|
|
921
|
+
const isReactionAddBtn = e.target.closest('.reaction-add') || e.target.closest('.btn-react')
|
|
910
922
|
if (!isReactionAddBtn) closeEmojiPicker()
|
|
911
923
|
}
|
|
912
924
|
}, { capture: true })
|
|
@@ -933,12 +945,14 @@ export default function CallIsland(root) {
|
|
|
933
945
|
}
|
|
934
946
|
})
|
|
935
947
|
|
|
936
|
-
// Delegated click on .reaction-add — open emoji picker
|
|
948
|
+
// Delegated click on .reaction-add or .btn-react — open emoji picker
|
|
937
949
|
messages.addEventListener('click', e => {
|
|
938
|
-
const
|
|
950
|
+
const addBtn = e.target.closest('.reaction-add')
|
|
951
|
+
const reactBtn = e.target.closest('.btn-react')
|
|
952
|
+
const btn = addBtn ?? reactBtn
|
|
939
953
|
if (!btn) return
|
|
940
954
|
e.stopPropagation()
|
|
941
|
-
const msgId = btn.dataset.msgId
|
|
955
|
+
const msgId = addBtn?.dataset.msgId ?? btn.closest('article.message')?.dataset.msgId
|
|
942
956
|
if (!msgId) return
|
|
943
957
|
// Toggle: close if already open for this message
|
|
944
958
|
if (emojiPickerEl && emojiPickerEl.parentNode && emojiPickerTarget === msgId) {
|
|
@@ -134,7 +134,7 @@ export function makeMessageEl({ msg_id, seq, user_id, user_display_name, ts, tex
|
|
|
134
134
|
const isSelf = userId != null && user_id === userId
|
|
135
135
|
const attachmentHtml = (attachments ?? []).map(a => renderAttachment(a)).join('')
|
|
136
136
|
const editedHtml = edited_at ? '<span class="message-edited">(edited)</span>' : ''
|
|
137
|
-
const actionsHtml = isSelf ? '<button class="btn-msg-actions btn-icon" type="button" title="Message actions">…</button>' : ''
|
|
137
|
+
const actionsHtml = `<div class="message-hover-actions"><button class="btn-react btn-icon" type="button" title="Add reaction" aria-label="Add reaction">🙂</button>${isSelf ? '<button class="btn-msg-actions btn-icon" type="button" title="Message actions">…</button>' : ''}</div>`
|
|
138
138
|
const textHtml = rendered_text ?? (text ? renderText(text, { userHandle }) : '')
|
|
139
139
|
article.innerHTML = `
|
|
140
140
|
<span class="message-handle${isSelf ? '' : ' dm-trigger'}" data-user-id="${escHtml(user_id)}" title="${isSelf ? '' : 'Send a direct message'}">${escHtml(user_display_name ?? user_id)}</span>
|
|
@@ -989,10 +989,11 @@ mark { background: color-mix(in srgb, var(--accent) 30%, transparent); color: va
|
|
|
989
989
|
}
|
|
990
990
|
.btn-back-mobile:hover { color: var(--text-primary); }
|
|
991
991
|
|
|
992
|
-
/*
|
|
993
|
-
.
|
|
992
|
+
/* Hover toolbar: no hover on touch — always visible, tucked inside message */
|
|
993
|
+
.message-hover-actions {
|
|
994
994
|
opacity: 1;
|
|
995
995
|
pointer-events: auto;
|
|
996
|
+
top: 2px;
|
|
996
997
|
}
|
|
997
998
|
}
|
|
998
999
|
|
|
@@ -1767,19 +1768,43 @@ form.inline { display: inline; }
|
|
|
1767
1768
|
font-size: 14px;
|
|
1768
1769
|
}
|
|
1769
1770
|
|
|
1770
|
-
/*
|
|
1771
|
-
.
|
|
1771
|
+
/* Hover action toolbar — floats above the message on hover (Discord-style) */
|
|
1772
|
+
article.message { position: relative; }
|
|
1773
|
+
.message-hover-actions {
|
|
1772
1774
|
position: absolute;
|
|
1773
|
-
top:
|
|
1775
|
+
top: -20px;
|
|
1774
1776
|
right: 4px;
|
|
1775
|
-
|
|
1776
|
-
|
|
1777
|
+
display: flex;
|
|
1778
|
+
gap: 1px;
|
|
1779
|
+
align-items: center;
|
|
1777
1780
|
opacity: 0;
|
|
1778
1781
|
pointer-events: none;
|
|
1779
1782
|
transition: opacity 0.1s;
|
|
1783
|
+
background: var(--bg-sidebar);
|
|
1784
|
+
border: 1px solid var(--border);
|
|
1785
|
+
border-radius: var(--radius-md);
|
|
1786
|
+
padding: 2px;
|
|
1787
|
+
box-shadow: 0 2px 8px rgba(0,0,0,0.15);
|
|
1788
|
+
z-index: 10;
|
|
1789
|
+
}
|
|
1790
|
+
article.message:hover .message-hover-actions { opacity: 1; pointer-events: auto; }
|
|
1791
|
+
.btn-react,
|
|
1792
|
+
.btn-msg-actions {
|
|
1793
|
+
background: none;
|
|
1794
|
+
border: none;
|
|
1795
|
+
cursor: pointer;
|
|
1796
|
+
padding: 3px 7px;
|
|
1797
|
+
border-radius: calc(var(--radius-md) - 2px);
|
|
1798
|
+
font-size: 15px;
|
|
1799
|
+
line-height: 1;
|
|
1800
|
+
color: var(--text-secondary);
|
|
1801
|
+
transition: background var(--transition), color var(--transition);
|
|
1802
|
+
}
|
|
1803
|
+
.btn-react:hover,
|
|
1804
|
+
.btn-msg-actions:hover {
|
|
1805
|
+
background: color-mix(in srgb, var(--accent) 15%, transparent);
|
|
1806
|
+
color: var(--text-primary);
|
|
1780
1807
|
}
|
|
1781
|
-
article.message:hover .btn-msg-actions { opacity: 1; pointer-events: auto; }
|
|
1782
|
-
article.message { position: relative; }
|
|
1783
1808
|
|
|
1784
1809
|
/* Context menu */
|
|
1785
1810
|
.msg-context-menu {
|