fix(ui): add toggle for holding long assistant replies

This commit is contained in:
Shantur Rathore
2026-04-11 19:47:57 +01:00
parent 2a3329b5ed
commit 8505a43b16
10 changed files with 75 additions and 2 deletions

View File

@@ -1,5 +1,5 @@
import { Show, createEffect, createMemo, createSignal, onCleanup, on, untrack } from "solid-js"
import { MoreHorizontal, Trash, X } from "lucide-solid"
import { MoreHorizontal, Pause, Trash, X } from "lucide-solid"
import Kbd from "./kbd"
import MessageBlock from "./message-block"
import { getMessageAnchorId, getMessageIdFromAnchorId } from "./message-anchors"
@@ -42,10 +42,11 @@ export interface MessageSectionProps {
}
export default function MessageSection(props: MessageSectionProps) {
const { preferences } = useConfig()
const { preferences, updatePreferences } = useConfig()
const { t } = useI18n()
const showUsagePreference = () => preferences().showUsageMetrics ?? true
const showTimelineToolsPreference = () => preferences().showTimelineTools ?? true
const holdLongAssistantRepliesEnabled = () => preferences().holdLongAssistantReplies ?? true
const store = createMemo<InstanceMessageStore>(() => messageStoreBus.getOrCreate(props.instanceId))
const messageIds = createMemo(() => store().getSessionMessageIds(props.sessionId))
const visibleMessageIds = createMemo(() => {
@@ -635,10 +636,15 @@ export default function MessageSection(props: MessageSectionProps) {
})
const autoPinHoldTargetKey = createMemo(() => {
if (!holdLongAssistantRepliesEnabled()) return null
const messageId = lastVisibleMessageId()
return isAssistantTextMessage(messageId) ? messageId : null
})
function toggleHoldLongAssistantReplies() {
updatePreferences({ holdLongAssistantReplies: !holdLongAssistantRepliesEnabled() })
}
function isAssistantTextMessage(messageId: string | null | undefined) {
if (!messageId) return false
const resolvedStore = store()
@@ -1109,6 +1115,52 @@ export default function MessageSection(props: MessageSectionProps) {
scrollToBottomAriaLabel={() => t("messageSection.scroll.toLatestAriaLabel")}
registerApi={(api) => setListApi(api)}
registerState={(state) => setListState(state)}
renderControls={(state, api) => (
<div class="message-scroll-button-wrapper">
<button
type="button"
class="message-scroll-button"
data-active={holdLongAssistantRepliesEnabled() ? "true" : "false"}
onClick={toggleHoldLongAssistantReplies}
aria-label={
holdLongAssistantRepliesEnabled()
? t("messageSection.scroll.disableHoldAriaLabel")
: t("messageSection.scroll.enableHoldAriaLabel")
}
title={
holdLongAssistantRepliesEnabled()
? t("messageSection.scroll.disableHoldAriaLabel")
: t("messageSection.scroll.enableHoldAriaLabel")
}
>
<Pause class="message-scroll-icon message-scroll-icon--toggle w-4 h-4" aria-hidden="true" />
</button>
<Show when={state.showScrollTopButton()}>
<button
type="button"
class="message-scroll-button"
onClick={() => api.scrollToTop()}
aria-label={t("messageSection.scroll.toFirstAriaLabel")}
>
<span class="message-scroll-icon" aria-hidden="true">
</span>
</button>
</Show>
<Show when={state.showScrollBottomButton()}>
<button
type="button"
class="message-scroll-button"
onClick={() => api.scrollToBottom()}
aria-label={t("messageSection.scroll.toLatestAriaLabel")}
>
<span class="message-scroll-icon" aria-hidden="true">
</span>
</button>
</Show>
</div>
)}
renderBeforeItems={() => (
<>
<Show when={!props.loading && visibleMessageIds().length === 0}>

View File

@@ -18,6 +18,8 @@ export const messagingMessages = {
"messageSection.loading.messages": "Loading messages...",
"messageSection.scroll.toFirstAriaLabel": "Scroll to first message",
"messageSection.scroll.toLatestAriaLabel": "Scroll to latest message",
"messageSection.scroll.enableHoldAriaLabel": "Enable hold for long assistant replies",
"messageSection.scroll.disableHoldAriaLabel": "Disable hold for long assistant replies",
"messageSection.quote.addAsQuote": "Add as quote",
"messageSection.quote.addAsCode": "Add as code",
"messageSection.quote.copy": "Copy",

View File

@@ -18,6 +18,8 @@ export const messagingMessages = {
"messageSection.loading.messages": "Cargando mensajes...",
"messageSection.scroll.toFirstAriaLabel": "Desplazarse al primer mensaje",
"messageSection.scroll.toLatestAriaLabel": "Desplazarse al último mensaje",
"messageSection.scroll.enableHoldAriaLabel": "Activar pausa para respuestas largas del asistente",
"messageSection.scroll.disableHoldAriaLabel": "Desactivar pausa para respuestas largas del asistente",
"messageSection.quote.addAsQuote": "Añadir como cita",
"messageSection.quote.addAsCode": "Añadir como código",
"messageSection.quote.copy": "Copiar",

View File

@@ -18,6 +18,8 @@ export const messagingMessages = {
"messageSection.loading.messages": "Chargement des messages...",
"messageSection.scroll.toFirstAriaLabel": "Aller au premier message",
"messageSection.scroll.toLatestAriaLabel": "Aller au dernier message",
"messageSection.scroll.enableHoldAriaLabel": "Activer le maintien pour les longues réponses de l'assistant",
"messageSection.scroll.disableHoldAriaLabel": "Désactiver le maintien pour les longues réponses de l'assistant",
"messageSection.quote.addAsQuote": "Ajouter en citation",
"messageSection.quote.addAsCode": "Ajouter en code",
"messageSection.quote.copy": "Copier",

View File

@@ -18,6 +18,8 @@ export const messagingMessages = {
"messageSection.loading.messages": "טוען הודעות...",
"messageSection.scroll.toFirstAriaLabel": "גלול להודעה הראשונה",
"messageSection.scroll.toLatestAriaLabel": "גלול להודעה האחרונה",
"messageSection.scroll.enableHoldAriaLabel": "הפעל עצירה לתגובות עוזר ארוכות",
"messageSection.scroll.disableHoldAriaLabel": "כבה עצירה לתגובות עוזר ארוכות",
"messageSection.quote.addAsQuote": "הוסף כציטוט",
"messageSection.quote.addAsCode": "הוסף כקוד",
"messageSection.quote.copy": "העתק",

View File

@@ -18,6 +18,8 @@ export const messagingMessages = {
"messageSection.loading.messages": "メッセージを読み込み中...",
"messageSection.scroll.toFirstAriaLabel": "最初のメッセージへスクロール",
"messageSection.scroll.toLatestAriaLabel": "最新のメッセージへスクロール",
"messageSection.scroll.enableHoldAriaLabel": "長いアシスタント返信の保持を有効にする",
"messageSection.scroll.disableHoldAriaLabel": "長いアシスタント返信の保持を無効にする",
"messageSection.quote.addAsQuote": "引用として追加",
"messageSection.quote.addAsCode": "コードとして追加",
"messageSection.quote.copy": "コピー",

View File

@@ -18,6 +18,8 @@ export const messagingMessages = {
"messageSection.loading.messages": "Загрузка сообщений…",
"messageSection.scroll.toFirstAriaLabel": "Прокрутить к первому сообщению",
"messageSection.scroll.toLatestAriaLabel": "Прокрутить к последнему сообщению",
"messageSection.scroll.enableHoldAriaLabel": "Включить удержание для длинных ответов ассистента",
"messageSection.scroll.disableHoldAriaLabel": "Выключить удержание для длинных ответов ассистента",
"messageSection.quote.addAsQuote": "Добавить как цитату",
"messageSection.quote.addAsCode": "Добавить как код",
"messageSection.quote.copy": "Копировать",

View File

@@ -18,6 +18,8 @@ export const messagingMessages = {
"messageSection.loading.messages": "正在加载消息...",
"messageSection.scroll.toFirstAriaLabel": "滚动到第一条消息",
"messageSection.scroll.toLatestAriaLabel": "滚动到最新消息",
"messageSection.scroll.enableHoldAriaLabel": "启用长助手回复保持",
"messageSection.scroll.disableHoldAriaLabel": "禁用长助手回复保持",
"messageSection.quote.addAsQuote": "作为引用添加",
"messageSection.quote.addAsCode": "作为代码添加",
"messageSection.quote.copy": "复制",

View File

@@ -55,6 +55,7 @@ export interface UiSettings {
showKeyboardShortcutHints: boolean
thinkingBlocksExpansion: ExpansionPreference
showTimelineTools: boolean
holdLongAssistantReplies: boolean
promptSubmitOnEnter: boolean
showPromptVoiceInput: boolean
locale?: string
@@ -133,6 +134,7 @@ const defaultUiSettings: UiSettings = {
showKeyboardShortcutHints: true,
thinkingBlocksExpansion: "expanded",
showTimelineTools: true,
holdLongAssistantReplies: true,
promptSubmitOnEnter: false,
showPromptVoiceInput: true,
diffViewMode: "split",
@@ -166,6 +168,7 @@ function normalizeUiSettings(input?: Partial<UiSettings> | null): UiSettings {
sanitized.showKeyboardShortcutHints ?? defaultUiSettings.showKeyboardShortcutHints,
thinkingBlocksExpansion: sanitized.thinkingBlocksExpansion ?? defaultUiSettings.thinkingBlocksExpansion,
showTimelineTools: sanitized.showTimelineTools ?? defaultUiSettings.showTimelineTools,
holdLongAssistantReplies: sanitized.holdLongAssistantReplies ?? defaultUiSettings.holdLongAssistantReplies,
promptSubmitOnEnter: sanitized.promptSubmitOnEnter ?? defaultUiSettings.promptSubmitOnEnter,
showPromptVoiceInput: sanitized.showPromptVoiceInput ?? defaultUiSettings.showPromptVoiceInput,
locale: sanitized.locale ?? defaultUiSettings.locale,

View File

@@ -242,6 +242,10 @@
color: var(--accent-primary);
}
.message-scroll-button[data-active="false"] .message-scroll-icon--toggle {
color: var(--text-secondary);
}
.message-quote-popover {
position: absolute;
z-index: 5;