<template>
  <a-drawer
    :visible="visible"
    :width="drawerWidth"
    :placement="isMobile ? 'bottom' : 'right'"
    :height="isMobile ? '100vh' : undefined"
    :footer="false"
    class="ai-chat-drawer"
    unmount-on-close
    @cancel="close"
  >
    <template #title>
      <div class="ai-chat-title">
        <a-avatar :size="36" class="ai-chat-title__avatar">
          <img src="@/assets/img/kefu.png" alt="小妍助理" draggable="false" />
        </a-avatar>
        <div class="ai-chat-title__copy">
          <div class="ai-chat-title__name">小妍助理</div>
          <div class="ai-chat-title__sub">
            <span class="status-dot"></span>
            在线
          </div>
        </div>
      </div>
    </template>

    <div class="ai-chat-shell" @paste="handlePaste">
      <div
        v-if="isMobile && showConversationPanel"
        class="conversation-backdrop"
        @click="showConversationPanel = false"
      ></div>
      <aside class="conversation-panel" :class="{ 'conversation-panel--open': showConversationPanel }">
        <div class="conversation-panel__head">
          <div>
            <div class="conversation-panel__label">会话</div>
            <div class="conversation-panel__count">{{ conversations.length }} 条</div>
          </div>
          <a-tooltip content="新建对话">
            <a-button type="primary" shape="circle" @click="createConversationAndOpen">
              <template #icon><icon-plus /></template>
            </a-button>
          </a-tooltip>
        </div>

        <div class="conversation-list">
          <div
            v-for="item in conversations"
            :key="item.id"
            class="conversation-card"
          >
            <button
              class="conversation-item"
              :class="{ 'conversation-item--active': item.id === currentConversationId, 'conversation-item--local': item.local }"
              @click="openConversation(item.id)"
            >
              <span class="conversation-item__top">
                <span class="conversation-item__title">
                  <span v-if="item.channel === 'wechat'" class="channel-badge channel-badge--wechat"><icon-wechat /></span>
                  {{ item.title || '新对话' }}
                </span>
                <span v-if="unreadMap[item.id]" class="conversation-item__dot"></span>
              </span>
              <span class="conversation-item__preview">{{ item.last_message_preview || '尚未开始' }}</span>
              <span class="conversation-item__foot">
                <span>{{ formatShortTime(item.update_time) || '新对话' }}</span>
              </span>
            </button>
            <a-popconfirm
              v-if="canShowConversationActions(item)"
              content="是否删除该对话？"
              @ok="deleteConversationById(item.id)"
            >
              <a-button
                class="conversation-card__more"
                type="text"
                shape="circle"
                size="mini"
                @click.stop
              >
                <template #icon><icon-delete /></template>
              </a-button>
            </a-popconfirm>
          </div>

        </div>
      </aside>

      <main
        class="chat-main"
        :class="{ 'chat-main--dragging': dragging }"
        @dragenter.prevent="dragging = true"
        @dragover.prevent="dragging = true"
        @dragleave.prevent="dragging = false"
        @drop.prevent="handleDrop"
      >
        <div class="chat-toolbar">
          <a-button v-if="isMobile" type="text" shape="circle" @click="showConversationPanel = !showConversationPanel">
            <template #icon><icon-menu /></template>
          </a-button>

          <div class="chat-toolbar__identity">
            <a-avatar :size="34" class="chat-toolbar__avatar">
              <img src="@/assets/img/ai.svg" alt="小妍助理" draggable="false" v-if="currentConversation?.channel !== 'wechat'"/>
              <img src="@/assets/img/wechat.svg" alt="微信聊天" draggable="false" v-else />
            </a-avatar>
            <div class="chat-toolbar__text">
              <strong>
                {{ currentConversation?.title || '新对话' }}
              </strong>
              <span v-if="currentConversation?.readonly">微信会话仅可在网页查看，请在微信内回复</span>
            </div>
          </div>

          <a-tooltip v-if="isMobile" content="关闭">
            <a-button type="text" shape="circle" class="chat-toolbar__close" @click="close">
              <template #icon><icon-close /></template>
            </a-button>
          </a-tooltip>
        </div>

        <div ref="messageContainer" class="message-list">
          <div v-if="!currentConversationId" class="empty-state">
            <a-avatar :size="64" class="empty-state__avatar">
              <img src="@/assets/img/ai.svg" alt="小妍助理" draggable="false" />
            </a-avatar>
            <div class="empty-state__title">开启一段新对话</div>
          </div>

          <template v-else>
            <div v-if="messagesLoading" class="message-loading">
              <div class="message-loading__hero">
                <a-avatar :size="42" class="message-loading__avatar">
                  <img src="@/assets/img/kefu.png" alt="小妍助理" draggable="false" />
                </a-avatar>
                <div class="message-loading__copy">
                  <strong>正在切换会话</strong>
                  <span>为你整理最近的消息</span>
                </div>
                <span class="message-loading__pulse"></span>
              </div>
              <div class="message-loading__thread" aria-hidden="true">
                <div class="message-loading__row">
                  <span class="message-loading__dot"></span>
                  <div class="message-loading__bubble message-loading__bubble--assistant">
                    <i></i>
                    <i></i>
                    <i></i>
                  </div>
                </div>
                <div class="message-loading__row message-loading__row--user">
                  <div class="message-loading__bubble message-loading__bubble--user">
                    <i></i>
                    <i></i>
                  </div>
                  <span class="message-loading__dot"></span>
                </div>
                <div class="message-loading__row">
                  <span class="message-loading__dot"></span>
                  <div class="message-loading__bubble message-loading__bubble--assistant message-loading__bubble--short">
                    <i></i>
                    <i></i>
                  </div>
                </div>
              </div>
            </div>

            <div v-if="!messagesLoading && !messages.length" class="welcome-card">
              <a-avatar :size="52" class="welcome-card__avatar">
                <img src="@/assets/img/kefu.png" alt="小妍助理" draggable="false" />
              </a-avatar>
              <div>
                <div class="welcome-card__title">我是小妍助理</div>
                <div class="welcome-card__text">可以让我做任何事情哦~</div>
              </div>
              <div class="welcome-prompts">
                <button
                  v-for="prompt in examplePrompts"
                  :key="prompt"
                  class="welcome-prompt"
                  type="button"
                  :disabled="sending"
                  @click="sendExamplePrompt(prompt)"
                >
                  {{ prompt }}
                </button>
              </div>
            </div>

            <div
              v-for="message in messages"
              :key="message.id"
              class="message-row"
              :class="`message-row--${message.role}`"
            >
              <a-avatar :size="36" class="message-avatar">
                <img v-if="message.role === 'assistant'" src="@/assets/img/kefu.png" alt="小妍助理" draggable="false" />
                <img v-else-if="message.role === 'user' && avatar" :src="avatar" draggable="false" />
                <icon-user v-else-if="message.role === 'user'" />
                <icon-info-circle v-else />
              </a-avatar>

              <div class="message-stack">
                <div class="message-meta">
                  <span>{{ roleLabel(message.role) }}</span>
                  <span>{{ formatFullTime(message.create_time) }}</span>
                </div>

                <div class="message-bubble">
                  <div v-if="message.thinking" class="thinking-indicator">
                    <span>思考中</span>
                    <i></i>
                    <i></i>
                    <i></i>
                  </div>
                  <div v-else-if="message.content" class="message-content" v-html="renderMarkdown(message.content)"></div>
                  <div v-if="message.images?.length" class="message-images">
                    <a-image
                      v-for="url in message.images"
                      :key="url"
                      :src="url"
                      width="118"
                      height="118"
                      fit="cover"
                    />
                  </div>
                  <div v-if="message.error_msg" class="message-error">{{ message.error_msg }}</div>
                </div>
                <div v-if="message.role === 'user'" class="message-status" :class="`message-status--${message.sendStatus || message.status || 'done'}`">
                  <icon-loading v-if="message.sendStatus === 'sending'" />
                  <icon-check-circle v-else-if="message.sendStatus === 'sent' || message.status === 'done'" />
                  <icon-refresh v-else-if="message.sendStatus === 'failed'" />
                  <span>{{ messageStatusText(message) }}</span>
                </div>
              </div>
            </div>
          </template>
        </div>

        <div v-if="uploadingImages.length" class="image-preview-bar">
          <div v-for="item in uploadingImages" :key="item.id" class="image-preview">
            <a-image :src="item.url" width="62" height="62" fit="cover" />
            <a-button size="mini" shape="circle" @click="removeImage(item.id)">
              <template #icon><icon-close /></template>
            </a-button>
          </div>
        </div>

        <div v-if="currentConversation?.readonly" class="readonly-bar">
          <span class="channel-badge channel-badge--wechat"><icon-wechat /></span>
          该会话来自微信，网页端仅支持查看
        </div>

        <div v-else class="composer-wrap">
          <div class="composer">
            <input ref="fileInput" type="file" accept="image/png,image/jpeg,image/jpg" multiple hidden @change="handleFileInput" />
            <a-textarea
              v-model="input"
              placeholder="输入文字或图片"
              :max-length="1000"
              :auto-size="{ minRows: 1, maxRows: 5 }"
              allow-clear
              @keydown.enter.exact.prevent="sendMessage()"
            />
            <div class="composer__actions">
              <div class="composer__left">
                <a-tooltip content="添加图片">
                  <a-button :loading="uploading" type="text" class="composer__tool" @click="fileInput?.click()">
                    <template #icon><icon-image /></template>
                  </a-button>
                </a-tooltip>
              </div>
              <div class="composer__right">
                <a-button type="primary" shape="circle" :loading="sending" :disabled="!canSend" class="composer__send" @click="sendMessage()">
                  <template #icon><icon-arrow-up /></template>
                </a-button>
              </div>
            </div>
          </div>
        </div>
      </main>
    </div>
  </a-drawer>
