File size: 5,335 Bytes
b8dc493
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
<script>

    import { tick } from "svelte";

    import SvelteMarkdown from "svelte-markdown";

    import botImage from "./assets/images/bot.jpeg";

    import meImage from "./assets/images/me.jpeg";

    import MdLink from "./lib/MdLink.svelte";

    import External from "./lib/External.svelte";

    import { chatStates, chatStore } from "./lib/chat.store.js";

    import Modal from "./lib/Modal.svelte";

    import { generationStore } from "./lib/generation.store";



    let ragMode = false;

    let question = "How can I calculate age from date of birth in Cypher?";

    let shouldAutoScroll = true;

    let input;

    let senderImages = { bot: botImage, me: meImage };

    let generationModalOpen = false;



    function send() {

        chatStore.send(question, ragMode);

        question = "";

    }



    function scrollToBottom(node, _) {

        const scroll = () => node.scrollTo({ top: node.scrollHeight });

        scroll();

        return { update: () => shouldAutoScroll && scroll() };

    }



    function scrolling(e) {

        shouldAutoScroll = e.target.scrollTop + e.target.clientHeight > e.target.scrollHeight - 55;

    }



    function generateTicket(text) {

        generationStore.generate(text);

        generationModalOpen = true;

    }



    $: $chatStore.state === chatStates.IDLE && input && focus(input);

    async function focus(node) {

        await tick();

        node.focus();

    }

    // send();

</script>

<main class="h-full text-sm bg-gradient-to-t from-indigo-100 bg-fixed overflow-hidden">

    <div on:scroll={scrolling} class="flex h-full flex-col py-12 overflow-y-auto" use:scrollToBottom={$chatStore}>

        <div class="w-4/5 mx-auto flex flex-col mb-32">

            {#each $chatStore.data as message (message.id)}

                <div

                    class="max-w-[80%] min-w-[40%] rounded-lg p-4 mb-4 overflow-x-auto bg-white border border-indigo-200"

                    class:self-end={message.from === "me"}

                    class:text-right={message.from === "me"}

                >

                    <div class="flex flex-row gap-2">

                        {#if message.from === "me"}

                            <button

                                aria-label="Generate a new internal ticket from this question"

                                title="Generate a new internal ticket from this question"

                                on:click={() => generateTicket(message.text)}

                                class="w-6 h-6 flex flex-col justify-center items-center border rounded border-indigo-200"

                                ><External --color="#ccc" --hover-color="#999" /></button

                            >

                        {/if}

                        <div

                            class:ml-auto={message.from === "me"}

                            class="relative w-12 h-12 border border-indigo-200 rounded flex justify-center items-center overflow-hidden"

                        >

                            <img src={senderImages[message.from]} alt="" class="rounded-sm" />

                        </div>

                        {#if message.from === "bot"}

                            <div class="text-sm">

                                <div>Model: {message.model ? message.model : ""}</div>

                                <div>RAG: {message.rag ? "Enabled" : "Disabled"}</div>

                            </div>

                        {/if}

                    </div>

                    <div class="mt-4"><SvelteMarkdown source={message.text} renderers={{ link: MdLink }} /></div>

                </div>
            {/each}
        </div>
        <div class="text-sm w-full fixed bottom-16">

            <div class="shadow-lg bg-indigo-50 rounded-lg w-4/5 xl:w-2/3 2xl:w-1/2 mx-auto">

                <div class="rounded-t-lg px-4 py-2 font-light">

                    <div class="font-semibold">RAG mode</div>

                    <div class="">

                        <label class="mr-2">

                            <input type="radio" bind:group={ragMode} value={false} /> Disabled

                        </label>

                        <label>

                            <input type="radio" bind:group={ragMode} value={true} /> Enabled

                        </label>

                    </div>

                </div>

                <form class="rounded-md w-full bg-white p-2 m-0" on:submit|preventDefault={send}>

                    <input

                        placeholder="What coding related question can I help you with?"

                        disabled={$chatStore.state === chatStates.RECEIVING}

                        class="text-lg w-full bg-white focus:outline-none px-4"

                        bind:value={question}

                        bind:this={input}

                        type="text"

                    />

                </form>

            </div>
        </div>
    </div>
</main>
{#if generationModalOpen}
    <Modal title="my title" text="my text" on:close={() => (generationModalOpen = false)} />
{/if}

<style>

    :global(pre) {

        @apply bg-gray-100 rounded-lg p-4 border border-indigo-200;

    }

    :global(code) {

        @apply text-indigo-500;

    }

</style>