fix(desktop): hover-state polish + Search width cap + reserve reaction-icon space

NoteCard hover (header+text inner clickable + avatar/name chip):
- Clip(RoundedCornerShape(8.dp)) before .clickable on the header+text
  Column so the ripple rounds instead of cutting a hard rectangle.
- Avatar+name chip gets a stadium clip (RoundedCornerShape(100.dp))
  before its clickable — matches how Slack / Notion / Linear render
  user-pill hover states.

Chat bubble hover (ChatBubbleLayout):
- combinedClickable was applied outside the Surface's shape clipping,
  so the ripple ignored ChatBubbleShapeMe/Them. Moved the shape into a
  named val and added Modifier.clip(bubbleShape) ahead of
  combinedClickable. Hover now rounds with the bubble.

Chat reaction icon (ChatPane detailRow):
- On hover the "add reaction" icon used to conditionally appear and
  shift every other element in the detail row, causing the whole list
  to reflow up or down. Wrapped in a Box with Modifier.alpha that
  fades in/out — the icon is always laid out so the row stays fixed.
  Button is disabled when not hovered so it's click-through when
  invisible.

Search text field:
- Now padded by LocalReadingSidePadding so it stays inside the 720dp
  reading column on wide displays (previously the search Row's
  fillMaxWidth spanned the whole window after the ReadingColumn
  refactor).
- Bumped height from 40dp → 44dp. M3 OutlinedTextField has ~16dp of
  internal vertical content padding that was clipping the bodyMedium
  placeholder at 40dp. Same change for the chat input.

https://claude.ai/code/session_01NufduPfZvYQVYwLkbCjCUo
This commit is contained in:
Claude
2026-04-24 15:55:57 +00:00
parent 3b9ef980a6
commit 7da69fbaee
4 changed files with 70 additions and 52 deletions
@@ -34,6 +34,7 @@ import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.compositeOver
import com.vitorpamplona.amethyst.commons.ui.theme.ChatBubbleMaxSizeModifier
@@ -128,18 +129,23 @@ fun ChatBubbleLayout(
)
}
val bubbleShape = if (isLoggedInUser) ChatBubbleShapeMe else ChatBubbleShapeThem
// Clip the hover/ripple to the bubble shape — combinedClickable otherwise
// paints a rectangular ripple that ignores the Surface's rounded corners.
val clickableModifier =
remember {
Modifier.combinedClickable(
onClick = {
if (!onClick()) {
if (!isComplete) {
showDetails.value = !showDetails.value
remember(bubbleShape) {
Modifier
.clip(bubbleShape)
.combinedClickable(
onClick = {
if (!onClick()) {
if (!isComplete) {
showDetails.value = !showDetails.value
}
}
}
},
onLongClick = { popupExpanded.value = true },
)
},
onLongClick = { popupExpanded.value = true },
)
}
Row(
@@ -148,7 +154,7 @@ fun ChatBubbleLayout(
) {
Surface(
color = bgColor.value,
shape = if (isLoggedInUser) ChatBubbleShapeMe else ChatBubbleShapeThem,
shape = bubbleShape,
modifier = clickableModifier,
) {
Column(modifier = MessageBubbleLimits, verticalArrangement = ChatRowColSpacing5dp) {