</template>

<script setup>
import { computed, nextTick, onMounted, onUnmounted, ref, watch } from 'vue'
import { Message, Notification } from '@arco-design/web-vue'
import MarkdownIt from 'markdown-it'
import storage from 'store'
import { useUserStore } from '@/store/modules/user'
import { uploadImg } from '@/api/upload'
import {
  deleteAIChatConversation,
  listAIChatConversations,
  listAIChatMessages,
  sendAIChatMessage
} from '@/api/aiChat'

const props = defineProps({
  visible: {
    type: Boolean,
    default: false
  },
  action: {
    type: Object,
    default: null
  }
})
const emit = defineEmits(['close', 'unread-change'])

const user = ref(null)
const md = new MarkdownIt({ html: false, linkify: true, breaks: true })
const userStore = useUserStore()
const MAX_UPLOAD_IMAGES = 3
const examplePrompts = [
  '登录器怎么下载？',
  '乐跑账号绑定失败怎么办？',
  '帮我添加一个乐跑账号',
  '如何查看我的订单和售后进度？'
]
const isMobile = ref(window.innerWidth <= 768)
const showConversationPanel = ref(false)
const conversations = ref([])
const messages = ref([])
const currentConversationId = ref(null)
const input = ref('')
const uploadingImages = ref([])
const messagesLoading = ref(false)
const sending = ref(false)
const uploading = ref(false)
const dragging = ref(false)
const assistantMode = ref('default')
const messageContainer = ref(null)
const fileInput = ref(null)
const unreadMap = ref({})
let conversationTimer = null
let messageTimer = null
let messagesRequestSeq = 0

const visible = computed(() => props.visible)
const drawerWidth = computed(() => (isMobile.value ? '100%' : '940px'))
const currentConversation = computed(() => conversations.value.find(item => item.id === currentConversationId.value))
const canDeleteCurrentConversation = computed(() => canShowConversationActions(currentConversation.value))
const currentReadonly = computed(() => Boolean(currentConversation.value?.readonly || currentConversation.value?.channel === 'wechat'))
const canSend = computed(() => currentConversationId.value && !currentReadonly.value && (input.value.trim() || uploadingImages.value.length > 0))
const unreadTotal = computed(() => Object.values(unreadMap.value).reduce((sum, value) => sum + Number(value || 0), 0))
const seenKey = computed(() => `aiChatSeen:${userStore.uuid || 'guest'}`)
const avatar = computed(() => {
  return userStore.avatar
})

