mirror of
https://github.com/esengine/DeepSeek-Reasonix.git
synced 2026-11-07 10:45:59 -10:00
Default desktop reasoning to live follow
Problem: Fresh installs still selected summary mode, so the new settings did not match the standard, live-follow, auto-collapse experience. Root cause: Backend and frontend fallback paths, browser mocks, and test fixtures retained the previous summary and compact defaults. Fix: Resolve missing or unknown reasoning modes to auto while preserving explicit selections and legacy localStorage migration. Align startup and browser defaults, and add config/Wails persistence coverage. Verification: - go test ./internal/config - cd desktop && go test ./... - cd desktop/frontend && pnpm test:typecheck - cd desktop/frontend && pnpm test - cd desktop/frontend && pnpm build - go run ./tools/repolint - git diff --check
This commit is contained in:
1 parent
c75a977215
commit
70dc3ba58d
11 files changed
+88
-34
No files matched your search
@@ -1563,7 +1563,7 @@ export default function App() {
|
||||
void syncDesktopPreferences().catch((e) => {
|
||||
console.warn("desktop preferences sync failed", e);
|
||||
setStartupUpdateChecksEnabled(true);
|
||||
hydrateReasoningDisplayMode("summary", false);
|
||||
hydrateReasoningDisplayMode("auto", false);
|
||||
});
|
||||
return () => {
|
||||
cancelled = true;
|
||||
|
||||
@@ -55,6 +55,10 @@ const rootEl = document.getElementById("root");
|
||||
if (!rootEl) throw new Error("missing root");
|
||||
const root = createRoot(rootEl);
|
||||
|
||||
// Most assertions in this file exercise the summary-mode disclosure behavior.
|
||||
// Select it explicitly so the test remains independent from the product default.
|
||||
hydrateReasoningDisplayMode("summary", true);
|
||||
|
||||
type ReasoningItem = React.ComponentProps<typeof AssistantMessage>["item"];
|
||||
|
||||
async function render(item: ReasoningItem, props: { defaultExpanded?: boolean } = {}) {
|
||||
|
||||
@@ -32,11 +32,14 @@ Object.defineProperty(globalThis, "localStorage", {
|
||||
});
|
||||
|
||||
storage.clear();
|
||||
hydrateReasoningDisplayMode("summary", false);
|
||||
ok(getReasoningDisplayMode() === "summary", "missing preferences default to summary");
|
||||
hydrateReasoningDisplayMode(undefined, false);
|
||||
ok(getReasoningDisplayMode() === "auto", "missing preferences default to live follow");
|
||||
|
||||
hydrateReasoningDisplayMode("future-mode", false);
|
||||
ok(getReasoningDisplayMode() === "auto", "unknown preferences use the live-follow fallback");
|
||||
|
||||
storage.set("reasonix-reasoning-summary", "0");
|
||||
hydrateReasoningDisplayMode("summary", false);
|
||||
hydrateReasoningDisplayMode("auto", false);
|
||||
ok(getReasoningDisplayMode() === "legacy-collapsed", "legacy summary-off preserves the old compatible behavior");
|
||||
|
||||
storage.set("reasonix-reasoning-summary", "1");
|
||||
@@ -47,6 +50,9 @@ storage.set("reasonix-reasoning-summary", "0");
|
||||
hydrateReasoningDisplayMode("hidden", true);
|
||||
ok(getReasoningDisplayMode() === "hidden", "explicit new mode wins over every legacy preference");
|
||||
|
||||
hydrateReasoningDisplayMode("summary", true);
|
||||
ok(getReasoningDisplayMode() === "summary", "an explicit summary selection remains persisted");
|
||||
|
||||
storage.set("reasonix-reasoning-summary", "0");
|
||||
ok(resolveReasoningDisplayMode("auto", false) === "legacy-collapsed", "resolver applies legacy precedence without hydration");
|
||||
hydrateReasoningDisplayMode("auto", false);
|
||||
|
||||
@@ -46,9 +46,13 @@ ok(
|
||||
"App loads startup chrome preferences through the lightweight settings call",
|
||||
);
|
||||
ok(
|
||||
appSource.includes('hydrateReasoningDisplayMode("summary", false);'),
|
||||
appSource.includes('hydrateReasoningDisplayMode("auto", false);'),
|
||||
"startup failure preserves legacy reasoning-display migration precedence",
|
||||
);
|
||||
ok(
|
||||
bridgeSource.includes('displayMode: "standard", reasoningDisplayMode: "auto", reasoningDisplayModeExplicit: false'),
|
||||
"browser startup defaults match the classic standard/live-follow experience",
|
||||
);
|
||||
ok(
|
||||
!/const\s+reloadSidebarImConnections[\s\S]*?app\.Settings\(\)[\s\S]*?\}, \[t\]\);/.test(appSource),
|
||||
"sidebar IM refresh avoids rebuilding the full Settings payload",
|
||||
|
||||
@@ -1723,7 +1723,7 @@ function makeMockApp(): AppBindings {
|
||||
desktopTerminalTheme: "auto",
|
||||
conversationWidth: "standard",
|
||||
closeBehavior: "background",
|
||||
displayMode: "compact", reasoningDisplayMode: "summary", reasoningDisplayModeExplicit: false,
|
||||
displayMode: "standard", reasoningDisplayMode: "auto", reasoningDisplayModeExplicit: false,
|
||||
statusBarStyle: "text",
|
||||
statusBarItems: [...DEFAULT_STATUS_BAR_ITEMS],
|
||||
defaultToolApprovalMode: "auto",
|
||||
|
||||
@@ -6,7 +6,7 @@ export type ResolvedReasoningDisplayMode = ReasoningDisplayMode | "legacy-collap
|
||||
const LEGACY_SUMMARY_KEY = "reasonix-reasoning-summary";
|
||||
const DISPLAY_EVENT = "reasonix:reasoning-display-mode";
|
||||
|
||||
let currentMode: ResolvedReasoningDisplayMode = "summary";
|
||||
let currentMode: ResolvedReasoningDisplayMode = "auto";
|
||||
let currentModeExplicit = false;
|
||||
const listeners = new Set<() => void>();
|
||||
|
||||
@@ -39,11 +39,15 @@ export function resolveReasoningDisplayMode(
|
||||
case "on":
|
||||
return "summary";
|
||||
}
|
||||
return normalized ?? "summary";
|
||||
return normalized ?? "auto";
|
||||
}
|
||||
|
||||
export function getReasoningDisplayMode(): ResolvedReasoningDisplayMode {
|
||||
if (!currentModeExplicit && currentMode === "summary" && legacySummaryValue() === "off") return "legacy-collapsed";
|
||||
if (!currentModeExplicit && currentMode !== "pending") {
|
||||
const legacy = legacySummaryValue();
|
||||
if (legacy === "off") return "legacy-collapsed";
|
||||
if (legacy === "on") return "summary";
|
||||
}
|
||||
return currentMode;
|
||||
}
|
||||
|
||||
|
||||
@@ -96,7 +96,7 @@ export function baseSettings(displayMode: "standard" | "compact" = "standard"):
|
||||
desktopTerminalTheme: "auto",
|
||||
closeBehavior: "background",
|
||||
displayMode,
|
||||
reasoningDisplayMode: "summary",
|
||||
reasoningDisplayMode: "auto",
|
||||
reasoningDisplayModeExplicit: false,
|
||||
statusBarStyle: "text",
|
||||
statusBarItems: ["model", "workspace", "git_branch", "cache", "balance"],
|
||||
|
||||
@@ -12,7 +12,7 @@ func desktopStartupSettingsFromConfig(cfg *config.Config) DesktopStartupSettings
|
||||
return DesktopStartupSettingsView{
|
||||
Bot: botSettingsView(config.BotConfig{}), DesktopLayoutStyle: "workbench",
|
||||
DesktopTheme: "auto", DesktopThemeStyle: "graphite", DesktopTerminalTheme: "auto",
|
||||
DisplayMode: "standard", ReasoningDisplayMode: "summary", StatusBarStyle: "text",
|
||||
DisplayMode: "standard", ReasoningDisplayMode: "auto", StatusBarStyle: "text",
|
||||
StatusBarItems: config.DefaultDesktopStatusBarItems(), CheckUpdates: true,
|
||||
UpdateChannel: "stable", ConversationWidth: "standard",
|
||||
}
|
||||
@@ -45,7 +45,7 @@ func (a *App) defaultSettingsView() SettingsView {
|
||||
},
|
||||
Bot: botSettingsView(config.BotConfig{}), AutoPlan: "off", DesktopLayoutStyle: "workbench",
|
||||
DesktopTheme: "auto", DesktopThemeStyle: "graphite", DesktopTerminalTheme: "auto",
|
||||
CloseBehavior: "background", DisplayMode: "standard", ReasoningDisplayMode: "summary",
|
||||
CloseBehavior: "background", DisplayMode: "standard", ReasoningDisplayMode: "auto",
|
||||
StatusBarStyle: "text", StatusBarItems: config.DefaultDesktopStatusBarItems(),
|
||||
DefaultToolApprovalMode: "auto", CheckUpdates: true, UpdateChannel: "stable",
|
||||
Telemetry: true, Metrics: true, ExpandThinking: false, ConversationWidth: "standard",
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"reasonix/internal/config"
|
||||
)
|
||||
|
||||
func TestReasoningDisplayDefaultsAndPersistsUserSelection(t *testing.T) {
|
||||
isolateDesktopUserDirs(t)
|
||||
|
||||
app := NewApp()
|
||||
settings := app.Settings()
|
||||
startup := app.DesktopStartupSettings()
|
||||
if settings.DisplayMode != "standard" || settings.ReasoningDisplayMode != "auto" || settings.ReasoningDisplayModeExplicit {
|
||||
t.Fatalf("Settings() defaults = display:%q reasoning:%q explicit:%t, want standard/auto/false", settings.DisplayMode, settings.ReasoningDisplayMode, settings.ReasoningDisplayModeExplicit)
|
||||
}
|
||||
if startup.DisplayMode != "standard" || startup.ReasoningDisplayMode != "auto" || startup.ReasoningDisplayModeExplicit {
|
||||
t.Fatalf("DesktopStartupSettings() defaults = display:%q reasoning:%q explicit:%t, want standard/auto/false", startup.DisplayMode, startup.ReasoningDisplayMode, startup.ReasoningDisplayModeExplicit)
|
||||
}
|
||||
|
||||
if err := app.SetReasoningDisplayMode("summary"); err != nil {
|
||||
t.Fatalf("SetReasoningDisplayMode(summary): %v", err)
|
||||
}
|
||||
settings = app.Settings()
|
||||
startup = app.DesktopStartupSettings()
|
||||
if settings.ReasoningDisplayMode != "summary" || !settings.ReasoningDisplayModeExplicit {
|
||||
t.Fatalf("Settings() user selection = reasoning:%q explicit:%t, want summary/true", settings.ReasoningDisplayMode, settings.ReasoningDisplayModeExplicit)
|
||||
}
|
||||
if startup.ReasoningDisplayMode != "summary" || !startup.ReasoningDisplayModeExplicit {
|
||||
t.Fatalf("DesktopStartupSettings() user selection = reasoning:%q explicit:%t, want summary/true", startup.ReasoningDisplayMode, startup.ReasoningDisplayModeExplicit)
|
||||
}
|
||||
persisted := config.LoadForEdit(config.UserConfigPath())
|
||||
if persisted.DesktopReasoningDisplayMode() != "summary" || !persisted.DesktopReasoningDisplayModeExplicit() {
|
||||
t.Fatalf("persisted user selection = %+v, want explicit summary", persisted.Desktop)
|
||||
}
|
||||
}
|
||||
@@ -12,13 +12,9 @@ func (c *Config) DesktopReasoningDisplayMode() string {
|
||||
case "hidden", "summary", "auto":
|
||||
return raw
|
||||
}
|
||||
if raw != "" {
|
||||
return "summary"
|
||||
}
|
||||
if c.Desktop.ExpandThinking {
|
||||
return "auto"
|
||||
}
|
||||
return "summary"
|
||||
// Missing and unknown values use the classic live-follow behavior. A user
|
||||
// selection is persisted as a valid enum and therefore returns above.
|
||||
return "auto"
|
||||
}
|
||||
|
||||
// DesktopReasoningDisplayModeExplicit reports whether a valid new enum was stored.
|
||||
@@ -55,13 +51,7 @@ func (c *Config) SetExpandThinking(on bool) error {
|
||||
}
|
||||
|
||||
func renderDesktopReasoningDisplayMode(b *strings.Builder, c *Config) {
|
||||
legacyExpand := c.Desktop.ExpandThinking
|
||||
if strings.TrimSpace(c.Desktop.ReasoningDisplayMode) != "" && !c.DesktopReasoningDisplayModeExplicit() {
|
||||
// Unknown future/invalid enums read as summary. When another setting is
|
||||
// saved, keep that safe fallback stable after the invalid field is omitted.
|
||||
legacyExpand = false
|
||||
}
|
||||
fmt.Fprintf(b, "expand_thinking = %v # desktop: legacy reasoning display alias; use reasoning_display_mode\n", legacyExpand)
|
||||
fmt.Fprintf(b, "expand_thinking = %v # desktop: legacy reasoning display alias; use reasoning_display_mode\n", c.Desktop.ExpandThinking)
|
||||
if c.DesktopReasoningDisplayModeExplicit() {
|
||||
fmt.Fprintf(b, "reasoning_display_mode = %q # desktop: hidden|summary|auto reasoning presentation\n", c.DesktopReasoningDisplayMode())
|
||||
}
|
||||
|
||||
@@ -15,13 +15,14 @@ func TestDesktopReasoningDisplayModeDefaultsAndLegacy(t *testing.T) {
|
||||
want string
|
||||
explicit bool
|
||||
}{
|
||||
{name: "missing defaults to summary", want: "summary"},
|
||||
{name: "missing defaults to live follow", want: "auto"},
|
||||
{name: "legacy expand false follows the new default", expand: false, want: "auto"},
|
||||
{name: "legacy expand true maps to auto", expand: true, want: "auto"},
|
||||
{name: "explicit hidden wins over legacy", mode: "hidden", expand: true, want: "hidden", explicit: true},
|
||||
{name: "explicit summary wins over legacy", mode: "summary", expand: true, want: "summary", explicit: true},
|
||||
{name: "explicit auto", mode: "auto", want: "auto", explicit: true},
|
||||
{name: "unknown safely falls back", mode: "future-mode", want: "summary"},
|
||||
{name: "unknown ignores legacy expand", mode: "future-mode", expand: true, want: "summary"},
|
||||
{name: "unknown follows the live-follow fallback", mode: "future-mode", want: "auto"},
|
||||
{name: "unknown with legacy expand remains auto", mode: "future-mode", expand: true, want: "auto"},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
@@ -79,9 +80,17 @@ func TestSetDesktopReasoningDisplayMode(t *testing.T) {
|
||||
|
||||
func TestRenderTOMLIncludesExplicitReasoningDisplayMode(t *testing.T) {
|
||||
cfg := Default()
|
||||
if got := RenderTOML(cfg); strings.Contains(got, "reasoning_display_mode =") {
|
||||
renderedDefault := RenderTOML(cfg)
|
||||
if strings.Contains(renderedDefault, "reasoning_display_mode =") {
|
||||
t.Fatal("implicit default unexpectedly rendered reasoning_display_mode")
|
||||
}
|
||||
var decodedDefault Config
|
||||
if _, err := toml.Decode(renderedDefault, &decodedDefault); err != nil {
|
||||
t.Fatalf("decode rendered default config: %v", err)
|
||||
}
|
||||
if got := decodedDefault.DesktopReasoningDisplayMode(); got != "auto" {
|
||||
t.Fatalf("round-tripped implicit default mode = %q, want auto", got)
|
||||
}
|
||||
if err := cfg.SetDesktopReasoningDisplayMode("hidden"); err != nil {
|
||||
t.Fatalf("SetDesktopReasoningDisplayMode: %v", err)
|
||||
}
|
||||
@@ -102,8 +111,8 @@ func TestRenderTOMLOmitsUnknownReasoningDisplayMode(t *testing.T) {
|
||||
if strings.Contains(rendered, "reasoning_display_mode =") {
|
||||
t.Fatalf("unknown reasoning display mode was canonicalized into an explicit preference:\n%s", rendered)
|
||||
}
|
||||
if !strings.Contains(rendered, "expand_thinking = false") {
|
||||
t.Fatalf("unknown mode was not normalized to the safe legacy summary alias:\n%s", rendered)
|
||||
if !strings.Contains(rendered, "expand_thinking = true") {
|
||||
t.Fatalf("unknown mode did not preserve the legacy compatibility alias:\n%s", rendered)
|
||||
}
|
||||
var decoded Config
|
||||
if _, err := toml.Decode(rendered, &decoded); err != nil {
|
||||
@@ -112,8 +121,8 @@ func TestRenderTOMLOmitsUnknownReasoningDisplayMode(t *testing.T) {
|
||||
if decoded.DesktopReasoningDisplayModeExplicit() {
|
||||
t.Fatalf("unknown mode became explicit after round trip: %+v", decoded.Desktop)
|
||||
}
|
||||
if got := decoded.DesktopReasoningDisplayMode(); got != "summary" {
|
||||
t.Fatalf("round-tripped unknown mode = %q, want stable summary fallback", got)
|
||||
if got := decoded.DesktopReasoningDisplayMode(); got != "auto" {
|
||||
t.Fatalf("round-tripped unknown mode = %q, want stable live-follow fallback", got)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user