zhaowei
2024-08-07 7e465ce011a27b730d529fd912eae5f9552520b4
src/views/ai/LanguageModel.vue
@@ -2,37 +2,102 @@
  <div class="page-container">
    <div class="outer-container">
      <div class="left-container">
        <div><img src="@/assets/page/languageModel/logo.png" style="width: 100%"></div>
        <a-button style="margin: 20px 0 10px" @click="createNewConversation">新增会话</a-button>
        <div class="logo-container"><img src="@/assets/page/languageModel/logo.png"></div>
        <div class="manage-history-container">
          <div @click="createNewConversation"
               :class="[isAtNewConversation?'create-history-container-active':'',isDeletingBatch?'fold-create-history-container':'expand-create-history-container']"
               class="create-new-conversation">
            <a-icon type="plus"></a-icon>
            <span v-if="!isDeletingBatch">新增会话</span>
          </div>
          <div @click="expandDeleteBatchContainer"
               :class="[isDeletingBatch?'expand-delete-batch-container':'fold-delete-batch-container',chatHistoryList.length===0?'disable-expand':'']"
               class="delete-batch-container">
            <a-icon type="delete" v-if="!isDeletingBatch"/>
            <template v-else>
              <div class="expand-delete-batch-inner">
                <div class="select-all-button">
                  <label class="checkbox-custom">
                    <input type="checkbox" @change="allHistoryCheckedChange"
                           id="select-all-checkbox"></input>
                    <span class="check-mark"></span>
                    <div>全选</div>
                  </label>
                </div>
                <div class="split-line"></div>
                <a-popover placement="top" :visible="deleteBatchPopVisible" trigger="click"
                           :getPopupContainer="node=>node.parentNode" overlayClassName="delete-batch-popover">
                  <template slot="content">
                    <div class="popover-content">
                      <div>删除后无法恢复,是否继续删除?</div>
                      <div>
                        <button class="cancel-delete-button" @click="deleteBatchPopVisible=false">取消</button>
                        <button id="confirm-delete-batch-button" @click="confirmDeleteBatchConversation">删除
                        </button>
                      </div>
                    </div>
                  </template>
                  <div @click="deleteBatchConversation" id="delete-batch-button"
                       :class="[checkedConversationIdList.length?'able-delete-button':'disable-delete-button']">
                    <a-icon type="delete"/>
                    <div>删除</div>
                  </div>
                </a-popover>
                <div class="split-line"></div>
                <div @click.stop="cancelDeleteBatchConversation">
                  <a-icon type="close"/>
                  <div>退出</div>
                </div>
              </div>
            </template>
          </div>
        </div>
        <div class="chat-history-container">
          <div v-for="(item,index) in chatHistoryList" :key="index"
               :class="[item.id===activeHistoryIndex?'single-history-active':'']"
               @click="switchToCurrentConversation(item,index)" @mouseenter="item.iconVisible=true"
          <div v-for="(item,index) in chatHistoryList" :key="item.id"
               :class="[item.id===activeHistoryIndex?'single-history-active':'',item.inputVisible?'input-visible-class':'']"
               @click="switchToCurrentConversation(item,$event)"
               @mouseenter="item.iconVisible=true"
               @mouseleave="item.iconVisible=false">
            <a-popconfirm ok-text="删除" cancel-text="取消" @confirm="confirmDeleteConversation(item,index)"
                          @cancel="cancelDeleteConversation(item)"
                          :visible="item.deletePopVisible" :arrowPointAtCenter="true">
              <template slot="title">
                删除后无法恢复,是否继续删除?
            <!--input放置在popover中无法使用功能-->
            <label v-if="isDeletingBatch" class="checkbox-custom">
              <input type="checkbox" v-model="checkedConversationIdList" :value="item.id"
                     @change="singleHistoryCheckedChange"/>
              <span class="check-mark"></span>
            </label>
            <a-popover placement="top" :visible="item.deletePopVisible" trigger="click"
                       :getPopupContainer="node=>node.parentNode">
              <template slot="content">
                <div class="popover-content">
                  <div>删除后无法恢复,是否继续删除?</div>
                  <div>
                    <button class="cancel-delete-button" @click="cancelDeleteConversation(item,$event)">取消</button>
                    <button @click="confirmDeleteConversation(item,index)" id="delete-conversation-button">删除</button>
                  </div>
                </div>
              </template>
              <template v-if="!item.inputVisible">
                <div class="conversation-title">{{item.title}}</div>
                <div class="icon-container" v-show="item.iconVisible">
                  <a-icon type="edit" @click.stop="editConversationTitle(item,index)"/>
                  <a-icon type="delete" @click.stop="deleteConversation(item,index)"/>
                <div class="icon-container hover-icon-container" v-show="!isDeletingBatch">
                  <a-icon type="edit" @click.stop="editConversationTitle(item)"/>
                  <a-icon type="delete" @click.stop="deleteConversation(item)"
                          @blur="cancelDeleteConversation(item,$event)"/>
                </div>
              </template>
              <template v-else>
                <input id="edit-input" v-model="editedConversationTitle"
                       @keydown.enter="confirmEditConversationTitle(item)" maxlength="15"></input>
                       @keydown.enter="confirmEditConversationTitle(item,$event)"
                       @blur="cancelEditConversationTitle(item,$event)"
                       maxlength="15">
                </input>
                <div class="icon-container">
                  <a-icon type="check" @click.stop="confirmEditConversationTitle(item)"/>
                  <a-icon type="close" @click.stop="cancelEditConversationTitle(item)"/>
                  <a-icon type="check" @click.stop="confirmEditConversationTitle(item,$event)"/>
                  <a-icon type="close"/>
                </div>
              </template>
            </a-popconfirm>
            </a-popover>
          </div>
        </div>
      </div>
