Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions desktop/frontend/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -418,6 +418,7 @@ export default function App() {
const [projectRevision, setProjectRevision] = useState(0);
const [composerInsertRequest, setComposerInsertRequest] = useState<ComposerInsertRequest | null>(null);
const [desktopPlatform, setDesktopPlatform] = useState<DesktopPlatform>(detectBrowserPlatform);
const [expandThinking, setExpandThinking] = useState(false);
const [renamingTopicId, setRenamingTopicId] = useState<string | null>(null);
const [topicTitleDraft, setTopicTitleDraft] = useState("");
const [topicExportOpen, setTopicExportOpen] = useState(false);
Expand Down Expand Up @@ -465,6 +466,7 @@ export default function App() {
const nextStyle = normalizeThemeStyleForTheme(settings.desktopThemeStyle, nextTheme);
applyTheme(nextTheme, nextStyle, { persist: false });
setLocalePref(normalizeLangPref(settings.desktopLanguage));
setExpandThinking(settings.expandThinking);
};
void syncDesktopPreferences().catch((e) => {
console.warn("desktop preferences sync failed", e);
Expand Down Expand Up @@ -1612,6 +1614,7 @@ export default function App() {
checkpoints={state.checkpoints}
actionPending={state.messageAction != null}
rewindDisabled={state.running || state.messageAction != null || state.approval != null || state.ask != null}
defaultExpandThinking={expandThinking}
/>
)}
</main>
Expand Down
4 changes: 3 additions & 1 deletion desktop/frontend/src/components/Message.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -238,8 +238,10 @@ export function TurnActions({

export const AssistantMessage = memo(function AssistantMessage({
item,
defaultExpanded = false,
}: {
item: AssistantItem;
defaultExpanded?: boolean;
}) {
const t = useT();
const hasText = item.streaming || item.text.trim() !== "";
Expand All @@ -259,7 +261,7 @@ export const AssistantMessage = memo(function AssistantMessage({
<span>{item.streaming ? t("msg.thinkingRunning") : t("msg.thinkingDone")}</span>
</>
}
defaultOpen={item.streaming}
defaultOpen={item.streaming || defaultExpanded}
>
<div className="reasoning__body">{item.reasoning}</div>
</ProcessCard>
Expand Down
14 changes: 14 additions & 0 deletions desktop/frontend/src/components/SettingsPanel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -515,6 +515,20 @@ function GeneralSection({ s, busy, apply }: SectionProps) {
))}
</div>
</SettingsField>
<SettingsField label={t("settings.expandThinking")}>
<div className="set-seg">
{([false, true] as const).map((val) => (
<button
key={val ? "on" : "off"}
className={`set-seg__btn${s.expandThinking === val ? " set-seg__btn--on" : ""}`}
disabled={busy}
onClick={() => void apply(() => app.SetExpandThinking(val))}
>
{val ? t("settings.expandThinking.expanded") : t("settings.expandThinking.collapsed")}
</button>
))}
</div>
</SettingsField>
<SettingsField label={t("settings.autoPlan")}>
<div className="set-seg">
{AUTO_PLAN_MODES.map((mode) => (
Expand Down
16 changes: 12 additions & 4 deletions desktop/frontend/src/components/Transcript.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,10 @@ type QuestionAnchor = { id: string; text: string; turn: number };
const QUESTION_NAV_MIN_COUNT = 2;
const LiveStreamContext = createContext<LiveStream | undefined>(undefined);

const LiveAssistantMessage = memo(function LiveAssistantMessage({ item }: { item: AssistantItem }) {
const LiveAssistantMessage = memo(function LiveAssistantMessage({ item, defaultExpanded = false }: { item: AssistantItem; defaultExpanded?: boolean }) {
const live = useContext(LiveStreamContext);
const shown = live && live.id === item.id ? { ...item, text: live.text, reasoning: live.reasoning, streaming: true } : item;
return <AssistantMessage item={shown} />;
return <AssistantMessage item={shown} defaultExpanded={defaultExpanded} />;
});

// ── Layer budgets ─────────────────────────────────────────────────────────────
Expand Down Expand Up @@ -151,6 +151,7 @@ export function Transcript({
actionPending = false,
rewindDisabled = false,
questionNavigator = true,
defaultExpandThinking = false,
}: {
items: Item[];
live?: LiveStream;
Expand All @@ -161,6 +162,7 @@ export function Transcript({
actionPending?: boolean;
rewindDisabled?: boolean;
questionNavigator?: boolean;
defaultExpandThinking?: boolean;
}) {
const scrollRef = useRef<HTMLDivElement>(null);
const stick = useRef(true);
Expand Down Expand Up @@ -377,7 +379,7 @@ export function Transcript({
break;
}
case "assistant":
out.push(<LiveAssistantMessage key={it.id} item={it as AssistantItem} />);
out.push(<LiveAssistantMessage key={it.id} item={it as AssistantItem} defaultExpanded={defaultExpandThinking} />);
if (!it.streaming && it.text.trim() !== "") {
actionText = it.text;
actionReady = true;
Expand Down Expand Up @@ -431,6 +433,7 @@ export function Transcript({
warmRewindDisabled={rewindDisabled}
warmOnRewind={onRewind}
warmSetOpenAction={setOpenAction}
defaultExpandThinking={defaultExpandThinking}
onToggleColdPage={() => setColdPage((p) => p + 1)}
onToggleWarmTurn={(g, expand) => {
setExpandedWarmTurns((prev) => {
Expand Down Expand Up @@ -466,6 +469,7 @@ const WarmZone = memo(function WarmZone({
warmRewindDisabled,
warmOnRewind,
warmSetOpenAction,
defaultExpandThinking = false,
onToggleColdPage,
onToggleWarmTurn,
}: {
Expand All @@ -483,6 +487,7 @@ const WarmZone = memo(function WarmZone({
warmRewindDisabled: boolean;
warmOnRewind: ((turn: number, scope: string) => void) | undefined;
warmSetOpenAction: (action: OpenTurnAction | null) => void;
defaultExpandThinking?: boolean;
onToggleColdPage: () => void;
onToggleWarmTurn: (g: number, expand: boolean) => void;
}) {
Expand Down Expand Up @@ -537,6 +542,7 @@ const WarmZone = memo(function WarmZone({
rewindDisabled={warmRewindDisabled}
onRewind={warmOnRewind}
setOpenAction={warmSetOpenAction}
defaultExpandThinking={defaultExpandThinking}
/>
</WarmTurnCard>,
);
Expand Down Expand Up @@ -580,6 +586,7 @@ function WarmTurnItems({
rewindDisabled,
onRewind,
setOpenAction,
defaultExpandThinking = false,
}: {
startIdx: number;
endIdx: number;
Expand All @@ -592,6 +599,7 @@ function WarmTurnItems({
rewindDisabled: boolean;
onRewind: ((turn: number, scope: string) => void) | undefined;
setOpenAction: (action: OpenTurnAction | null) => void;
defaultExpandThinking?: boolean;
}) {
const nodes: React.ReactNode[] = [];
let actionText = "";
Expand Down Expand Up @@ -634,7 +642,7 @@ function WarmTurnItems({
break;
}
case "assistant": {
nodes.push(<AssistantMessage key={it.id} item={it} />);
nodes.push(<AssistantMessage key={it.id} item={it} defaultExpanded={defaultExpandThinking} />);
if (!it.streaming && it.text.trim() !== "") {
actionText = it.text;
actionReady = true;
Expand Down
5 changes: 5 additions & 0 deletions desktop/frontend/src/lib/bridge.ts
Original file line number Diff line number Diff line change
Expand Up @@ -188,6 +188,7 @@ export interface AppBindings {
SetCloseBehavior(mode: string): Promise<void>;
SetDesktopLanguage(lang: string): Promise<void>;
SetDesktopAppearance(theme: string, style: string): Promise<void>;
SetExpandThinking(on: boolean): Promise<void>;
MigrateDesktopPreferences(language: string, theme: string, style: string): Promise<void>;
SetAgentParams(temperature: number, maxSteps: number, plannerMaxSteps: number, systemPrompt: string): Promise<void>;
SetTrayLocale(locale: "en" | "zh"): Promise<void>;
Expand Down Expand Up @@ -574,6 +575,7 @@ function makeMockApp(): AppBindings {
desktopTheme: "dark",
desktopThemeStyle: "graphite",
closeBehavior: "background",
expandThinking: false,
configPath: "~/projects/reasonix/reasonix.toml",
providerKinds: ["openai"],
bypass: false,
Expand Down Expand Up @@ -1543,6 +1545,9 @@ function makeMockApp(): AppBindings {
settings.desktopTheme = theme === "auto" || theme === "light" ? theme : "dark";
settings.desktopThemeStyle = style;
},
async SetExpandThinking(on: boolean) {
settings.expandThinking = on;
},
async MigrateDesktopPreferences(language: string, theme: string, style: string) {
if (!settings.desktopLanguage) settings.desktopLanguage = language === "en" || language === "zh" ? language : "";
if (!settings.desktopTheme && !settings.desktopThemeStyle) {
Expand Down
1 change: 1 addition & 0 deletions desktop/frontend/src/lib/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -553,6 +553,7 @@ export interface SettingsView {
desktopTheme: string; // "auto" | "dark" | "light"
desktopThemeStyle: string;
closeBehavior: string; // "background" | "quit"
expandThinking: boolean; // show reasoning text expanded by default
configPath: string;
providerKinds: string[]; // provider implementations the kernel registered (for the kind picker)
bypass: boolean; // live YOLO state (runtime-only) — whether approvals are skipped this session
Expand Down
3 changes: 3 additions & 0 deletions desktop/frontend/src/locales/en.ts
Original file line number Diff line number Diff line change
Expand Up @@ -474,6 +474,9 @@ export const en = {
"settings.closeBehavior": "When closing window",
"settings.closeBehavior.background": "Keep running",
"settings.closeBehavior.quit": "Quit Reasonix",
"settings.expandThinking": "Thinking display",
"settings.expandThinking.expanded": "Expanded",
"settings.expandThinking.collapsed": "Collapsed",
"settings.manageProviders": "Manage providers",
"settings.activeProvider": "Active provider",
"settings.plannerStatus": "Planning mode",
Expand Down
3 changes: 3 additions & 0 deletions desktop/frontend/src/locales/zh.ts
Original file line number Diff line number Diff line change
Expand Up @@ -476,6 +476,9 @@ export const zh: Record<DictKey, string> = {
"settings.closeBehavior": "关闭窗口时",
"settings.closeBehavior.background": "保持后台运行",
"settings.closeBehavior.quit": "退出 Reasonix",
"settings.expandThinking": "思考过程显示",
"settings.expandThinking.expanded": "默认展开",
"settings.expandThinking.collapsed": "默认折叠",
"settings.manageProviders": "管理模型服务",
"settings.activeProvider": "当前模型服务",
"settings.plannerStatus": "规划方式",
Expand Down
9 changes: 9 additions & 0 deletions desktop/settings_app.go
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,7 @@ type SettingsView struct {
DesktopTheme string `json:"desktopTheme"`
DesktopThemeStyle string `json:"desktopThemeStyle"`
CloseBehavior string `json:"closeBehavior"`
ExpandThinking bool `json:"expandThinking"`
ConfigPath string `json:"configPath"`
// ProviderKinds lists the provider implementations the kernel actually
// registered (provider.Kinds()), so the editor's "kind" picker offers only
Expand Down Expand Up @@ -268,6 +269,7 @@ func (a *App) Settings() SettingsView {
DesktopTheme: "dark",
DesktopThemeStyle: "graphite",
CloseBehavior: "background",
ExpandThinking: false,
}
}
ctrl := a.activeCtrl()
Expand Down Expand Up @@ -310,6 +312,7 @@ func (a *App) Settings() SettingsView {
DesktopTheme: cfg.DesktopTheme(),
DesktopThemeStyle: cfg.DesktopThemeStyle(),
CloseBehavior: cfg.DesktopCloseBehavior(),
ExpandThinking: cfg.Desktop.ExpandThinking,
ConfigPath: cfgPath,
ProviderKinds: nonNil(provider.Kinds()),
Bypass: ctrl != nil && ctrl.Bypass(),
Expand Down Expand Up @@ -1085,6 +1088,12 @@ func (a *App) SetDesktopAppearance(theme, style string) error {
return a.applyConfigOnly(func(c *config.Config) error { return c.SetDesktopAppearance(theme, style) })
}

// SetExpandThinking sets whether reasoning text is expanded by default on
// the desktop. It is desktop-only and does not rebuild the controller.
func (a *App) SetExpandThinking(on bool) error {
return a.applyConfigOnly(func(c *config.Config) error { return c.SetExpandThinking(on) })
}

// MigrateDesktopPreferences imports old browser-local desktop preferences into
// the user config once. Existing [desktop] values win so stale localStorage never
// overwrites an explicit config edit.
Expand Down
6 changes: 6 additions & 0 deletions internal/cli/chat_tui.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import (

"reasonix/internal/agent"
"reasonix/internal/command"
"reasonix/internal/config"
"reasonix/internal/control"
"reasonix/internal/event"
"reasonix/internal/hook"
Expand Down Expand Up @@ -117,6 +118,7 @@ type chatTUI struct {
pendingCommit *[]string
renderer *mdRenderer
showReasoning bool // Ctrl+O / /verbose: show raw thinking text in the CLI
cfg *config.Config
// reasoningLineIdx is the transcript index of the live "▎ thinking…" marker
// while a reasoning block streams; it's rewritten to "▎ thought for Ns" when
// the block closes. -1 when no block is open. transcriptDirty forces a
Expand Down Expand Up @@ -2681,6 +2683,10 @@ func (m *chatTUI) cycleMode() {

func (m *chatTUI) toggleVerboseReasoning(notify bool) {
m.showReasoning = !m.showReasoning
if m.cfg != nil {
_ = m.cfg.SetShowReasoning(m.showReasoning)
_ = m.cfg.Save()
}
if !notify {
return
}
Expand Down
2 changes: 2 additions & 0 deletions internal/cli/cli.go
Original file line number Diff line number Diff line change
Expand Up @@ -435,6 +435,8 @@ func chatREPL(args []string) int {
if cfg, err := config.Load(); err == nil {
m.outputStyle = cfg.Agent.OutputStyle // shown as the active entry in /output-style
m.statuslineCmd = cfg.Statusline.Command // custom status-line command, "" = built-in row
m.showReasoning = cfg.UI.ShowReasoning // /verbose persistence: start with config default
m.cfg = cfg
}

// /model support: a pure builder the TUI calls to rebuild on a different
Expand Down
1 change: 1 addition & 0 deletions internal/cli/help_view.go
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ func builtinHelpItems() []compItem {
{label: "/hooks", hint: i18n.M.CmdHooks},
{label: "/memory", hint: i18n.M.CmdMemory},
{label: "/output-style", hint: i18n.M.CmdOutputStyle},
{label: "/verbose", hint: i18n.M.CmdVerbose},
{label: "/language", hint: i18n.M.CmdLanguage},
{label: "/auto-plan", hint: i18n.M.CmdAutoPlan},
{label: "/help", hint: i18n.M.CmdHelp},
Expand Down
2 changes: 2 additions & 0 deletions internal/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ type UIConfig struct {
Theme string `toml:"theme"` // auto|dark|light; empty resolves to auto
ThemeStyle string `toml:"theme_style"` // graphite|ember|aurora|midnight|sandstone|porcelain|linen|glacier
CloseBehavior string `toml:"close_behavior"` // legacy desktop close behavior; prefer desktop.close_behavior
ShowReasoning bool `toml:"show_reasoning"` // Ctrl+O / /verbose: show thinking text in CLI; false = collapsed
}

// DesktopConfig controls desktop-only UI preferences. It is intentionally
Expand All @@ -75,6 +76,7 @@ type DesktopConfig struct {
ThemeStyle string `toml:"theme_style"` // graphite|ember|aurora|midnight|sandstone|porcelain|linen|glacier
CloseBehavior string `toml:"close_behavior"` // quit|background; desktop window close behavior
ProviderAccess []string `toml:"provider_access"` // desktop-only list of provider entries shown in Settings > Model > Access
ExpandThinking bool `toml:"expand_thinking"` // true = show reasoning text expanded by default; false = collapsed
}

// NotificationsConfig controls optional system notifications for CLI chat/run.
Expand Down
17 changes: 17 additions & 0 deletions internal/config/edit.go
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,23 @@ func (c *Config) SetUICloseBehavior(mode string) error {
return c.SetDesktopCloseBehavior(mode)
}

// SetExpandThinking sets whether the desktop reasoning/thinking section is
// expanded by default. It is desktop-only and must not affect CLI output or
// provider-visible request data.
func (c *Config) SetExpandThinking(on bool) error {
c.Desktop.ExpandThinking = on
return nil
}

// SetShowReasoning sets the CLI's default verbose-reasoning preference. When
// true, thinking text is shown in the chat TUI on startup; when false (the
// default), it stays collapsed until the user toggles it with Ctrl+O or
// /verbose.
func (c *Config) SetShowReasoning(on bool) error {
c.UI.ShowReasoning = on
return nil
}

// SetProviderThinking updates a provider's provider-specific thinking mode knob.
func (c *Config) SetProviderThinking(name, thinking string) error {
for i := range c.Providers {
Expand Down
6 changes: 6 additions & 0 deletions internal/config/render.go
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,11 @@ func RenderTOMLForScope(c *Config, scope RenderScope) string {
if strings.TrimSpace(c.UI.CloseBehavior) != "" && scope == RenderScopeProject {
fmt.Fprintf(&b, "close_behavior = %q # legacy desktop close behavior; prefer [desktop].close_behavior in user config\n", c.DesktopCloseBehavior())
}
if c.UI.ShowReasoning {
b.WriteString("show_reasoning = true # CLI: show thinking text by default; false = collapsed (toggle with Ctrl+O)\n")
} else {
b.WriteString("# show_reasoning = true # CLI: show thinking text by default; false = collapsed (toggle with Ctrl+O)\n")
}
b.WriteString("\n")
}

Expand All @@ -83,6 +88,7 @@ func RenderTOMLForScope(c *Config, scope RenderScope) string {
if len(c.Desktop.ProviderAccess) > 0 {
fmt.Fprintf(&b, "provider_access = %s # desktop settings: providers shown on Settings > Model > Access\n", renderStringArray(c.Desktop.ProviderAccess))
}
fmt.Fprintf(&b, "expand_thinking = %v # desktop: show reasoning text expanded by default; false = collapsed\n", c.Desktop.ExpandThinking)
b.WriteString("\n")

b.WriteString("[notifications]\n")
Expand Down
Loading