Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
86 changes: 38 additions & 48 deletions src/renderer/src/components/message/MessageList.vue
Original file line number Diff line number Diff line change
Expand Up @@ -68,18 +68,19 @@
leave-from-class="opacity-100 translate-y-0"
leave-to-class="opacity-0 translate-y-2"
>
<Button
<div
v-if="aboveThreshold || showCancelButton"
variant="outline"
size="icon"
:class="[
'w-8 h-8 shrink-0 rounded-lg relative',
showCancelButton ? 'scroll-to-bottom-loading' : ''
]"
@click="scrollToBottom"
:class="['relative', showCancelButton ? 'scroll-to-bottom-loading-container' : '']"
>
<Icon icon="lucide:arrow-down" class="w-5 h-5 text-muted-foreground" />
</Button>
<Button
variant="outline"
size="icon"
class="w-8 h-8 shrink-0 rounded-lg relative z-10"
@click="scrollToBottom"
>
<Icon icon="lucide:arrow-down" class="w-5 h-5 text-muted-foreground" />
</Button>
</div>
Comment on lines +75 to +83
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Add accessible label (i18n) to icon-only button

Screen readers won’t have a name for this control. Provide an aria-label/title and hide the decorative icon from AT.

Apply this diff:

-            <Button
-              variant="outline"
-              size="icon"
-              class="w-8 h-8 shrink-0 rounded-lg relative z-10"
-              @click="scrollToBottom"
-            >
-              <Icon icon="lucide:arrow-down" class="w-5 h-5 text-muted-foreground" />
-            </Button>
+            <Button
+              variant="outline"
+              size="icon"
+              class="w-8 h-8 shrink-0 rounded-lg relative z-10"
+              :aria-label="t('common.scrollToBottom')"
+              :title="t('common.scrollToBottom')"
+              @click="scrollToBottom"
+            >
+              <Icon
+                icon="lucide:arrow-down"
+                class="w-5 h-5 text-muted-foreground"
+                aria-hidden="true"
+              />
+            </Button>

If the i18n key doesn’t exist, please add common.scrollToBottom.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
<Button
variant="outline"
size="icon"
class="w-8 h-8 shrink-0 rounded-lg relative z-10"
@click="scrollToBottom"
>
<Icon icon="lucide:arrow-down" class="w-5 h-5 text-muted-foreground" />
</Button>
</div>
<Button
variant="outline"
size="icon"
class="w-8 h-8 shrink-0 rounded-lg relative z-10"
:aria-label="t('common.scrollToBottom')"
:title="t('common.scrollToBottom')"
@click="scrollToBottom"
>
<Icon
icon="lucide:arrow-down"
class="w-5 h-5 text-muted-foreground"
aria-hidden="true"
/>
</Button>
🤖 Prompt for AI Agents
In src/renderer/src/components/message/MessageList.vue around lines 75 to 83,
the icon-only Button lacks an accessible name; add an aria-label and title that
use the i18n key common.scrollToBottom (e.g.,
:aria-label="t('common.scrollToBottom')" and
:title="t('common.scrollToBottom')") and mark the decorative Icon as
aria-hidden="true" so assistive tech ignores it; also ensure the translations
file(s) include a common.scrollToBottom entry (add it if missing).

</transition>
</div>
</template>
Expand Down Expand Up @@ -417,57 +418,46 @@ defineExpose({
background-color: rgba(59, 130, 246, 0.15);
}

.scroll-to-bottom-loading {
.scroll-to-bottom-loading-container {
position: relative;
overflow: visible;
isolation: isolate;
}

.scroll-to-bottom-loading::before {
.scroll-to-bottom-loading-container::before {
content: '';
position: absolute;
top: -1px;
left: -1px;
right: -1px;
bottom: -1px;
border-radius: inherit;
border: 1px solid hsl(var(--border));
top: -3px;
left: -3px;
right: -3px;
bottom: -3px;
border-radius: 0.5rem;
background: linear-gradient(135deg, #9b59b6, #84cdfa, #5ad1cd);
animation: rotate-glow 1.2s linear infinite;
pointer-events: none;
z-index: 1;
filter: blur(8px);
}

.scroll-to-bottom-loading::after {
.scroll-to-bottom-loading-container::after {
content: '';
position: absolute;
top: -2px;
left: -2px;
right: -2px;
bottom: -2px;
border-radius: inherit;
background: conic-gradient(
from 0deg,
transparent,
transparent 25%,
hsl(var(--primary) / 0.15),
hsl(var(--primary) / 0.4),
hsl(var(--primary) / 0.8),
hsl(var(--primary) / 0.9),
hsl(var(--primary) / 0.6),
hsl(var(--primary) / 0.25),
transparent 80%,
transparent
);
mask: radial-gradient(circle at center, transparent calc(50% - 1px), black 50%, black 100%);
-webkit-mask: radial-gradient(
circle at center,
transparent calc(50% - 1px),
black 50%,
black 100%
);
animation: spin 1.2s linear infinite;
top: -5px;
left: -5px;
right: -5px;
bottom: -5px;
border-radius: 0.5rem;
background: linear-gradient(135deg, #9b59b6, #84cdfa, #5ad1cd);
animation: rotate-glow 1.2s linear infinite;
pointer-events: none;
will-change: transform;
z-index: 0;
filter: blur(20px);
opacity: 0.6;
}

@keyframes spin {
@keyframes rotate-glow {
from {
transform: rotate(0deg);
}
to {
transform: rotate(360deg);
}
Expand Down