Merge pull request #2661 from SiYue-ZO/feature/toggle-thought-visibility

feat: add thought visibility toggle
This commit is contained in:
美電球 2026-04-25 17:15:49 +08:00 committed by GitHub
commit 726ef4fa99
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 48 additions and 17 deletions

View file

@ -1,4 +1,5 @@
import { IconPlus } from "@tabler/icons-react" import { IconPlus } from "@tabler/icons-react"
import { useAtom } from "jotai"
import { type ChangeEvent, useEffect, useRef, useState } from "react" import { type ChangeEvent, useEffect, useRef, useState } from "react"
import { useTranslation } from "react-i18next" import { useTranslation } from "react-i18next"
import { toast } from "sonner" import { toast } from "sonner"
@ -15,12 +16,14 @@ import { TypingIndicator } from "@/components/chat/typing-indicator"
import { UserMessage } from "@/components/chat/user-message" import { UserMessage } from "@/components/chat/user-message"
import { PageHeader } from "@/components/page-header" import { PageHeader } from "@/components/page-header"
import { Button } from "@/components/ui/button" import { Button } from "@/components/ui/button"
import { Switch } from "@/components/ui/switch"
import { useChatModels } from "@/hooks/use-chat-models" import { useChatModels } from "@/hooks/use-chat-models"
import { useGateway } from "@/hooks/use-gateway" import { useGateway } from "@/hooks/use-gateway"
import { usePicoChat } from "@/hooks/use-pico-chat" import { usePicoChat } from "@/hooks/use-pico-chat"
import { useSessionHistory } from "@/hooks/use-session-history" import { useSessionHistory } from "@/hooks/use-session-history"
import type { ConnectionState } from "@/store/chat" import type { ConnectionState } from "@/store/chat"
import type { ChatAttachment } from "@/store/chat" import type { ChatAttachment } from "@/store/chat"
import { showThoughtsAtom } from "@/store/chat"
import type { GatewayState } from "@/store/gateway" import type { GatewayState } from "@/store/gateway"
const MAX_IMAGE_SIZE_BYTES = 7 * 1024 * 1024 const MAX_IMAGE_SIZE_BYTES = 7 * 1024 * 1024
@ -109,6 +112,7 @@ export function ChatPage() {
const [hasScrolled, setHasScrolled] = useState(false) const [hasScrolled, setHasScrolled] = useState(false)
const [input, setInput] = useState("") const [input, setInput] = useState("")
const [attachments, setAttachments] = useState<ChatAttachment[]>([]) const [attachments, setAttachments] = useState<ChatAttachment[]>([])
const [showThoughts, setShowThoughts] = useAtom(showThoughtsAtom)
const { const {
messages, messages,
@ -265,6 +269,18 @@ export function ChatPage() {
) )
} }
> >
<div className="hidden items-center gap-2 rounded-lg border border-border/60 px-3 py-1.5 sm:flex">
<span className="text-muted-foreground text-sm">
{t("chat.showThoughts")}
</span>
<Switch
checked={showThoughts}
onCheckedChange={setShowThoughts}
aria-label={t("chat.showThoughts")}
size="sm"
/>
</div>
<Button <Button
variant="secondary" variant="secondary"
size="sm" size="sm"
@ -306,23 +322,29 @@ export function ChatPage() {
/> />
)} )}
{messages.map((msg) => ( {messages.map((msg) => {
<div key={msg.id} className="flex w-full"> if (msg.kind === "thought" && !showThoughts) {
{msg.role === "assistant" ? ( return null
<AssistantMessage }
content={msg.content}
attachments={msg.attachments} return (
isThought={msg.kind === "thought"} <div key={msg.id} className="flex w-full">
timestamp={msg.timestamp} {msg.role === "assistant" ? (
/> <AssistantMessage
) : ( content={msg.content}
<UserMessage attachments={msg.attachments}
content={msg.content} isThought={msg.kind === "thought"}
attachments={msg.attachments} timestamp={msg.timestamp}
/> />
)} ) : (
</div> <UserMessage
))} content={msg.content}
attachments={msg.attachments}
/>
)}
</div>
)
})}
{isTyping && <TypingIndicator />} {isTyping && <TypingIndicator />}
</div> </div>

View file

@ -60,6 +60,7 @@
"step4": "Almost there..." "step4": "Almost there..."
}, },
"reasoningLabel": "Reasoning", "reasoningLabel": "Reasoning",
"showThoughts": "Show reasoning",
"toolLabel": "Tool", "toolLabel": "Tool",
"history": "History", "history": "History",
"noHistory": "No chat history yet", "noHistory": "No chat history yet",

View file

@ -60,6 +60,7 @@
"step4": "马上就好..." "step4": "马上就好..."
}, },
"reasoningLabel": "思考", "reasoningLabel": "思考",
"showThoughts": "展示思考过程",
"toolLabel": "工具", "toolLabel": "工具",
"history": "历史记录", "history": "历史记录",
"noHistory": "暂无对话历史", "noHistory": "暂无对话历史",

View file

@ -1,4 +1,5 @@
import { atom, getDefaultStore } from "jotai" import { atom, getDefaultStore } from "jotai"
import { atomWithStorage } from "jotai/utils"
import { import {
getInitialActiveSessionId, getInitialActiveSessionId,
@ -47,6 +48,8 @@ export interface ChatStoreState {
type ChatStorePatch = Partial<ChatStoreState> type ChatStorePatch = Partial<ChatStoreState>
const SHOW_THOUGHTS_STORAGE_KEY = "picoclaw:chat-show-thoughts"
const DEFAULT_CHAT_STATE: ChatStoreState = { const DEFAULT_CHAT_STATE: ChatStoreState = {
messages: [], messages: [],
connectionState: "disconnected", connectionState: "disconnected",
@ -56,6 +59,10 @@ const DEFAULT_CHAT_STATE: ChatStoreState = {
} }
export const chatAtom = atom<ChatStoreState>(DEFAULT_CHAT_STATE) export const chatAtom = atom<ChatStoreState>(DEFAULT_CHAT_STATE)
export const showThoughtsAtom = atomWithStorage<boolean>(
SHOW_THOUGHTS_STORAGE_KEY,
true,
)
const store = getDefaultStore() const store = getDefaultStore()