<template>
<div class="msg-user">
<div class="msg-user-wrap">
<div class="msg-user-bubble">
<div class="msg-user-text" v-html="renderUserText(msg.text)"></div>
<div v-if="msg.images?.length || msg.files?.length" class="msg-user-attachments">
<img
v-for="(src, i) in msg.images"
:key="`img-${i}`"
:src="src"
class="msg-user-image"
@click="openImage(src)"
/>
<span
v-for="(file, i) in msg.files"
:key="`file-${i}`"
class="msg-user-file-pill"
>
<i class="ph ph-file"></i>
{{ file.name }}
</span>
</div>
</div>
<div class="msg-user-footer">
<span v-if="msgTime" class="msg-time msg-time--user">{{ msgTime }}</span>
<GnIconButton
:icon="copied ? 'ph-check' : 'ph-copy'"
:label="copied ? 'Copied!' : 'Copy'"
class="msg-copy-btn"
@click="copy(msg.text)"
/>
</div>
</div>
</div>
</template>
<script setup>
import { computed } from 'vue'
import { useTimeLabel } from '@/composables/useTime.js'
import { useLightbox } from '@/composables/useLightbox.js'
import { useCopy } from '@/composables/useCopy.js'
const props = defineProps({ msg: { type: Object, required: true } })
const tsRef = computed(() => props.msg.time)
const msgTime = useTimeLabel(tsRef)
const { open: openImage } = useLightbox()
const { copied, copy } = useCopy()
function escapeHtml(str) {
return str
.replace(/&/g, '&')
.replace(/</g, '<')
.replace(/>/g, '>')
.replace(/"/g, '"')
.replace(/'/g, ''')
}
function renderUserText(text) {
if (!text) return ''
const lines = text.split('\n')
let quoteLines = []
let textLines = []
for (const line of lines) {
if (line.startsWith('> ')) {
quoteLines.push(escapeHtml(line.slice(2)))
} else if (line.trim()) {
textLines.push(escapeHtml(line))
}
}
const quote = quoteLines.length
? `<blockquote class="msg-user-quote">${quoteLines.join('<br>')}</blockquote>`
: ''
const body = textLines.join(' ')
return quote + body
}
</script>