mirror of
https://github.com/getpaseo/paseo.git
synced 2026-07-29 12:01:31 +00:00
feat(app): add auto-expand reasoning setting
This commit is contained in:
@@ -52,6 +52,7 @@ import type { AgentScreenAgent } from "@/hooks/use-agent-screen-state-machine";
|
||||
import { useSessionStore } from "@/stores/session-store";
|
||||
import { useFileExplorerActions } from "@/hooks/use-file-explorer-actions";
|
||||
import { useLoadOlderAgentHistory } from "@/hooks/use-load-older-agent-history";
|
||||
import { useSettings } from "@/hooks/use-settings";
|
||||
import type { ToastApi } from "@/components/toast-host";
|
||||
import type { DaemonClient } from "@getpaseo/client/internal/daemon-client";
|
||||
import { ToolCallDetailsContent } from "@/components/tool-call-details";
|
||||
@@ -318,6 +319,7 @@ const AgentStreamViewComponent = forwardRef<AgentStreamViewHandle, AgentStreamVi
|
||||
) {
|
||||
const { t } = useTranslation();
|
||||
const router = useRouter();
|
||||
const { settings } = useSettings();
|
||||
const viewportRef = useRef<StreamViewportHandle | null>(null);
|
||||
const isMobile = useIsCompactFormFactor();
|
||||
const streamRenderStrategy = useMemo(
|
||||
@@ -633,10 +635,12 @@ const AgentStreamViewComponent = forwardRef<AgentStreamViewHandle, AgentStreamVi
|
||||
args={item.text}
|
||||
status={item.status === "ready" ? "completed" : "executing"}
|
||||
isLastInSequence={layoutItem.isLastInToolSequence}
|
||||
defaultExpanded={settings.autoExpandReasoning}
|
||||
forceInline={settings.autoExpandReasoning}
|
||||
/>
|
||||
);
|
||||
},
|
||||
[setInlineDetailsExpanded],
|
||||
[setInlineDetailsExpanded, settings.autoExpandReasoning],
|
||||
);
|
||||
|
||||
const renderToolCallItem = useCallback(
|
||||
|
||||
@@ -3038,6 +3038,8 @@ interface ToolCallProps {
|
||||
onInlineDetailsHoverChange?: (hovered: boolean) => void;
|
||||
onInlineDetailsExpandedChange?: (expanded: boolean) => void;
|
||||
onOpenFilePath?: (filePath: string) => void;
|
||||
defaultExpanded?: boolean;
|
||||
forceInline?: boolean;
|
||||
}
|
||||
|
||||
export const ToolCall = memo(function ToolCall({
|
||||
@@ -3054,11 +3056,14 @@ export const ToolCall = memo(function ToolCall({
|
||||
onInlineDetailsHoverChange,
|
||||
onInlineDetailsExpandedChange,
|
||||
onOpenFilePath,
|
||||
defaultExpanded,
|
||||
forceInline = false,
|
||||
}: ToolCallProps) {
|
||||
const { openToolCall } = useToolCallSheet();
|
||||
const [isExpanded, setIsExpanded] = useState(false);
|
||||
const [isExpanded, setIsExpanded] = useState(defaultExpanded ?? false);
|
||||
|
||||
const isMobile = useIsCompactFormFactor();
|
||||
const shouldRenderInline = !isMobile || forceInline;
|
||||
|
||||
const effectiveDetail = useMemo<ToolCallDetail | undefined>(() => {
|
||||
if (detail) {
|
||||
@@ -3096,7 +3101,7 @@ export const ToolCall = memo(function ToolCall({
|
||||
}, [presentation.openFilePath, onOpenFilePath]);
|
||||
|
||||
const handleToggle = useCallback(() => {
|
||||
if (isMobile) {
|
||||
if (!shouldRenderInline) {
|
||||
openToolCall({
|
||||
displayName: presentation.displayName,
|
||||
summary: presentation.summary,
|
||||
@@ -3109,7 +3114,7 @@ export const ToolCall = memo(function ToolCall({
|
||||
setIsExpanded((prev) => !prev);
|
||||
}
|
||||
}, [
|
||||
isMobile,
|
||||
shouldRenderInline,
|
||||
openToolCall,
|
||||
presentation.displayName,
|
||||
presentation.summary,
|
||||
@@ -3120,22 +3125,22 @@ export const ToolCall = memo(function ToolCall({
|
||||
]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!onInlineDetailsHoverChange || isMobile || isExpanded) {
|
||||
if (!onInlineDetailsHoverChange || !shouldRenderInline || isExpanded) {
|
||||
return;
|
||||
}
|
||||
onInlineDetailsHoverChange(false);
|
||||
}, [isExpanded, isMobile, onInlineDetailsHoverChange]);
|
||||
}, [isExpanded, shouldRenderInline, onInlineDetailsHoverChange]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!onInlineDetailsExpandedChange) {
|
||||
return;
|
||||
}
|
||||
if (isMobile) {
|
||||
if (!shouldRenderInline) {
|
||||
onInlineDetailsExpandedChange(false);
|
||||
return;
|
||||
}
|
||||
onInlineDetailsExpandedChange(isExpanded);
|
||||
}, [isExpanded, isMobile, onInlineDetailsExpandedChange]);
|
||||
}, [isExpanded, shouldRenderInline, onInlineDetailsExpandedChange]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!onInlineDetailsExpandedChange) {
|
||||
@@ -3148,7 +3153,7 @@ export const ToolCall = memo(function ToolCall({
|
||||
|
||||
// Render inline details for desktop
|
||||
const renderDetails = useCallback(() => {
|
||||
if (isMobile) return null;
|
||||
if (!shouldRenderInline) return null;
|
||||
return (
|
||||
<ToolCallDetailsContent
|
||||
detail={effectiveDetail}
|
||||
@@ -3157,7 +3162,7 @@ export const ToolCall = memo(function ToolCall({
|
||||
showLoadingSkeleton={presentation.isLoadingDetails}
|
||||
/>
|
||||
);
|
||||
}, [isMobile, effectiveDetail, presentation.errorText, presentation.isLoadingDetails]);
|
||||
}, [shouldRenderInline, effectiveDetail, presentation.errorText, presentation.isLoadingDetails]);
|
||||
|
||||
if (presentation.isPlan && effectiveDetail?.type === "plan") {
|
||||
return (
|
||||
@@ -3175,10 +3180,10 @@ export const ToolCall = memo(function ToolCall({
|
||||
label={presentation.displayName}
|
||||
secondaryLabel={presentation.summary}
|
||||
icon={presentation.icon}
|
||||
isExpanded={!isMobile && isExpanded}
|
||||
isExpanded={shouldRenderInline && isExpanded}
|
||||
onToggle={presentation.canOpenDetails ? handleToggle : undefined}
|
||||
onOpenFile={handleOpenFile}
|
||||
renderDetails={presentation.canOpenDetails && !isMobile ? renderDetails : undefined}
|
||||
renderDetails={presentation.canOpenDetails && shouldRenderInline ? renderDetails : undefined}
|
||||
isLoading={status === "running" || status === "executing"}
|
||||
isError={status === "failed"}
|
||||
isLastInSequence={isLastInSequence}
|
||||
@@ -3200,5 +3205,7 @@ function areToolCallPropsEqual(previous: ToolCallProps, next: ToolCallProps) {
|
||||
if (previous.isLastInSequence !== next.isLastInSequence) return false;
|
||||
if (previous.disableOuterSpacing !== next.disableOuterSpacing) return false;
|
||||
if (previous.onOpenFilePath !== next.onOpenFilePath) return false;
|
||||
if (previous.defaultExpanded !== next.defaultExpanded) return false;
|
||||
if (previous.forceInline !== next.forceInline) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -177,6 +177,9 @@ export function useSettings(): UseSettingsReturn {
|
||||
if (updates.workspaceTitleSource !== undefined) {
|
||||
appUpdates.workspaceTitleSource = updates.workspaceTitleSource;
|
||||
}
|
||||
if (updates.autoExpandReasoning !== undefined) {
|
||||
appUpdates.autoExpandReasoning = updates.autoExpandReasoning;
|
||||
}
|
||||
const promises: Promise<void>[] = [];
|
||||
if (Object.keys(appUpdates).length > 0) {
|
||||
promises.push(appSettings.updateSettings(appUpdates));
|
||||
|
||||
@@ -39,6 +39,7 @@ export interface AppSettings {
|
||||
codeFontSize: number; // clamped px, default 12
|
||||
syntaxTheme: SyntaxThemeId; // default "one"
|
||||
workspaceTitleSource: WorkspaceTitleSource;
|
||||
autoExpandReasoning: boolean;
|
||||
}
|
||||
|
||||
export interface Settings extends AppSettings {
|
||||
@@ -58,6 +59,7 @@ export const DEFAULT_CLIENT_SETTINGS: AppSettings = {
|
||||
codeFontSize: DEFAULT_CODE_FONT_SIZE,
|
||||
syntaxTheme: "one",
|
||||
workspaceTitleSource: "title",
|
||||
autoExpandReasoning: false,
|
||||
};
|
||||
|
||||
export const DEFAULT_APP_SETTINGS: Settings = {
|
||||
@@ -204,6 +206,9 @@ function pickAppSettings(stored: Partial<AppSettings>): Partial<AppSettings> {
|
||||
) {
|
||||
result.workspaceTitleSource = stored.workspaceTitleSource;
|
||||
}
|
||||
if (typeof stored.autoExpandReasoning === "boolean") {
|
||||
result.autoExpandReasoning = stored.autoExpandReasoning;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
@@ -1457,6 +1457,10 @@ export const ar: TranslationResources = {
|
||||
description: "يتم الاحتفاظ بالخطوط في المخزن المؤقت الطرفي المدمج",
|
||||
accessibilityLabel: "خطوط التمرير Terminal",
|
||||
},
|
||||
autoExpandReasoning: {
|
||||
label: "عرض التفكير دائماً",
|
||||
description: "إظهار تفكير الوكيل وخطوات الاستدلال بشكل كامل بشكل افتراضي",
|
||||
},
|
||||
language: {
|
||||
label: "لغة",
|
||||
description: "لغة التطبيق",
|
||||
|
||||
@@ -1464,6 +1464,10 @@ export const en = {
|
||||
description: "Lines kept in the built-in terminal buffer",
|
||||
accessibilityLabel: "Terminal scrollback lines",
|
||||
},
|
||||
autoExpandReasoning: {
|
||||
label: "Always expand reasoning",
|
||||
description: "Show agent thinking and chain-of-thought blocks fully expanded by default",
|
||||
},
|
||||
language: {
|
||||
label: "Language",
|
||||
description: "App language",
|
||||
|
||||
@@ -1497,6 +1497,11 @@ export const es: TranslationResources = {
|
||||
description: "Líneas mantenidas en el búfer de terminal incorporado",
|
||||
accessibilityLabel: "Líneas del historial de terminal",
|
||||
},
|
||||
autoExpandReasoning: {
|
||||
label: "Siempre expandir razonamiento",
|
||||
description:
|
||||
"Mostrar los bloques de pensamiento y razonamiento del agente totalmente expandidos de forma predeterminada",
|
||||
},
|
||||
language: {
|
||||
label: "Idioma",
|
||||
description: "Idioma de la aplicación",
|
||||
|
||||
@@ -1500,6 +1500,10 @@ export const fr: TranslationResources = {
|
||||
description: "Lignes conservées dans le tampon du terminal intégré",
|
||||
accessibilityLabel: "Lignes de défilementTerminal",
|
||||
},
|
||||
autoExpandReasoning: {
|
||||
label: "Toujours afficher le raisonnement",
|
||||
description: "Afficher le raisonnement de l'agent entièrement développé par défaut",
|
||||
},
|
||||
language: {
|
||||
label: "Langue",
|
||||
description: "Langue de l'application",
|
||||
|
||||
@@ -1473,6 +1473,10 @@ export const ja: TranslationResources = {
|
||||
description: "組み込みターミナルバッファに保持する行数",
|
||||
accessibilityLabel: "ターミナルスクロールバック行数",
|
||||
},
|
||||
autoExpandReasoning: {
|
||||
label: "常に思考プロセスを展開",
|
||||
description: "デフォルトでAIのエージェント思考・推論ブロックを完全に展開して表示します",
|
||||
},
|
||||
language: {
|
||||
label: "言語",
|
||||
description: "アプリの言語",
|
||||
|
||||
@@ -1482,6 +1482,11 @@ export const ptBR: TranslationResources = {
|
||||
description: "Linhas mantidas no buffer do terminal integrado",
|
||||
accessibilityLabel: "Linhas do scrollback do terminal",
|
||||
},
|
||||
autoExpandReasoning: {
|
||||
label: "Sempre expandir raciocínio",
|
||||
description:
|
||||
"Mostrar os blocos de pensamento e raciocínio do agente totalmente expandidos por padrão",
|
||||
},
|
||||
language: {
|
||||
label: "Idioma",
|
||||
description: "Idioma do app",
|
||||
|
||||
@@ -1487,6 +1487,11 @@ export const ru: TranslationResources = {
|
||||
description: "Строки, хранящиеся во встроенном буфере терминала.",
|
||||
accessibilityLabel: "Линии прокрутки Terminal",
|
||||
},
|
||||
autoExpandReasoning: {
|
||||
label: "Всегда разворачивать размышления",
|
||||
description:
|
||||
"По умолчанию показывать блоки размышлений и логики агента полностью развернутыми",
|
||||
},
|
||||
language: {
|
||||
label: "Язык",
|
||||
description: "Язык приложения",
|
||||
|
||||
@@ -1439,6 +1439,10 @@ export const zhCN: TranslationResources = {
|
||||
description: "内置终端缓冲区保留的行数",
|
||||
accessibilityLabel: "终端回滚行数",
|
||||
},
|
||||
autoExpandReasoning: {
|
||||
label: "始终展开推理过程",
|
||||
description: "默认情况下完全展开 AI 的思考和推理过程",
|
||||
},
|
||||
language: {
|
||||
label: "语言",
|
||||
description: "应用语言",
|
||||
|
||||
@@ -67,6 +67,7 @@ import { AddHostModal } from "@/components/add-host-modal";
|
||||
import { PairLinkModal } from "@/components/pair-link-modal";
|
||||
import { KeyboardShortcutsSection } from "@/screens/settings/keyboard-shortcuts-section";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Switch } from "@/components/ui/switch";
|
||||
import { CommunityLinks } from "@/components/community-links";
|
||||
import { SegmentedControl } from "@/components/ui/segmented-control";
|
||||
import { DropdownMenu, DropdownMenuContent, DropdownMenuItem } from "@/components/ui/dropdown-menu";
|
||||
@@ -244,6 +245,7 @@ interface GeneralSectionProps {
|
||||
handleServiceUrlBehaviorChange: (behavior: ServiceUrlBehavior) => void;
|
||||
handleLanguageChange: (language: AppLanguage) => void;
|
||||
handleTerminalScrollbackLinesChange: (lines: number) => void;
|
||||
handleAutoExpandReasoningChange: (autoExpand: boolean) => void;
|
||||
}
|
||||
|
||||
interface ServiceUrlBehaviorMenuItemProps {
|
||||
@@ -300,6 +302,7 @@ function GeneralSection({
|
||||
handleServiceUrlBehaviorChange,
|
||||
handleLanguageChange,
|
||||
handleTerminalScrollbackLinesChange,
|
||||
handleAutoExpandReasoningChange,
|
||||
}: GeneralSectionProps) {
|
||||
const { t, i18n } = useTranslation();
|
||||
const activeLocale = getActiveLocale(i18n.language);
|
||||
@@ -433,6 +436,20 @@ function GeneralSection({
|
||||
accessibilityLabel={t("settings.general.terminalScrollback.accessibilityLabel")}
|
||||
/>
|
||||
</View>
|
||||
<View style={ROW_WITH_BORDER_STYLE}>
|
||||
<View style={settingsStyles.rowContent}>
|
||||
<Text style={settingsStyles.rowTitle}>
|
||||
{t("settings.general.autoExpandReasoning.label")}
|
||||
</Text>
|
||||
<Text style={settingsStyles.rowHint}>
|
||||
{t("settings.general.autoExpandReasoning.description")}
|
||||
</Text>
|
||||
</View>
|
||||
<Switch
|
||||
value={settings.autoExpandReasoning}
|
||||
onValueChange={handleAutoExpandReasoningChange}
|
||||
/>
|
||||
</View>
|
||||
</View>
|
||||
</SettingsSection>
|
||||
);
|
||||
@@ -1180,6 +1197,13 @@ export default function SettingsScreen({ view, openAddHostIntent = null }: Setti
|
||||
[updateSettings],
|
||||
);
|
||||
|
||||
const handleAutoExpandReasoningChange = useCallback(
|
||||
(autoExpandReasoning: boolean) => {
|
||||
void updateSettings({ autoExpandReasoning });
|
||||
},
|
||||
[updateSettings],
|
||||
);
|
||||
|
||||
const handlePlaybackTest = useCallback(async () => {
|
||||
if (!voiceAudioEngine || isPlaybackTestRunning) {
|
||||
return;
|
||||
@@ -1385,6 +1409,7 @@ export default function SettingsScreen({ view, openAddHostIntent = null }: Setti
|
||||
handleServiceUrlBehaviorChange={handleServiceUrlBehaviorChange}
|
||||
handleLanguageChange={handleLanguageChange}
|
||||
handleTerminalScrollbackLinesChange={handleTerminalScrollbackLinesChange}
|
||||
handleAutoExpandReasoningChange={handleAutoExpandReasoningChange}
|
||||
/>
|
||||
);
|
||||
case "daemon":
|
||||
|
||||
Reference in New Issue
Block a user