Merge pull request #14720 from itk-dev/feature/accessibible-chat-buttons

Feat: accessibible chat buttons
This commit is contained in:
Tim Jaeryang Baek 2025-06-06 12:55:46 +04:00 committed by GitHub
commit 1ff7cb7d2f
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 35 additions and 5 deletions

View File

@ -869,12 +869,14 @@
{#if siblings.length > 1} {#if siblings.length > 1}
<div class="flex self-center min-w-fit" dir="ltr"> <div class="flex self-center min-w-fit" dir="ltr">
<button <button
aria-label={$i18n.t('Previous message')}
class="self-center p-1 hover:bg-black/5 dark:hover:bg-white/5 dark:hover:text-white hover:text-black rounded-md transition" class="self-center p-1 hover:bg-black/5 dark:hover:bg-white/5 dark:hover:text-white hover:text-black rounded-md transition"
on:click={() => { on:click={() => {
showPreviousMessage(message); showPreviousMessage(message);
}} }}
> >
<svg <svg
aria-hidden="true"
xmlns="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg"
fill="none" fill="none"
viewBox="0 0 24 24" viewBox="0 0 24 24"
@ -940,10 +942,12 @@
on:click={() => { on:click={() => {
showNextMessage(message); showNextMessage(message);
}} }}
aria-label={$i18n.t('Next message')}
> >
<svg <svg
xmlns="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg"
fill="none" fill="none"
aria-hidden="true"
viewBox="0 0 24 24" viewBox="0 0 24 24"
stroke="currentColor" stroke="currentColor"
stroke-width="2.5" stroke-width="2.5"
@ -964,6 +968,7 @@
{#if $user?.role === 'user' ? ($user?.permissions?.chat?.edit ?? true) : true} {#if $user?.role === 'user' ? ($user?.permissions?.chat?.edit ?? true) : true}
<Tooltip content={$i18n.t('Edit')} placement="bottom"> <Tooltip content={$i18n.t('Edit')} placement="bottom">
<button <button
aria-label={$i18n.t('Edit')}
class="{isLastMessage class="{isLastMessage
? 'visible' ? 'visible'
: 'invisible group-hover:visible'} p-1.5 hover:bg-black/5 dark:hover:bg-white/5 rounded-lg dark:hover:text-white hover:text-black transition" : 'invisible group-hover:visible'} p-1.5 hover:bg-black/5 dark:hover:bg-white/5 rounded-lg dark:hover:text-white hover:text-black transition"
@ -976,6 +981,7 @@
fill="none" fill="none"
viewBox="0 0 24 24" viewBox="0 0 24 24"
stroke-width="2.3" stroke-width="2.3"
aria-hidden="true"
stroke="currentColor" stroke="currentColor"
class="w-4 h-4" class="w-4 h-4"
> >
@ -992,6 +998,7 @@
<Tooltip content={$i18n.t('Copy')} placement="bottom"> <Tooltip content={$i18n.t('Copy')} placement="bottom">
<button <button
aria-label={$i18n.t('Copy')}
class="{isLastMessage class="{isLastMessage
? 'visible' ? 'visible'
: 'invisible group-hover:visible'} p-1.5 hover:bg-black/5 dark:hover:bg-white/5 rounded-lg dark:hover:text-white hover:text-black transition copy-response-button" : 'invisible group-hover:visible'} p-1.5 hover:bg-black/5 dark:hover:bg-white/5 rounded-lg dark:hover:text-white hover:text-black transition copy-response-button"
@ -1002,6 +1009,7 @@
<svg <svg
xmlns="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg"
fill="none" fill="none"
aria-hidden="true"
viewBox="0 0 24 24" viewBox="0 0 24 24"
stroke-width="2.3" stroke-width="2.3"
stroke="currentColor" stroke="currentColor"
@ -1019,6 +1027,7 @@
{#if $user?.role === 'admin' || ($user?.permissions?.chat?.tts ?? true)} {#if $user?.role === 'admin' || ($user?.permissions?.chat?.tts ?? true)}
<Tooltip content={$i18n.t('Read Aloud')} placement="bottom"> <Tooltip content={$i18n.t('Read Aloud')} placement="bottom">
<button <button
aria-label={$i18n.t('Read Aloud')}
id="speak-button-{message.id}" id="speak-button-{message.id}"
class="{isLastMessage class="{isLastMessage
? 'visible' ? 'visible'
@ -1034,6 +1043,7 @@
class=" w-4 h-4" class=" w-4 h-4"
fill="currentColor" fill="currentColor"
viewBox="0 0 24 24" viewBox="0 0 24 24"
aria-hidden="true"
xmlns="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg"
> >
<style> <style>
@ -1066,6 +1076,7 @@
xmlns="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg"
fill="none" fill="none"
viewBox="0 0 24 24" viewBox="0 0 24 24"
aria-hidden="true"
stroke-width="2.3" stroke-width="2.3"
stroke="currentColor" stroke="currentColor"
class="w-4 h-4" class="w-4 h-4"
@ -1081,6 +1092,7 @@
xmlns="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg"
fill="none" fill="none"
viewBox="0 0 24 24" viewBox="0 0 24 24"
aria-hidden="true"
stroke-width="2.3" stroke-width="2.3"
stroke="currentColor" stroke="currentColor"
class="w-4 h-4" class="w-4 h-4"
@ -1099,6 +1111,7 @@
{#if $config?.features.enable_image_generation && ($user?.role === 'admin' || $user?.permissions?.features?.image_generation) && !readOnly} {#if $config?.features.enable_image_generation && ($user?.role === 'admin' || $user?.permissions?.features?.image_generation) && !readOnly}
<Tooltip content={$i18n.t('Generate Image')} placement="bottom"> <Tooltip content={$i18n.t('Generate Image')} placement="bottom">
<button <button
aria-label={$i18n.t('Generate Image')}
class="{isLastMessage class="{isLastMessage
? 'visible' ? 'visible'
: 'invisible group-hover:visible'} p-1.5 hover:bg-black/5 dark:hover:bg-white/5 rounded-lg dark:hover:text-white hover:text-black transition" : 'invisible group-hover:visible'} p-1.5 hover:bg-black/5 dark:hover:bg-white/5 rounded-lg dark:hover:text-white hover:text-black transition"
@ -1110,6 +1123,7 @@
> >
{#if generatingImage} {#if generatingImage}
<svg <svg
aria-hidden="true"
class=" w-4 h-4" class=" w-4 h-4"
fill="currentColor" fill="currentColor"
viewBox="0 0 24 24" viewBox="0 0 24 24"
@ -1144,6 +1158,7 @@
<svg <svg
xmlns="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg"
fill="none" fill="none"
aria-hidden="true"
viewBox="0 0 24 24" viewBox="0 0 24 24"
stroke-width="2.3" stroke-width="2.3"
stroke="currentColor" stroke="currentColor"
@ -1176,6 +1191,7 @@
placement="bottom" placement="bottom"
> >
<button <button
aria-hidden="true"
class=" {isLastMessage class=" {isLastMessage
? 'visible' ? 'visible'
: 'invisible group-hover:visible'} p-1.5 hover:bg-black/5 dark:hover:bg-white/5 rounded-lg dark:hover:text-white hover:text-black transition whitespace-pre-wrap" : 'invisible group-hover:visible'} p-1.5 hover:bg-black/5 dark:hover:bg-white/5 rounded-lg dark:hover:text-white hover:text-black transition whitespace-pre-wrap"
@ -1185,6 +1201,7 @@
id="info-{message.id}" id="info-{message.id}"
> >
<svg <svg
aria-hidden="true"
xmlns="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg"
fill="none" fill="none"
viewBox="0 0 24 24" viewBox="0 0 24 24"
@ -1206,6 +1223,7 @@
{#if !$temporaryChatEnabled && ($config?.features.enable_message_rating ?? true)} {#if !$temporaryChatEnabled && ($config?.features.enable_message_rating ?? true)}
<Tooltip content={$i18n.t('Good Response')} placement="bottom"> <Tooltip content={$i18n.t('Good Response')} placement="bottom">
<button <button
aria-label={$i18n.t('Good Response')}
class="{isLastMessage class="{isLastMessage
? 'visible' ? 'visible'
: 'invisible group-hover:visible'} p-1.5 hover:bg-black/5 dark:hover:bg-white/5 rounded-lg {( : 'invisible group-hover:visible'} p-1.5 hover:bg-black/5 dark:hover:bg-white/5 rounded-lg {(
@ -1224,6 +1242,7 @@
}} }}
> >
<svg <svg
aria-hidden="true"
stroke="currentColor" stroke="currentColor"
fill="none" fill="none"
stroke-width="2.3" stroke-width="2.3"
@ -1242,6 +1261,7 @@
<Tooltip content={$i18n.t('Bad Response')} placement="bottom"> <Tooltip content={$i18n.t('Bad Response')} placement="bottom">
<button <button
aria-label={$i18n.t('Bad Response')}
class="{isLastMessage class="{isLastMessage
? 'visible' ? 'visible'
: 'invisible group-hover:visible'} p-1.5 hover:bg-black/5 dark:hover:bg-white/5 rounded-lg {( : 'invisible group-hover:visible'} p-1.5 hover:bg-black/5 dark:hover:bg-white/5 rounded-lg {(
@ -1260,6 +1280,7 @@
}} }}
> >
<svg <svg
aria-hidden="true"
stroke="currentColor" stroke="currentColor"
fill="none" fill="none"
stroke-width="2.3" stroke-width="2.3"
@ -1280,6 +1301,7 @@
{#if isLastMessage} {#if isLastMessage}
<Tooltip content={$i18n.t('Continue Response')} placement="bottom"> <Tooltip content={$i18n.t('Continue Response')} placement="bottom">
<button <button
aria-label={$i18n.t('Continue Response')}
type="button" type="button"
id="continue-response-button" id="continue-response-button"
class="{isLastMessage class="{isLastMessage
@ -1290,6 +1312,7 @@
}} }}
> >
<svg <svg
aria-hidden="true"
xmlns="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg"
fill="none" fill="none"
viewBox="0 0 24 24" viewBox="0 0 24 24"
@ -1315,6 +1338,7 @@
<Tooltip content={$i18n.t('Regenerate')} placement="bottom"> <Tooltip content={$i18n.t('Regenerate')} placement="bottom">
<button <button
type="button" type="button"
aria-label={$i18n.t('Regenerate')}
class="{isLastMessage class="{isLastMessage
? 'visible' ? 'visible'
: 'invisible group-hover:visible'} p-1.5 hover:bg-black/5 dark:hover:bg-white/5 rounded-lg dark:hover:text-white hover:text-black transition regenerate-response-button" : 'invisible group-hover:visible'} p-1.5 hover:bg-black/5 dark:hover:bg-white/5 rounded-lg dark:hover:text-white hover:text-black transition regenerate-response-button"
@ -1340,6 +1364,7 @@
fill="none" fill="none"
viewBox="0 0 24 24" viewBox="0 0 24 24"
stroke-width="2.3" stroke-width="2.3"
aria-hidden="true"
stroke="currentColor" stroke="currentColor"
class="w-4 h-4" class="w-4 h-4"
> >
@ -1356,6 +1381,7 @@
<Tooltip content={$i18n.t('Delete')} placement="bottom"> <Tooltip content={$i18n.t('Delete')} placement="bottom">
<button <button
type="button" type="button"
aria-label={$i18n.t('Delete')}
id="delete-response-button" id="delete-response-button"
class="{isLastMessage class="{isLastMessage
? 'visible' ? 'visible'
@ -1370,6 +1396,7 @@
viewBox="0 0 24 24" viewBox="0 0 24 24"
stroke-width="2" stroke-width="2"
stroke="currentColor" stroke="currentColor"
aria-hidden="true"
class="w-4 h-4" class="w-4 h-4"
> >
<path <path
@ -1387,6 +1414,7 @@
<Tooltip content={action.name} placement="bottom"> <Tooltip content={action.name} placement="bottom">
<button <button
type="button" type="button"
aria-label={action.name}
class="{isLastMessage class="{isLastMessage
? 'visible' ? 'visible'
: 'invisible group-hover:visible'} p-1.5 hover:bg-black/5 dark:hover:bg-white/5 rounded-lg dark:hover:text-white hover:text-black transition" : 'invisible group-hover:visible'} p-1.5 hover:bg-black/5 dark:hover:bg-white/5 rounded-lg dark:hover:text-white hover:text-black transition"

View File

@ -2,10 +2,8 @@
import DOMPurify from 'dompurify'; import DOMPurify from 'dompurify';
import { onDestroy } from 'svelte'; import { onDestroy } from 'svelte';
import { marked } from 'marked';
import tippy from 'tippy.js'; import tippy from 'tippy.js';
import { roundArrow } from 'tippy.js';
export let placement = 'top'; export let placement = 'top';
export let content = `I'm a tooltip!`; export let content = `I'm a tooltip!`;
@ -47,6 +45,6 @@
}); });
</script> </script>
<div bind:this={tooltipElement} aria-label={DOMPurify.sanitize(content)} class={className}> <div bind:this={tooltipElement} class={className}>
<slot /> <slot />
</div> </div>

