diff --git a/src/ui/UserPortal/components/ChatInput.vue b/src/ui/UserPortal/components/ChatInput.vue index b90aa68e76..1c1a9ab1c8 100644 --- a/src/ui/UserPortal/components/ChatInput.vue +++ b/src/ui/UserPortal/components/ChatInput.vue @@ -3,8 +3,8 @@ <div class="input-wrapper"> <div class="tooltip-component"> <VTooltip :auto-hide="isMobile" :popper-triggers="isMobile ? [] : ['hover']"> - <i class="pi pi-info-circle" tabindex="0"></i> - <template #popper> Use Shift+Enter to add a new line </template> + <i class="pi pi-info-circle" tabindex="0" @keydown.esc="hideAllPoppers" aria-label="info icon"></i> + <template #popper role="tooltip"><div role="tooltip">Use Shift+Enter to add a new line</div></template> </VTooltip> </div> <VTooltip :auto-hide="isMobile" :popper-triggers="isMobile ? [] : ['hover']"> @@ -16,11 +16,14 @@ class="file-upload-button secondary-button" style="height: 100%" @click="showFileUploadDialog = true" + @keydown.esc="hideAllPoppers" /> <template #popper> - Attach files ({{ - fileArrayFiltered.length === 1 ? '1 file' : fileArrayFiltered.length + ' files' - }}) + <div role="tooltip"> + Attach files ({{ + fileArrayFiltered.length === 1 ? '1 file' : fileArrayFiltered.length + ' files' + }}) + </div> </template> </VTooltip> <Dialog @@ -182,6 +185,7 @@ <script lang="ts"> import { Mentionable } from 'vue-mention'; import 'floating-vue/dist/style.css'; +import { hideAllPoppers } from 'floating-vue'; export default { name: 'ChatInput', @@ -387,6 +391,10 @@ export default { } }); }, + + hideAllPoppers() { + hideAllPoppers(); + }, }, }; </script> diff --git a/src/ui/UserPortal/components/ChatMessage.vue b/src/ui/UserPortal/components/ChatMessage.vue index 29a68ac28a..d9e34c32c9 100644 --- a/src/ui/UserPortal/components/ChatMessage.vue +++ b/src/ui/UserPortal/components/ChatMessage.vue @@ -28,11 +28,13 @@ }" /> <VTooltip :auto-hide="isMobile" :popper-triggers="isMobile ? [] : ['hover']"> - <span class="time-stamp" tabindex="0">{{ + <span class="time-stamp" tabindex="0" @keydown.esc="hideAllPoppers">{{ $filters.timeAgo(new Date(message.timeStamp)) }}</span> <template #popper> - {{ formatTimeStamp(message.timeStamp) }} + <div role="tooltip"> + {{ formatTimeStamp(message.timeStamp) }} + </div> </template> </VTooltip> @@ -46,8 +48,9 @@ icon="pi pi-copy" aria-label="Copy Message" @click.stop="handleCopyMessageContent" + @keydown.esc="hideAllPoppers" /> - <template #popper>Copy Message</template> + <template #popper><div role="tooltip">Copy Message</div></template> </VTooltip> </span> </div> @@ -210,6 +213,8 @@ import { fetchBlobUrl } from '@/js/fileService'; import CodeBlockHeader from '@/components/CodeBlockHeader.vue'; import ChatMessageContentBlock from '@/components/ChatMessageContentBlock.vue'; +import { hideAllPoppers } from 'floating-vue'; + function processLatex(content) { const blockLatexPattern = /\\\[\s*([\s\S]+?)\s*\\\]/g; const inlineLatexPattern = /\\\(([\s\S]+?)\\\)/g; @@ -483,6 +488,10 @@ export default { this.viewPrompt = true; }, + hideAllPoppers() { + hideAllPoppers(); + }, + handleFileLinkInText(event: MouseEvent) { const link = (event.target as HTMLElement).closest('a.file-download-link'); if (link && link.dataset.href) { diff --git a/src/ui/UserPortal/components/ChatSidebar.vue b/src/ui/UserPortal/components/ChatSidebar.vue index f3956fa955..2b335649d0 100644 --- a/src/ui/UserPortal/components/ChatSidebar.vue +++ b/src/ui/UserPortal/components/ChatSidebar.vue @@ -16,8 +16,9 @@ class="secondary-button" aria-label="Toggle sidebar" @click="$appStore.toggleSidebar" + @keydown.esc="hideAllPoppers" /> - <template #popper>Toggle sidebar</template> + <template #popper><div role="tooltip">Toggle sidebar</div></template> </VTooltip> </div> <div class="chat-sidebar__section-header"> @@ -30,8 +31,9 @@ aria-label="Add new chat" :disabled="createProcessing" @click="handleAddSession" + @keydown.esc="hideAllPoppers" /> - <template #popper>Add new chat</template> + <template #popper><div role="tooltip">Add new chat</div></template> </VTooltip> </div> @@ -49,9 +51,11 @@ <!-- Chat name --> <VTooltip :auto-hide="isMobile" :popper-triggers="isMobile ? [] : ['hover']"> - <span class="chat__name" tabindex="0">{{ session.name }}</span> + <span class="chat__name" tabindex="0" @keydown.esc="hideAllPoppers">{{ session.name }}</span> <template #popper> - {{ session.name }} + <div role="tooltip"> + {{ session.name }} + </div> </template> </VTooltip> @@ -66,8 +70,9 @@ text aria-label="Rename chat session" @click.stop="openRenameModal(session)" + @keydown.esc="hideAllPoppers" /> - <template #popper> Rename chat session </template> + <template #popper><div role="tooltip">Rename chat session</div></template> </VTooltip> <!-- Delete session --> @@ -79,8 +84,9 @@ text aria-label="Delete chat session" @click.stop="sessionToDelete = session" + @keydown.esc="hideAllPoppers" /> - <template #popper> Delete chat session </template> + <template #popper><div role="tooltip">Delete chat session</div></template> </VTooltip> </span> </div> @@ -118,6 +124,7 @@ :style="{ width: '100%' }" type="text" placeholder="New chat name" + aria-label="New chat name" autofocus @keydown="renameSessionInputKeydown" ></InputText> @@ -167,6 +174,7 @@ <script lang="ts"> import type { Session } from '@/js/types'; +import { hideAllPoppers } from 'floating-vue'; declare const process: any; export default { @@ -272,6 +280,10 @@ export default { this.sessionToDelete = null; } }, + + hideAllPoppers() { + hideAllPoppers(); + }, }, }; </script> diff --git a/src/ui/UserPortal/components/ChatThread.vue b/src/ui/UserPortal/components/ChatThread.vue index 33fe7d0a6e..fa77d4e941 100644 --- a/src/ui/UserPortal/components/ChatThread.vue +++ b/src/ui/UserPortal/components/ChatThread.vue @@ -27,7 +27,6 @@ :message="message" :show-word-animation="index === 0 && userSentMessage && message.sender === 'Assistant'" role="log" - :tabindex="getMessageOrderFromReversedIndex(index)" :aria-flowto=" index === 0 ? null : `message-${getMessageOrderFromReversedIndex(index) + 1}` " diff --git a/src/ui/UserPortal/components/NavBar.vue b/src/ui/UserPortal/components/NavBar.vue index d209412a9e..3ac323231c 100644 --- a/src/ui/UserPortal/components/NavBar.vue +++ b/src/ui/UserPortal/components/NavBar.vue @@ -18,8 +18,9 @@ class="secondary-button" aria-label="Toggle sidebar" @click="$appStore.toggleSidebar" + @keydown.esc="hideAllPoppers" /> - <template #popper>Toggle sidebar</template> + <template #popper><div role="tooltip">Toggle sidebar</div></template> </VTooltip> </template> </div> @@ -62,8 +63,9 @@ :src="$appConfigStore.agentIconUrl || '~/assets/FLLM-Agent-Light.svg'" alt="Select an agent" tabindex="0" + @keydown.esc="hideAllPoppers" /> - <template #popper> Select an agent </template> + <template #popper><div role="tooltip">Select an agent</div></template> </VTooltip> <Dropdown v-model="agentSelection" @@ -76,6 +78,7 @@ option-label="label" placeholder="--Select--" aria-label="Select an agent" + aria-activedescendant="selected-agent-{{ agentSelection?.label }}" @change="handleAgentChange" /> </span> @@ -87,6 +90,7 @@ <script lang="ts"> import type { Session } from '@/js/types'; +import { hideAllPoppers } from 'floating-vue'; interface AgentDropdownOption { label: string; @@ -209,6 +213,10 @@ export default { (option) => option.value.resource.object_id === agent.resource.object_id, ) || null; }, + + hideAllPoppers() { + hideAllPoppers(); + }, }, }; </script> diff --git a/src/ui/UserPortal/pages/auth/index.vue b/src/ui/UserPortal/pages/auth/index.vue index 6fe9535ef8..a7ee6095da 100644 --- a/src/ui/UserPortal/pages/auth/index.vue +++ b/src/ui/UserPortal/pages/auth/index.vue @@ -1,7 +1,7 @@ <template> <div class="login-page"> <div class="login-container"> - <img :src="$appConfigStore.logoUrl" class="login__logo" alt="Logo" /> + <img :src="$appConfigStore.logoUrl" class="login__logo" :alt="$appConfigStore.logoText" /> <Button class="primary-button" icon="pi pi-microsoft" diff --git a/src/ui/UserPortal/pages/index/index.vue b/src/ui/UserPortal/pages/index/index.vue index 719cfea277..7e9e43ee11 100644 --- a/src/ui/UserPortal/pages/index/index.vue +++ b/src/ui/UserPortal/pages/index/index.vue @@ -1,17 +1,21 @@ <template> <div class="chat-app"> - <NavBar /> + <header role="banner"> + <NavBar /> + </header> <div class="chat-content"> - <div v-show="!$appStore.isSidebarClosed" ref="sidebar" class="chat-sidebar-wrapper"> + <aside v-show="!$appStore.isSidebarClosed" ref="sidebar" class="chat-sidebar-wrapper" role="navigation"> <ChatSidebar class="chat-sidebar" :style="{ width: sidebarWidth + 'px' }" /> <div class="resize-handle" @mousedown="startResizing"></div> - </div> + </aside> <div v-show="!$appStore.isSidebarClosed" class="sidebar-blur" @click="$appStore.toggleSidebar" /> - <ChatThread /> + <main role="main" class="chat-main"> + <ChatThread /> + </main> </div> </div> </template> @@ -101,6 +105,11 @@ export default { height: 100%; } +.chat-main { + width: 100%; + height: 100%; +} + @media only screen and (max-width: 620px) { .sidebar-blur { position: absolute;