fix(ui): add toggle for holding long assistant replies
This commit is contained in:
@@ -1,5 +1,5 @@
|
|||||||
import { Show, createEffect, createMemo, createSignal, onCleanup, on, untrack } from "solid-js"
|
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 Kbd from "./kbd"
|
||||||
import MessageBlock from "./message-block"
|
import MessageBlock from "./message-block"
|
||||||
import { getMessageAnchorId, getMessageIdFromAnchorId } from "./message-anchors"
|
import { getMessageAnchorId, getMessageIdFromAnchorId } from "./message-anchors"
|
||||||
@@ -42,10 +42,11 @@ export interface MessageSectionProps {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export default function MessageSection(props: MessageSectionProps) {
|
export default function MessageSection(props: MessageSectionProps) {
|
||||||
const { preferences } = useConfig()
|
const { preferences, updatePreferences } = useConfig()
|
||||||
const { t } = useI18n()
|
const { t } = useI18n()
|
||||||
const showUsagePreference = () => preferences().showUsageMetrics ?? true
|
const showUsagePreference = () => preferences().showUsageMetrics ?? true
|
||||||
const showTimelineToolsPreference = () => preferences().showTimelineTools ?? true
|
const showTimelineToolsPreference = () => preferences().showTimelineTools ?? true
|
||||||
|
const holdLongAssistantRepliesEnabled = () => preferences().holdLongAssistantReplies ?? true
|
||||||
const store = createMemo<InstanceMessageStore>(() => messageStoreBus.getOrCreate(props.instanceId))
|
const store = createMemo<InstanceMessageStore>(() => messageStoreBus.getOrCreate(props.instanceId))
|
||||||
const messageIds = createMemo(() => store().getSessionMessageIds(props.sessionId))
|
const messageIds = createMemo(() => store().getSessionMessageIds(props.sessionId))
|
||||||
const visibleMessageIds = createMemo(() => {
|
const visibleMessageIds = createMemo(() => {
|
||||||
@@ -635,10 +636,15 @@ export default function MessageSection(props: MessageSectionProps) {
|
|||||||
})
|
})
|
||||||
|
|
||||||
const autoPinHoldTargetKey = createMemo(() => {
|
const autoPinHoldTargetKey = createMemo(() => {
|
||||||
|
if (!holdLongAssistantRepliesEnabled()) return null
|
||||||
const messageId = lastVisibleMessageId()
|
const messageId = lastVisibleMessageId()
|
||||||
return isAssistantTextMessage(messageId) ? messageId : null
|
return isAssistantTextMessage(messageId) ? messageId : null
|
||||||
})
|
})
|
||||||
|
|
||||||
|
function toggleHoldLongAssistantReplies() {
|
||||||
|
updatePreferences({ holdLongAssistantReplies: !holdLongAssistantRepliesEnabled() })
|
||||||
|
}
|
||||||
|
|
||||||
function isAssistantTextMessage(messageId: string | null | undefined) {
|
function isAssistantTextMessage(messageId: string | null | undefined) {
|
||||||
if (!messageId) return false
|
if (!messageId) return false
|
||||||
const resolvedStore = store()
|
const resolvedStore = store()
|
||||||
@@ -1109,6 +1115,52 @@ export default function MessageSection(props: MessageSectionProps) {
|
|||||||
scrollToBottomAriaLabel={() => t("messageSection.scroll.toLatestAriaLabel")}
|
scrollToBottomAriaLabel={() => t("messageSection.scroll.toLatestAriaLabel")}
|
||||||
registerApi={(api) => setListApi(api)}
|
registerApi={(api) => setListApi(api)}
|
||||||
registerState={(state) => setListState(state)}
|
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={() => (
|
renderBeforeItems={() => (
|
||||||
<>
|
<>
|
||||||
<Show when={!props.loading && visibleMessageIds().length === 0}>
|
<Show when={!props.loading && visibleMessageIds().length === 0}>
|
||||||
|
|||||||
@@ -18,6 +18,8 @@ export const messagingMessages = {
|
|||||||
"messageSection.loading.messages": "Loading messages...",
|
"messageSection.loading.messages": "Loading messages...",
|
||||||
"messageSection.scroll.toFirstAriaLabel": "Scroll to first message",
|
"messageSection.scroll.toFirstAriaLabel": "Scroll to first message",
|
||||||
"messageSection.scroll.toLatestAriaLabel": "Scroll to latest 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.addAsQuote": "Add as quote",
|
||||||
"messageSection.quote.addAsCode": "Add as code",
|
"messageSection.quote.addAsCode": "Add as code",
|
||||||
"messageSection.quote.copy": "Copy",
|
"messageSection.quote.copy": "Copy",
|
||||||
|
|||||||
@@ -18,6 +18,8 @@ export const messagingMessages = {
|
|||||||
"messageSection.loading.messages": "Cargando mensajes...",
|
"messageSection.loading.messages": "Cargando mensajes...",
|
||||||
"messageSection.scroll.toFirstAriaLabel": "Desplazarse al primer mensaje",
|
"messageSection.scroll.toFirstAriaLabel": "Desplazarse al primer mensaje",
|
||||||
"messageSection.scroll.toLatestAriaLabel": "Desplazarse al último 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.addAsQuote": "Añadir como cita",
|
||||||
"messageSection.quote.addAsCode": "Añadir como código",
|
"messageSection.quote.addAsCode": "Añadir como código",
|
||||||
"messageSection.quote.copy": "Copiar",
|
"messageSection.quote.copy": "Copiar",
|
||||||
|
|||||||
@@ -18,6 +18,8 @@ export const messagingMessages = {
|
|||||||
"messageSection.loading.messages": "Chargement des messages...",
|
"messageSection.loading.messages": "Chargement des messages...",
|
||||||
"messageSection.scroll.toFirstAriaLabel": "Aller au premier message",
|
"messageSection.scroll.toFirstAriaLabel": "Aller au premier message",
|
||||||
"messageSection.scroll.toLatestAriaLabel": "Aller au dernier 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.addAsQuote": "Ajouter en citation",
|
||||||
"messageSection.quote.addAsCode": "Ajouter en code",
|
"messageSection.quote.addAsCode": "Ajouter en code",
|
||||||
"messageSection.quote.copy": "Copier",
|
"messageSection.quote.copy": "Copier",
|
||||||
|
|||||||
@@ -18,6 +18,8 @@ export const messagingMessages = {
|
|||||||
"messageSection.loading.messages": "טוען הודעות...",
|
"messageSection.loading.messages": "טוען הודעות...",
|
||||||
"messageSection.scroll.toFirstAriaLabel": "גלול להודעה הראשונה",
|
"messageSection.scroll.toFirstAriaLabel": "גלול להודעה הראשונה",
|
||||||
"messageSection.scroll.toLatestAriaLabel": "גלול להודעה האחרונה",
|
"messageSection.scroll.toLatestAriaLabel": "גלול להודעה האחרונה",
|
||||||
|
"messageSection.scroll.enableHoldAriaLabel": "הפעל עצירה לתגובות עוזר ארוכות",
|
||||||
|
"messageSection.scroll.disableHoldAriaLabel": "כבה עצירה לתגובות עוזר ארוכות",
|
||||||
"messageSection.quote.addAsQuote": "הוסף כציטוט",
|
"messageSection.quote.addAsQuote": "הוסף כציטוט",
|
||||||
"messageSection.quote.addAsCode": "הוסף כקוד",
|
"messageSection.quote.addAsCode": "הוסף כקוד",
|
||||||
"messageSection.quote.copy": "העתק",
|
"messageSection.quote.copy": "העתק",
|
||||||
|
|||||||
@@ -18,6 +18,8 @@ export const messagingMessages = {
|
|||||||
"messageSection.loading.messages": "メッセージを読み込み中...",
|
"messageSection.loading.messages": "メッセージを読み込み中...",
|
||||||
"messageSection.scroll.toFirstAriaLabel": "最初のメッセージへスクロール",
|
"messageSection.scroll.toFirstAriaLabel": "最初のメッセージへスクロール",
|
||||||
"messageSection.scroll.toLatestAriaLabel": "最新のメッセージへスクロール",
|
"messageSection.scroll.toLatestAriaLabel": "最新のメッセージへスクロール",
|
||||||
|
"messageSection.scroll.enableHoldAriaLabel": "長いアシスタント返信の保持を有効にする",
|
||||||
|
"messageSection.scroll.disableHoldAriaLabel": "長いアシスタント返信の保持を無効にする",
|
||||||
"messageSection.quote.addAsQuote": "引用として追加",
|
"messageSection.quote.addAsQuote": "引用として追加",
|
||||||
"messageSection.quote.addAsCode": "コードとして追加",
|
"messageSection.quote.addAsCode": "コードとして追加",
|
||||||
"messageSection.quote.copy": "コピー",
|
"messageSection.quote.copy": "コピー",
|
||||||
|
|||||||
@@ -18,6 +18,8 @@ export const messagingMessages = {
|
|||||||
"messageSection.loading.messages": "Загрузка сообщений…",
|
"messageSection.loading.messages": "Загрузка сообщений…",
|
||||||
"messageSection.scroll.toFirstAriaLabel": "Прокрутить к первому сообщению",
|
"messageSection.scroll.toFirstAriaLabel": "Прокрутить к первому сообщению",
|
||||||
"messageSection.scroll.toLatestAriaLabel": "Прокрутить к последнему сообщению",
|
"messageSection.scroll.toLatestAriaLabel": "Прокрутить к последнему сообщению",
|
||||||
|
"messageSection.scroll.enableHoldAriaLabel": "Включить удержание для длинных ответов ассистента",
|
||||||
|
"messageSection.scroll.disableHoldAriaLabel": "Выключить удержание для длинных ответов ассистента",
|
||||||
"messageSection.quote.addAsQuote": "Добавить как цитату",
|
"messageSection.quote.addAsQuote": "Добавить как цитату",
|
||||||
"messageSection.quote.addAsCode": "Добавить как код",
|
"messageSection.quote.addAsCode": "Добавить как код",
|
||||||
"messageSection.quote.copy": "Копировать",
|
"messageSection.quote.copy": "Копировать",
|
||||||
|
|||||||
@@ -18,6 +18,8 @@ export const messagingMessages = {
|
|||||||
"messageSection.loading.messages": "正在加载消息...",
|
"messageSection.loading.messages": "正在加载消息...",
|
||||||
"messageSection.scroll.toFirstAriaLabel": "滚动到第一条消息",
|
"messageSection.scroll.toFirstAriaLabel": "滚动到第一条消息",
|
||||||
"messageSection.scroll.toLatestAriaLabel": "滚动到最新消息",
|
"messageSection.scroll.toLatestAriaLabel": "滚动到最新消息",
|
||||||
|
"messageSection.scroll.enableHoldAriaLabel": "启用长助手回复保持",
|
||||||
|
"messageSection.scroll.disableHoldAriaLabel": "禁用长助手回复保持",
|
||||||
"messageSection.quote.addAsQuote": "作为引用添加",
|
"messageSection.quote.addAsQuote": "作为引用添加",
|
||||||
"messageSection.quote.addAsCode": "作为代码添加",
|
"messageSection.quote.addAsCode": "作为代码添加",
|
||||||
"messageSection.quote.copy": "复制",
|
"messageSection.quote.copy": "复制",
|
||||||
|
|||||||
@@ -55,6 +55,7 @@ export interface UiSettings {
|
|||||||
showKeyboardShortcutHints: boolean
|
showKeyboardShortcutHints: boolean
|
||||||
thinkingBlocksExpansion: ExpansionPreference
|
thinkingBlocksExpansion: ExpansionPreference
|
||||||
showTimelineTools: boolean
|
showTimelineTools: boolean
|
||||||
|
holdLongAssistantReplies: boolean
|
||||||
promptSubmitOnEnter: boolean
|
promptSubmitOnEnter: boolean
|
||||||
showPromptVoiceInput: boolean
|
showPromptVoiceInput: boolean
|
||||||
locale?: string
|
locale?: string
|
||||||
@@ -133,6 +134,7 @@ const defaultUiSettings: UiSettings = {
|
|||||||
showKeyboardShortcutHints: true,
|
showKeyboardShortcutHints: true,
|
||||||
thinkingBlocksExpansion: "expanded",
|
thinkingBlocksExpansion: "expanded",
|
||||||
showTimelineTools: true,
|
showTimelineTools: true,
|
||||||
|
holdLongAssistantReplies: true,
|
||||||
promptSubmitOnEnter: false,
|
promptSubmitOnEnter: false,
|
||||||
showPromptVoiceInput: true,
|
showPromptVoiceInput: true,
|
||||||
diffViewMode: "split",
|
diffViewMode: "split",
|
||||||
@@ -166,6 +168,7 @@ function normalizeUiSettings(input?: Partial<UiSettings> | null): UiSettings {
|
|||||||
sanitized.showKeyboardShortcutHints ?? defaultUiSettings.showKeyboardShortcutHints,
|
sanitized.showKeyboardShortcutHints ?? defaultUiSettings.showKeyboardShortcutHints,
|
||||||
thinkingBlocksExpansion: sanitized.thinkingBlocksExpansion ?? defaultUiSettings.thinkingBlocksExpansion,
|
thinkingBlocksExpansion: sanitized.thinkingBlocksExpansion ?? defaultUiSettings.thinkingBlocksExpansion,
|
||||||
showTimelineTools: sanitized.showTimelineTools ?? defaultUiSettings.showTimelineTools,
|
showTimelineTools: sanitized.showTimelineTools ?? defaultUiSettings.showTimelineTools,
|
||||||
|
holdLongAssistantReplies: sanitized.holdLongAssistantReplies ?? defaultUiSettings.holdLongAssistantReplies,
|
||||||
promptSubmitOnEnter: sanitized.promptSubmitOnEnter ?? defaultUiSettings.promptSubmitOnEnter,
|
promptSubmitOnEnter: sanitized.promptSubmitOnEnter ?? defaultUiSettings.promptSubmitOnEnter,
|
||||||
showPromptVoiceInput: sanitized.showPromptVoiceInput ?? defaultUiSettings.showPromptVoiceInput,
|
showPromptVoiceInput: sanitized.showPromptVoiceInput ?? defaultUiSettings.showPromptVoiceInput,
|
||||||
locale: sanitized.locale ?? defaultUiSettings.locale,
|
locale: sanitized.locale ?? defaultUiSettings.locale,
|
||||||
|
|||||||
@@ -242,6 +242,10 @@
|
|||||||
color: var(--accent-primary);
|
color: var(--accent-primary);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.message-scroll-button[data-active="false"] .message-scroll-icon--toggle {
|
||||||
|
color: var(--text-secondary);
|
||||||
|
}
|
||||||
|
|
||||||
.message-quote-popover {
|
.message-quote-popover {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
z-index: 5;
|
z-index: 5;
|
||||||
|
|||||||
Reference in New Issue
Block a user