<template>
<div
v-if="msg.thinking || msg.tools?.length || msg.text || !msg.done"
class="msg-assistant"
>
<!-- Thinking block -->
<ThinkingCard
v-if="msg.thinking"
:msg="msg"
/>
<!-- Tool / plan cards -->
<template v-for="(item, i) in msg.tools" :key="i">
<ThinkingCard v-if="item.kind === 'turn_thinking'" :msg="item" />
<details v-else-if="item.kind === 'plan'" class="plan-card" open>
<summary>
<i class="ph ph-map-trifold"></i>
Plan
<i class="ph ph-caret-down plan-chevron"></i>
</summary>
<div class="plan-body" v-html="renderMarkdown(item.text)" />
</details>
<ToolCard v-else :tool="item" />
</template>
<!-- Text response -->
<div
v-if="msg.text"
ref="contentEl"
class="msg-assistant-content"
v-html="renderedText"
/>
<!-- Planning indicator: streaming but no content yet -->
<div
v-if="!msg.done && !msg.thinking && !msg.tools?.length && !msg.text"
class="planning-indicator"
>
<span class="spinner"></span>
<span>Planning…</span>
</div>
<!-- Streaming cursor -->
<span v-else-if="!msg.done && msg.text" class="stream-cursor" />
<!-- Timestamp -->
<span v-if="msgTime" class="msg-time">{{ msgTime }}</span>
</div>
</template>
<script setup>
import { ref, computed, watch, nextTick } from 'vue'
import ThinkingCard from './ThinkingCard.vue'
import ToolCard from './ToolCard.vue'
import { renderMarkdown, attachCopyButtons } from '@/composables/useMarkdown.js'
import { useTimeLabel } from '@/composables/useTime.js'
const props = defineProps({ msg: { type: Object, required: true } })
const contentEl = ref(null)
const renderedText = computed(() => renderMarkdown(props.msg.text))
const tsRef = computed(() => props.msg.done ? props.msg.time : null)
const msgTime = useTimeLabel(tsRef)
// Attach copy buttons after render
watch(renderedText, () => nextTick(() => attachCopyButtons(contentEl.value)))
</script>
<style scoped>
.stream-cursor {
display: inline-block;
width: 2px;
height: 1em;
background: currentColor;
margin-left: 2px;
vertical-align: text-bottom;
animation: blink 1s step-end infinite;
}
@keyframes blink {
0%, 100% { opacity: 1; }
50% { opacity: 0; }
}
</style>