@@ -55,7 +120,7 @@
          </div>
        </div>
        <div class="input-container" :class="[textareaFocused?'input-container-active':'']">
          <textarea v-model="inputQuestion" placeholder="Enter发送,Shift+Enter换行"
          <textarea v-model="inputQuestion" :placeholder="textareaPlaceholder"
                    @keydown.enter="sendQuestion($event)" @focus="textareaFocused=true"
                    @blur="textareaFocused=false"></textarea>
          <img src="@/assets/page/languageModel/send-message.png" @click="sendQuestion($event)" v-if="!isResponding">
@@ -73,7 +138,8 @@
  import { randomUUID } from '@/utils/util'
  message.config({
    maxCount: 1
    maxCount: 1,
    duration: 2
  })
  export default {
@@ -82,9 +148,11 @@
    data() {
      return {
        chatHistoryList: [],
        checkedConversationIdList: [],
        deleteBatchPopVisible: false,
        currentConversation: {
          'id': '683a65fd-8feb-4446-ad32-714c4785f667',
          'messages': [
          id: '683a65fd-8feb-4446-ad32-714c4785f667',
          messages: [
            {
              'role': 'user',
              'content': '你是谁?你是谁你是谁你是谁你是谁你是谁你是谁你是谁你是谁你是谁你是谁你是谁你是谁你是谁你是谁你是谁你是谁你是谁'
@@ -118,12 +186,15 @@
              'content': '我能站在未来视角帮助同学们做好生涯规划指导。'
            }
          ],
          'stream': false,
          'max_tokens': 500,
          stream: false,
          max_tokens: 500,
          iconVisible: false,
          inputVisible: false,
          deletePopVisible: false
        },
        // currentConversation: {},
        isAtNewConversation: false,
        isDeletingBatch: false,
        activeHistoryIndex: null,
        editingHistoryIndex: null,
        deletingHistoryIndex: null,
@@ -133,15 +204,25 @@
        deletePopVisible: false,
        conversationContainer: null,
        inputQuestion: '',
        textareaPlaceholder: 'Enter发送,Shift+Enter换行',
        textareaFocused: false,
        isResponding: false
      }
    },
    created() {
      this.getConversationByApi()
    watch: {
      deleteBatchPopVisible: {
        handler(val) {
          if (val) {
            document.addEventListener('click', this.handleDocumentClick)
          } else {
            document.removeEventListener('click', this.handleDocumentClick)
          }
        }
      }
    },
    mounted() {
      this.getConversationByApi()
      this.conversationContainer = document.querySelector('.conversation-container')
    },
    methods: {
@@ -152,13 +233,16 @@
        this.currentConversation.title = this.currentConversation.messages[0].content.slice(0, 15)
        this.chatHistoryList.push(this.currentConversation)
        this.activeHistoryIndex = this.chatHistoryList[0].id
        if (!this.chatHistoryList.length) this.textareaPlaceholder = '请尝试问我:你是谁?'
      },
      /* 创建一个新会话 */
      createNewConversation() {
        if (this.isAtNewConversation) this.$message.info('当前已是最新对话')
        this.isAtNewConversation = true
        this.currentConversation = {
          id: randomUUID(),
          title: '未命名对话',
          title: '',
          messages: [],
          stream: true,
          max_tokens: 500,
@@ -166,66 +250,138 @@
          inputVisible: false,
          deletePopVisible: false
        }
        // 退出其他功能
        if (this.editingHistoryIndex !== null) this.cancelEditConversationTitle(this.chatHistoryList.find(item => item.id === this.editingHistoryIndex))
        if (this.deletingHistoryIndex !== null) this.cancelDeleteConversation(this.chatHistoryList.find(item => item.id === this.deletingHistoryIndex))
        this.chatHistoryList.unshift(this.currentConversation)
        this.activeHistoryIndex = this.chatHistoryList[0].id
        console.log(this.chatHistoryList)
        // 新建会话时取消原先被选中的历史对话,更清晰告诉用户现在界面处于新建对话中
        if (this.activeHistoryIndex !== null) this.activeHistoryIndex = null
        this.textareaPlaceholder = '请尝试问我:你是谁?'
      },
      /* 切换至当前点击会话 */
      switchToCurrentConversation(record, index) {
        if (record.inputVisible) return // 当条会话正在被修改时再次点击本条会话无反馈
        this.activeHistoryIndex = record.id
        this.currentConversation = this.chatHistoryList[index]
      /* 点击全选按钮后改变勾选后触发 */
      allHistoryCheckedChange(event) {
        // event.target.indeterminate = true
        if (event.target.checked) {
          this.checkedConversationIdList = this.chatHistoryList.map(item => item.id)
        } else {
          this.checkedConversationIdList = []
        }
        console.log('触发全选', event.target.checked)
      },
        if (this.editingHistoryIndex !== null) {
          this.chatHistoryList.find(item => item.id === this.editingHistoryIndex).inputVisible = false
          this.editingHistoryIndex = null
      /* 点击历史记录中多选框改变勾选后触发 */
      singleHistoryCheckedChange() {
        // 仅在批量删除展开时获取到
        const selectAllCheckbox = document.getElementById('select-all-checkbox')
        if (this.checkedConversationIdList.length > 0) {
          if (this.checkedConversationIdList.length !== this.chatHistoryList.length) {
            console.log('触发未全选中', selectAllCheckbox.indeterminate)
            this.$nextTick(() => selectAllCheckbox.indeterminate = true)
          } else {
            console.log('触发全被选中', selectAllCheckbox)
            // document.getElementById('select-all-checkbox').indeterminate = false
            this.$nextTick(() => {
              selectAllCheckbox.indeterminate = false
              selectAllCheckbox.checked = true
            })
          }
        } else {
          selectAllCheckbox.indeterminate = false
          selectAllCheckbox.checked = false
        }
      },
      editConversationTitle(record, index) {
        // 仅开启最后一次点击编辑按钮后的输入框
        if (this.editingHistoryIndex !== null && this.editingHistoryIndex !== record.id) this.chatHistoryList.find(item => item.id === this.editingHistoryIndex).inputVisible = false
        // 进入编辑后关闭所有确认删除弹窗
        if (this.deletingHistoryIndex !== null) this.cancelDeleteConversation(this.chatHistoryList.find(item => item.id === this.deletingHistoryIndex))
        console.log('进入修改', record)
        this.editingHistoryIndex = record.id
        record.inputVisible = true
        this.editedConversationTitle = record.title
        this.$nextTick(() => {
          document.getElementById('edit-input').focus()
      /* 点击批量删除会话图标时展开批量管理区域 */
      expandDeleteBatchContainer() {
        if (this.chatHistoryList.length === 0) return
        this.isDeletingBatch = true
      },
      /* 点击红色图标批量删除按钮后触发 */
      deleteBatchConversation() {
        if (!this.checkedConversationIdList.length) return
        this.deleteBatchPopVisible = !this.deleteBatchPopVisible
      },
      /* 点击文档空白关闭批量删除popover */
      handleDocumentClick(e) {
        const popover = document.querySelector('.delete-batch-popover')
        const button = document.getElementById('delete-batch-button')
        if (popover && !popover.contains(e.target) && !button.contains(e.target)) this.deleteBatchPopVisible = false
      },
      /* 确认批量删除对话 */
      confirmDeleteBatchConversation() {
        if (this.checkedConversationIdList.includes(this.activeHistoryIndex)) this.createNewConversation()
        this.chatHistoryList = this.chatHistoryList.filter(item => !this.checkedConversationIdList.includes(item.id))
        this.$message.success('删除成功')
        const timer = setTimeout(() => {
          this.cancelDeleteBatchConversation()
          clearTimeout(timer)
        })
      },
      deleteConversation(record, index) {
      /* 取消批量删除会话功能 */
      cancelDeleteBatchConversation() {
        this.deleteBatchPopVisible = false
        this.isDeletingBatch = false
        this.checkedConversationIdList = []
      },
      /* 切换至当前点击会话 */
      switchToCurrentConversation(record, event = {}) {
        if (event.target && event.target.type === 'checkbox') return //点击多选按钮避免传递给此事件
        if (record.inputVisible) return // 当条会话正在被修改时再次点击本条会话无反馈
        // 关闭点击编辑按钮后的输入框
        if (this.editingHistoryIndex !== null && this.editingHistoryIndex !== record.id) this.cancelEditConversationTitle(this.chatHistoryList.find(item => item.id === this.editingHistoryIndex))
        // 关闭所有确认删除弹窗
        if (this.deletingHistoryIndex !== null) this.cancelDeleteConversation(this.chatHistoryList.find(item => item.id === this.deletingHistoryIndex))
        this.activeHistoryIndex = record.id
        this.currentConversation = this.chatHistoryList.find(item => item.id === record.id)
        this.isAtNewConversation = false
        // 切换对话时更改提问输入框提示文字
        if (record.messages.length > 0) this.textareaPlaceholder = 'Enter发送,Shift+Enter换行'
      },
      /* 点击编辑会话标题按钮时触发 */
      editConversationTitle(record) {
        // 仅开启最后一次点击编辑按钮后的输入框
        if (this.editingHistoryIndex !== null && this.editingHistoryIndex !== record.id) this.cancelEditConversationTitle(this.chatHistoryList.find(item => item.id === this.editingHistoryIndex))
        // 进入编辑后关闭所有确认删除弹窗
        if (this.deletingHistoryIndex !== null) this.cancelDeleteConversation(this.chatHistoryList.find(item => item.id === this.deletingHistoryIndex))
        this.editingHistoryIndex = record.id
        record.inputVisible = true
        this.editedConversationTitle = record.title
        this.$nextTick(() => document.getElementById('edit-input').focus())
      },
      /* 点击单个删除会话按钮时触发 */
      deleteConversation(record) {
        // 点击删除按钮时关闭所有正在编辑的输入框
        if (this.editingHistoryIndex !== null) {
          this.cancelEditConversationTitle(this.chatHistoryList.find(item => item.id === this.editingHistoryIndex))
          return
        }
        // 仅可使用最后一次点击删除按钮的功能
        if (this.deletingHistoryIndex !== null && this.deletingHistoryIndex !== record.id) this.chatHistoryList.find(item => item.id === this.deletingHistoryIndex).deletePopVisible = false
        record.deletePopVisible = true
        if (this.deletingHistoryIndex !== null && this.deletingHistoryIndex !== record.id) this.cancelDeleteConversation(this.chatHistoryList.find(item => item.id === this.deletingHistoryIndex))
        record.deletePopVisible = !record.deletePopVisible
        this.deletingHistoryIndex = record.id
      },
      confirmEditConversationTitle(record) {
      /* 确认编辑会话标题 */
      confirmEditConversationTitle(record, event) {
        record.title = this.editedConversationTitle
        record.inputVisible = false
        this.editingHistoryIndex = null
        this.editedConversationTitle = ''
        this.cancelEditConversationTitle(record, event)
      },
      /* 确认删除会话时触发 */
      confirmDeleteConversation(record, index) {
        this.chatHistoryList = this.chatHistoryList.filter(item => item.id !== this.deletingHistoryIndex)
        if (this.chatHistoryList.length > 0) {
          // 判断当前会话是不是要删除的会话
          // TODO 由于目前没有唯一标识ID,暂时使用历史记录集合长度作为ID使用,后期必须调整
          if (this.activeHistoryIndex === record.id) {
            if (this.chatHistoryList[index]) {
              console.log('删除非最后一条')
@@ -239,19 +395,26 @@
          }
        } else {
          console.log('删除前只有一条')
          this.currentConversation = {}
          this.activeHistoryIndex = null
          this.createNewConversation()
        }
        record.deletePopVisible = false
        this.deletingHistoryIndex = null
        this.$message.success('删除成功!')
      },
      cancelEditConversationTitle(record) {
      /* 取消编辑会话标题时触发 */
      cancelEditConversationTitle(record, event) {
        // 失去焦点事件时若点击的元素是确认编辑按钮则不进行失去焦点事件,直接进入确认编辑事件
        if (event.relatedTarget && event.relatedTarget.className === 'anticon anticon-check') return
        record.inputVisible = false
        this.editingHistoryIndex = null
      },
      cancelDeleteConversation(record) {
      /* 取消删除会话时触发 */
      cancelDeleteConversation(record, event) {
        // 失去焦点事件时若点击的元素是确认删除按钮则不进行失去焦点事件,直接进入确认删除事件
        if (event.relatedTarget && event.relatedTarget.id === 'delete-conversation-button') return
        record.deletePopVisible = false
        this.deletingHistoryIndex = null
      },
@@ -270,7 +433,6 @@
            this.$message.error('请等待机器人回复后再发送哦~')
            return
          }
          if (!this.inputQuestion) {
            this.$message.error('你没有输入内容哦')
            return
@@ -281,6 +443,14 @@
            content: this.inputQuestion
          }
          this.currentConversation.messages.push(newQuestion)
          // 当新建对话时需先提问再讲对话加入到历史记录中
          if (this.currentConversation.messages.length === 1) {
            this.currentConversation.title = newQuestion.content.slice(0, 15)
            this.chatHistoryList.unshift(this.currentConversation)
            if (this.isDeletingBatch) this.singleHistoryCheckedChange()
            this.switchToCurrentConversation(this.currentConversation)
          }
          this.isResponding = true
          const response = {
@@ -302,14 +472,17 @@
</script>
<style scoped lang="less">
  @background: rgba(255, 255, 255, .8);
  @main-container-background: rgba(255, 255, 255, .8);
  @container-border-radius: 12px;
  @container-padding: 10px;
  @single-history-border: 3px solid #ABC0CC;
  @single-conversation-border: 1px solid #7295AB;
  @single-history-edit-border: 3px solid #ABC0CC;
  @single-history-hover-background: #f1f1f1;
  @single-history-active-background: #e5ebed;
  @input-container-border: 3px solid #B8CAD5;
  @conversation-content-container-box-shadow: 2px 2px 10px 0px #eeeeee;
  @input-container-box-shadow: 2px 2px 10px 0px #7295AB;
  @user-question-background: #e5ebed;
  @assistant-answer-background: #F0F5F5;
  @conversation-content-container-box-shadow: 2px 2px 10px 0px #ddd;
  @input-container-box-shadow: 2px 2px 10px 0px #ABC0CC;
  .page-container {
    display: flex;
@@ -317,6 +490,7 @@
    align-items: center;
    font-size: 18px;
    height: 100%;
    font-family: ali_r_main;
    .outer-container {
      width: 100%;
@@ -328,7 +502,7 @@
      .left-container {
        width: 20%;
        height: 100%;
        background-color: @background;
        background-color: @main-container-background;
        border-radius: @container-border-radius;
        padding: @container-padding;
        margin-right: 25px;
@@ -336,8 +510,127 @@
        flex-direction: column;
        justify-content: space-between;
        .logo-container {
          height: 80px;
          img {
            width: 100%;
            height: 100%;
          }
        }
        .manage-history-container {
          margin: 20px 0 15px;
          display: flex;
          & > div {
            border: 1px solid #d9d9d9;
            display: flex;
            justify-content: center;
            align-items: center;
            cursor: pointer;
            color: #585258;
            transition: box-shadow .2s ease-in-out;
            &:hover {
              box-shadow: @conversation-content-container-box-shadow;
            }
          }
          .create-new-conversation {
            margin-right: 10px;
            &.create-history-container-active {
              border: 1px solid transparent;
              background-color: @single-history-active-background;
              &:hover {
                box-shadow: none;
              }
            }
            &.fold-create-history-container {
              flex: none;
              width: 40px;
              height: 40px;
              border-radius: 50%;
            }
            &.expand-create-history-container {
              flex: 1;
              height: 100%;
              border-radius: 20px;
              .anticon {
                margin-right: 10px;
              }
            }
          }
          .delete-batch-container {
            &.expand-delete-batch-container {
              flex: 1;
              border-radius: 20px;
              cursor: default;
              box-shadow: none;
              .expand-delete-batch-inner {
                display: flex;
                justify-content: space-evenly;
                align-items: center;
                height: 100%;
                width: 100%;
                & > div {
                  display: flex;
                  align-items: center;
                  &.select-all-button {
                    height: 100%;
                  }
                  &.able-delete-button {
                    color: #D9737A;
                  }
                  &.disable-delete-button {
                    color: #bbb;
                    cursor: not-allowed !important;
                  }
                  &:not(.split-line) {
                    cursor: pointer;
                  }
                  .anticon {
                    margin-right: 10px;
                  }
                  &.split-line {
                    width: 1px;
                    background-color: #000;
                    height: 50%;
                  }
                }
              }
            }
            &.fold-delete-batch-container {
              width: 40px;
              height: 40px;
              border-radius: 50%;
            }
            &.disable-expand {
              cursor: not-allowed;
              &:hover {
                box-shadow: none;
              }
            }
          }
        }
        .chat-history-container {
          height: 582px;
          flex: 1;
          overflow: auto;
          & > div {
@@ -345,16 +638,26 @@
            border-radius: 10px;
            padding: 10px 20px;
            cursor: pointer;
            display: flex;
            align-items: center;
            & > span {
              display: flex;
              justify-content: space-between;
              position: relative;
              flex: 1;
              .conversation-title {
                flex: 1;
                white-space: nowrap;
                text-overflow: ellipsis;
                overflow: hidden;
                white-space: nowrap;
              }
              .hover-icon-container {
                // display:none的消失方式会让点击删除弹出的popover卡片无法点击空白处消失
                opacity: 0;
                position: absolute;
                right: 0;
              }
              .icon-container {
@@ -365,12 +668,12 @@
                  margin-left: 5px;
                  &:hover {
                    background-color: rgba(0, 0, 0, .2);
                    background-color: rgba(0, 0, 0, .1);
                  }
                }
              }
              input {
              #edit-input {
                flex: 1;
                height: 100%;
                border: none;
@@ -386,12 +689,153 @@
            }
            &:hover {
              background-color: #eee;
              background-color: @single-history-hover-background;
              .icon-container {
                opacity: 1;
                background-color: @single-history-hover-background;
              }
            }
            &.single-history-active {
              // border: @single-history-border;
              background-color: #e5ebed;
              background-color: @single-history-active-background;
              &:hover {
                .icon-container {
                  background-color: @single-history-active-background;
                }
              }
            }
            &.input-visible-class {
              background-color: @main-container-background;
              border: @single-history-edit-border;
              &:hover {
                .icon-container {
                  background-color: transparent;
                }
              }
            }
          }
        }
        /deep/ .ant-popover {
          padding-bottom: 10px;
          .ant-popover-arrow {
            display: none;
          }
          .ant-popover-inner {
            border-radius: @container-border-radius;
            box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
            .ant-popover-inner-content {
              padding: 12px 16px;
              .popover-content {
                font-size: 16px;
                font-weight: bold;
                display: flex;
                flex-direction: column;
                align-items: center;
                font-family: ali_r_main;
                & > div {
                  width: 100%;
                  display: flex;
                  justify-content: space-evenly;
                  margin-top: 10px;
                  button {
                    border: none;
                    padding: 5px 30px;
                    border-radius: 20px;
                    font-weight: bold;
                    cursor: pointer;
                    outline: none;
                  }
                  .cancel-delete-button {
                    background-color: #D9D9D9;
                  }
                  #delete-conversation-button, #confirm-delete-batch-button {
                    background-color: #7295AB;
                    color: #fff;
                    font-weight: normal;
                  }
                }
              }
            }
          }
        }
        .checkbox-custom {
          height: 100%;
          position: relative;
          display: flex;
          justify-content: flex-end;
          align-items: center;
          cursor: pointer;
          -webkit-user-select: none;
          -moz-user-select: none;
          -ms-user-select: none;
          user-select: none;
          input {
            position: absolute;
            opacity: 0;
            cursor: pointer;
            height: 0;
            width: 0;
            &:checked ~ .check-mark {
              background-color: #7295AB;
              border: none;
              &:after {
                display: block;
              }
            }
            &:indeterminate ~ .check-mark {
              background-color: #fff;
              border: 1px solid #818181;
              &:after {
                display: block;
                width: 10px;
                height: 3px;
                background: #818181;
                border-radius: 12px;
              }
            }
          }
          .check-mark {
            position: relative;
            height: 16px;
            width: 16px;
            background-color: transparent;
            border: 1px solid #818181;
            border-radius: 50%;
            margin-right: 10px;
            &:after {
              content: "";
              position: absolute;
              display: none;
              top: 50%;
              left: 50%;
              width: 8px;
              height: 8px;
              border-radius: 50%;
              background: #fff;
              transform: translate(-50%, -50%);
            }
            &:hover {
              border: 3px solid #7295AB;
            }
          }
        }
@@ -407,7 +851,7 @@
        .conversation-container {
          height: 570px;
          background-color: @background;
          background-color: @main-container-background;
          padding: @container-padding*2;
          border-radius: @container-border-radius;
          margin-bottom: 25px;
@@ -422,10 +866,17 @@
              &.user-question {
                align-items: flex-end;
                .content {
                  background-color: @user-question-background;
                }
              }
              &.assistant-answer {
                align-items: flex-start;
                .content {
                  background-color: @assistant-answer-background;
                }
              }
              .avatar {
@@ -443,7 +894,6 @@
                box-shadow: @conversation-content-container-box-shadow;
                border-radius: @container-border-radius;
                padding: @container-padding;
                background-color: #e5ebed;
              }
            }
@@ -455,7 +905,7 @@
        .input-container {
          flex: 1;
          background-color: @background;
          background-color: @main-container-background;
          border-radius: @container-border-radius;
          padding: @container-padding*2;
          border: 3px solid transparent;
@@ -495,6 +945,5 @@
        }
      }
    }
  }
</style>