Source: components/chat.vue

<template>
    <Header :name="$route.name" :title="$t($route.name)" />
    <q-btn style="position:absolute; top:40px; right:5px; z-index: 9999;" @click="toolbarOpen = true" padding="xs"
        icon="settings" size="sm" />
    <Toolbar ref="toolbar" :open="toolbarOpen" width="300px" top="75px" align="right" @close="toolbarOpen = false"
        bg-color="whitesmoke">
        <component :is="'ChatToolbar'" bgColor="whitesmoke" :data="$data" />
    </Toolbar>
    <div id="chat-container">
        <q-scroll-area @scroll="scrolled" ref="scroll" :style="{ height: ($q.screen.height - 75 - 65) + 'px' }">
            <div v-for="msg of messages" key="id" class="row">
                <div v-if="msg.parent_id == null" class="row q-ml-sm q-mt-sm">
                    <div class="prompt"><span v-html="msg.message" />
                        <q-btn flat style="position:absolute; right:50px;" round size="xs" icon="edit"
                            @click="message = msg.message"></q-btn>
                        <q-btn v-if="msg.id && msg.id != 0" flat style="position:absolute; right:30px;" round size="xs"
                            icon="delete" @click="deleteMessage(msg.id)"></q-btn>
                    </div>
                </div>
                <div v-else class="row response" v-html="msg.message.replaceAll('\\', '')">
                </div>
            </div>
        </q-scroll-area>
    </div>
    <div class="row q-ml-sm">
        <div class="col-11">
            <q-input v-model="message" :label="$t('Type your prompt')" @keyup.enter="sendMessage" />
        </div>
        <div class="col-1 right-item">
            <q-btn flat class="q-ma-sm" round size="sm" icon="send" @click="sendMessage"></q-btn>
        </div>
    </div>
</template>

<script>
/**
 * Chat component
 * @component components/chat
 * @description Chat component
 * @example
 *  <Chat />
 */

import Header from "./header.vue";
import Toolbar from "./toolbar.vue";
import ChatToolbar from "./chat-toolbar.vue";
export default {
    name: "Chat",
    components: {
        Header,
        Toolbar,
        ChatToolbar
    },
    data() {
        return {
            storable: ["selectedModel", "saveMessages"],
            message: '',
            models: [],
            messages: [],
            selectedModel: { value: 1, label: "mixtral" },
            saveMessages: false,
            toolbarOpen: false,
        };
    },
    beforeRouteEnter(to, from, next) {
        next(vm => { vm.init(to.name); });
    },
    beforeRouteUpdate(to, from, next) {
        this.routeKey++;
        this.init(to.name);
        next();
    },
    beforeRouteLeave(to, from, next) {
        this.saveStorable(this, from.name);
        next();
    },
    async mounted() {
    },
    methods: {
        async deleteMessage(id) {
            let response = await this.post("Chat/DeleteMessage", { id: id.toString() });
            if (response != null) {
                let index = this.messages.findIndex(m => m.id == id);
                this.messages.splice(index, 1);
                index = this.messages.findIndex(m => m.parent_id == id);
                this.messages.splice(index, 1);
            };
        },
        async init(name) {
            this.loadStorable(this, name);
            this.models = this.$store.catalogs.aiModels; // await this.get("Chat/GetAIModels");
            this.messages = await this.get("Chat/GetMessages");
            this.scrollToBottom();
        },
        scrolled(info) {
        },
        scrollToBottom() {
            this.$nextTick(() => {
                //this.$refs.scroll.setScrollPercentage("vertical", 1., 0);
                this.$refs.scroll.setScrollPosition("vertical", 9999999);
            });
        },
        async sendMessage() {
            if (this.message.trim() !== '') {
                this.messages.push({ id: null, parent_id: null, message: this.message });
                let response = await this.post("Chat/SendMessage", { message: this.message, saveMessages: this.saveMessages.toString(), model: this.selectedModel.label });
                if (response != null) {
                    this.messages.push({
                        id: response.id_response,
                        message: response.content,
                        parent_id: response.id_prompt
                    });
                    let pm = this.messages.find(m => m.id == null);
                    pm.id = response.id_prompt;
                    this.message = '';
                    this.scrollToBottom();
                };
            }
        }
    }
};
</script>

<style scoped>
#chat-container {
    display: absolute;
    width: 100%;
}

.prompt {
    color: teal;
}

.response {
    margin-left: 20px;
}

.input {
    position: absolute;
    bottom: 0px;
    height: 50px;
    background-color: white;
}
</style>