Skip to content

Commit

Permalink
Add settings, directives and component changes
Browse files Browse the repository at this point in the history
  • Loading branch information
jlpereira committed Jan 26, 2024
1 parent 63d45c5 commit 994246a
Show file tree
Hide file tree
Showing 20 changed files with 377 additions and 82 deletions.
103 changes: 61 additions & 42 deletions src/App.vue
Original file line number Diff line number Diff line change
@@ -1,81 +1,100 @@
<template>
<div class="flex flex-col h-full justify-between">
<div
class="max-w-7xl container mx-auto flex justify-between items-center px-4"
>
<h1 class="text-4xl py-6 font-bold">BHL Quest AI</h1>
<button
type="button"
class="rounded-full bg-primary-color text-primary-text p-2"
title="New chat"
@click="() => (messages = [])"
<div class="relative z-0 flex h-full w-full overflow-hidden">
<PanelSettings />
<div class="flex flex-col h-full justify-between w-full">
<div class="w-full bg-secondary-color">
<Navbar />
</div>
<div
ref="chatContainer"
class="overflow-y-auto h-full border-t scroll scrollbar-gutter-stable"
>
<IconPlus class="w-5 h-5" />
</button>
</div>
<ChatView
class="max-w-7xl container mx-auto px-6 py-4"
v-if="messages.length"
:messages="messages"
:is-loading="isAsking"
/>
<EmptyContainer
v-else
class="max-w-7xl container mx-auto h-full flex-col flex justify-center px-4"
@select="askBHL"
/>
</div>

<div
ref="chatContainer"
class="overflow-y-auto h-full border-t scroll scrollbar-gutter-stable"
>
<ChatView
class="max-w-7xl container mx-auto px-6 py-4"
v-if="messages.length"
:messages="messages"
:is-loading="isAsking"
/>
<EmptyContainer
v-else
class="max-w-7xl container mx-auto h-full flex-col flex justify-center px-4"
@select="askBHL"
<UserInput
class="max-w-7xl container mx-auto px-4"
:disabled="isAsking"
@submit="askBHL"
/>
</div>

<UserInput
class="max-w-7xl container mx-auto px-4"
:disabled="isAsking"
@submit="askBHL"
/>
</div>
</template>

<script setup lang="ts">
import { defineOptions, ref, nextTick } from 'vue'
import { defineOptions, ref, nextTick, onBeforeMount } from 'vue'
import { makeIAMessage, makeUserMessage } from '@/adapters'
import { BHLQuest } from './services'
import { MESSAGE_TYPE } from './constants'
import { useSettings } from './store'
import EmptyContainer from '@/components/EmptyContainer.vue'
import ChatView from '@/components/Chat/ChatView.vue'
import UserInput from '@/components/Chat/User/ChatUserInput.vue'
import IconPlus from './components/Icon/IconPlus.vue'
import Navbar from '@/components/Navbar.vue'
import PanelSettings from '@/components/Setting/PanelSettings.vue'
defineOptions({
name: 'ChatBHL'
})
const messages = ref<any[]>([])
const isAsking = ref<boolean>(false)
const chatContainer = ref(null)
const chatContainer = ref<HTMLElement | null>(null)
const { keepChat, parameters } = useSettings()
async function askBHL(question: string) {
isAsking.value = true
messages.value.push(makeUserMessage(question))
const userMessage = makeUserMessage(question)
messages.value = keepChat.value
? [...messages.value, userMessage]
: [userMessage]
nextTick(scrollToBottom)
isAsking.value = true
try {
const response = await BHLQuest.ask({ question })
const response = await BHLQuest.ask({ question, ...parameters.value })
messages.value.push(makeIAMessage(response))
isAsking.value = false
nextTick(scrollToBottom)
isAsking.value = false
} catch (e) {
isAsking.value = false
console.log(e)
}
}
function scrollToBottom() {
chatContainer.value.scrollTop = chatContainer.value.scrollHeight
if (chatContainer.value) {
const index = messages.value.findLastIndex(
(m) => m.type === MESSAGE_TYPE.User
)
const element = chatContainer.value.firstChild.children[index]
const distance = element.offsetTop - chatContainer.value.offsetTop - 16
chatContainer.value.scrollTop = distance
}
}
onBeforeMount(() => {
const queryString = window.location.search
const parameters = new URLSearchParams(queryString)
const ask = parameters.get('ask')
if (ask) {
askBHL(ask)
}
})
</script>