const roleLabel = (role) => ({ user: '我', assistant: '小妍助理', system: '系统' }[role] || role)
const isLocalConversation = (id) => String(id || '').startsWith('local-')
const canShowConversationActions = (conversation) => {
  return Boolean(conversation?.id && !conversation.local && Number(conversation.last_message_time || 0) > 0)
}
const messageStatusText = (message) => {
  if (message.sendStatus === 'sending') return '发送中'
  if (message.sendStatus === 'failed') return '发送失败'
  if (message.sendStatus === 'sent' || message.status === 'done') return '已发送'
  return ''
}
const renderMarkdown = (text) => md.render(text || '')
const formatFullTime = (time) => time ? new Date(Number(time)).toLocaleString('zh-CN', { hour12: false }) : ''
const formatShortTime = (time) => {
  if (!time) return ''
  return new Date(Number(time)).toLocaleString('zh-CN', { month: '2-digit', day: '2-digit', hour: '2-digit', minute: '2-digit', hour12: false })
}
const getLastRemoteMessageId = () => {
  const remoteMessage = [...messages.value].reverse().find(item => !item.local && Number(item.id) > 0)
  return remoteMessage?.id || 0
}
const removeThinkingMessages = () => {
  messages.value = messages.value.filter(item => !item.thinking)
}
const normalizeMessageImages = (images = []) => JSON.stringify([...(images || [])].sort())
const isSameOptimisticUserMessage = (incoming, localMessage) => {
  return Boolean(
    incoming?.role === 'user' &&
    localMessage?.local &&
    localMessage.role === 'user' &&
    localMessage.sendStatus !== 'failed' &&
    String(incoming.content || '') === String(localMessage.content || '') &&
    normalizeMessageImages(incoming.images) === normalizeMessageImages(localMessage.images)
  )
}
const mergeIncomingMessages = (incoming = []) => {
  const nextIncoming = []
  incoming.forEach((item) => {
    if (messages.value.some(message => Number(message.id) === Number(item.id))) return
    const localIndex = messages.value.findIndex(message => isSameOptimisticUserMessage(item, message))
    if (localIndex !== -1) {
      messages.value[localIndex] = {
        ...item,
        sendStatus: 'sent'
      }
      return
    }
    nextIncoming.push(item)
  })
  if (nextIncoming.some(item => item.role === 'assistant' || item.role === 'system')) {
    removeThinkingMessages()
  }
  if (nextIncoming.length) messages.value.push(...nextIncoming)
  return nextIncoming.length > 0
}

const loadSeen = () => storage.get(seenKey.value) || {}
const saveSeen = (seen) => storage.set(seenKey.value, seen)

const updateUnreadByConversations = (list) => {
  const oldTotal = unreadTotal.value
  const seen = loadSeen()
  const next = {}
  list.forEach((item) => {
    if (item.id === currentConversationId.value && visible.value) return
    if (Number(item.last_message_time || 0) > Number(seen[item.id] || 0)) next[item.id] = 1
  })
  unreadMap.value = next
  emit('unread-change', unreadTotal.value)
  if (!visible.value && unreadTotal.value > oldTotal) {
    Notification.info({ title: '小妍助理有新消息', content: '点击右下角助手查看回复' })
  }
}

const markCurrentSeen = () => {
  if (!currentConversationId.value) return
  const seen = loadSeen()
  const last = messages.value[messages.value.length - 1]
  const conv = currentConversation.value
  seen[currentConversationId.value] = Math.max(Number(last?.create_time || 0), Number(conv?.last_message_time || 0), Date.now())
  saveSeen(seen)
  delete unreadMap.value[currentConversationId.value]
  unreadMap.value = { ...unreadMap.value }
  emit('unread-change', unreadTotal.value)
}

const scrollToBottom = () => {
  nextTick(() => {
    if (messageContainer.value) messageContainer.value.scrollTop = messageContainer.value.scrollHeight
  })
}

const loadConversations = async () => {
  const res = await listAIChatConversations({ current: 1, pagesize: 30 })
  if (!res || res.code !== 0) return
  const localItems = conversations.value.filter(item => item.local)
  conversations.value = [...localItems, ...(res.data || [])]
  updateUnreadByConversations(conversations.value)
  if (visible.value && !currentConversationId.value) {
    if (conversations.value.length) {
      await openConversation(conversations.value[0].id)
    } else {
      await createConversationAndOpen()
    }
  }
}

const loadMessages = async (conversationId = currentConversationId.value) => {
  if (!conversationId) return
  const requestSeq = ++messagesRequestSeq
  messages.value = []
  if (isLocalConversation(conversationId)) {
    messages.value = []
    messagesLoading.value = false
    return
  }
  messagesLoading.value = true
  try {
    const res = await listAIChatMessages({ conversation_id: conversationId, current: 1, pagesize: 80 })
    if (requestSeq !== messagesRequestSeq || currentConversationId.value !== conversationId) return
    if (!res || res.code !== 0) return Message.error(res?.msg || '获取消息失败')
    messages.value = res.data || []
    markCurrentSeen()
    scrollToBottom()
  } finally {
    if (requestSeq === messagesRequestSeq && currentConversationId.value === conversationId) {
      messagesLoading.value = false
    }
  }
}

const pollNewMessages = async () => {
  if (!currentConversationId.value || !visible.value || isLocalConversation(currentConversationId.value)) return
  const conversationId = currentConversationId.value
  const lastId = getLastRemoteMessageId()
  const res = await listAIChatMessages({ conversation_id: conversationId, after_id: lastId })
  if (currentConversationId.value !== conversationId) return
  if (!res || res.code !== 0) return
  const incoming = res.data || []
  if (!incoming.length) return
  const localIds = new Set(messages.value.filter(item => item.local).map(item => item.id))
  const filteredIncoming = incoming.filter(item => !localIds.has(item.client_id))
  if (!filteredIncoming.length) return
  const hasNewMessages = mergeIncomingMessages(filteredIncoming)
  if (!hasNewMessages) return
  // if (hasAssistant) Notification.info({ title: '小妍助理有新回复', content: incoming[incoming.length - 1]?.content || '收到新消息' })
  markCurrentSeen()
  scrollToBottom()
  await loadConversations()
}

const openConversation = async (id) => {
  if (!id) return
  if (currentConversationId.value === id) {
    showConversationPanel.value = false
    return
  }
  currentConversationId.value = id
  messagesRequestSeq += 1
  messages.value = []
  messagesLoading.value = !isLocalConversation(id)
  showConversationPanel.value = false
  await loadMessages(id)
}

const createConversationAndOpen = async () => {
  const blank = conversations.value.find(item => (item.local || Number(item.last_message_time || 0) === 0) && item.channel !== 'wechat' && !item.readonly)
  if (blank) {
    await openConversation(blank.id)
    return
  }

  const now = Date.now()
  const localConversation = {
    id: `local-${now}`,
    title: '新对话',
    last_message_preview: '',
    last_message_time: 0,
    create_time: now,
    update_time: now,
    local: true
  }
  conversations.value = [localConversation, ...conversations.value]
  currentConversationId.value = localConversation.id
  messages.value = []
  showConversationPanel.value = false
  scrollToBottom()
}

