Fix thinking toggle defaults
This commit is contained in:
@@ -38,7 +38,6 @@ export const Flag = {
|
|||||||
),
|
),
|
||||||
OPENCODE_EXPERIMENTAL_DISABLE_COPY_ON_SELECT:
|
OPENCODE_EXPERIMENTAL_DISABLE_COPY_ON_SELECT:
|
||||||
copy === undefined ? process.platform === "win32" : truthy("OPENCODE_EXPERIMENTAL_DISABLE_COPY_ON_SELECT"),
|
copy === undefined ? process.platform === "win32" : truthy("OPENCODE_EXPERIMENTAL_DISABLE_COPY_ON_SELECT"),
|
||||||
OPENCODE_EXPERIMENTAL_MINIMAL_THINKING: truthy("OPENCODE_EXPERIMENTAL_MINIMAL_THINKING"),
|
|
||||||
OPENCODE_MODELS_URL: process.env["OPENCODE_MODELS_URL"],
|
OPENCODE_MODELS_URL: process.env["OPENCODE_MODELS_URL"],
|
||||||
OPENCODE_MODELS_PATH: process.env["OPENCODE_MODELS_PATH"],
|
OPENCODE_MODELS_PATH: process.env["OPENCODE_MODELS_PATH"],
|
||||||
OPENCODE_DB: process.env["OPENCODE_DB"],
|
OPENCODE_DB: process.env["OPENCODE_DB"],
|
||||||
|
|||||||
@@ -1,10 +1,9 @@
|
|||||||
import { createMemo, type Setter } from "solid-js"
|
import { createMemo, type Setter } from "solid-js"
|
||||||
import { Flag } from "@opencode-ai/core/flag/flag"
|
|
||||||
import { useKV } from "./kv"
|
import { useKV } from "./kv"
|
||||||
|
|
||||||
export type ThinkingMode = "show" | "minimal" | "hide"
|
export type ThinkingMode = "show" | "hide"
|
||||||
|
|
||||||
const MODES: readonly ThinkingMode[] = ["show", "minimal", "hide"] as const
|
const MODES: readonly ThinkingMode[] = ["show", "hide"] as const
|
||||||
|
|
||||||
// OpenAI's Responses API surfaces reasoning summaries that start with a bolded
|
// OpenAI's Responses API surfaces reasoning summaries that start with a bolded
|
||||||
// title line: "**Inspecting PR workflow**\n\n<body>". GitHub Copilot routes
|
// title line: "**Inspecting PR workflow**\n\n<body>". GitHub Copilot routes
|
||||||
@@ -20,7 +19,7 @@ export function isThinkingMode(value: unknown): value is ThinkingMode {
|
|||||||
return typeof value === "string" && (MODES as readonly string[]).includes(value)
|
return typeof value === "string" && (MODES as readonly string[]).includes(value)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Cycle order matches the slash command: show → minimal → hide → show.
|
// Cycle order matches the slash command: show → hide → show.
|
||||||
export function nextThinkingMode(current: ThinkingMode): ThinkingMode {
|
export function nextThinkingMode(current: ThinkingMode): ThinkingMode {
|
||||||
const idx = MODES.indexOf(current)
|
const idx = MODES.indexOf(current)
|
||||||
return MODES[(idx + 1) % MODES.length] ?? "show"
|
return MODES[(idx + 1) % MODES.length] ?? "show"
|
||||||
@@ -33,7 +32,7 @@ export function useThinkingMode() {
|
|||||||
// The KVProvider only renders children once kv.ready, so reads here are safe.
|
// The KVProvider only renders children once kv.ready, so reads here are safe.
|
||||||
const hadStored = kv.get("thinking_mode") !== undefined
|
const hadStored = kv.get("thinking_mode") !== undefined
|
||||||
const legacy = kv.get("thinking_visibility")
|
const legacy = kv.get("thinking_visibility")
|
||||||
const [stored, setStored] = kv.signal<ThinkingMode>("thinking_mode", "minimal")
|
const [stored, setStored] = kv.signal<ThinkingMode>("thinking_mode", "hide")
|
||||||
|
|
||||||
// The kv signal exposes its setter typed as `Setter<T>` which carries Solid's
|
// The kv signal exposes its setter typed as `Setter<T>` which carries Solid's
|
||||||
// overload set; passing an updater fn through a property access loses the
|
// overload set; passing an updater fn through a property access loses the
|
||||||
@@ -47,21 +46,21 @@ export function useThinkingMode() {
|
|||||||
|
|
||||||
// Preserve previous experience for users who had explicitly toggled the
|
// Preserve previous experience for users who had explicitly toggled the
|
||||||
// legacy `thinking_visibility` boolean. First-time users (no legacy key)
|
// legacy `thinking_visibility` boolean. First-time users (no legacy key)
|
||||||
// get the new "minimal" default.
|
// get the new "hide" default (collapsed thinking).
|
||||||
if (!hadStored) {
|
if (!hadStored) {
|
||||||
if (legacy === true) set("show")
|
if (legacy === true) set("show")
|
||||||
else if (legacy === false) set("hide")
|
else if (legacy === false) set("hide")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ((stored() as string) === "minimal") set("hide")
|
||||||
|
|
||||||
const mode = createMemo<ThinkingMode>(() => {
|
const mode = createMemo<ThinkingMode>(() => {
|
||||||
if (Flag.OPENCODE_EXPERIMENTAL_MINIMAL_THINKING) return "minimal"
|
|
||||||
const value = stored()
|
const value = stored()
|
||||||
return isThinkingMode(value) ? value : "minimal"
|
return isThinkingMode(value) ? value : "hide"
|
||||||
})
|
})
|
||||||
|
|
||||||
return {
|
return {
|
||||||
mode,
|
mode,
|
||||||
set,
|
set,
|
||||||
locked: () => Flag.OPENCODE_EXPERIMENTAL_MINIMAL_THINKING === true,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -392,7 +392,7 @@ function AssistantReasoning(props: {
|
|||||||
const thinking = useThinkingMode()
|
const thinking = useThinkingMode()
|
||||||
const [expanded, setExpanded] = createSignal(false)
|
const [expanded, setExpanded] = createSignal(false)
|
||||||
const content = createMemo(() => props.part.text.replace("[REDACTED]", "").trim())
|
const content = createMemo(() => props.part.text.replace("[REDACTED]", "").trim())
|
||||||
const inMinimal = createMemo(() => thinking.mode() === "minimal")
|
const inMinimal = createMemo(() => thinking.mode() === "hide")
|
||||||
// v2 reasoning parts have no per-part `time.end` (see SessionMessageAssistantReasoning
|
// v2 reasoning parts have no per-part `time.end` (see SessionMessageAssistantReasoning
|
||||||
// in the v2 SDK); we settle on parent-message completion instead.
|
// in the v2 SDK); we settle on parent-message completion instead.
|
||||||
const isDone = createMemo(() => props.completedAt() !== undefined)
|
const isDone = createMemo(() => props.completedAt() !== undefined)
|
||||||
@@ -404,7 +404,7 @@ function AssistantReasoning(props: {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Show when={content() && thinking.mode() !== "hide"}>
|
<Show when={content()}>
|
||||||
<Switch>
|
<Switch>
|
||||||
<Match when={!inMinimal() || expanded()}>
|
<Match when={!inMinimal() || expanded()}>
|
||||||
<box
|
<box
|
||||||
|
|||||||
@@ -218,7 +218,7 @@ export function Session() {
|
|||||||
const [conceal, setConceal] = createSignal(true)
|
const [conceal, setConceal] = createSignal(true)
|
||||||
const thinking = useThinkingMode()
|
const thinking = useThinkingMode()
|
||||||
const thinkingMode = thinking.mode
|
const thinkingMode = thinking.mode
|
||||||
const showThinking = createMemo(() => thinkingMode() !== "hide")
|
const showThinking = createMemo(() => true)
|
||||||
const [timestamps, setTimestamps] = kv.signal<"hide" | "show">("timestamps", "hide")
|
const [timestamps, setTimestamps] = kv.signal<"hide" | "show">("timestamps", "hide")
|
||||||
const [showDetails, setShowDetails] = kv.signal("tool_details_visibility", true)
|
const [showDetails, setShowDetails] = kv.signal("tool_details_visibility", true)
|
||||||
const [showAssistantMetadata, _setShowAssistantMetadata] = kv.signal("assistant_metadata_visibility", true)
|
const [showAssistantMetadata, _setShowAssistantMetadata] = kv.signal("assistant_metadata_visibility", true)
|
||||||
@@ -689,9 +689,8 @@ export function Session() {
|
|||||||
{
|
{
|
||||||
title: (() => {
|
title: (() => {
|
||||||
const next = nextThinkingMode(thinkingMode())
|
const next = nextThinkingMode(thinkingMode())
|
||||||
if (next === "minimal") return "Switch thinking to minimal"
|
if (next === "hide") return "Collapse thinking"
|
||||||
if (next === "hide") return "Hide thinking"
|
return "Expand thinking"
|
||||||
return "Show thinking"
|
|
||||||
})(),
|
})(),
|
||||||
value: "session.toggle.thinking",
|
value: "session.toggle.thinking",
|
||||||
category: "Session",
|
category: "Session",
|
||||||
@@ -700,16 +699,6 @@ export function Session() {
|
|||||||
aliases: ["toggle-thinking"],
|
aliases: ["toggle-thinking"],
|
||||||
},
|
},
|
||||||
run: () => {
|
run: () => {
|
||||||
// Env override forces minimal for the process. Updating KV here would
|
|
||||||
// silently diverge from what's rendered; tell the user instead.
|
|
||||||
if (thinking.locked()) {
|
|
||||||
toast.show({
|
|
||||||
message: "Thinking mode is locked to minimal by OPENCODE_EXPERIMENTAL_MINIMAL_THINKING",
|
|
||||||
variant: "info",
|
|
||||||
})
|
|
||||||
dialog.clear()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
thinking.set(nextThinkingMode(thinkingMode()))
|
thinking.set(nextThinkingMode(thinkingMode()))
|
||||||
dialog.clear()
|
dialog.clear()
|
||||||
},
|
},
|
||||||
@@ -1512,7 +1501,7 @@ const PART_MAPPING = {
|
|||||||
function ReasoningPart(props: { last: boolean; part: ReasoningPart; message: AssistantMessage }) {
|
function ReasoningPart(props: { last: boolean; part: ReasoningPart; message: AssistantMessage }) {
|
||||||
const { theme, subtleSyntax } = useTheme()
|
const { theme, subtleSyntax } = useTheme()
|
||||||
const ctx = use()
|
const ctx = use()
|
||||||
// Collapsed by default in minimal mode: a single line throughout, so the
|
// Collapsed by default in hide mode: a single line throughout, so the
|
||||||
// layout never shifts. Click to open the full markdown block, click to close.
|
// layout never shifts. Click to open the full markdown block, click to close.
|
||||||
const [expanded, setExpanded] = createSignal(false)
|
const [expanded, setExpanded] = createSignal(false)
|
||||||
|
|
||||||
@@ -1523,7 +1512,7 @@ function ReasoningPart(props: { last: boolean; part: ReasoningPart; message: Ass
|
|||||||
// Reasoning is finalized when the server sets `time.end` (see processor.ts).
|
// Reasoning is finalized when the server sets `time.end` (see processor.ts).
|
||||||
// Flips independently of the parent message completing.
|
// Flips independently of the parent message completing.
|
||||||
const isDone = createMemo(() => props.part.time.end !== undefined)
|
const isDone = createMemo(() => props.part.time.end !== undefined)
|
||||||
const inMinimal = createMemo(() => ctx.thinkingMode() === "minimal")
|
const inMinimal = createMemo(() => ctx.thinkingMode() === "hide")
|
||||||
const duration = createMemo(() => {
|
const duration = createMemo(() => {
|
||||||
const end = props.part.time.end
|
const end = props.part.time.end
|
||||||
return end === undefined ? 0 : Math.max(0, end - props.part.time.start)
|
return end === undefined ? 0 : Math.max(0, end - props.part.time.start)
|
||||||
@@ -1539,10 +1528,10 @@ function ReasoningPart(props: { last: boolean; part: ReasoningPart; message: Ass
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Show when={content() && ctx.thinkingMode() !== "hide"}>
|
<Show when={content()}>
|
||||||
<Switch>
|
<Switch>
|
||||||
<Match when={!inMinimal() || expanded()}>
|
<Match when={!inMinimal() || expanded()}>
|
||||||
{/* Full markdown block: `show` mode, or `minimal` after the user opens it. */}
|
{/* Full markdown block: `show` mode, or `hide` after the user opens it. */}
|
||||||
<box
|
<box
|
||||||
id={"text-" + props.part.id}
|
id={"text-" + props.part.id}
|
||||||
paddingLeft={2}
|
paddingLeft={2}
|
||||||
@@ -1558,7 +1547,7 @@ function ReasoningPart(props: { last: boolean; part: ReasoningPart; message: Ass
|
|||||||
drawUnstyledText={false}
|
drawUnstyledText={false}
|
||||||
streaming={true}
|
streaming={true}
|
||||||
syntaxStyle={subtleSyntax()}
|
syntaxStyle={subtleSyntax()}
|
||||||
content={(inMinimal() ? "▼ " : "") + "_Thinking:_ " + content()}
|
content={(inMinimal() ? "▼ " : "") + (isDone() ? "_Thought:_ " : "_Thinking:_ ") + content()}
|
||||||
conceal={ctx.conceal()}
|
conceal={ctx.conceal()}
|
||||||
fg={theme.textMuted}
|
fg={theme.textMuted}
|
||||||
/>
|
/>
|
||||||
|
|||||||
Reference in New Issue
Block a user