<style>
Expand Down
22 changes: 13 additions & 9 deletions src/adapters/makeIAMessage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,15 +10,19 @@ export function makeIAMessage(response: BHLQuestResponse): BHLQuestMessage {
type: MESSAGE_TYPE.IA,
text: response.summary,
date: +new Date(),
references: response.results.map(
(item): BHLQuestMessageReference => ({
link: item.outlink,
text: item.text,
title: item.reference,
pageId: item.pageId,
pageIndex: item.pageIndex,
pages: item.pages
})
references: response.results?.map(
(item): BHLQuestMessageReference =>
({
link: item.outlink,
text: item.text,
title: item.reference,
pageId: item.pageId,
pageIndex: item.pageIndex,
pages: item.pages.map((item) => ({
id: item.id,
pageNumber: item.pageSeq
}))
} || [])
)
}
}
4 changes: 3 additions & 1 deletion src/assets/stylesheets/style.css
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,11 @@
--base-background-color: 255, 255, 255;
--base-border-color: 156, 163, 175;
--btn-disabled-color: 148, 163, 184;
--primary-color: 8, 145, 178;
--primary-color: 61, 144, 200;
--primary-text-color: 255, 255, 255;
--secondary-color: 246, 237, 226;
--text-color: 33, 53, 71;
--text-highlight-color: 241, 228, 215;

font-family: Inter, system-ui, Avenir, Helvetica, Arial, sans-serif;
line-height: 1.5;
Expand Down
30 changes: 17 additions & 13 deletions src/components/Chat/BHLQuest/BHLQuestMessage.vue
Original file line number Diff line number Diff line change
Expand Up @@ -2,22 +2,26 @@
<div>
<div class="font-bold text-lg text-primary-color">BHL Quest</div>
<div
v-if="message.text"
class="whitespace-pre-wrap"
v-html="message.text"
/>
<h3 class="my-2 font-semibold">References</h3>
<ul>
<li
v-for="(reference, index) in message.references"
:key="message.date"
class="my-2"
>
<ChatAIReference
:reference="reference"
:index="index"
/>
</li>
</ul>
<div v-else>I cannot provide an answer to your question.</div>
<template v-if="message.references">
<h3 class="my-2 font-semibold">References</h3>
<ul>
<li
v-for="(reference, index) in message.references"
:key="message.date"
class="my-2"
>
<ChatAIReference
:reference="reference"
:index="index"
/>
</li>
</ul>
</template>
</div>
</template>

