+
{timestamp}
)}
@@ -126,20 +126,22 @@ export default function GooseMessage({
{toolRequests.length > 0 && (
-
- {toolRequests.map((toolRequest) => (
+ {toolRequests.map((toolRequest) => (
+
- ))}
-
+
+ ))}
{timestamp}
diff --git a/ui/desktop/src/components/ToolCallWithResponse.tsx b/ui/desktop/src/components/ToolCallWithResponse.tsx
index 327cf16d..9e6d2287 100644
--- a/ui/desktop/src/components/ToolCallWithResponse.tsx
+++ b/ui/desktop/src/components/ToolCallWithResponse.tsx
@@ -34,24 +34,24 @@ export default function ToolCallWithResponse({
interface ToolCallExpandableProps {
label: string | React.ReactNode;
- defaultExpanded?: boolean;
- forceExpand?: boolean;
+ isStartExpanded?: boolean;
+ isForceExpand?: boolean;
children: React.ReactNode;
className?: string;
}
function ToolCallExpandable({
label,
- defaultExpanded = false,
- forceExpand,
+ isStartExpanded = false,
+ isForceExpand,
children,
className = '',
}: ToolCallExpandableProps) {
- const [isExpanded, setIsExpanded] = React.useState(defaultExpanded);
+ const [isExpanded, setIsExpanded] = React.useState(isStartExpanded);
const toggleExpand = () => setIsExpanded((prev) => !prev);
React.useEffect(() => {
- if (forceExpand) setIsExpanded(true);
- }, [forceExpand]);
+ if (isForceExpand) setIsExpanded(true);
+ }, [isForceExpand]);
return (
@@ -74,12 +74,23 @@ interface ToolCallViewProps {
}
function ToolCallView({ isCancelledMessage, toolCall, toolResponse }: ToolCallViewProps) {
+ const responseStyle = localStorage.getItem('response_style');
+ const isExpandToolDetails = (() => {
+ switch (responseStyle) {
+ case 'concise':
+ return false;
+ case 'detailed':
+ default:
+ return true;
+ }
+ })();
+
const isToolDetails = Object.entries(toolCall?.arguments).length > 0;
const loadingStatus: LoadingStatus = !toolResponse?.toolResult.status
? 'loading'
: toolResponse?.toolResult.status;
- const toolResults: { result: Content; defaultExpanded: boolean }[] =
+ const toolResults: { result: Content; isExpandToolResults: boolean }[] =
loadingStatus === 'success' && Array.isArray(toolResponse?.toolResult.value)
? toolResponse.toolResult.value
.filter((item) => {
@@ -88,16 +99,16 @@ function ToolCallView({ isCancelledMessage, toolCall, toolResponse }: ToolCallVi
})
.map((item) => ({
result: item,
- defaultExpanded: ((item.annotations?.priority as number | undefined) ?? -1) >= 0.5,
+ isExpandToolResults: ((item.annotations?.priority as number | undefined) ?? -1) >= 0.5,
}))
: [];
- const shouldExpand = toolResults.some((v) => v.defaultExpanded);
+ const isShouldExpand = isExpandToolDetails || toolResults.some((v) => v.isExpandToolResults);
return (
@@ -110,21 +121,24 @@ function ToolCallView({ isCancelledMessage, toolCall, toolResponse }: ToolCallVi
{/* Tool Details */}
{isToolDetails && (
-
+
)}
{/* Tool Output */}
{!isCancelledMessage && (
<>
- {toolResults.map(({ result, defaultExpanded }, index) => {
+ {toolResults.map(({ result, isExpandToolResults }, index) => {
const isLast = index === toolResults.length - 1;
return (
0 ? '' : 'rounded-t'}
+ ${isLast ? 'rounded-b' : ''}
+ `}
>
-
+
);
})}
@@ -139,11 +153,16 @@ interface ToolDetailsViewProps {
name: string;
arguments: Record;
};
+ isStartExpanded: boolean;
}
-function ToolDetailsView({ toolCall }: ToolDetailsViewProps) {
+function ToolDetailsView({ toolCall, isStartExpanded }: ToolDetailsViewProps) {
return (
-
+
{toolCall.arguments && }
);
@@ -151,14 +170,14 @@ function ToolDetailsView({ toolCall }: ToolDetailsViewProps) {
interface ToolResultViewProps {
result: Content;
- defaultExpanded: boolean;
+ isStartExpanded: boolean;
}
-function ToolResultView({ result, defaultExpanded }: ToolResultViewProps) {
+function ToolResultView({ result, isStartExpanded }: ToolResultViewProps) {
return (
Output}
- defaultExpanded={defaultExpanded}
+ isStartExpanded={isStartExpanded}
>
{result.type === 'text' && result.text && (
diff --git a/ui/desktop/src/components/settings_v2/SettingsView.tsx b/ui/desktop/src/components/settings_v2/SettingsView.tsx
index 091959e3..f670cdba 100644
--- a/ui/desktop/src/components/settings_v2/SettingsView.tsx
+++ b/ui/desktop/src/components/settings_v2/SettingsView.tsx
@@ -5,6 +5,7 @@ import ExtensionsSection from './extensions/ExtensionsSection';
import ModelsSection from './models/ModelsSection';
import { ModeSection } from './mode/ModeSection';
import SessionSharingSection from './sessions/SessionSharingSection';
+import { ResponseStylesSection } from './response_styles/ResponseStylesSection';
import { ExtensionConfig } from '../../api';
import MoreMenuLayout from '../more_menu/MoreMenuLayout';
@@ -47,6 +48,8 @@ export default function SettingsView({
{/*Session sharing*/}
+ {/* Response Styles */}
+
diff --git a/ui/desktop/src/components/settings_v2/response_styles/ResponseStyleSelectionItem.tsx b/ui/desktop/src/components/settings_v2/response_styles/ResponseStyleSelectionItem.tsx
new file mode 100644
index 00000000..c5918ed4
--- /dev/null
+++ b/ui/desktop/src/components/settings_v2/response_styles/ResponseStyleSelectionItem.tsx
@@ -0,0 +1,75 @@
+import { useEffect, useState } from 'react';
+
+export interface ResponseStyle {
+ key: string;
+ label: string;
+ description: string;
+}
+
+export const all_response_styles: ResponseStyle[] = [
+ {
+ key: 'detailed',
+ label: 'Detailed',
+ description: 'Tool calls are by default shown open to expose details',
+ },
+ {
+ key: 'concise',
+ label: 'Concise',
+ description: 'Tool calls are by default closed and only show the tool used',
+ },
+];
+
+interface ResponseStyleSelectionItemProps {
+ currentStyle: string;
+ style: ResponseStyle;
+ showDescription: boolean;
+ handleStyleChange: (newStyle: string) => void;
+}
+
+export function ResponseStyleSelectionItem({
+ currentStyle,
+ style,
+ showDescription,
+ handleStyleChange,
+}: ResponseStyleSelectionItemProps) {
+ const [checked, setChecked] = useState(currentStyle === style.key);
+
+ useEffect(() => {
+ setChecked(currentStyle === style.key);
+ }, [currentStyle, style.key]);
+
+ return (
+
+
handleStyleChange(style.key)}
+ >
+
+
+
{style.label}
+ {showDescription && (
+
{style.description}
+ )}
+
+
+
+
+
handleStyleChange(style.key)}
+ className="peer sr-only"
+ />
+
+
+
+
+ );
+}
diff --git a/ui/desktop/src/components/settings_v2/response_styles/ResponseStylesSection.tsx b/ui/desktop/src/components/settings_v2/response_styles/ResponseStylesSection.tsx
new file mode 100644
index 00000000..756f7b70
--- /dev/null
+++ b/ui/desktop/src/components/settings_v2/response_styles/ResponseStylesSection.tsx
@@ -0,0 +1,46 @@
+import { useEffect, useState } from 'react';
+import { all_response_styles, ResponseStyleSelectionItem } from './ResponseStyleSelectionItem';
+
+export const ResponseStylesSection = () => {
+ const [currentStyle, setCurrentStyle] = useState('detailed');
+
+ useEffect(() => {
+ const savedStyle = localStorage.getItem('response_style');
+ if (savedStyle) {
+ try {
+ setCurrentStyle(savedStyle);
+ } catch (error) {
+ console.error('Error parsing response style:', error);
+ }
+ }
+ }, []);
+
+ const handleStyleChange = async (newStyle: string) => {
+ setCurrentStyle(newStyle);
+ localStorage.setItem('response_style', newStyle);
+ };
+
+ return (
+
+
+
Response Styles
+
+
+
+ Choose how Goose should format and style its responses
+
+
+ {all_response_styles.map((style) => (
+
+ ))}
+
+
+
+ );
+};
diff --git a/ui/desktop/src/components/settings_v2/sessions/SessionSharingSection.tsx b/ui/desktop/src/components/settings_v2/sessions/SessionSharingSection.tsx
index 13c524c3..916d0a56 100644
--- a/ui/desktop/src/components/settings_v2/sessions/SessionSharingSection.tsx
+++ b/ui/desktop/src/components/settings_v2/sessions/SessionSharingSection.tsx
@@ -80,13 +80,13 @@ export default function SessionSharingSection() {
};
return (
-
+
{/*Title*/}
-
+
Session sharing
-
+
{envBaseUrlShare ? (
Session sharing is configured but fully opt-in — your sessions are only shared when you