Add thinking expansion preference and step finish styling

This commit is contained in:
Shantur Rathore
2025-11-27 13:39:03 +00:00
parent 5c82a2d653
commit c123714271
7 changed files with 74 additions and 7 deletions

View File

@@ -59,7 +59,7 @@ export function setupStorageIPC() {
return await readConfigWithCache()
} catch (error) {
// Return empty config if file doesn't exist
return JSON.stringify({ preferences: { showThinkingBlocks: false }, recentFolders: [] }, null, 2)
return JSON.stringify({ preferences: { showThinkingBlocks: false, thinkingBlocksExpansion: "expanded" }, recentFolders: [] }, null, 2)
}
})

View File

@@ -10,6 +10,7 @@ const AgentModelSelectionsSchema = z.record(z.string(), AgentModelSelectionSchem
const PreferencesSchema = z.object({
showThinkingBlocks: z.boolean().default(false),
thinkingBlocksExpansion: z.enum(["expanded", "collapsed"]).default("expanded"),
lastUsedBinary: z.string().optional(),
environmentVariables: z.record(z.string()).default({}),
modelRecents: z.array(ModelPreferenceSchema).default([]),

View File

@@ -51,6 +51,7 @@ const App: Component = () => {
setDiffViewMode,
setToolOutputExpansion,
setDiagnosticsExpansion,
setThinkingBlocksExpansion,
} = useConfig()
const [escapeInDebounce, setEscapeInDebounce] = createSignal(false)
const [launchErrorBinary, setLaunchErrorBinary] = createSignal<string | null>(null)
@@ -210,6 +211,7 @@ const App: Component = () => {
setDiffViewMode,
setToolOutputExpansion,
setDiagnosticsExpansion,
setThinkingBlocksExpansion,
handleNewInstanceRequest,
handleCloseInstance,
handleNewSession,

View File

@@ -23,6 +23,10 @@ const codeNomadLogo = new URL("../images/CodeNomad-Icon.png", import.meta.url).h
const messageItemCache = new Map<string, ContentDisplayItem>()
const toolItemCache = new Map<string, ToolDisplayItem>()
const USER_BORDER_COLOR = "var(--message-user-border)"
const ASSISTANT_BORDER_COLOR = "var(--message-assistant-border)"
const TOOL_BORDER_COLOR = "var(--message-tool-border)"
type ToolCallPart = Extract<ClientPart, { type: "tool" }>
type ToolState = import("@opencode-ai/sdk").ToolState
@@ -139,6 +143,7 @@ interface StepDisplayItem {
key: string
part: ClientPart
messageInfo?: MessageInfo
accentColor?: string
}
type ReasoningDisplayItem = {
@@ -147,6 +152,7 @@ type ReasoningDisplayItem = {
part: ClientPart
messageInfo?: MessageInfo
showAgentMeta?: boolean
defaultExpanded: boolean
}
type MessageBlockItem = ContentDisplayItem | ToolDisplayItem | StepDisplayItem | ReasoningDisplayItem
@@ -261,6 +267,7 @@ export default function MessageStreamV2(props: MessageStreamV2Props) {
const infoMap = messageInfoMap()
const showThinking = preferences().showThinkingBlocks
const showUsageMetrics = showUsagePreference()
const thinkingDefaultExpanded = (preferences().thinkingBlocksExpansion ?? "expanded") === "expanded"
const revert = revertTarget()
const instanceId = props.instanceId
const blocks: MessageDisplayBlock[] = []
@@ -284,6 +291,8 @@ export default function MessageStreamV2(props: MessageStreamV2Props) {
let segmentIndex = 0
let pendingParts: ClientPart[] = []
let agentMetaAttached = record.role !== "assistant"
const defaultAccentColor = record.role === "user" ? USER_BORDER_COLOR : ASSISTANT_BORDER_COLOR
let lastAccentColor = defaultAccentColor
const flushContent = () => {
if (pendingParts.length === 0) return
@@ -317,6 +326,7 @@ export default function MessageStreamV2(props: MessageStreamV2Props) {
}
items.push(cached)
usedMessageKeys.add(segmentKey)
lastAccentColor = defaultAccentColor
pendingParts = []
}
@@ -349,6 +359,7 @@ export default function MessageStreamV2(props: MessageStreamV2Props) {
}
items.push(toolItem)
usedToolKeys.add(cacheKey)
lastAccentColor = TOOL_BORDER_COLOR
return
}
@@ -361,7 +372,9 @@ export default function MessageStreamV2(props: MessageStreamV2Props) {
flushContent()
if (showUsageMetrics) {
const key = makeInstanceCacheKey(instanceId, `${record.id}:${part.id ?? partIndex}:${part.type}`)
items.push({ type: part.type, key, part, messageInfo })
const accentColor = lastAccentColor || defaultAccentColor
items.push({ type: part.type, key, part, messageInfo, accentColor })
lastAccentColor = accentColor
}
return
}
@@ -374,7 +387,15 @@ export default function MessageStreamV2(props: MessageStreamV2Props) {
if (showAgentMeta) {
agentMetaAttached = true
}
items.push({ type: "reasoning", key, part, messageInfo, showAgentMeta })
items.push({
type: "reasoning",
key,
part,
messageInfo,
showAgentMeta,
defaultExpanded: thinkingDefaultExpanded,
})
lastAccentColor = ASSISTANT_BORDER_COLOR
}
return
}
@@ -707,6 +728,7 @@ export default function MessageStreamV2(props: MessageStreamV2Props) {
part={(item as StepDisplayItem).part}
messageInfo={(item as StepDisplayItem).messageInfo}
showUsage={showUsagePreference()}
borderColor={(item as StepDisplayItem).accentColor}
/>
</Match>
<Match when={item.type === "reasoning"}>
@@ -716,6 +738,7 @@ export default function MessageStreamV2(props: MessageStreamV2Props) {
instanceId={props.instanceId}
sessionId={props.sessionId}
showAgentMeta={(item as ReasoningDisplayItem).showAgentMeta}
defaultExpanded={(item as ReasoningDisplayItem).defaultExpanded}
/>
</Match>
</Switch>
@@ -764,6 +787,7 @@ interface StepCardProps {
messageInfo?: MessageInfo
showAgentMeta?: boolean
showUsage?: boolean
borderColor?: string
}
function StepCard(props: StepCardProps) {
@@ -815,6 +839,8 @@ function StepCard(props: StepCardProps) {
}
}
const finishStyle = () => (props.borderColor ? { "border-left-color": props.borderColor } : undefined)
const renderUsageChips = (usage: NonNullable<ReturnType<typeof usageStats>>) => (
<div class="message-step-usage">
<div class="inline-flex items-center gap-1 rounded-full border border-[var(--border-base)] px-2 py-0.5 text-[10px]">
@@ -850,7 +876,7 @@ function StepCard(props: StepCardProps) {
return null
}
return (
<div class={`message-step-card message-step-finish`}>
<div class={`message-step-card message-step-finish message-step-finish-flush`} style={finishStyle()}>
{renderUsageChips(usage)}
</div>
)
@@ -886,10 +912,15 @@ interface ReasoningCardProps {
instanceId: string
sessionId: string
showAgentMeta?: boolean
defaultExpanded?: boolean
}
function ReasoningCard(props: ReasoningCardProps) {
const [expanded, setExpanded] = createSignal(false)
const [expanded, setExpanded] = createSignal(Boolean(props.defaultExpanded))
createEffect(() => {
setExpanded(Boolean(props.defaultExpanded))
})
const timestamp = () => {
const value = props.messageInfo?.time?.created ?? (props.part as any)?.time?.start ?? Date.now()

View File

@@ -24,6 +24,7 @@ export interface UseCommandsOptions {
setDiffViewMode: (mode: "split" | "unified") => void
setToolOutputExpansion: (mode: ExpansionPreference) => void
setDiagnosticsExpansion: (mode: ExpansionPreference) => void
setThinkingBlocksExpansion: (mode: ExpansionPreference) => void
handleNewInstanceRequest: () => void
handleCloseInstance: (instanceId: string) => Promise<void>
handleNewSession: (instanceId: string) => Promise<void>
@@ -385,10 +386,26 @@ export function useCommands(options: UseCommandsOptions) {
label: () => `${options.preferences().showThinkingBlocks ? "Hide" : "Show"} Thinking Blocks`,
description: "Show/hide AI thinking process",
category: "System",
keywords: ["/thinking", "toggle", "show", "hide"],
keywords: ["/thinking", "thinking", "reasoning", "toggle", "show", "hide"],
action: options.toggleShowThinkingBlocks,
})
commandRegistry.register({
id: "thinking-default-visibility",
label: () => {
const mode = options.preferences().thinkingBlocksExpansion ?? "expanded"
return `Thinking Blocks Default · ${mode === "expanded" ? "Expanded" : "Collapsed"}`
},
description: "Toggle whether thinking blocks start expanded",
category: "System",
keywords: ["/thinking", "thinking", "reasoning", "expand", "collapse", "default"],
action: () => {
const mode = options.preferences().thinkingBlocksExpansion ?? "expanded"
const next: ExpansionPreference = mode === "expanded" ? "collapsed" : "expanded"
options.setThinkingBlocksExpansion(next)
},
})
commandRegistry.register({
id: "diff-view-split",
label: () => `${(options.preferences().diffViewMode || "split") === "split" ? "✓ " : ""}Use Split Diff View`,

View File

@@ -29,6 +29,7 @@ export type ExpansionPreference = "expanded" | "collapsed"
export interface Preferences {
showThinkingBlocks: boolean
thinkingBlocksExpansion: ExpansionPreference
lastUsedBinary?: string
environmentVariables: Record<string, string>
modelRecents: ModelPreference[]
@@ -56,6 +57,7 @@ const MAX_RECENT_MODELS = 5
const defaultPreferences: Preferences = {
showThinkingBlocks: false,
thinkingBlocksExpansion: "expanded",
environmentVariables: {},
modelRecents: [],
diffViewMode: "split",
@@ -88,6 +90,7 @@ function normalizePreferences(pref?: Partial<Preferences> & { agentModelSelectio
return {
showThinkingBlocks: sanitized.showThinkingBlocks ?? defaultPreferences.showThinkingBlocks,
thinkingBlocksExpansion: sanitized.thinkingBlocksExpansion ?? defaultPreferences.thinkingBlocksExpansion,
lastUsedBinary: sanitized.lastUsedBinary ?? defaultPreferences.lastUsedBinary,
environmentVariables,
modelRecents,
@@ -269,6 +272,11 @@ function setDiagnosticsExpansion(mode: ExpansionPreference): void {
updatePreferences({ diagnosticsExpansion: mode })
}
function setThinkingBlocksExpansion(mode: ExpansionPreference): void {
if (preferences().thinkingBlocksExpansion === mode) return
updatePreferences({ thinkingBlocksExpansion: mode })
}
function toggleShowThinkingBlocks(): void {
updatePreferences({ showThinkingBlocks: !preferences().showThinkingBlocks })
}
@@ -381,6 +389,7 @@ interface ConfigContextValue {
setDiffViewMode: typeof setDiffViewMode
setToolOutputExpansion: typeof setToolOutputExpansion
setDiagnosticsExpansion: typeof setDiagnosticsExpansion
setThinkingBlocksExpansion: typeof setThinkingBlocksExpansion
addRecentFolder: typeof addRecentFolder
removeRecentFolder: typeof removeRecentFolder
addOpenCodeBinary: typeof addOpenCodeBinary
@@ -412,6 +421,7 @@ const configContextValue: ConfigContextValue = {
setDiffViewMode,
setToolOutputExpansion,
setDiagnosticsExpansion,
setThinkingBlocksExpansion,
addRecentFolder,
removeRecentFolder,
addOpenCodeBinary,
@@ -480,7 +490,9 @@ export {
setDiffViewMode,
setToolOutputExpansion,
setDiagnosticsExpansion,
setThinkingBlocksExpansion,
themePreference,
setThemePreference,
recordWorkspaceLaunch,
}
}

View File

@@ -27,6 +27,10 @@
margin-bottom: 0.125rem;
}
.message-step-finish-flush {
margin-top: -0.125rem;
}
.message-step-usage {
@apply flex flex-wrap items-center gap-1 text-[10px] text-[var(--text-muted)];
}