Expand Down
59 changes: 48 additions & 11 deletions src/components/Chat/BHLQuest/BHLQuestReference.vue
Original file line number Diff line number Diff line change
Expand Up @@ -26,21 +26,43 @@
{{ reference.title }}
</a>
</div>
<div
v-if="isExpanded"
class="mt-2"
>
<ImageViewer
:page-ids="reference.pages"
:page-index="reference.pageIndex"
/>
</div>
<template v-if="isExpanded">
<div
v-for="(text, index) in reference.text"
class="py-4 px-8"
>
<a
class="text-primary-color"
:href="`https://www.biodiversitylibrary.org/page/${
reference.pages[reference.pageIndex + index].id
}`"
>Page #{{
reference.pages[reference.pageIndex + index].pageNumber
}}</a
>
<component
:is="textComponent"
class="text-wrap font-main"
v-html="text.trim()"
/>
</div>
<div
v-if="isImageViewerVisible"
class="mt-2"
>
<ImageViewer
:page-ids="reference.pages"
:page-index="reference.pageIndex"
/>
</div>
</template>
</div>
</template>

<script setup lang="ts">
import { nextTick, ref, watch } from 'vue'
import { nextTick, ref, watch, computed } from 'vue'
import { BHLQuestMessageReference } from '@/types'
import { useSettings } from '@/store'
import IconChevronLeft from '@/components/Icon/IconChevronLeft.vue'
import IconChevronDown from '@/components/Icon/IconChevronDown.vue'
import ImageViewer from '@/components/ImageViewer/ImageViewer.vue'
Expand All @@ -52,14 +74,29 @@ interface Props {
const props = defineProps<Props>()
const referenceRef = ref<HTMLElement | null>(null)
const { referencePreformattedText, referenceExpanded } = useSettings()
const isExpanded = ref(false)
const textComponent = computed(() =>
referencePreformattedText.value ? 'pre' : 'p'
)
const isExpanded = ref(true)
const isImageViewerVisible = ref(false)
watch(isExpanded, () => {
if (isExpanded) {
nextTick(() => {
referenceRef.value?.scrollIntoView()
})
}
})
watch(referenceExpanded, (newVal) => {
isExpanded.value = newVal
})
</script>

<style>
em {
background-color: rgb(var(--text-highlight-color));
}
</style>
7 changes: 6 additions & 1 deletion src/components/Chat/User/ChatUserMessage.vue
Original file line number Diff line number Diff line change
@@ -1,14 +1,19 @@
<template>
<div>
<div class="font-bold text-lg">You</div>
<div class="whitespace-pre-wrap font-semibold text-2xl">
<div class="whitespace-pre-wrap font-semibold text-2xl flex items-center">
{{ message.text }}
<a :href="`?ask=${message.text}`">
<IconLink class="w-6 h-6 text-primary-color" />
</a>
</div>
</div>
</template>

<script setup lang="ts">
import { computed } from 'vue'
import { ChatMessage } from '@/types'
import IconLink from '@/components/Icon/IconLink.vue'
type Props = {
message: ChatMessage
Expand Down
15 changes: 15 additions & 0 deletions src/components/Icon/IconLink.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<template>
<svg
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
stroke-width="1.5"
stroke="currentColor"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
d="M13.19 8.688a4.5 4.5 0 0 1 1.242 7.244l-4.5 4.5a4.5 4.5 0 0 1-6.364-6.364l1.757-1.757m13.35-.622 1.757-1.757a4.5 4.5 0 0 0-6.364-6.364l-4.5 4.5a4.5 4.5 0 0 0 1.242 7.244"
/>
</svg>
</template>
15 changes: 15 additions & 0 deletions src/components/Icon/IconSettings.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<template>
<svg
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
stroke-width="1.5"
stroke="currentColor"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
d="M10.5 6h9.75M10.5 6a1.5 1.5 0 1 1-3 0m3 0a1.5 1.5 0 1 0-3 0M3.75 6H7.5m3 12h9.75m-9.75 0a1.5 1.5 0 0 1-3 0m3 0a1.5 1.5 0 0 0-3 0m-3.75 0H7.5m9-6h3.75m-3.75 0a1.5 1.5 0 0 1-3 0m3 0a1.5 1.5 0 0 0-3 0m-9.75 0h9.75"
/>
</svg>
</template>
21 changes: 21 additions & 0 deletions src/components/Navbar.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
<template>
<div
class="max-w-7xl container mx-auto flex justify-between items-center px-4"
>
<h1 class="text-4xl py-4 font-semibold">BHL Quest AI</h1>
<button
type="button"
class="rounded-full bg-primary-color text-primary-text p-2"
title="New chat"
@click="() => (openSettings = !openSettings)"
>
<IconSettings class="w-5 h-5" />
</button>
</div>
</template>
<script setup>
import IconSettings from '@/components/Icon/IconSettings.vue'
import { useSettings } from '@/store'
const { openSettings } = useSettings()
</script>
Loading

0 comments on commit 994246a

Please sign in to comment.