View File

@ -1380,5 +1380,7 @@
"Your entire contribution will go directly to the plugin developer; Open WebUI does not take any percentage. However, the chosen funding platform might have its own fees.": "Hele dit bidrag går direkte til plugin-udvikleren; Open WebUI tager ikke nogen procentdel. Den valgte finansieringsplatform kan dog have sine egne gebyrer.", "Your entire contribution will go directly to the plugin developer; Open WebUI does not take any percentage. However, the chosen funding platform might have its own fees.": "Hele dit bidrag går direkte til plugin-udvikleren; Open WebUI tager ikke nogen procentdel. Den valgte finansieringsplatform kan dog have sine egne gebyrer.",
"Youtube": "Youtube", "Youtube": "Youtube",
"Youtube Language": "Youtube sprog", "Youtube Language": "Youtube sprog",
"Youtube Proxy URL": "Youtube Proxy URL" "Youtube Proxy URL": "Youtube Proxy URL",
"Previous message": "Forrige besked",
"Next message": "Næste besked"
} }

View File

@ -1380,5 +1380,7 @@
"Your entire contribution will go directly to the plugin developer; Open WebUI does not take any percentage. However, the chosen funding platform might have its own fees.": "", "Your entire contribution will go directly to the plugin developer; Open WebUI does not take any percentage. However, the chosen funding platform might have its own fees.": "",
"Youtube": "", "Youtube": "",
"Youtube Language": "", "Youtube Language": "",
"Youtube Proxy URL": "" "Youtube Proxy URL": "",
"Previous message": "Previous message",
"Next message": "Next message"
} }