const openDraftConversation = async (draft = '') => {
  await createConversationAndOpen()
  if (currentReadonly.value) await createConversationAndOpen()
  input.value = String(draft || '')
  uploadingImages.value = []
  await nextTick()
  scrollToBottom()
}

const clearCurrentConversationIfNeeded = (id) => {
  if (currentConversationId.value !== id) return
  currentConversationId.value = null
  messages.value = []
}

const deleteConversationById = async (id) => {
  if (!id) return
  if (isLocalConversation(id)) {
    conversations.value = conversations.value.filter(item => item.id !== id)
    clearCurrentConversationIfNeeded(id)
    await loadConversations()
    return
  }
  const res = await deleteAIChatConversation({ id })
  if (!res || res.code !== 0) return Message.error(res?.msg || '删除失败')
  conversations.value = conversations.value.filter(item => item.id !== id)
  clearCurrentConversationIfNeeded(id)
  await loadConversations()
}

const deleteCurrentConversation = async () => {
  if (!currentConversationId.value) return
  await deleteConversationById(currentConversationId.value)
}

const uploadFiles = async (files) => {
  const filesList = Array.from(files || []).filter(file => file.type?.startsWith('image/'))
  const remaining = MAX_UPLOAD_IMAGES - uploadingImages.value.length
  if (remaining <= 0) {
    Message.warning(`最多上传 ${MAX_UPLOAD_IMAGES} 张图片`)
    return
  }
  if (filesList.length > remaining) {
    Message.warning(`最多上传 ${MAX_UPLOAD_IMAGES} 张图片，已自动保留前 ${remaining} 张`)
  }
  const imageFiles = filesList.slice(0, remaining)
  if (!imageFiles.length) return
  uploading.value = true
  try {
    for (const file of imageFiles) {
      const formData = new FormData()
      formData.append('uuid', userStore.uuid)
      formData.append('session', userStore.session)
      formData.append('upload', file)
      const res = await uploadImg(formData)
      if (!res || res.code !== 0) {
        Message.error(res?.msg || '图片上传失败')
        continue
      }
      const url = res.data?.picturePath
      if (url) uploadingImages.value.push({ id: `${Date.now()}-${Math.random()}`, url })
    }
  } finally {
    uploading.value = false
    if (fileInput.value) fileInput.value.value = ''
  }
}

const handleFileInput = (event) => uploadFiles(event.target.files)
const handlePaste = (event) => {
  const files = Array.from(event.clipboardData?.files || [])
  if (files.length) uploadFiles(files)
}
const handleDrop = (event) => {
  dragging.value = false
  uploadFiles(event.dataTransfer?.files)
}
const removeImage = (id) => {
  uploadingImages.value = uploadingImages.value.filter(item => item.id !== id)
}

const upsertConversation = (conversation, previousId = null) => {
  if (!conversation?.id) return
  const filtered = conversations.value.filter(item => item.id !== conversation.id && item.id !== previousId)
  conversations.value = [conversation, ...filtered]
}

const sendMessage = async (overrideContent = '') => {
  const presetContent = String(overrideContent || '').trim()
  if (currentReadonly.value) {
    Message.info('微信会话仅支持查看，请在微信内继续对话')
    return
  }
  if ((!presetContent && !canSend.value) || sending.value) return
  const previousConversationId = currentConversationId.value
  const content = presetContent || input.value.trim()
  const images = uploadingImages.value.map(item => item.url)
  const localMessageId = `local-message-${Date.now()}`
  const thinkingMessageId = `local-thinking-${Date.now()}`
  const now = Date.now()
  const optimisticMessage = {
    id: localMessageId,
    conversation_id: previousConversationId,
    role: 'user',
    content,
    images,
    status: 'sending',
    sendStatus: 'sending',
    error_msg: '',
    create_time: now,
    update_time: now,
    local: true
  }
  const thinkingMessage = {
    id: thinkingMessageId,
    conversation_id: previousConversationId,
    role: 'assistant',
    content: '',
    images: [],
    status: 'thinking',
    error_msg: '',
    create_time: now + 1,
    update_time: now + 1,
    thinking: true,
    local: true
  }

  input.value = ''
  uploadingImages.value = []
  messages.value.push(optimisticMessage, thinkingMessage)
  const current = currentConversation.value
  if (current) {
    current.last_message_preview = content || (images.length ? `[图片] x${images.length}` : '')
    current.last_message_time = now
    current.update_time = now
    if (!current.title || current.title === '新对话') current.title = current.last_message_preview?.slice(0, 40) || '新对话'
  }
  scrollToBottom()
  sending.value = true
  try {
    const payload = {
      content,
      images
    }
    if (!isLocalConversation(previousConversationId)) payload.conversation_id = previousConversationId
    const res = await sendAIChatMessage(payload)
    if (!res || res.code !== 0) {
      Message.error(res?.msg || '发送失败')
      const localMessage = messages.value.find(item => item.id === localMessageId)
      if (localMessage) {
        localMessage.status = 'error'
        localMessage.sendStatus = 'failed'
        localMessage.error_msg = res?.msg || '发送失败'
      }
      messages.value = messages.value.filter(item => item.id !== thinkingMessageId)
      input.value = content
      uploadingImages.value = images.map(url => ({ id: `${Date.now()}-${Math.random()}`, url }))
      return
    }
    if (res.data?.conversation) {
      upsertConversation(res.data.conversation, previousConversationId)
      currentConversationId.value = res.data.conversation.id
      const thinkingMessage = messages.value.find(item => item.id === thinkingMessageId)
      if (thinkingMessage) thinkingMessage.conversation_id = res.data.conversation.id
    }
    const localIndex = messages.value.findIndex(item => item.id === localMessageId)
    if (localIndex !== -1) {
      messages.value[localIndex] = {
        ...(res.data?.message || messages.value[localIndex]),
        sendStatus: 'sent'
      }
    }
    const thinkingIndex = messages.value.findIndex(item => item.id === thinkingMessageId)
    if (thinkingIndex !== -1 && Array.isArray(res.data?.assistant_messages) && res.data.assistant_messages.length) {
      messages.value.splice(thinkingIndex, 1, ...res.data.assistant_messages)
    }
    await loadConversations()
    markCurrentSeen()
    scrollToBottom()
  } finally {
    sending.value = false
  }
}

