feat(messaging-ui): reaction picker + attachments in the Thread

- useMessages exposes upload(); Thread gains a hover reaction picker (react to
  a message) and an attach button (upload → stage → send), plus inline image /
  file rendering of message attachments — all capability-degrading (hidden when
  the adapter lacks react/upload)
- themeable styles for the picker, attach button, staged chip, and attachments
- 57 tests still green

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-07-22 00:08:26 +05:30
parent ade1d68015
commit 7a0fb2ca97
3 changed files with 238 additions and 25 deletions
+113
View File
@@ -149,6 +149,57 @@
.miu-msg.is-pending {
opacity: 0.6;
}
.miu-bubble-row {
display: flex;
align-items: center;
gap: 4px;
}
.miu-msg.is-mine .miu-bubble-row {
flex-direction: row-reverse;
}
.miu-react-wrap {
position: relative;
flex-shrink: 0;
}
.miu-react-btn {
border: none;
background: none;
cursor: pointer;
opacity: 0;
font-size: 14px;
padding: 2px;
transition: opacity 0.12s;
}
.miu-msg:hover .miu-react-btn {
opacity: 0.6;
}
.miu-react-btn:hover {
opacity: 1;
}
.miu-react-picker {
position: absolute;
bottom: 100%;
margin-bottom: 4px;
display: flex;
gap: 2px;
padding: 4px;
border-radius: 999px;
border: 1px solid var(--miu-border);
background: var(--miu-panel);
box-shadow: 0 6px 20px rgba(0, 0, 0, 0.4);
z-index: 5;
}
.miu-react-emoji {
border: none;
background: none;
cursor: pointer;
font-size: 16px;
padding: 2px 4px;
border-radius: 6px;
}
.miu-react-emoji:hover {
background: var(--miu-panel-2);
}
.miu-reactions {
display: flex;
gap: 4px;
@@ -160,10 +211,35 @@
border-radius: 999px;
border: 1px solid var(--miu-border);
background: var(--miu-panel-2);
color: var(--miu-text);
cursor: pointer;
}
.miu-reaction.is-mine {
border-color: var(--miu-accent);
}
/* attachments */
.miu-att-img-link {
display: inline-block;
margin-top: 4px;
}
.miu-att-img {
max-width: 260px;
max-height: 220px;
border-radius: 8px;
border: 1px solid var(--miu-border);
}
.miu-att-file {
display: inline-block;
margin-top: 4px;
padding: 6px 10px;
border-radius: 8px;
border: 1px solid var(--miu-border);
background: var(--miu-panel-2);
color: var(--miu-text);
text-decoration: none;
font-size: 13px;
}
.miu-seen {
font-size: 11px;
color: var(--miu-muted);
@@ -189,10 +265,47 @@
.miu-composer {
position: relative;
display: flex;
flex-direction: column;
gap: 8px;
padding: 12px;
border-top: 1px solid var(--miu-border);
}
.miu-composer-row {
display: flex;
gap: 8px;
}
.miu-file-input {
display: none;
}
.miu-attach-btn {
flex-shrink: 0;
width: 38px;
border-radius: 10px;
border: 1px solid var(--miu-border);
background: var(--miu-panel);
color: var(--miu-text);
cursor: pointer;
font-size: 16px;
}
.miu-staged {
display: inline-flex;
align-items: center;
gap: 8px;
align-self: flex-start;
padding: 5px 10px;
border-radius: 999px;
border: 1px solid var(--miu-border);
background: var(--miu-panel-2);
font-size: 13px;
}
.miu-staged-x {
border: none;
background: none;
color: var(--miu-muted);
cursor: pointer;
padding: 0;
line-height: 1;
}
.miu-suggest {
position: absolute;
left: 12px;