mini: add quiet transcript settings (#38152)
This commit is contained in:
parent
6f4b9504e5
commit
dd6c95fdc7
22 changed files with 398 additions and 52 deletions
|
|
@ -389,7 +389,6 @@ describe("run entry body", () => {
|
|||
type: "text",
|
||||
content: "$ pwd",
|
||||
})
|
||||
|
||||
expect(
|
||||
entryBody(
|
||||
commit({
|
||||
|
|
@ -411,6 +410,15 @@ describe("run entry body", () => {
|
|||
})
|
||||
})
|
||||
|
||||
test("hides shell tool output but not direct shell output", () => {
|
||||
const output = commit({ kind: "tool", text: "output", phase: "progress", source: "tool", tool: "shell" })
|
||||
expect(entryBody(output, { shellOutput: false })).toEqual({ type: "none" })
|
||||
expect(entryBody({ ...output, shell: { command: "pwd" } }, { shellOutput: false })).toEqual({
|
||||
type: "text",
|
||||
content: "\noutput",
|
||||
})
|
||||
})
|
||||
|
||||
test("falls back to patch summary when patch has no visible diff items", () => {
|
||||
expect(
|
||||
entryBody(
|
||||
|
|
|
|||
|
|
@ -55,6 +55,7 @@ test("down opens subagents from an empty prompt", async () => {
|
|||
subagent={subagents}
|
||||
theme={() => RUN_THEME_FALLBACK}
|
||||
tuiConfig={config}
|
||||
miniSettings={() => ({ thinking: "hide", shell_output: "hide" })}
|
||||
onSubmit={() => true}
|
||||
onPermissionReply={() => {}}
|
||||
onFormReply={() => {}}
|
||||
|
|
@ -69,6 +70,7 @@ test("down opens subagents from an empty prompt", async () => {
|
|||
onRows={() => {}}
|
||||
onLayout={() => {}}
|
||||
onStatus={() => {}}
|
||||
onMiniSettingChange={() => {}}
|
||||
/>
|
||||
</Keymap.Provider>
|
||||
)
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ import {
|
|||
RunCommandMenuBody,
|
||||
RunModelSelectBody,
|
||||
RunQueuedPromptSelectBody,
|
||||
RunSettingsBody,
|
||||
RunSkillSelectBody,
|
||||
RunSubagentSelectBody,
|
||||
RunVariantSelectBody,
|
||||
|
|
@ -23,6 +24,8 @@ import type {
|
|||
FooterSubagentState,
|
||||
FooterSubagentTab,
|
||||
FooterView,
|
||||
MiniSettingChange,
|
||||
MiniSettings,
|
||||
RunCommand,
|
||||
RunInput,
|
||||
RunPrompt,
|
||||
|
|
@ -117,6 +120,8 @@ async function renderFooter(
|
|||
onSubmit?: (prompt: RunPrompt) => boolean
|
||||
view?: FooterView
|
||||
onFormReply?: (input: unknown) => void
|
||||
miniSettings?: MiniSettings
|
||||
onMiniSettingChange?: (change: MiniSettingChange) => void
|
||||
} = {},
|
||||
) {
|
||||
const [view, setView] = createSignal<FooterView>(input.view ?? { type: "prompt" })
|
||||
|
|
@ -125,6 +130,7 @@ async function renderFooter(
|
|||
)
|
||||
const state = footerState(input.state)
|
||||
const config = input.tuiConfig ?? tuiConfig
|
||||
const [miniSettings] = createSignal<MiniSettings>(input.miniSettings ?? { thinking: "hide", shell_output: "hide" })
|
||||
function Harness() {
|
||||
return (
|
||||
<Keymap.Provider config={config}>
|
||||
|
|
@ -143,6 +149,7 @@ async function renderFooter(
|
|||
subagent={subagents}
|
||||
theme={input.theme ?? (() => RUN_THEME_FALLBACK)}
|
||||
tuiConfig={config}
|
||||
miniSettings={miniSettings}
|
||||
onSubmit={input.onSubmit ?? (() => true)}
|
||||
onPermissionReply={() => {}}
|
||||
onFormReply={(value) => input.onFormReply?.(value)}
|
||||
|
|
@ -157,6 +164,7 @@ async function renderFooter(
|
|||
onRows={() => {}}
|
||||
onLayout={() => {}}
|
||||
onStatus={() => {}}
|
||||
onMiniSettingChange={(change) => input.onMiniSettingChange?.(change)}
|
||||
/>
|
||||
</Keymap.Provider>
|
||||
)
|
||||
|
|
@ -369,6 +377,7 @@ test("direct command panel renders grouped command palette", async () => {
|
|||
onQueued={() => {}}
|
||||
onVariant={() => {}}
|
||||
onVariantCycle={() => {}}
|
||||
onSettings={() => {}}
|
||||
onCommand={() => {}}
|
||||
onNew={() => {}}
|
||||
onExit={() => {}}
|
||||
|
|
@ -408,6 +417,40 @@ test("direct command panel renders grouped command palette", async () => {
|
|||
}
|
||||
})
|
||||
|
||||
test("direct settings panel changes Mini transcript preferences", async () => {
|
||||
const [settings, setSettings] = createSignal<MiniSettings>({ thinking: "hide", shell_output: "hide" })
|
||||
const app = await testRender(
|
||||
() => (
|
||||
<box width={100} height={RUN_COMMAND_PANEL_ROWS}>
|
||||
<RunSettingsBody
|
||||
theme={() => RUN_THEME_FALLBACK.footer}
|
||||
settings={settings}
|
||||
onClose={() => {}}
|
||||
onChange={(change) => {
|
||||
setSettings((current) => ({ ...current, [change.key]: change.value }))
|
||||
}}
|
||||
/>
|
||||
</box>
|
||||
),
|
||||
{ width: 100, height: RUN_COMMAND_PANEL_ROWS },
|
||||
)
|
||||
|
||||
try {
|
||||
await app.renderOnce()
|
||||
expect(app.captureCharFrame()).toContain("Settings")
|
||||
expect(app.captureCharFrame()).toContain("Thinking")
|
||||
expect(app.captureCharFrame()).toContain("Shell tool output")
|
||||
expect(app.captureCharFrame()).toContain("left/right change")
|
||||
|
||||
app.mockInput.pressKey("ARROW_RIGHT")
|
||||
await app.renderOnce()
|
||||
|
||||
expect(settings()).toEqual({ thinking: "show", shell_output: "hide" })
|
||||
} finally {
|
||||
app.renderer.destroy()
|
||||
}
|
||||
})
|
||||
|
||||
test("direct skill panel renders searchable skill list", async () => {
|
||||
const [commands] = createSignal<RunCommand[] | undefined>([
|
||||
command({ name: "review", description: "Review code" }),
|
||||
|
|
@ -518,6 +561,7 @@ test("direct command panel shows subagent entry when available", async () => {
|
|||
onQueued={() => {}}
|
||||
onVariant={() => {}}
|
||||
onVariantCycle={() => {}}
|
||||
onSettings={() => {}}
|
||||
onCommand={() => {}}
|
||||
onNew={() => {}}
|
||||
onExit={() => {}}
|
||||
|
|
@ -566,6 +610,7 @@ test("direct command panel keeps completed subagents available", async () => {
|
|||
onQueued={() => {}}
|
||||
onVariant={() => {}}
|
||||
onVariantCycle={() => {}}
|
||||
onSettings={() => {}}
|
||||
onCommand={() => {}}
|
||||
onNew={() => {}}
|
||||
onExit={() => {}}
|
||||
|
|
@ -822,7 +867,7 @@ test("direct footer submits slash autocomplete selections without dispatching sh
|
|||
await app.renderOnce()
|
||||
|
||||
app.mockInput.pressKey("!")
|
||||
"/rev".split("").forEach((key) => app.mockInput.pressKey(key))
|
||||
"/settings".split("").forEach((key) => app.mockInput.pressKey(key))
|
||||
await app.renderOnce()
|
||||
app.mockInput.pressEnter()
|
||||
await app.renderOnce()
|
||||
|
|
@ -834,7 +879,7 @@ test("direct footer submits slash autocomplete selections without dispatching sh
|
|||
{ text: "/new ", parts: [] },
|
||||
{ text: "/new ", parts: [] },
|
||||
])
|
||||
expect(app.captureCharFrame()).toContain("/review")
|
||||
expect(app.renderer.currentFocusedEditor?.plainText).toBe("/settings ")
|
||||
} finally {
|
||||
app.cleanup()
|
||||
}
|
||||
|
|
@ -867,6 +912,26 @@ test("direct footer slash autocomplete keeps a real skills command", async () =>
|
|||
}
|
||||
})
|
||||
|
||||
test("direct footer closes settings with ctrl-c instead of arming exit", async () => {
|
||||
const app = await renderFooter({ height: 20 })
|
||||
|
||||
try {
|
||||
await app.renderOnce()
|
||||
"/settings".split("").forEach((key) => app.mockInput.pressKey(key))
|
||||
await app.renderOnce()
|
||||
app.mockInput.pressEnter()
|
||||
await app.renderOnce()
|
||||
expect(app.captureCharFrame()).toContain("Shell tool output")
|
||||
|
||||
app.mockInput.pressKey("c", { ctrl: true })
|
||||
await app.renderOnce()
|
||||
expect(app.captureCharFrame()).not.toContain("Shell tool output")
|
||||
expect(app.renderer.currentFocusedEditor?.plainText).toBe("")
|
||||
} finally {
|
||||
app.cleanup()
|
||||
}
|
||||
})
|
||||
|
||||
test("selectedCommand backfills the catalog source for bound drafts", () => {
|
||||
const catalog = [command({ name: "opencode-ts", description: "TS skill", source: "skill" })]
|
||||
|
||||
|
|
@ -1034,6 +1099,7 @@ test("direct footer shows authoritative pending work while running", async () =>
|
|||
]}
|
||||
theme={() => RUN_THEME_FALLBACK}
|
||||
tuiConfig={tuiConfig}
|
||||
miniSettings={() => ({ thinking: "hide", shell_output: "hide" })}
|
||||
onSubmit={() => true}
|
||||
onPermissionReply={() => {}}
|
||||
onFormReply={() => {}}
|
||||
|
|
@ -1048,6 +1114,7 @@ test("direct footer shows authoritative pending work while running", async () =>
|
|||
onRows={() => {}}
|
||||
onLayout={() => {}}
|
||||
onStatus={() => {}}
|
||||
onMiniSettingChange={() => {}}
|
||||
/>
|
||||
</Keymap.Provider>
|
||||
)
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import { afterEach, describe, expect, mock, spyOn, test } from "bun:test"
|
||||
import { OpenCode } from "@opencode-ai/client/promise"
|
||||
import type { Resolved } from "../../src/config"
|
||||
import { resolveModelInfo, resolveRunTuiConfig } from "../../src/mini/runtime.boot"
|
||||
import { resolveMiniSettings, resolveModelInfo, resolveRunTuiConfig } from "../../src/mini/runtime.boot"
|
||||
import { catalogModel, catalogProvider } from "./fixture/catalog"
|
||||
import { createTuiResolvedConfig } from "./fixture/tui-runtime"
|
||||
|
||||
|
|
@ -91,18 +91,23 @@ describe("run runtime boot", () => {
|
|||
expect(result.keybinds.get("leader")).toEqual([])
|
||||
})
|
||||
|
||||
test("preserves current theme mode, leader, and thinking config", async () => {
|
||||
test("preserves shared config while resolving independent Mini defaults", async () => {
|
||||
const result = await resolveRunTuiConfig(
|
||||
createTuiResolvedConfig({
|
||||
theme: { mode: "light" },
|
||||
leader_timeout: 450,
|
||||
session: { thinking: "hide" },
|
||||
session: { thinking: "show" },
|
||||
}),
|
||||
)
|
||||
|
||||
expect(result.theme).toEqual({ mode: "light" })
|
||||
expect(result.leader.timeout).toBe(450)
|
||||
expect(result.session?.thinking).toBe("hide")
|
||||
expect(result.session?.thinking).toBe("show")
|
||||
expect(resolveMiniSettings(result)).toEqual({ thinking: "hide", shell_output: "hide" })
|
||||
expect(resolveMiniSettings({ mini: { thinking: "show", shell_output: "show" } })).toEqual({
|
||||
thinking: "show",
|
||||
shell_output: "show",
|
||||
})
|
||||
})
|
||||
|
||||
test("loads v2 providers and models for model selector data", async () => {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue