Add thinking expansion preference and step finish styling
This commit is contained in:
@@ -59,7 +59,7 @@ export function setupStorageIPC() {
|
|||||||
return await readConfigWithCache()
|
return await readConfigWithCache()
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
// Return empty config if file doesn't exist
|
// 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({
|
const PreferencesSchema = z.object({
|
||||||
showThinkingBlocks: z.boolean().default(false),
|
showThinkingBlocks: z.boolean().default(false),
|
||||||
|
thinkingBlocksExpansion: z.enum(["expanded", "collapsed"]).default("expanded"),
|
||||||
lastUsedBinary: z.string().optional(),
|
lastUsedBinary: z.string().optional(),
|
||||||
environmentVariables: z.record(z.string()).default({}),
|
environmentVariables: z.record(z.string()).default({}),
|
||||||
modelRecents: z.array(ModelPreferenceSchema).default([]),
|
modelRecents: z.array(ModelPreferenceSchema).default([]),
|
||||||
|
|||||||
@@ -51,6 +51,7 @@ const App: Component = () => {
|
|||||||
setDiffViewMode,
|
setDiffViewMode,
|
||||||
setToolOutputExpansion,
|
setToolOutputExpansion,
|
||||||
setDiagnosticsExpansion,
|
setDiagnosticsExpansion,
|
||||||
|
setThinkingBlocksExpansion,
|
||||||
} = useConfig()
|
} = useConfig()
|
||||||
const [escapeInDebounce, setEscapeInDebounce] = createSignal(false)
|
const [escapeInDebounce, setEscapeInDebounce] = createSignal(false)
|
||||||
const [launchErrorBinary, setLaunchErrorBinary] = createSignal<string | null>(null)
|
const [launchErrorBinary, setLaunchErrorBinary] = createSignal<string | null>(null)
|
||||||
@@ -210,6 +211,7 @@ const App: Component = () => {
|
|||||||
setDiffViewMode,
|
setDiffViewMode,
|
||||||
setToolOutputExpansion,
|
setToolOutputExpansion,
|
||||||
setDiagnosticsExpansion,
|
setDiagnosticsExpansion,
|
||||||
|
setThinkingBlocksExpansion,
|
||||||
handleNewInstanceRequest,
|
handleNewInstanceRequest,
|
||||||
handleCloseInstance,
|
handleCloseInstance,
|
||||||
handleNewSession,
|
handleNewSession,
|
||||||
|
|||||||
@@ -23,6 +23,10 @@ const codeNomadLogo = new URL("../images/CodeNomad-Icon.png", import.meta.url).h
|
|||||||
const messageItemCache = new Map<string, ContentDisplayItem>()
|
const messageItemCache = new Map<string, ContentDisplayItem>()
|
||||||
const toolItemCache = new Map<string, ToolDisplayItem>()
|
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 ToolCallPart = Extract<ClientPart, { type: "tool" }>
|
||||||
|
|
||||||
type ToolState = import("@opencode-ai/sdk").ToolState
|
type ToolState = import("@opencode-ai/sdk").ToolState
|
||||||
@@ -139,6 +143,7 @@ interface StepDisplayItem {
|
|||||||
key: string
|
key: string
|
||||||
part: ClientPart
|
part: ClientPart
|
||||||
messageInfo?: MessageInfo
|
messageInfo?: MessageInfo
|
||||||
|
accentColor?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
type ReasoningDisplayItem = {
|
type ReasoningDisplayItem = {
|
||||||
@@ -147,6 +152,7 @@ type ReasoningDisplayItem = {
|
|||||||
part: ClientPart
|
part: ClientPart
|
||||||
messageInfo?: MessageInfo
|
messageInfo?: MessageInfo
|
||||||
showAgentMeta?: boolean
|
showAgentMeta?: boolean
|
||||||
|
defaultExpanded: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
type MessageBlockItem = ContentDisplayItem | ToolDisplayItem | StepDisplayItem | ReasoningDisplayItem
|
type MessageBlockItem = ContentDisplayItem | ToolDisplayItem | StepDisplayItem | ReasoningDisplayItem
|
||||||
@@ -261,6 +267,7 @@ export default function MessageStreamV2(props: MessageStreamV2Props) {
|
|||||||
const infoMap = messageInfoMap()
|
const infoMap = messageInfoMap()
|
||||||
const showThinking = preferences().showThinkingBlocks
|
const showThinking = preferences().showThinkingBlocks
|
||||||
const showUsageMetrics = showUsagePreference()
|
const showUsageMetrics = showUsagePreference()
|
||||||
|
const thinkingDefaultExpanded = (preferences().thinkingBlocksExpansion ?? "expanded") === "expanded"
|
||||||
const revert = revertTarget()
|
const revert = revertTarget()
|
||||||
const instanceId = props.instanceId
|
const instanceId = props.instanceId
|
||||||
const blocks: MessageDisplayBlock[] = []
|
const blocks: MessageDisplayBlock[] = []
|
||||||
@@ -284,6 +291,8 @@ export default function MessageStreamV2(props: MessageStreamV2Props) {
|
|||||||
let segmentIndex = 0
|
let segmentIndex = 0
|
||||||
let pendingParts: ClientPart[] = []
|
let pendingParts: ClientPart[] = []
|
||||||
let agentMetaAttached = record.role !== "assistant"
|
let agentMetaAttached = record.role !== "assistant"
|
||||||
|
const defaultAccentColor = record.role === "user" ? USER_BORDER_COLOR : ASSISTANT_BORDER_COLOR
|
||||||
|
let lastAccentColor = defaultAccentColor
|
||||||
|
|
||||||
const flushContent = () => {
|
const flushContent = () => {
|
||||||
if (pendingParts.length === 0) return
|
if (pendingParts.length === 0) return
|
||||||
@@ -317,6 +326,7 @@ export default function MessageStreamV2(props: MessageStreamV2Props) {
|
|||||||
}
|
}
|
||||||
items.push(cached)
|
items.push(cached)
|
||||||
usedMessageKeys.add(segmentKey)
|
usedMessageKeys.add(segmentKey)
|
||||||
|
lastAccentColor = defaultAccentColor
|
||||||
pendingParts = []
|
pendingParts = []
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -349,6 +359,7 @@ export default function MessageStreamV2(props: MessageStreamV2Props) {
|
|||||||
}
|
}
|
||||||
items.push(toolItem)
|
items.push(toolItem)
|
||||||
usedToolKeys.add(cacheKey)
|
usedToolKeys.add(cacheKey)
|
||||||
|
lastAccentColor = TOOL_BORDER_COLOR
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -361,7 +372,9 @@ export default function MessageStreamV2(props: MessageStreamV2Props) {
|
|||||||
flushContent()
|
flushContent()
|
||||||
if (showUsageMetrics) {
|
if (showUsageMetrics) {
|
||||||
const key = makeInstanceCacheKey(instanceId, `${record.id}:${part.id ?? partIndex}:${part.type}`)
|
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
|
return
|
||||||
}
|
}
|
||||||
@@ -374,7 +387,15 @@ export default function MessageStreamV2(props: MessageStreamV2Props) {
|
|||||||
if (showAgentMeta) {
|
if (showAgentMeta) {
|
||||||
agentMetaAttached = true
|
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
|
return
|
||||||
}
|
}
|
||||||
@@ -707,6 +728,7 @@ export default function MessageStreamV2(props: MessageStreamV2Props) {
|
|||||||
part={(item as StepDisplayItem).part}
|
part={(item as StepDisplayItem).part}
|
||||||
messageInfo={(item as StepDisplayItem).messageInfo}
|
messageInfo={(item as StepDisplayItem).messageInfo}
|
||||||
showUsage={showUsagePreference()}
|
showUsage={showUsagePreference()}
|
||||||
|
borderColor={(item as StepDisplayItem).accentColor}
|
||||||
/>
|
/>
|
||||||
</Match>
|
</Match>
|
||||||
<Match when={item.type === "reasoning"}>
|
<Match when={item.type === "reasoning"}>
|
||||||
@@ -716,6 +738,7 @@ export default function MessageStreamV2(props: MessageStreamV2Props) {
|
|||||||
instanceId={props.instanceId}
|
instanceId={props.instanceId}
|
||||||
sessionId={props.sessionId}
|
sessionId={props.sessionId}
|
||||||
showAgentMeta={(item as ReasoningDisplayItem).showAgentMeta}
|
showAgentMeta={(item as ReasoningDisplayItem).showAgentMeta}
|
||||||
|
defaultExpanded={(item as ReasoningDisplayItem).defaultExpanded}
|
||||||
/>
|
/>
|
||||||
</Match>
|
</Match>
|
||||||
</Switch>
|
</Switch>
|
||||||
@@ -764,6 +787,7 @@ interface StepCardProps {
|
|||||||
messageInfo?: MessageInfo
|
messageInfo?: MessageInfo
|
||||||
showAgentMeta?: boolean
|
showAgentMeta?: boolean
|
||||||
showUsage?: boolean
|
showUsage?: boolean
|
||||||
|
borderColor?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
function StepCard(props: StepCardProps) {
|
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>>) => (
|
const renderUsageChips = (usage: NonNullable<ReturnType<typeof usageStats>>) => (
|
||||||
<div class="message-step-usage">
|
<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]">
|
<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 null
|
||||||
}
|
}
|
||||||
return (
|
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)}
|
{renderUsageChips(usage)}
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
@@ -886,10 +912,15 @@ interface ReasoningCardProps {
|
|||||||
instanceId: string
|
instanceId: string
|
||||||
sessionId: string
|
sessionId: string
|
||||||
showAgentMeta?: boolean
|
showAgentMeta?: boolean
|
||||||
|
defaultExpanded?: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
function ReasoningCard(props: ReasoningCardProps) {
|
function ReasoningCard(props: ReasoningCardProps) {
|
||||||
const [expanded, setExpanded] = createSignal(false)
|
const [expanded, setExpanded] = createSignal(Boolean(props.defaultExpanded))
|
||||||
|
|
||||||
|
createEffect(() => {
|
||||||
|
setExpanded(Boolean(props.defaultExpanded))
|
||||||
|
})
|
||||||
|
|
||||||
const timestamp = () => {
|
const timestamp = () => {
|
||||||
const value = props.messageInfo?.time?.created ?? (props.part as any)?.time?.start ?? Date.now()
|
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
|
setDiffViewMode: (mode: "split" | "unified") => void
|
||||||
setToolOutputExpansion: (mode: ExpansionPreference) => void
|
setToolOutputExpansion: (mode: ExpansionPreference) => void
|
||||||
setDiagnosticsExpansion: (mode: ExpansionPreference) => void
|
setDiagnosticsExpansion: (mode: ExpansionPreference) => void
|
||||||
|
setThinkingBlocksExpansion: (mode: ExpansionPreference) => void
|
||||||
handleNewInstanceRequest: () => void
|
handleNewInstanceRequest: () => void
|
||||||
handleCloseInstance: (instanceId: string) => Promise<void>
|
handleCloseInstance: (instanceId: string) => Promise<void>
|
||||||
handleNewSession: (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`,
|
label: () => `${options.preferences().showThinkingBlocks ? "Hide" : "Show"} Thinking Blocks`,
|
||||||
description: "Show/hide AI thinking process",
|
description: "Show/hide AI thinking process",
|
||||||
category: "System",
|
category: "System",
|
||||||
keywords: ["/thinking", "toggle", "show", "hide"],
|
keywords: ["/thinking", "thinking", "reasoning", "toggle", "show", "hide"],
|
||||||
action: options.toggleShowThinkingBlocks,
|
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({
|
commandRegistry.register({
|
||||||
id: "diff-view-split",
|
id: "diff-view-split",
|
||||||
label: () => `${(options.preferences().diffViewMode || "split") === "split" ? "✓ " : ""}Use Split Diff View`,
|
label: () => `${(options.preferences().diffViewMode || "split") === "split" ? "✓ " : ""}Use Split Diff View`,
|
||||||
|
|||||||
@@ -29,6 +29,7 @@ export type ExpansionPreference = "expanded" | "collapsed"
|
|||||||
|
|
||||||
export interface Preferences {
|
export interface Preferences {
|
||||||
showThinkingBlocks: boolean
|
showThinkingBlocks: boolean
|
||||||
|
thinkingBlocksExpansion: ExpansionPreference
|
||||||
lastUsedBinary?: string
|
lastUsedBinary?: string
|
||||||
environmentVariables: Record<string, string>
|
environmentVariables: Record<string, string>
|
||||||
modelRecents: ModelPreference[]
|
modelRecents: ModelPreference[]
|
||||||
@@ -56,6 +57,7 @@ const MAX_RECENT_MODELS = 5
|
|||||||
|
|
||||||
const defaultPreferences: Preferences = {
|
const defaultPreferences: Preferences = {
|
||||||
showThinkingBlocks: false,
|
showThinkingBlocks: false,
|
||||||
|
thinkingBlocksExpansion: "expanded",
|
||||||
environmentVariables: {},
|
environmentVariables: {},
|
||||||
modelRecents: [],
|
modelRecents: [],
|
||||||
diffViewMode: "split",
|
diffViewMode: "split",
|
||||||
@@ -88,6 +90,7 @@ function normalizePreferences(pref?: Partial<Preferences> & { agentModelSelectio
|
|||||||
|
|
||||||
return {
|
return {
|
||||||
showThinkingBlocks: sanitized.showThinkingBlocks ?? defaultPreferences.showThinkingBlocks,
|
showThinkingBlocks: sanitized.showThinkingBlocks ?? defaultPreferences.showThinkingBlocks,
|
||||||
|
thinkingBlocksExpansion: sanitized.thinkingBlocksExpansion ?? defaultPreferences.thinkingBlocksExpansion,
|
||||||
lastUsedBinary: sanitized.lastUsedBinary ?? defaultPreferences.lastUsedBinary,
|
lastUsedBinary: sanitized.lastUsedBinary ?? defaultPreferences.lastUsedBinary,
|
||||||
environmentVariables,
|
environmentVariables,
|
||||||
modelRecents,
|
modelRecents,
|
||||||
@@ -269,6 +272,11 @@ function setDiagnosticsExpansion(mode: ExpansionPreference): void {
|
|||||||
updatePreferences({ diagnosticsExpansion: mode })
|
updatePreferences({ diagnosticsExpansion: mode })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function setThinkingBlocksExpansion(mode: ExpansionPreference): void {
|
||||||
|
if (preferences().thinkingBlocksExpansion === mode) return
|
||||||
|
updatePreferences({ thinkingBlocksExpansion: mode })
|
||||||
|
}
|
||||||
|
|
||||||
function toggleShowThinkingBlocks(): void {
|
function toggleShowThinkingBlocks(): void {
|
||||||
updatePreferences({ showThinkingBlocks: !preferences().showThinkingBlocks })
|
updatePreferences({ showThinkingBlocks: !preferences().showThinkingBlocks })
|
||||||
}
|
}
|
||||||
@@ -381,6 +389,7 @@ interface ConfigContextValue {
|
|||||||
setDiffViewMode: typeof setDiffViewMode
|
setDiffViewMode: typeof setDiffViewMode
|
||||||
setToolOutputExpansion: typeof setToolOutputExpansion
|
setToolOutputExpansion: typeof setToolOutputExpansion
|
||||||
setDiagnosticsExpansion: typeof setDiagnosticsExpansion
|
setDiagnosticsExpansion: typeof setDiagnosticsExpansion
|
||||||
|
setThinkingBlocksExpansion: typeof setThinkingBlocksExpansion
|
||||||
addRecentFolder: typeof addRecentFolder
|
addRecentFolder: typeof addRecentFolder
|
||||||
removeRecentFolder: typeof removeRecentFolder
|
removeRecentFolder: typeof removeRecentFolder
|
||||||
addOpenCodeBinary: typeof addOpenCodeBinary
|
addOpenCodeBinary: typeof addOpenCodeBinary
|
||||||
@@ -412,6 +421,7 @@ const configContextValue: ConfigContextValue = {
|
|||||||
setDiffViewMode,
|
setDiffViewMode,
|
||||||
setToolOutputExpansion,
|
setToolOutputExpansion,
|
||||||
setDiagnosticsExpansion,
|
setDiagnosticsExpansion,
|
||||||
|
setThinkingBlocksExpansion,
|
||||||
addRecentFolder,
|
addRecentFolder,
|
||||||
removeRecentFolder,
|
removeRecentFolder,
|
||||||
addOpenCodeBinary,
|
addOpenCodeBinary,
|
||||||
@@ -480,7 +490,9 @@ export {
|
|||||||
setDiffViewMode,
|
setDiffViewMode,
|
||||||
setToolOutputExpansion,
|
setToolOutputExpansion,
|
||||||
setDiagnosticsExpansion,
|
setDiagnosticsExpansion,
|
||||||
|
setThinkingBlocksExpansion,
|
||||||
themePreference,
|
themePreference,
|
||||||
setThemePreference,
|
setThemePreference,
|
||||||
recordWorkspaceLaunch,
|
recordWorkspaceLaunch,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -27,6 +27,10 @@
|
|||||||
margin-bottom: 0.125rem;
|
margin-bottom: 0.125rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.message-step-finish-flush {
|
||||||
|
margin-top: -0.125rem;
|
||||||
|
}
|
||||||
|
|
||||||
.message-step-usage {
|
.message-step-usage {
|
||||||
@apply flex flex-wrap items-center gap-1 text-[10px] text-[var(--text-muted)];
|
@apply flex flex-wrap items-center gap-1 text-[10px] text-[var(--text-muted)];
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user