eleven / messenger Public

web: reveal a reply icon beside the bubble on hover (desktop)

Desktop now shows a reply affordance on message hover (touch keeps double-tap). The button is only created when !isCoarse and only shown under @media (hover: hover); clicking it stages the reply. Reactions still use press-and-hold on desktop.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Paul Campbell c6eed65ea26b09ec77b414ed9551a4dd88fa27e0 parent 735ffe2
2 files changed, +37 −1
  • web/static/chat.js +20 −1
  • web/static/style.css +17 −0
diff --git a/web/static/chat.js b/web/static/chat.js
index fd781fa..272dbd8 100644
--- a/web/static/chat.js
+++ b/web/static/chat.js
@@ -447,6 +447,21 @@ async function start(session) {
return q;
}
+ // Desktop-only: a reply icon that reveals beside the bubble on hover, so reply
+ // is discoverable without the press-and-hold. (Touch uses double-tap instead.)
+ function buildHoverReply(meta) {
+ const b = document.createElement("button");
+ b.type = "button";
+ b.className = "reply-hover";
+ b.innerHTML = ICON_REPLY;
+ b.setAttribute("aria-label", "Reply");
+ b.onclick = (e) => {
+ e.stopPropagation();
+ startReply(meta);
+ };
+ return b;
+ }
+
async function render(msg) {
if (seen.has(msg.id)) return;
seen.add(msg.id);
@@ -497,10 +512,14 @@ async function start(session) {
}
if (payload.text) addLinkPreview(content, payload.text);
bubbleRow.appendChild(content);
+
+ const replyMeta = { id: msg.id, name: payload.name, snippet: replySnippet(payload) };
+ // Desktop: a hover-revealed reply icon beside the bubble (touch uses double-tap).
+ if (!isCoarse) bubbleRow.appendChild(buildHoverReply(replyMeta));
el.appendChild(bubbleRow);
// React or reply: double-tap on touch, press-and-hold on desktop.
- attachReact(el, msg.id, { id: msg.id, name: payload.name, snippet: replySnippet(payload) });
+ attachReact(el, msg.id, replyMeta);
const reactions = document.createElement("div");
reactions.className = "reactions";
diff --git a/web/static/style.css b/web/static/style.css
index b25da3c..0473d5c 100644
--- a/web/static/style.css
+++ b/web/static/style.css
@@ -181,6 +181,23 @@ input:focus, textarea:focus { outline: 2px solid var(--accent); border-color: tr
.msg .bubble a { color: inherit; text-decoration: underline; text-underline-offset: 2px; }
.msg .time { font-size: 11px; color: var(--muted); margin: 3px 6px 0; }
+/* desktop hover reply icon, revealed beside the bubble on hover */
+.reply-hover {
+ flex: none;
+ background: transparent;
+ color: var(--muted);
+ padding: 4px 6px;
+ border-radius: 50%;
+ display: none;
+ align-items: center;
+ line-height: 1;
+}
+.reply-hover svg { width: 18px; height: 18px; }
+.reply-hover:hover { background: var(--bubble-them); color: var(--accent); }
+@media (hover: hover) {
+ .msg:hover .reply-hover { display: inline-flex; }
+}
+
/* message content (text bubble + any attachments) */
.content {
display: flex;