const sendExamplePrompt = async (prompt) => {
  await sendMessage(prompt)
}

const close = () => emit('close')
const syncMobile = () => { isMobile.value = window.innerWidth <= 768 }

const startPolling = () => {
  if (!conversationTimer) conversationTimer = setInterval(loadConversations, 8000)
  if (!messageTimer) messageTimer = setInterval(pollNewMessages, 3000)
}
const stopPolling = () => {
  clearInterval(conversationTimer)
  clearInterval(messageTimer)
  conversationTimer = null
  messageTimer = null
}

watch(() => props.visible, async (value) => {
  if (value) {
    await loadConversations()
    if (!currentConversationId.value) await createConversationAndOpen()
    markCurrentSeen()
    scrollToBottom()
  }
})

watch(() => props.action, async (action) => {
  if (!action) return
  if (action.newConversation || action.draft) {
    await openDraftConversation(action.draft || '')
  }
}, { deep: true })

watch(unreadTotal, value => emit('unread-change', value))

onMounted(async () => {
  await userStore.getInfo()
  window.addEventListener('resize', syncMobile)
  await loadConversations()
  startPolling()
})

onUnmounted(() => {
  window.removeEventListener('resize', syncMobile)
  stopPolling()
})
</script>

<style lang="less" scoped>
.ai-chat-title {
  display: flex;
  align-items: center;
  gap: 12px;

  &__avatar {
    background: linear-gradient(135deg, #eaf2ff, #f7ecff);
    box-shadow: 0 8px 20px rgba(48, 91, 189, 0.16);
  }

  &__copy {
    display: grid;
    gap: 3px;
  }

  &__name {
    color: #1d2129;
    font-size: 16px;
    font-weight: 700;
    line-height: 1.2;
  }

  &__sub {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    color: #4e5969;
    font-size: 12px;
  }
}

.ai-chat-title__avatar,
.chat-toolbar__avatar,
.empty-state__avatar,
.welcome-card__avatar,
.message-avatar {
  user-select: none;

  :deep(img) {
    user-select: none;
    -webkit-user-drag: none;
    pointer-events: none;
  }
}

:global(.ai-chat-drawer .arco-drawer-header) {
  height: 48px;
  padding: 7px 16px;
  border-bottom-color: rgba(29, 33, 41, 0.08);
}

:global(.ai-chat-drawer .arco-drawer-body) {
  height: calc(100% - 48px);
  padding: 0;
  overflow: hidden;
}

.status-dot {
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: #16b364;
  box-shadow: 0 0 0 4px rgba(22, 179, 100, 0.12);
}

.ai-chat-shell {
  position: relative;
  display: grid;
  grid-template-columns: 270px minmax(0, 1fr);
  height: 100%;
  min-height: 0;
  overflow: hidden;
  margin: 0;
  background:
    radial-gradient(circle at 18% 12%, rgba(43, 179, 169, 0.14), transparent 30%),
    radial-gradient(circle at 90% 6%, rgba(116, 89, 255, 0.12), transparent 28%),
    linear-gradient(135deg, #f7f9ff 0%, #ffffff 48%, #f3fbf8 100%);
  border-top: 1px solid rgba(29, 33, 41, 0.08);
}

.conversation-backdrop {
  position: absolute;
  inset: 0;
  z-index: 9;
  background: rgba(15, 23, 42, 0.18);
  backdrop-filter: blur(1px);
}

.conversation-panel {
  display: flex;
  flex-direction: column;
  min-width: 0;
  min-height: 0;
  overflow: hidden;
  border-right: 1px solid rgba(29, 33, 41, 0.08);
  background: rgba(255, 255, 255, 0.84);
  backdrop-filter: blur(18px);
}

.conversation-panel__head {
  flex: 0 0 auto;
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 18px 16px 12px;
  border-bottom: 1px solid rgba(29, 33, 41, 0.05);
}

.conversation-panel__label {
  color: #1d2129;
  font-size: 16px;
  font-weight: 720;
}

.conversation-panel__count {
  margin-top: 2px;
  color: #86909c;
  font-size: 12px;
}

.conversation-list {
  flex: 1;
  min-height: 0;
  overflow-y: auto;
  overscroll-behavior: contain;
  padding: 0 12px 18px;
}

.conversation-card {
  position: relative;
  margin-top: 5px;
  margin-bottom: 10px;

  &:hover .conversation-card__more,
  &:focus-within .conversation-card__more {
    opacity: 1;
    transform: translateY(0);
    pointer-events: auto;
  }

  &__more {
    position: absolute;
    top: 8px;
    right: 8px;
    z-index: 2;
    width: 26px;
    height: 26px;
    color: #4e5969;
    background: rgba(255, 255, 255, 0.94);
    box-shadow: 0 8px 18px rgba(15, 23, 42, 0.1);
    opacity: 0;
    pointer-events: none;
    transform: translateY(-3px);
    transition: opacity 0.16s ease, transform 0.16s ease, color 0.16s ease, background 0.16s ease;

    &:hover {
      color: #f53f3f;
      background: #fff1f0;
    }
  }
}

.conversation-item {
  position: relative;
  display: grid;
  width: 100%;
  gap: 7px;
  padding: 13px 14px;
  border: 1px solid rgba(29, 33, 41, 0.07);
  border-radius: 8px;
  background: rgba(255, 255, 255, 0.72);
  box-shadow: 0 10px 24px rgba(15, 23, 42, 0.04);
  text-align: left;
  cursor: pointer;
  transition: border-color 0.18s ease, box-shadow 0.18s ease, transform 0.18s ease, background 0.18s ease;

  &::before {
    content: '';
    position: absolute;
    inset: 9px auto 9px 0;
    width: 3px;
    border-radius: 0 999px 999px 0;
    background: linear-gradient(180deg, #2bb3a9, #7459ff);
    opacity: 0;
    transform: scaleY(0.45);
    transition: opacity 0.18s ease, transform 0.18s ease;
  }

  &:hover,
  &--active {
    border-color: rgba(116, 89, 255, 0.36);
    background: #ffffff;
    box-shadow: 0 12px 28px rgba(82, 71, 190, 0.12);
    transform: translateY(-1px);

    &::before {
      opacity: 1;
      transform: scaleY(1);
    }
  }

  &--local {
    border-style: dashed;
  }

  &__top,
  &__foot {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 10px;
    min-width: 0;
  }

  &__title,
  &__preview {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
  }

  &__title {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    min-width: 0;
    color: #1d2129;
    font-weight: 650;
  }

  &__preview {
    color: #4e5969;
    font-size: 13px;
  }

  &__foot {
    color: #86909c;
    font-size: 11px;
  }

  &__dot {
    flex: 0 0 auto;
    width: 8px;
    height: 8px;
    border-radius: 50%;
    background: #f53f3f;
    box-shadow: 0 0 0 4px rgba(245, 63, 63, 0.12);
  }
}

.channel-badge {
  display: inline-grid;
  place-items: center;
  width: 18px;
  height: 18px;
  flex: 0 0 auto;
  border-radius: 6px;
  color: #ffffff;
  font-size: 11px;
  font-weight: 800;
  line-height: 1;

  &--wechat {
    background: linear-gradient(135deg, #12b76a, #039855);
    box-shadow: 0 6px 14px rgba(18, 183, 106, 0.22);
  }
}

.readonly-bar {
  display: flex;
  align-items: center;
  gap: 8px;
  margin: 0 18px 18px;
  padding: 12px 14px;
  border: 1px solid rgba(18, 183, 106, 0.22);
  border-radius: 12px;
  background: rgba(240, 253, 244, 0.92);
  color: #027a48;
  font-size: 13px;
  box-shadow: 0 12px 28px rgba(3, 152, 85, 0.08);
}

.chat-main {
  position: relative;
  display: grid;
  grid-template-rows: auto minmax(0, 1fr) auto auto;
  min-width: 0;
  min-height: 0;
  overflow: hidden;
  background:
    linear-gradient(180deg, rgba(255, 255, 255, 0.32), rgba(255, 255, 255, 0.74)),
    radial-gradient(circle at 24px 24px, rgba(116, 89, 255, 0.07) 0 1px, transparent 1px);
  background-size: auto, 22px 22px;

  &--dragging::after {
    content: '释放上传图片';
    position: absolute;
    inset: 16px;
    z-index: 5;
    display: grid;
    place-items: center;
    border: 2px dashed #2563eb;
    border-radius: 8px;
    background: rgba(255, 255, 255, 0.88);
    color: #1d4ed8;
    font-size: 15px;
    font-weight: 700;
    backdrop-filter: blur(6px);
  }
}

.chat-toolbar {
  display: flex;
  align-items: center;
  gap: 12px;
  min-height: 56px;
  padding: 10px 16px;
  border-bottom: 1px solid rgba(29, 33, 41, 0.08);
  background: rgba(255, 255, 255, 0.78);
  backdrop-filter: blur(16px);

  &__identity {
    display: flex;
    align-items: center;
    gap: 11px;
    flex: 1;
    min-width: 0;
  }

  &__avatar {
    flex: 0 0 auto;
    background: linear-gradient(135deg, #eef7ff, #edfdf7);
    box-shadow: 0 8px 20px rgba(43, 179, 169, 0.12);
  }

  &__text {
    display: grid;
    flex: 1;
    min-width: 0;
    gap: 3px;

    strong,
    span {
      overflow: hidden;
      text-overflow: ellipsis;
      white-space: nowrap;
    }

    strong {
      color: #1d2129;
      font-size: 15px;
      font-weight: 700;
    }

    span {
      color: #86909c;
      font-size: 12px;
    }
  }
}

.message-list {
  min-height: 0;
  overflow-y: auto;
  overscroll-behavior: contain;
  padding: 22px 22px 18px;
}

.conversation-list,
.message-list {
  scrollbar-width: thin;
  scrollbar-color: rgba(143, 118, 255, 0.5) rgba(244, 247, 255, 0.55);

  &::-webkit-scrollbar {
    width: 10px;
    height: 10px;
  }

  &::-webkit-scrollbar-track {
    border-radius: 999px;
    background: rgba(244, 247, 255, 0.55);
  }

  &::-webkit-scrollbar-thumb {
    min-height: 44px;
    border: 3px solid rgba(244, 247, 255, 0.9);
    border-radius: 999px;
    background:
      linear-gradient(180deg, rgba(198, 171, 255, 0.95), rgba(143, 118, 255, 0.72));
    background-clip: padding-box;
    box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.35);
  }

  &::-webkit-scrollbar-thumb:hover {
    background:
      linear-gradient(180deg, rgba(178, 145, 255, 1), rgba(116, 89, 255, 0.86));
    background-clip: padding-box;
  }

  &::-webkit-scrollbar-corner {
    background: transparent;
  }
}

.message-loading {
  display: grid;
  align-content: center;
  gap: 22px;
  min-height: 100%;
  padding: 22px;
}

.message-loading__hero {
  position: relative;
  display: flex;
  align-items: center;
  gap: 12px;
  width: min(430px, 100%);
  margin: 0 auto;
  padding: 14px 16px;
  border: 1px solid rgba(116, 89, 255, 0.14);
  border-radius: 16px;
  background: rgba(255, 255, 255, 0.78);
  box-shadow:
    0 20px 46px rgba(36, 48, 94, 0.08),
    inset 0 1px 0 rgba(255, 255, 255, 0.86);
  overflow: hidden;
}

.message-loading__hero::before {
  content: '';
  position: absolute;
  inset: 0;
  background: linear-gradient(110deg, transparent 0%, rgba(255, 255, 255, 0.7) 42%, transparent 70%);
  transform: translateX(-100%);
  animation: ai-chat-shimmer 1.9s ease-in-out infinite;
}

.message-loading__avatar {
  position: relative;
  z-index: 1;
  flex: 0 0 auto;
  background: linear-gradient(135deg, #eaf2ff, #edfbf7);
  box-shadow: 0 10px 24px rgba(43, 179, 169, 0.16);
}

.message-loading__copy {
  position: relative;
  z-index: 1;
  display: grid;
  gap: 3px;
  min-width: 0;
}

.message-loading__copy strong {
  color: #1d2129;
  font-size: 15px;
  font-weight: 720;
}

.message-loading__copy span {
  color: #667085;
  font-size: 12px;
}

.message-loading__pulse {
  position: relative;
  z-index: 1;
  width: 10px;
  height: 10px;
  margin-left: auto;
  border-radius: 50%;
  background: #2bb3a9;
  box-shadow: 0 0 0 0 rgba(43, 179, 169, 0.28);
  animation: ai-chat-pulse 1.4s ease-out infinite;
}

.message-loading__thread {
  display: grid;
  gap: 16px;
  width: min(560px, 100%);
  margin: 0 auto;
}

.message-loading__row {
  display: flex;
  align-items: flex-start;
  gap: 10px;
}

.message-loading__row--user {
  justify-content: flex-end;
}

.message-loading__dot {
  width: 34px;
  height: 34px;
  flex: 0 0 34px;
  border-radius: 50%;
  background:
    linear-gradient(135deg, rgba(234, 242, 255, 0.95), rgba(237, 251, 247, 0.95));
  box-shadow: 0 10px 24px rgba(15, 23, 42, 0.08);
}

.message-loading__bubble {
  display: grid;
  gap: 9px;
  width: min(72%, 390px);
  padding: 14px;
  border: 1px solid rgba(29, 33, 41, 0.06);
  border-radius: 16px;
  background: rgba(255, 255, 255, 0.76);
  box-shadow: 0 14px 34px rgba(15, 23, 42, 0.06);
}

.message-loading__bubble--user {
  width: min(58%, 310px);
  background: linear-gradient(135deg, rgba(116, 89, 255, 0.9), rgba(43, 179, 169, 0.86));

  i {
    background: rgba(255, 255, 255, 0.46);
  }
}

.message-loading__bubble--short {
  width: min(52%, 280px);
}

.message-loading__bubble i {
  display: block;
  height: 10px;
  border-radius: 999px;
  background:
    linear-gradient(90deg, rgba(226, 232, 240, 0.76), rgba(245, 247, 251, 0.96), rgba(226, 232, 240, 0.76));
  background-size: 220% 100%;
  animation: ai-chat-skeleton 1.45s ease-in-out infinite;

  &:nth-child(1) {
    width: 92%;
  }

  &:nth-child(2) {
    width: 74%;
    animation-delay: 0.08s;
  }

  &:nth-child(3) {
    width: 48%;
    animation-delay: 0.16s;
  }
}

.message-loading__bubble--user i {
  background:
    linear-gradient(90deg, rgba(255, 255, 255, 0.3), rgba(255, 255, 255, 0.62), rgba(255, 255, 255, 0.3));
  background-size: 220% 100%;
}

.empty-state,
.welcome-card {
  display: grid;
  place-items: center;
  gap: 10px;
  height: 100%;
  color: #86909c;
}

.empty-state__avatar,
.welcome-card__avatar {
  background: linear-gradient(135deg, #eaf2ff, #f7ecff);
  user-select: none;
}

.empty-state__title,
.welcome-card__title {
  color: #1d2129;
  font-size: 16px;
  font-weight: 700;
}

.welcome-card {
  justify-content: center;
  align-content: center;
  height: auto;
  min-height: 220px;
  text-align: center;
}

.welcome-card__text {
  margin-top: 4px;
  color: #86909c;
  font-size: 13px;
}

.welcome-prompts {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 9px;
  width: min(560px, 100%);
  margin-top: 8px;
}

.welcome-prompt {
  max-width: 100%;
  padding: 8px 12px;
  border: 1px solid rgba(116, 89, 255, 0.2);
  border-radius: 999px;
  background: rgba(255, 255, 255, 0.82);
  color: #344054;
  font-size: 13px;
  line-height: 1.2;
  box-shadow: 0 8px 18px rgba(82, 71, 190, 0.08);
  cursor: pointer;
  transition: border-color 0.16s ease, color 0.16s ease, transform 0.16s ease, box-shadow 0.16s ease;

  &:hover:not(:disabled) {
    border-color: rgba(116, 89, 255, 0.48);
    color: #5f49d8;
    box-shadow: 0 10px 22px rgba(82, 71, 190, 0.12);
    transform: translateY(-1px);
  }

  &:disabled {
    cursor: not-allowed;
    opacity: 0.58;
  }
}

.message-row {
  display: flex;
  align-items: flex-start;
  gap: 11px;
  margin-bottom: 18px;
  animation: ai-chat-message-in 0.24s ease-out both;

  &--user {
    flex-direction: row-reverse;

    .message-stack {
      align-items: flex-end;
    }

    .message-meta {
      flex-direction: row-reverse;
    }

    .message-bubble {
      color: #ffffff;
      border-color: rgba(255, 255, 255, 0.2);
      background: linear-gradient(135deg, #6757f5, #1aa99a);
      box-shadow: 0 16px 32px rgba(43, 86, 132, 0.2);
    }

    .message-content :deep(a) {
      color: #ffffff;
    }
  }

  &--system {
    .message-avatar {
      background: #fff7e8;
      color: #d46b08;
      user-select: none;
    }

    .message-bubble {
      background: #fffaf0;
      border-color: rgba(245, 158, 11, 0.22);
    }
  }
}

.message-avatar {
  flex: 0 0 36px;
  background: #eef4ff;
  box-shadow: 0 8px 20px rgba(15, 23, 42, 0.08);
  user-select: none;
}

.message-stack {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  max-width: min(78%, 640px);
  min-width: 0;
}

.message-meta {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 8px;
  margin-bottom: 6px;
  color: #86909c;
  font-size: 12px;
}

.message-bubble {
  max-width: 100%;
  padding: 12px 14px;
  border: 1px solid rgba(29, 33, 41, 0.07);
  border-radius: 14px;
  background: rgba(255, 255, 255, 0.92);
  box-shadow:
    0 14px 30px rgba(15, 23, 42, 0.06),
    inset 0 1px 0 rgba(255, 255, 255, 0.78);
  line-height: 1.72;
  word-break: break-word;
}

.thinking-indicator {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  color: #4e5969;
  font-size: 13px;
  line-height: 1.4;

  span {
    margin-right: 2px;
  }

  i {
    width: 5px;
    height: 5px;
    border-radius: 50%;
    background: #8f7cff;
    opacity: 0.35;
    animation: ai-chat-thinking 1.2s ease-in-out infinite;

    &:nth-of-type(2) {
      animation-delay: 0.16s;
    }

    &:nth-of-type(3) {
      animation-delay: 0.32s;
    }
  }
}

.message-status {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  margin-top: 5px;
  color: #86909c;
  font-size: 12px;
  line-height: 1;

  svg {
    font-size: 13px;
  }

  &--sending {
    color: #6b7280;

    svg {
      animation: ai-chat-spin 1s linear infinite;
    }
  }

  &--sent,
  &--done {
    color: #16a34a;
  }

  &--failed,
  &--error {
    color: #f53f3f;
  }
}

.message-content {
  :deep(p) {
    margin: 0 0 7px;
  }

  :deep(p:last-child) {
    margin-bottom: 0;
  }

  :deep(ul),
  :deep(ol) {
    margin: 6px 0;
    padding-left: 20px;
  }

  :deep(code) {
    border-radius: 4px;
    padding: 2px 5px;
    background: rgba(15, 23, 42, 0.08);
    font-size: 12px;
  }
}

.message-images {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  margin-top: 9px;

  :deep(.arco-image-img) {
    border-radius: 8px;
  }
}

.message-error {
  margin-top: 7px;
  color: #f53f3f;
  font-size: 12px;
}

.image-preview-bar {
  display: flex;
  gap: 10px;
  overflow-x: auto;
  padding: 10px 18px 0;
}

.image-preview {
  position: relative;
  flex: 0 0 auto;
  border-radius: 8px;
  box-shadow: 0 8px 20px rgba(15, 23, 42, 0.1);

  :deep(.arco-image-img) {
    border-radius: 8px;
  }

  :deep(.arco-btn) {
    position: absolute;
    top: -8px;
    right: -8px;
    background: #ffffff;
  }
}

.composer-wrap {
  padding: 12px 18px 20px;
  background: linear-gradient(180deg, rgba(255, 255, 255, 0), rgba(250, 252, 255, 0.98) 32%);
}

.composer {
  display: flex;
  flex-direction: column;
  gap: 8px;
  min-height: 98px;
  padding: 12px 14px 10px;
  border: 1px solid rgba(103, 87, 245, 0.28);
  border-radius: 18px;
  background: rgba(255, 255, 255, 0.96);
  box-shadow:
    0 18px 44px rgba(35, 48, 86, 0.1),
    0 0 0 3px rgba(43, 179, 169, 0.06);
  transition: border-color 0.18s ease, box-shadow 0.18s ease;

  &:focus-within {
    border-color: rgba(43, 179, 169, 0.5);
    box-shadow:
      0 20px 48px rgba(35, 48, 86, 0.13),
      0 0 0 4px rgba(43, 179, 169, 0.12);
  }

  :deep(.arco-textarea-wrapper) {
    flex: 1;
    border: 0;
    background: transparent;
    box-shadow: none;
    padding: 0;
  }

  :deep(textarea) {
    min-height: 36px;
    padding: 0;
    font-size: 14px;
    line-height: 1.65;
  }

  :deep(.arco-textarea-clear-btn) {
    right: 0;
  }

  &__actions,
  &__left,
  &__right {
    display: flex;
    align-items: center;
  }

  &__actions {
    justify-content: space-between;
    gap: 10px;
  }

  &__left,
  &__right {
    gap: 8px;
    min-width: 0;
  }

  &__tool {
    height: 30px;
    padding: 0 6px;
    border-radius: 999px;
    color: #1f2937;
    background: transparent;

    &--label {
      gap: 4px;
      padding: 0 8px;
      font-size: 13px;
    }
  }

  &__mode {
    width: 76px;

    :deep(.arco-select-view-single) {
      height: 30px;
      border: 0;
      border-radius: 999px;
      background: #f5f6f8;
      box-shadow: none;
    }
  }

  &__send {
    width: 32px;
    height: 32px;
    border: 0;
    background: linear-gradient(135deg, #6757f5, #20b7a7);
    box-shadow: 0 10px 22px rgba(43, 179, 169, 0.28);
  }
}

@keyframes ai-chat-spin {
  from {
    transform: rotate(0deg);
  }

  to {
    transform: rotate(360deg);
  }
}

@keyframes ai-chat-thinking {
  0%,
  80%,
  100% {
    opacity: 0.35;
    transform: translateY(0);
  }

  40% {
    opacity: 1;
    transform: translateY(-3px);
  }
}

@keyframes ai-chat-shimmer {
  0% {
    transform: translateX(-100%);
  }

  58%,
  100% {
    transform: translateX(100%);
  }
}

@keyframes ai-chat-skeleton {
  0% {
    background-position: 120% 0;
  }

  100% {
    background-position: -120% 0;
  }
}

@keyframes ai-chat-pulse {
  0% {
    box-shadow: 0 0 0 0 rgba(43, 179, 169, 0.3);
    transform: scale(0.92);
  }

  70% {
    box-shadow: 0 0 0 9px rgba(43, 179, 169, 0);
    transform: scale(1);
  }

  100% {
    box-shadow: 0 0 0 0 rgba(43, 179, 169, 0);
    transform: scale(0.92);
  }
}

@keyframes ai-chat-message-in {
  from {
    opacity: 0;
    transform: translateY(5px);
  }

  to {
    opacity: 1;
    transform: translateY(0);
  }
}

@media (max-width: 768px) {
  .ai-chat-shell {
    grid-template-columns: 1fr;
    height: 100%;
    margin: 0;
  }

  .conversation-panel {
    position: absolute;
    top: 0;
    bottom: 0;
    left: 0;
    z-index: 10;
    width: min(86vw, 310px);
    transform: translateX(-100%);
    transition: transform 0.22s ease;
    box-shadow: 12px 0 32px rgba(15, 23, 42, 0.16);

    &--open {
      transform: translateX(0);
    }
  }

  .chat-toolbar {
    min-height: 54px;
    padding: 10px 12px;
  }

  .message-list {
    padding: 16px 12px 12px;
  }

  .message-loading {
    gap: 18px;
    padding: 14px 4px;
  }

  .message-loading__hero {
    width: 100%;
    padding: 12px;
    border-radius: 14px;
  }

  .message-loading__thread {
    gap: 13px;
  }

  .message-loading__bubble {
    width: min(76%, 320px);
  }

  .message-loading__bubble--user {
    width: min(68%, 280px);
  }

  .welcome-card {
    min-height: 260px;
    padding: 0 6px;
  }

  .welcome-prompts {
    justify-content: flex-start;
  }

  .welcome-prompt {
    width: 100%;
    border-radius: 12px;
    text-align: left;
  }

  .message-stack {
    max-width: calc(100% - 52px);
  }

  .message-bubble {
    padding: 11px 12px;
  }

  .image-preview-bar {
    padding: 8px 12px 0;
  }

  .composer-wrap {
    padding: 10px 12px max(12px, env(safe-area-inset-bottom));
  }

  .composer {
    gap: 8px;
    min-height: 94px;
    padding: 10px 12px 9px;
    border-radius: 20px;
  }

  .composer__actions {
    align-items: flex-end;
  }

  .composer__left {
    flex-wrap: wrap;
  }
}
</style>
