refactor(chat): share inline attachment list (#1313) (#1350)

This commit is contained in:
m57 authored and GitHub committed 2026-09-28 11:29:12 +03:00
1 parent f2675e0b1a
commit af0c508c1f
3 files changed
+43 -22

No files matched your search

@@ -34,6 +34,39 @@ import androidx.compose.ui.unit.dp
import com.m57.hermescontrol.R import com.m57.hermescontrol.R
import com.m57.hermescontrol.data.model.Attachment import com.m57.hermescontrol.data.model.Attachment
/**
* Renders a message's attachments, shared by user bubbles and agent messages.
* Emits directly into the caller's Column (no wrapper) so spacing is unchanged:
* 6dp before the list, 4dp after each item. Renders nothing when empty.
*/
@Composable
internal fun InlineAttachmentList(
attachments: List<Attachment>?,
textColor: Color,
onOpen: (Attachment) -> Unit,
onSave: (Attachment) -> Unit,
savingPath: String?,
openingPath: String?,
canSave: Boolean,
onImageClick: (ImageViewerModel) -> Unit,
) {
if (attachments.isNullOrEmpty()) return
Spacer(modifier = Modifier.height(6.dp))
attachments.forEach { attachment ->
InlineAttachment(
attachment = attachment,
textColor = textColor,
onOpen = onOpen,
onSave = onSave,
savingPath = savingPath,
openingPath = openingPath,
canSave = canSave,
onImageClick = onImageClick,
)
Spacer(modifier = Modifier.height(4.dp))
}
}
/** /**
* Renders an attachment inline inside a chat bubble. * Renders an attachment inline inside a chat bubble.
* Images are displayed as thumbnails; other files show a compact card. * Images are displayed as thumbnails; other files show a compact card.
@@ -177,22 +177,16 @@ fun UserBubble(
) )
} }
// Render inline attachments // Render inline attachments
if (!message.attachments.isNullOrEmpty()) { InlineAttachmentList(
Spacer(modifier = Modifier.height(6.dp)) attachments = message.attachments,
message.attachments.forEach { attachment ->
InlineAttachment(
attachment = attachment,
textColor = userBubbleTextColor, textColor = userBubbleTextColor,
onOpen = { onOpenAttachment(it) }, onOpen = onOpenAttachment,
onSave = { onSaveAttachment(it) }, onSave = onSaveAttachment,
savingPath = savingAttachmentPath, savingPath = savingAttachmentPath,
openingPath = openingAttachmentPath, openingPath = openingAttachmentPath,
canSave = canSaveAttachment, canSave = canSaveAttachment,
onImageClick = onImageClick, onImageClick = onImageClick,
) )
Spacer(modifier = Modifier.height(4.dp))
}
}
if (!message.isStreaming) { if (!message.isStreaming) {
FlowRow( FlowRow(
modifier = modifier =
@@ -36,7 +36,7 @@ import com.m57.hermescontrol.R
import com.m57.hermescontrol.data.model.Attachment import com.m57.hermescontrol.data.model.Attachment
import com.m57.hermescontrol.ui.chat.ChatMessage import com.m57.hermescontrol.ui.chat.ChatMessage
import com.m57.hermescontrol.ui.chat.ImageViewerModel import com.m57.hermescontrol.ui.chat.ImageViewerModel
import com.m57.hermescontrol.ui.chat.InlineAttachment import com.m57.hermescontrol.ui.chat.InlineAttachmentList
import com.m57.hermescontrol.ui.chat.MarkdownText import com.m57.hermescontrol.ui.chat.MarkdownText
import com.m57.hermescontrol.ui.chat.TokenEstimator import com.m57.hermescontrol.ui.chat.TokenEstimator
import com.m57.hermescontrol.ui.chat.components.ReasoningCard import com.m57.hermescontrol.ui.chat.components.ReasoningCard
@@ -109,24 +109,18 @@ internal fun FullBleedAgentMessage(
} }
} }
// Render inline attachments (mirrors ChatBubble so agent-delivered // Inline attachments (shared with UserBubble so agent-delivered
// media — images, files — shows in full-bleed mode too). // media — images, files — shows in full-bleed mode too).
if (!message.attachments.isNullOrEmpty()) { InlineAttachmentList(
Spacer(modifier = Modifier.height(6.dp)) attachments = message.attachments,
message.attachments.forEach { attachment ->
InlineAttachment(
attachment = attachment,
textColor = textColor, textColor = textColor,
onOpen = { onOpenAttachment(it) }, onOpen = onOpenAttachment,
onSave = { onSaveAttachment(it) }, onSave = onSaveAttachment,
savingPath = savingAttachmentPath, savingPath = savingAttachmentPath,
openingPath = openingAttachmentPath, openingPath = openingAttachmentPath,
canSave = canSaveAttachment, canSave = canSaveAttachment,
onImageClick = onImageClick, onImageClick = onImageClick,
) )
Spacer(modifier = Modifier.height(4.dp))
}
}
if (!message.isStreaming && message.content.isNotBlank()) { if (!message.isStreaming && message.content.isNotBlank()) {
val showTokenStat = val showTokenStat =