diff --git a/packages/ui/src/components/message-section.tsx b/packages/ui/src/components/message-section.tsx index 51ef2b26..3fbdd6c9 100644 --- a/packages/ui/src/components/message-section.tsx +++ b/packages/ui/src/components/message-section.tsx @@ -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(() => 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) => ( +
+ + + + + + + +
+ )} renderBeforeItems={() => ( <> diff --git a/packages/ui/src/lib/i18n/messages/en/messaging.ts b/packages/ui/src/lib/i18n/messages/en/messaging.ts index 93e07cb4..58422334 100644 --- a/packages/ui/src/lib/i18n/messages/en/messaging.ts +++ b/packages/ui/src/lib/i18n/messages/en/messaging.ts @@ -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", diff --git a/packages/ui/src/lib/i18n/messages/es/messaging.ts b/packages/ui/src/lib/i18n/messages/es/messaging.ts index 2fbedea7..2e4173cf 100644 --- a/packages/ui/src/lib/i18n/messages/es/messaging.ts +++ b/packages/ui/src/lib/i18n/messages/es/messaging.ts @@ -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", diff --git a/packages/ui/src/lib/i18n/messages/fr/messaging.ts b/packages/ui/src/lib/i18n/messages/fr/messaging.ts index 6c3a8751..32bb8a9a 100644 --- a/packages/ui/src/lib/i18n/messages/fr/messaging.ts +++ b/packages/ui/src/lib/i18n/messages/fr/messaging.ts @@ -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", diff --git a/packages/ui/src/lib/i18n/messages/he/messaging.ts b/packages/ui/src/lib/i18n/messages/he/messaging.ts index 54777f9a..b661e012 100644 --- a/packages/ui/src/lib/i18n/messages/he/messaging.ts +++ b/packages/ui/src/lib/i18n/messages/he/messaging.ts @@ -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": "העתק", diff --git a/packages/ui/src/lib/i18n/messages/ja/messaging.ts b/packages/ui/src/lib/i18n/messages/ja/messaging.ts index 2c3cb6c3..893581db 100644 --- a/packages/ui/src/lib/i18n/messages/ja/messaging.ts +++ b/packages/ui/src/lib/i18n/messages/ja/messaging.ts @@ -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": "コピー", diff --git a/packages/ui/src/lib/i18n/messages/ru/messaging.ts b/packages/ui/src/lib/i18n/messages/ru/messaging.ts index 0635a95a..b4b7cbf5 100644 --- a/packages/ui/src/lib/i18n/messages/ru/messaging.ts +++ b/packages/ui/src/lib/i18n/messages/ru/messaging.ts @@ -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": "Копировать", diff --git a/packages/ui/src/lib/i18n/messages/zh-Hans/messaging.ts b/packages/ui/src/lib/i18n/messages/zh-Hans/messaging.ts index 0f653b57..202a8693 100644 --- a/packages/ui/src/lib/i18n/messages/zh-Hans/messaging.ts +++ b/packages/ui/src/lib/i18n/messages/zh-Hans/messaging.ts @@ -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": "复制", diff --git a/packages/ui/src/stores/preferences.tsx b/packages/ui/src/stores/preferences.tsx index 21209c75..39b76e5a 100644 --- a/packages/ui/src/stores/preferences.tsx +++ b/packages/ui/src/stores/preferences.tsx @@ -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 | 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, diff --git a/packages/ui/src/styles/messaging/message-section.css b/packages/ui/src/styles/messaging/message-section.css index 7e6f2475..9582b48d 100644 --- a/packages/ui/src/styles/messaging/message-section.css +++ b/packages/ui/src/styles/messaging/message-section.css @@ -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;