Skip to content
Merged
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
33 changes: 2 additions & 31 deletions ui/desktop/src/components/GooseSidebar/AppSidebar.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import React, { useEffect, useState } from 'react';
import { FileText, Clock, Home, Puzzle, History, FolderKanban } from 'lucide-react';
import { useNavigate } from 'react-router-dom';
import { motion, AnimatePresence } from 'framer-motion';
import { motion } from 'framer-motion';
import {
SidebarContent,
SidebarFooter,
Expand All @@ -10,15 +10,13 @@ import {
SidebarMenuButton,
SidebarGroup,
SidebarGroupContent,
useSidebar,
SidebarSeparator,
} from '../ui/sidebar';
import { ChatSmart, Gear } from '../icons';
import { ViewOptions, View } from '../../App';
import { Recipe } from '../../recipe';
import { saveRecipe } from '../../recipe/recipeStorage';
import { useChatContext } from '../../contexts/ChatContext';
import { Goose } from '../icons/Goose';

interface SidebarProps {
onSelectSession: (sessionId: string) => void;
Expand All @@ -38,7 +36,6 @@ const AppSidebar: React.FC<SidebarProps> = ({ currentPath }) => {
const [saveGlobal, setSaveGlobal] = useState(true);
const [saving, setSaving] = useState(false);
const { resetChat } = useChatContext();
const { state } = useSidebar();

useEffect(() => {
// Trigger animation after a small delay
Expand Down Expand Up @@ -282,33 +279,7 @@ const AppSidebar: React.FC<SidebarProps> = ({ currentPath }) => {
</SidebarMenu>
</SidebarContent>

<SidebarFooter className="mb-2">
<motion.div
initial={{ opacity: 0, x: 20 }}
animate={{ opacity: 1, x: 0 }}
transition={{ type: 'spring', stiffness: 300, damping: 25, delay: 0.5 }}
className="flex flex-col gap-2 w-full"
>
<div className="flex items-center gap-2">
<Goose className="w-6 h-6" />
<AnimatePresence mode="wait">
{state === 'expanded' && (
<motion.span
key="logo-text"
initial={{ opacity: 0, width: 0 }}
animate={{ opacity: 1, width: 'auto' }}
exit={{ opacity: 0, width: 0 }}
transition={{ duration: 0.2, ease: 'easeInOut' }}
className="text-base overflow-hidden whitespace-nowrap leading-tight"
>
codename
<br /> goose
</motion.span>
)}
</AnimatePresence>
</div>
</motion.div>
</SidebarFooter>
<SidebarFooter />

{/* Save Recipe Dialog */}
{showSaveDialog && (
Expand Down
1 change: 0 additions & 1 deletion ui/desktop/src/components/Splash.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,6 @@ export default function Splash({ append, activities, title }: SplashProps) {
<div className="flex flex-col px-6 mb-0">
<Greeting className="text-text-prominent text-4xl font-light mb-2" />
</div>

<div className="flex flex-col">
{messagePill && (
<div className="mb-4 p-3 rounded-lg border animate-[fadein_500ms_ease-in_forwards]">
Expand Down
8 changes: 4 additions & 4 deletions ui/desktop/src/components/UserMessage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ export default function UserMessage({ message }: UserMessageProps) {
const urls = extractUrls(displayText, []);

return (
<div className="message flex justify-start mt-[16px] w-full opacity-0 animate-[appear_150ms_ease-in_forwards]">
<div className="message flex justify-end mt-[16px] w-full opacity-0 animate-[appear_150ms_ease-in_forwards]">
<div className="flex-col max-w-[85%] w-fit">
<div className="flex flex-col group">
<div className="flex bg-background-accent text-text-on-accent rounded-xl py-2.5 px-4">
Expand All @@ -52,11 +52,11 @@ export default function UserMessage({ message }: UserMessageProps) {
</div>
)}

<div className="relative h-[22px] flex justify-end">
<div className="absolute font-mono left-0 w-40 text-xs text-text-muted pt-1 transition-all duration-200 group-hover:-translate-y-4 group-hover:opacity-0">
<div className="relative h-[22px] flex justify-end text-right overflow-hidden">
<div className="absolute font-mono right-4 w-40 text-xs text-text-muted pt-1 transition-all duration-200 group-hover:-translate-y-4 group-hover:opacity-0">
{timestamp}
</div>
<div className="absolute left-0 pt-1">
<div className="absolute right-4 pt-1">
<MessageCopyLink text={displayText} contentRef={contentRef} />
</div>
</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,9 +58,9 @@ export const BottomMenuModeSelection = ({ setView }: BottomMenuModeSelectionProp
{getValueByKey(gooseMode).toLowerCase()}
</span>
</DropdownMenuTrigger>
<DropdownMenuContent className="w-[240px] px-2" side="top" align="end">
<DropdownMenuContent className="w-64" side="top" align="center">
{all_goose_modes.map((mode) => (
<DropdownMenuItem key={mode.key} className="p-0">
<DropdownMenuItem key={mode.key} asChild>
<ModeSelectionItem
mode={mode}
currentMode={gooseMode}
Expand Down
10 changes: 4 additions & 6 deletions ui/desktop/src/components/common/Greeting.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -48,11 +48,9 @@ export function Greeting({
const messageRef = useTextAnimator({ text: greeting.message });

return (
<div className="px-8 pb-12 pt-16 bg-background-default">
<h1 className={className} key={forceRefresh ? Date.now() : undefined}>
{/* <span>{greeting.prefix}</span> */}
<span ref={messageRef}>{greeting.message}</span>
</h1>
</div>
<h1 className={className} key={forceRefresh ? Date.now() : undefined}>
{/* <span>{greeting.prefix}</span> */}
<span ref={messageRef}>{greeting.message}</span>
</h1>
);
}
13 changes: 12 additions & 1 deletion ui/desktop/src/components/sessions/SessionsInsights.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { useNavigate } from 'react-router-dom';
import { Button } from '../ui/button';
import { ChatSmart } from '../icons/';
import { motion, AnimatePresence } from 'framer-motion';
import { Goose } from '../icons/Goose';

interface SessionInsightsType {
totalSessions: number;
Expand Down Expand Up @@ -106,7 +107,17 @@ export function SessionInsights() {

return (
<div className="bg-background-muted">
<Greeting />
<div className="px-8 pb-12 pt-19 bg-background-default space-y-4">
<motion.div
initial={{ opacity: 0, scale: 0.25, x: -5, y: 5, rotate: -20 }}
animate={{ opacity: 1, scale: 1, x: 0, y: 0, rotate: 0 }}
transition={{ type: 'spring', stiffness: 300, damping: 25 }}
className="origin-bottom-left"
>
<Goose className="size-8" />
</motion.div>
<Greeting />
</div>

<div className="grid gap-[2px] mt-0.5">
{/* Top row with three equal columns */}
Expand Down
8 changes: 3 additions & 5 deletions ui/desktop/src/components/settings/mode/ModeSelectionItem.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -59,17 +59,15 @@ export function ModeSelectionItem({
}, [currentMode, mode.key]);

return (
<div className="group hover:cursor-pointer">
<div className="group hover:cursor-pointer text-sm">
<div
className={`flex items-center justify-between text-text-default py-2 px-2 ${checked ? 'bg-background-muted' : 'bg-background-default hover:bg-background-muted'} rounded-lg transition-all`}
onClick={() => handleModeChange(mode.key)}
>
<div className="flex">
<div>
<h3 className="text-text-default text-xs">{mode.label}</h3>
{showDescription && (
<p className="text-xs text-text-muted mt-[2px]">{mode.description}</p>
)}
<h3 className="text-text-default">{mode.label}</h3>
{showDescription && <p className="text-text-muted mt-[2px]">{mode.description}</p>}
</div>
</div>

Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,17 @@
import { Sliders } from 'lucide-react';
import React, { useEffect, useState, useRef } from 'react';
import React, { useEffect, useState, useRef, useCallback } from 'react';
import { useModelAndProvider } from '../../../ModelAndProviderContext';
import { AddModelModal } from '../subcomponents/AddModelModal';
import { LeadWorkerSettings } from '../subcomponents/LeadWorkerSettings';
import { View } from '../../../../App';
import { Tooltip, TooltipTrigger, TooltipContent, TooltipProvider } from '../../../ui/Tooltip';
import { Tooltip, TooltipTrigger, TooltipContent } from '../../../ui/Tooltip';
import { Dialog, DialogContent } from '../../../ui/dialog';
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuTrigger,
} from '../../../ui/dropdown-menu';
import { useCurrentModelInfo } from '../../../ChatView';
import { useConfig } from '../../../ConfigContext';

Expand All @@ -17,15 +23,14 @@ export default function ModelsBottomBar({ dropdownRef, setView }: ModelsBottomBa
const { currentModel } = useModelAndProvider();
const currentModelInfo = useCurrentModelInfo();
const { read } = useConfig();
const [isModelMenuOpen, setIsModelMenuOpen] = useState(false);
const [isAddModelModalOpen, setIsAddModelModalOpen] = useState(false);
const [isLeadWorkerModalOpen, setIsLeadWorkerModalOpen] = useState(false);
const [isLeadWorkerActive, setIsLeadWorkerActive] = useState(false);
const menuRef = useRef<HTMLDivElement>(null);
const [isModelTruncated, setIsModelTruncated] = useState(false);
// eslint-disable-next-line no-undef
const modelRef = useRef<HTMLSpanElement>(null);
const [isTooltipOpen, setIsTooltipOpen] = useState(false);
const [isDropdownOpen, setIsDropdownOpen] = useState(false);

// Check if lead/worker mode is active
useEffect(() => {
Expand Down Expand Up @@ -62,35 +67,31 @@ export default function ModelsBottomBar({ dropdownRef, setView }: ModelsBottomBa
setIsTooltipOpen(false);
}, [isModelTruncated]);

// Add click outside handler
useEffect(() => {
function handleClickOutside(event: MouseEvent) {
if (menuRef.current && !menuRef.current.contains(event.target as Node)) {
setIsModelMenuOpen(false);
const handleTooltipOpenChange = useCallback(
(open: boolean) => {
// Only allow tooltip to open if dropdown is closed
if (!isDropdownOpen) {
setIsTooltipOpen(open);
}
}
},
[isDropdownOpen]
);

// Add the event listener when the menu is open
if (isModelMenuOpen) {
document.addEventListener('mousedown', handleClickOutside);
const handleDropdownOpenChange = useCallback((open: boolean) => {
setIsDropdownOpen(open);
// Close tooltip when dropdown opens
if (open) {
setIsTooltipOpen(false);
}

// Clean up the event listener
return () => {
document.removeEventListener('mousedown', handleClickOutside);
};
}, [isModelMenuOpen]);
}, []);

return (
<div className="relative flex items-center" ref={dropdownRef}>
<div ref={menuRef} className="relative">
<div
className="flex items-center hover:cursor-pointer max-w-[180px] md:max-w-[200px] lg:max-w-[380px] min-w-0 group hover:text-textStandard transition-colors"
onClick={() => setIsModelMenuOpen(!isModelMenuOpen)}
>
<TooltipProvider>
<Tooltip open={isTooltipOpen} onOpenChange={setIsTooltipOpen}>
<TooltipTrigger asChild>
<Tooltip open={isTooltipOpen && !isDropdownOpen} onOpenChange={handleTooltipOpenChange}>
<TooltipTrigger>
<DropdownMenu open={isDropdownOpen} onOpenChange={handleDropdownOpenChange}>
<DropdownMenuTrigger asChild>
<div className="flex items-center hover:cursor-pointer max-w-[180px] md:max-w-[200px] lg:max-w-[380px] min-w-0 group hover:text-textStandard transition-colors">
<span
ref={modelRef}
className="truncate text-text-default/70 hover:text-text-default hover:scale-100 hover:bg-transparent text-xs max-w-[130px] md:max-w-[200px] lg:max-w-[360px] min-w-0 block"
Expand All @@ -100,49 +101,29 @@ export default function ModelsBottomBar({ dropdownRef, setView }: ModelsBottomBa
<span className="ml-1 text-[10px] opacity-60">({modelMode})</span>
)}
</span>
</TooltipTrigger>
{isModelTruncated && (
<TooltipContent className="max-w-96 overflow-auto scrollbar-thin" side="top">
{displayModel}
{isLeadWorkerActive && modelMode && (
<span className="ml-1 text-[10px] opacity-60">({modelMode})</span>
)}
</TooltipContent>
)}
</Tooltip>
</TooltipProvider>
</div>

{/* Dropdown Menu */}
{isModelMenuOpen && (
<div className="absolute bottom-[24px] right-[-55px] w-[300px] z-50 bg-background-default rounded-lg border">
<div className="">
<div
className="flex items-center justify-between text-textStandard p-2 cursor-pointer transition-colors hover:bg-bgStandard
border-t border-borderSubtle mt-2"
onClick={() => {
setIsModelMenuOpen(false);
setIsAddModelModalOpen(true);
}}
>
<span className="text-sm">Change Model</span>
<Sliders className="w-4 h-4 ml-2 rotate-90" />
</div>
<div
className="flex items-center justify-between text-textStandard p-2 cursor-pointer transition-colors hover:bg-bgStandard
border-t border-borderSubtle"
onClick={() => {
setIsModelMenuOpen(false);
setIsLeadWorkerModalOpen(true);
}}
>
<span className="text-sm">Lead/Worker Settings</span>
<Sliders className="w-4 h-4 ml-2" />
</div>
</div>
</div>
</DropdownMenuTrigger>
<DropdownMenuContent side="top" align="center" className="w-64 text-sm">
<DropdownMenuItem onClick={() => setIsAddModelModalOpen(true)}>
<span>Change Model</span>
<Sliders className="ml-auto h-4 w-4 rotate-90" />
</DropdownMenuItem>
<DropdownMenuItem onClick={() => setIsLeadWorkerModalOpen(true)}>
<span>Lead/Worker Settings</span>
<Sliders className="ml-auto h-4 w-4" />
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
</TooltipTrigger>
{isModelTruncated && (
<TooltipContent className="max-w-96 overflow-auto scrollbar-thin" side="top">
{displayModel}
{isLeadWorkerActive && modelMode && (
<span className="ml-1 text-[10px] opacity-60">({modelMode})</span>
)}
</TooltipContent>
)}
</div>
</Tooltip>

{isAddModelModalOpen ? (
<AddModelModal setView={setView} onClose={() => setIsAddModelModalOpen(false)} />
Expand Down
Loading