Implement toggle thinking blocks with localStorage persistence

- Add preferences store to manage user preferences in localStorage
- Toggle thinking blocks visibility via command palette (default: hidden)
- Dynamic command label shows current state (Show/Hide Thinking Blocks)
- Filter reasoning parts based on preference in message-stream
- Conditionally render reasoning parts in message-part component
- Support function labels in Command interface for dynamic text
This commit is contained in:
Shantur Rathore
2025-10-24 18:24:35 +01:00
parent e58462b926
commit be3acd6724
6 changed files with 72 additions and 20 deletions

View File

@@ -34,7 +34,8 @@ const CommandPalette: Component<CommandPaletteProps> = (props) => {
if (!q) return props.commands
return props.commands.filter((cmd) => {
const labelMatch = cmd.label.toLowerCase().includes(q)
const label = typeof cmd.label === "function" ? cmd.label() : cmd.label
const labelMatch = label.toLowerCase().includes(q)
const descMatch = cmd.description.toLowerCase().includes(q)
const keywordMatch = cmd.keywords?.some((k) => k.toLowerCase().includes(q))
const categoryMatch = cmd.category?.toLowerCase().includes(q)
@@ -190,7 +191,9 @@ const CommandPalette: Component<CommandPaletteProps> = (props) => {
onMouseEnter={() => setSelectedIndex(commandIndex)}
>
<div class="flex-1 min-w-0">
<div class="font-medium text-gray-900 dark:text-gray-100">{command.label}</div>
<div class="font-medium text-gray-900 dark:text-gray-100">
{typeof command.label === "function" ? command.label() : command.label}
</div>
<div class="text-sm text-gray-600 dark:text-gray-400 mt-0.5">
{command.description}
</div>

View File

@@ -3,6 +3,7 @@ import ToolCall from "./tool-call"
import { isItemExpanded, toggleItemExpanded } from "../stores/tool-call-state"
import { Markdown } from "./markdown"
import { useTheme } from "../lib/theme"
import { preferences } from "../stores/preferences"
interface MessagePartProps {
part: any
@@ -38,19 +39,21 @@ export default function MessagePart(props: MessagePartProps) {
</Match>
<Match when={partType() === "reasoning"}>
<div class="message-reasoning">
<div class="reasoning-container">
<div class="reasoning-header" onClick={handleReasoningClick}>
<span class="reasoning-icon">{isReasoningExpanded() ? "▼" : "▶"}</span>
<span class="reasoning-label">Reasoning</span>
</div>
<Show when={isReasoningExpanded()}>
<div class="message-text mt-2">
<Markdown content={props.part.text || ""} isDark={isDark()} />
<Show when={preferences().showThinkingBlocks}>
<div class="message-reasoning">
<div class="reasoning-container">
<div class="reasoning-header" onClick={handleReasoningClick}>
<span class="reasoning-icon">{isReasoningExpanded() ? "▼" : "▶"}</span>
<span class="reasoning-label">Reasoning</span>
</div>
</Show>
<Show when={isReasoningExpanded()}>
<div class="message-text mt-2">
<Markdown content={props.part.text || ""} isDark={isDark()} />
</div>
</Show>
</div>
</div>
</div>
</Show>
</Match>
</Switch>
)

View File

@@ -4,6 +4,7 @@ import MessageItem from "./message-item"
import ToolCall from "./tool-call"
import { sseManager } from "../lib/sse-manager"
import Kbd from "./kbd"
import { preferences } from "../stores/preferences"
interface MessageStreamProps {
instanceId: string
@@ -72,7 +73,7 @@ export default function MessageStream(props: MessageStreamProps) {
const textParts = message.parts.filter((p) => p.type === "text" && !p.synthetic)
const toolParts = message.parts.filter((p) => p.type === "tool")
const reasoningParts = message.parts.filter((p) => p.type === "reasoning")
const reasoningParts = preferences().showThinkingBlocks ? message.parts.filter((p) => p.type === "reasoning") : []
const isQueued = message.type === "user" && message.id > lastAssistantMessageId