Add thinking expansion preference and step finish styling
This commit is contained in:
@@ -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)
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
@@ -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([]),
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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`,
|
||||
|
||||
@@ -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,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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)];
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user