mirror of
https://github.com/aljazceru/goose.git
synced 2026-02-05 22:54:33 +01:00
Fix a few ui edge cases - refresh occasionally crashing, chat loader over text and chat input height returning to auto (#3469)
This commit is contained in:
@@ -10,7 +10,7 @@
|
||||
"license": {
|
||||
"name": "Apache-2.0"
|
||||
},
|
||||
"version": "1.0.36"
|
||||
"version": "1.1.0"
|
||||
},
|
||||
"paths": {
|
||||
"/agent/tools": {
|
||||
|
||||
@@ -446,9 +446,6 @@ function BaseChatContent({
|
||||
|
||||
{/* Custom content after messages */}
|
||||
{renderAfterMessages && renderAfterMessages()}
|
||||
|
||||
{/* Bottom padding to make space for the loading indicator */}
|
||||
<div className="block h-20" />
|
||||
</ScrollArea>
|
||||
|
||||
{/* Fixed loading indicator at bottom left of chat container */}
|
||||
|
||||
@@ -427,7 +427,6 @@ export default function ChatInput({
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [numTokens, toolCount, tokenLimit, isTokenLimitLoaded, addAlert, clearAlerts]);
|
||||
|
||||
const minHeight = '1rem';
|
||||
const maxHeight = 10 * 24;
|
||||
|
||||
// Debounced function to update actual value
|
||||
@@ -456,6 +455,13 @@ export default function ChatInput({
|
||||
}
|
||||
}, [debouncedAutosize, displayValue]);
|
||||
|
||||
// Reset textarea height when displayValue is empty
|
||||
useEffect(() => {
|
||||
if (textAreaRef.current && displayValue === '') {
|
||||
textAreaRef.current.style.height = 'auto';
|
||||
}
|
||||
}, [displayValue]);
|
||||
|
||||
const handleChange = (evt: React.ChangeEvent<HTMLTextAreaElement>) => {
|
||||
const val = evt.target.value;
|
||||
const cursorPosition = evt.target.selectionStart;
|
||||
@@ -910,7 +916,6 @@ export default function ChatInput({
|
||||
ref={textAreaRef}
|
||||
rows={1}
|
||||
style={{
|
||||
minHeight: `${minHeight}px`,
|
||||
maxHeight: `${maxHeight}px`,
|
||||
overflowY: 'auto',
|
||||
opacity: isRecording ? 0 : 1,
|
||||
|
||||
@@ -199,7 +199,7 @@ export default function Pair({
|
||||
onMessageStreamFinish={handleMessageStreamFinish}
|
||||
renderBeforeMessages={renderBeforeMessages}
|
||||
customChatInputProps={customChatInputProps}
|
||||
contentClassName={cn('pr-1', (isMobile || sidebarState === 'collapsed') && 'pt-11')} // Use dynamic content class with mobile margin and sidebar state
|
||||
contentClassName={cn('pr-1 pb-10', (isMobile || sidebarState === 'collapsed') && 'pt-11')} // Use dynamic content class with mobile margin and sidebar state
|
||||
showPopularTopics={!isTransitioningFromHub} // Don't show popular topics while transitioning from Hub
|
||||
suppressEmptyState={isTransitioningFromHub} // Suppress all empty state content while transitioning from Hub
|
||||
/>
|
||||
|
||||
@@ -23,6 +23,7 @@ export function SessionInsights() {
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
const [recentSessions, setRecentSessions] = useState<Session[]>([]);
|
||||
const [isLoading, setIsLoading] = useState(true);
|
||||
const [isLoadingSessions, setIsLoadingSessions] = useState(true);
|
||||
// const [recentProjects, setRecentProjects] = useState<ProjectMetadata[]>([]);
|
||||
const navigate = useNavigate();
|
||||
|
||||
@@ -67,7 +68,8 @@ export function SessionInsights() {
|
||||
setRecentSessions(sessions.slice(0, 3));
|
||||
} catch (error) {
|
||||
console.error('Failed to load recent sessions:', error);
|
||||
// Don't set loading to false here since insights is the primary data
|
||||
} finally {
|
||||
setIsLoadingSessions(false);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -400,7 +402,32 @@ export function SessionInsights() {
|
||||
</Button>
|
||||
</div>
|
||||
<div className="space-y-1 min-h-[96px] transition-all duration-300 ease-in-out">
|
||||
{recentSessions.length > 0 ? (
|
||||
{isLoadingSessions ? (
|
||||
// Show skeleton while sessions are loading
|
||||
<>
|
||||
<div className="flex items-center justify-between py-1 px-2">
|
||||
<div className="flex items-center space-x-2">
|
||||
<Skeleton className="h-4 w-4 rounded-sm" />
|
||||
<Skeleton className="h-4 w-48" />
|
||||
</div>
|
||||
<Skeleton className="h-4 w-16" />
|
||||
</div>
|
||||
<div className="flex items-center justify-between py-1 px-2">
|
||||
<div className="flex items-center space-x-2">
|
||||
<Skeleton className="h-4 w-4 rounded-sm" />
|
||||
<Skeleton className="h-4 w-40" />
|
||||
</div>
|
||||
<Skeleton className="h-4 w-16" />
|
||||
</div>
|
||||
<div className="flex items-center justify-between py-1 px-2">
|
||||
<div className="flex items-center space-x-2">
|
||||
<Skeleton className="h-4 w-4 rounded-sm" />
|
||||
<Skeleton className="h-4 w-52" />
|
||||
</div>
|
||||
<Skeleton className="h-4 w-16" />
|
||||
</div>
|
||||
</>
|
||||
) : recentSessions.length > 0 ? (
|
||||
recentSessions.map((session, index) => (
|
||||
<div
|
||||
key={session.id}
|
||||
|
||||
@@ -651,9 +651,39 @@ const createChat = async (
|
||||
// We need to wait for the window to load before we can access localStorage
|
||||
mainWindow.webContents.on('did-finish-load', () => {
|
||||
const configStr = JSON.stringify(windowConfig).replace(/'/g, "\\'");
|
||||
mainWindow.webContents.executeJavaScript(`
|
||||
localStorage.setItem('gooseConfig', '${configStr}')
|
||||
`);
|
||||
// Add error handling and retry logic for localStorage access
|
||||
mainWindow.webContents
|
||||
.executeJavaScript(
|
||||
`
|
||||
try {
|
||||
if (typeof Storage !== 'undefined' && window.localStorage) {
|
||||
localStorage.setItem('gooseConfig', '${configStr}');
|
||||
} else {
|
||||
console.warn('localStorage not available, retrying in 100ms');
|
||||
setTimeout(() => {
|
||||
try {
|
||||
localStorage.setItem('gooseConfig', '${configStr}');
|
||||
} catch (e) {
|
||||
console.error('Failed to set localStorage after retry:', e);
|
||||
}
|
||||
}, 100);
|
||||
}
|
||||
} catch (e) {
|
||||
console.error('Failed to access localStorage:', e);
|
||||
// Retry after a short delay
|
||||
setTimeout(() => {
|
||||
try {
|
||||
localStorage.setItem('gooseConfig', '${configStr}');
|
||||
} catch (retryError) {
|
||||
console.error('Failed to set localStorage after retry:', retryError);
|
||||
}
|
||||
}, 100);
|
||||
}
|
||||
`
|
||||
)
|
||||
.catch((error) => {
|
||||
console.error('Failed to execute localStorage script:', error);
|
||||
});
|
||||
});
|
||||
|
||||
// Handle new window creation for links
|
||||
|
||||
@@ -116,7 +116,20 @@ type AppConfigAPI = {
|
||||
const electronAPI: ElectronAPI = {
|
||||
platform: process.platform,
|
||||
reactReady: () => ipcRenderer.send('react-ready'),
|
||||
getConfig: () => config,
|
||||
getConfig: () => {
|
||||
// Add fallback to localStorage if config from preload is empty or missing
|
||||
if (!config || Object.keys(config).length === 0) {
|
||||
try {
|
||||
const storedConfig = localStorage.getItem('gooseConfig');
|
||||
if (storedConfig) {
|
||||
return JSON.parse(storedConfig);
|
||||
}
|
||||
} catch (e) {
|
||||
console.warn('Failed to parse stored config from localStorage:', e);
|
||||
}
|
||||
}
|
||||
return config;
|
||||
},
|
||||
hideWindow: () => ipcRenderer.send('hide-window'),
|
||||
directoryChooser: (replace?: boolean) => ipcRenderer.invoke('directory-chooser', replace),
|
||||
createChatWindow: (
|
||||
|
||||
Reference in New Issue
Block a user