mirror of
https://github.com/aljazceru/goose.git
synced 2025-12-21 08:04:20 +01:00
Bottom and top bar refinement (#2303)
Co-authored-by: Nahiyan Khan <nahiyan@squareup.com>
This commit is contained in:
10
ui/desktop/package-lock.json
generated
10
ui/desktop/package-lock.json
generated
@@ -37,7 +37,7 @@
|
||||
"express": "^4.21.1",
|
||||
"framer-motion": "^11.11.11",
|
||||
"lodash": "^4.17.21",
|
||||
"lucide-react": "^0.454.0",
|
||||
"lucide-react": "^0.475.0",
|
||||
"react": "^18.3.1",
|
||||
"react-dom": "^18.3.1",
|
||||
"react-icons": "^5.3.0",
|
||||
@@ -11454,12 +11454,12 @@
|
||||
}
|
||||
},
|
||||
"node_modules/lucide-react": {
|
||||
"version": "0.454.0",
|
||||
"resolved": "https://registry.npmjs.org/lucide-react/-/lucide-react-0.454.0.tgz",
|
||||
"integrity": "sha512-hw7zMDwykCLnEzgncEEjHeA6+45aeEzRYuKHuyRSOPkhko+J3ySGjGIzu+mmMfDFG1vazHepMaYFYHbTFAZAAQ==",
|
||||
"version": "0.475.0",
|
||||
"resolved": "https://registry.npmjs.org/lucide-react/-/lucide-react-0.475.0.tgz",
|
||||
"integrity": "sha512-NJzvVu1HwFVeZ+Gwq2q00KygM1aBhy/ZrhY9FsAgJtpB+E4R7uxRk9M2iKvHa6/vNxZydIB59htha4c2vvwvVg==",
|
||||
"license": "ISC",
|
||||
"peerDependencies": {
|
||||
"react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0-rc"
|
||||
"react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/magic-string": {
|
||||
|
||||
@@ -109,7 +109,7 @@
|
||||
"express": "^4.21.1",
|
||||
"framer-motion": "^11.11.11",
|
||||
"lodash": "^4.17.21",
|
||||
"lucide-react": "^0.454.0",
|
||||
"lucide-react": "^0.475.0",
|
||||
"react": "^18.3.1",
|
||||
"react-dom": "^18.3.1",
|
||||
"react-icons": "^5.3.0",
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
import React, { useRef, useState, useEffect, useCallback } from 'react';
|
||||
import { Button } from './ui/button';
|
||||
import type { View } from '../App';
|
||||
import Stop from './ui/Stop';
|
||||
import { Attach, Send } from './icons';
|
||||
import { debounce } from 'lodash';
|
||||
import BottomMenu from './bottom_menu/BottomMenu';
|
||||
|
||||
interface InputProps {
|
||||
handleSubmit: (e: React.FormEvent) => void;
|
||||
@@ -11,6 +13,8 @@ interface InputProps {
|
||||
commandHistory?: string[];
|
||||
initialValue?: string;
|
||||
droppedFiles?: string[];
|
||||
setView: (view: View) => void;
|
||||
numTokens?: number;
|
||||
}
|
||||
|
||||
export default function Input({
|
||||
@@ -19,10 +23,13 @@ export default function Input({
|
||||
onStop,
|
||||
commandHistory = [],
|
||||
initialValue = '',
|
||||
setView,
|
||||
numTokens,
|
||||
droppedFiles = [],
|
||||
}: InputProps) {
|
||||
const [_value, setValue] = useState(initialValue);
|
||||
const [displayValue, setDisplayValue] = useState(initialValue); // For immediate visual feedback
|
||||
const [isFocused, setIsFocused] = useState(false);
|
||||
|
||||
// Update internal value when initialValue changes
|
||||
useEffect(() => {
|
||||
@@ -205,65 +212,82 @@ export default function Input({
|
||||
};
|
||||
|
||||
return (
|
||||
<form
|
||||
onSubmit={onFormSubmit}
|
||||
className="flex relative h-auto px-[16px] pr-[68px] py-[1rem] border-t border-borderSubtle"
|
||||
<div
|
||||
className={`flex flex-col relative h-auto border rounded-lg transition-colors ${
|
||||
isFocused
|
||||
? 'border-borderProminent hover:border-borderProminent'
|
||||
: 'border-borderSubtle hover:border-borderStandard'
|
||||
} bg-bgApp z-10`}
|
||||
>
|
||||
<textarea
|
||||
data-testid="chat-input"
|
||||
autoFocus
|
||||
id="dynamic-textarea"
|
||||
placeholder="What can goose help with? ⌘↑/⌘↓"
|
||||
value={displayValue}
|
||||
onChange={handleChange}
|
||||
onCompositionStart={handleCompositionStart}
|
||||
onCompositionEnd={handleCompositionEnd}
|
||||
onKeyDown={handleKeyDown}
|
||||
ref={textAreaRef}
|
||||
rows={1}
|
||||
style={{
|
||||
minHeight: `${minHeight}px`,
|
||||
maxHeight: `${maxHeight}px`,
|
||||
overflowY: 'auto',
|
||||
}}
|
||||
className="w-full outline-none border-none focus:ring-0 bg-transparent p-0 text-base resize-none text-textStandard"
|
||||
/>
|
||||
<Button
|
||||
type="button"
|
||||
size="icon"
|
||||
variant="ghost"
|
||||
onClick={handleFileSelect}
|
||||
className="absolute right-[40px] top-1/2 -translate-y-1/2 text-textSubtle hover:text-textStandard"
|
||||
>
|
||||
<Attach />
|
||||
</Button>
|
||||
{isLoading ? (
|
||||
<Button
|
||||
type="button"
|
||||
size="icon"
|
||||
variant="ghost"
|
||||
onClick={(e) => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
onStop?.();
|
||||
<form onSubmit={onFormSubmit}>
|
||||
<textarea
|
||||
data-testid="chat-input"
|
||||
autoFocus
|
||||
id="dynamic-textarea"
|
||||
placeholder="What can goose help with? ⌘↑/⌘↓"
|
||||
value={displayValue}
|
||||
onChange={handleChange}
|
||||
onCompositionStart={handleCompositionStart}
|
||||
onCompositionEnd={handleCompositionEnd}
|
||||
onKeyDown={handleKeyDown}
|
||||
onFocus={() => setIsFocused(true)}
|
||||
onBlur={() => setIsFocused(false)}
|
||||
ref={textAreaRef}
|
||||
rows={1}
|
||||
style={{
|
||||
minHeight: `${minHeight}px`,
|
||||
maxHeight: `${maxHeight}px`,
|
||||
overflowY: 'auto',
|
||||
}}
|
||||
className="absolute right-2 top-1/2 -translate-y-1/2 [&_svg]:size-5 text-textSubtle hover:text-textStandard"
|
||||
>
|
||||
<Stop size={24} />
|
||||
</Button>
|
||||
) : (
|
||||
<Button
|
||||
type="submit"
|
||||
size="icon"
|
||||
variant="ghost"
|
||||
disabled={!displayValue.trim()}
|
||||
className={`absolute right-2 top-1/2 -translate-y-1/2 text-textSubtle hover:text-textStandard ${
|
||||
!displayValue.trim() ? 'text-textSubtle cursor-not-allowed' : ''
|
||||
}`}
|
||||
>
|
||||
<Send />
|
||||
</Button>
|
||||
)}
|
||||
</form>
|
||||
className="w-full pl-4 pr-[68px] outline-none border-none focus:ring-0 bg-transparent pt-3 pb-1.5 text-sm resize-none text-textStandard"
|
||||
/>
|
||||
|
||||
{isLoading ? (
|
||||
<Button
|
||||
type="button"
|
||||
size="icon"
|
||||
variant="ghost"
|
||||
onClick={(e) => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
onStop?.();
|
||||
}}
|
||||
className="absolute right-3 top-2 text-textSubtle rounded-full border border-borderSubtle hover:border-borderStandard hover:text-textStandard w-7 h-7 [&_svg]:size-4"
|
||||
>
|
||||
<Stop size={24} />
|
||||
</Button>
|
||||
) : (
|
||||
<Button
|
||||
type="submit"
|
||||
size="icon"
|
||||
variant="ghost"
|
||||
disabled={!displayValue.trim()}
|
||||
className={`absolute right-3 top-2 transition-colors rounded-full hover:cursor w-7 h-7 [&_svg]:size-4 ${
|
||||
!displayValue.trim()
|
||||
? 'text-textSubtle cursor-not-allowed'
|
||||
: 'bg-bgAppInverse text-white'
|
||||
}`}
|
||||
>
|
||||
<Send />
|
||||
</Button>
|
||||
)}
|
||||
</form>
|
||||
|
||||
<div className="flex items-center transition-colors text-textSubtle relative text-xs p-2 pr-3 border-t border-borderSubtle gap-2">
|
||||
<div className="gap-1 flex items-center justify-between w-full">
|
||||
<Button
|
||||
type="button"
|
||||
size="icon"
|
||||
variant="ghost"
|
||||
onClick={handleFileSelect}
|
||||
className="text-textSubtle hover:text-textStandard w-7 h-7 [&_svg]:size-4"
|
||||
>
|
||||
<Attach />
|
||||
</Button>
|
||||
|
||||
<BottomMenu setView={setView} numTokens={numTokens} />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -1,9 +1,7 @@
|
||||
import React, { useEffect, useRef, useState, useMemo } from 'react';
|
||||
import { getApiUrl } from '../config';
|
||||
import BottomMenu from './bottom_menu/BottomMenu';
|
||||
import FlappyGoose from './FlappyGoose';
|
||||
import GooseMessage from './GooseMessage';
|
||||
import Input from './Input';
|
||||
import { type View, ViewOptions } from '../App';
|
||||
import LoadingGoose from './LoadingGoose';
|
||||
import MoreMenuLayout from './more_menu/MoreMenuLayout';
|
||||
@@ -34,6 +32,7 @@ import {
|
||||
ToolResponseMessageContent,
|
||||
ToolConfirmationRequestMessageContent,
|
||||
} from '../types/message';
|
||||
import ChatInput from './ChatInput';
|
||||
|
||||
export interface ChatType {
|
||||
id: string;
|
||||
@@ -469,9 +468,11 @@ function ChatContent({
|
||||
<div className="flex flex-col w-full h-screen items-center justify-center">
|
||||
{/* Loader when generating recipe */}
|
||||
{isGeneratingRecipe && <LayingEggLoader />}
|
||||
<div className="relative flex items-center h-[36px] w-full">
|
||||
<MoreMenuLayout setView={setView} setIsGoosehintsModalOpen={setIsGoosehintsModalOpen} />
|
||||
</div>
|
||||
<MoreMenuLayout
|
||||
hasMessages={hasMessages}
|
||||
setView={setView}
|
||||
setIsGoosehintsModalOpen={setIsGoosehintsModalOpen}
|
||||
/>
|
||||
|
||||
<Card
|
||||
className="flex flex-col flex-1 rounded-none h-[calc(100vh-95px)] w-full bg-bgApp mt-0 border-none relative"
|
||||
@@ -559,21 +560,23 @@ function ChatContent({
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
<div className="block h-16" />
|
||||
<div className="block h-8" />
|
||||
</ScrollArea>
|
||||
)}
|
||||
|
||||
<div className="relative">
|
||||
<div className="relative p-4 pt-0 z-10 animate-[fadein_400ms_ease-in_forwards]">
|
||||
{isLoading && <LoadingGoose />}
|
||||
<Input
|
||||
<ChatInput
|
||||
handleSubmit={handleSubmit}
|
||||
isLoading={isLoading}
|
||||
onStop={onStopGoose}
|
||||
commandHistory={commandHistory}
|
||||
initialValue={_input}
|
||||
setView={setView}
|
||||
hasMessages={hasMessages}
|
||||
numTokens={sessionTokenCount}
|
||||
droppedFiles={droppedFiles}
|
||||
/>
|
||||
<BottomMenu hasMessages={hasMessages} setView={setView} numTokens={sessionTokenCount} />
|
||||
</div>
|
||||
</Card>
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@ const LoadingGoose = () => {
|
||||
<div className="w-full pb-[2px]">
|
||||
<div
|
||||
data-testid="loading-indicator"
|
||||
className="flex items-center text-xs text-textStandard mb-2 mt-2 pl-4 animate-[appear_250ms_ease-in_forwards]"
|
||||
className="flex items-center text-xs text-textStandard mb-2 mt-2 animate-[appear_250ms_ease-in_forwards]"
|
||||
>
|
||||
<GooseLogo className="mr-2" size="small" hover={false} />
|
||||
goose is working on it…
|
||||
|
||||
@@ -3,6 +3,7 @@ import { ProviderGrid } from './ProviderGrid';
|
||||
import { ScrollArea } from './ui/scroll-area';
|
||||
import { Button } from './ui/button';
|
||||
import WelcomeGooseLogo from './WelcomeGooseLogo';
|
||||
import MoreMenuLayout from './more_menu/MoreMenuLayout';
|
||||
|
||||
// Extending React CSSProperties to include custom webkit property
|
||||
declare module 'react' {
|
||||
@@ -18,11 +19,7 @@ interface WelcomeScreenProps {
|
||||
export default function WelcomeScreen({ onSubmit }: WelcomeScreenProps) {
|
||||
return (
|
||||
<div className="h-screen w-full select-none bg-white dark:bg-black">
|
||||
{/* Draggable title bar region */}
|
||||
<div
|
||||
className="relative flex items-center h-[36px] w-full bg-bgSubtle"
|
||||
style={{ WebkitAppRegion: 'drag' }}
|
||||
></div>
|
||||
<MoreMenuLayout showMenu={false} />
|
||||
|
||||
{/* Content area - explicitly set as non-draggable */}
|
||||
<div
|
||||
|
||||
@@ -5,25 +5,22 @@ import { AlertType, useAlerts } from '../alerts';
|
||||
import { useToolCount } from '../alerts/useToolCount';
|
||||
import BottomMenuAlertPopover from './BottomMenuAlertPopover';
|
||||
import { ModelRadioList } from '../settings/models/ModelRadioList';
|
||||
import { Document, ChevronUp, ChevronDown } from '../icons';
|
||||
import { ChevronUp, ChevronDown } from '../icons';
|
||||
import type { View, ViewOptions } from '../../App';
|
||||
import { settingsV2Enabled } from '../../flags';
|
||||
import { BottomMenuModeSelection } from './BottomMenuModeSelection';
|
||||
import ModelsBottomBar from '../settings_v2/models/bottom_bar/ModelsBottomBar';
|
||||
import { useConfig } from '../ConfigContext';
|
||||
import { getCurrentModelAndProvider } from '../settings_v2/models';
|
||||
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from '../ui/Tooltip';
|
||||
|
||||
const TOKEN_LIMIT_DEFAULT = 128000; // fallback for custom models that the backend doesn't know about
|
||||
const TOKEN_WARNING_THRESHOLD = 0.8; // warning shows at 80% of the token limit
|
||||
const TOOLS_MAX_SUGGESTED = 60; // max number of tools before we show a warning
|
||||
|
||||
export default function BottomMenu({
|
||||
hasMessages,
|
||||
setView,
|
||||
numTokens = 0,
|
||||
}: {
|
||||
hasMessages: boolean;
|
||||
setView: (view: View, viewOptions?: ViewOptions) => void;
|
||||
numTokens?: number;
|
||||
}) {
|
||||
@@ -34,10 +31,6 @@ export default function BottomMenu({
|
||||
const toolCount = useToolCount();
|
||||
const { getProviders, read } = useConfig();
|
||||
const [tokenLimit, setTokenLimit] = useState<number>(TOKEN_LIMIT_DEFAULT);
|
||||
const [isDirTruncated, setIsDirTruncated] = useState(false);
|
||||
// eslint-disable-next-line no-undef
|
||||
const dirRef = useRef<HTMLSpanElement>(null);
|
||||
const [isTooltipOpen, setIsTooltipOpen] = useState(false);
|
||||
|
||||
// Load providers and get current model's token limit
|
||||
const loadProviderDetails = async () => {
|
||||
@@ -142,62 +135,12 @@ export default function BottomMenu({
|
||||
};
|
||||
}, [isModelMenuOpen]);
|
||||
|
||||
useEffect(() => {
|
||||
const checkTruncation = () => {
|
||||
if (dirRef.current) {
|
||||
setIsDirTruncated(dirRef.current.scrollWidth > dirRef.current.clientWidth);
|
||||
}
|
||||
};
|
||||
checkTruncation();
|
||||
window.addEventListener('resize', checkTruncation);
|
||||
return () => window.removeEventListener('resize', checkTruncation);
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
setIsTooltipOpen(false);
|
||||
}, [isDirTruncated]);
|
||||
|
||||
return (
|
||||
<div className="flex justify-between items-center text-textSubtle relative bg-bgSubtle border-t border-borderSubtle text-xs pl-4 h-[40px] pb-1 align-middle">
|
||||
{/* Directory Chooser - Always visible */}
|
||||
<span
|
||||
className="cursor-pointer flex items-center [&>svg]:size-4"
|
||||
onClick={async () => {
|
||||
if (hasMessages) {
|
||||
window.electron.directoryChooser();
|
||||
} else {
|
||||
window.electron.directoryChooser(true);
|
||||
}
|
||||
}}
|
||||
>
|
||||
<Document className="mr-1" />
|
||||
<TooltipProvider>
|
||||
<Tooltip open={isTooltipOpen} onOpenChange={setIsTooltipOpen}>
|
||||
<TooltipTrigger asChild>
|
||||
<span
|
||||
ref={dirRef}
|
||||
className="max-w-[170px] md:max-w-[200px] lg:max-w-[380px] min-w-0 block overflow-hidden text-ellipsis whitespace-nowrap [direction:rtl] text-left"
|
||||
>
|
||||
{window.appConfig.get('GOOSE_WORKING_DIR') as string}
|
||||
</span>
|
||||
</TooltipTrigger>
|
||||
{isDirTruncated && (
|
||||
<TooltipContent className="max-w-96 overflow-auto scrollbar-thin" side="top">
|
||||
{window.appConfig.get('GOOSE_WORKING_DIR') as string}
|
||||
</TooltipContent>
|
||||
)}
|
||||
</Tooltip>
|
||||
</TooltipProvider>
|
||||
<ChevronUp className="ml-1" />
|
||||
</span>
|
||||
|
||||
{/* Goose Mode Selector Dropdown */}
|
||||
<BottomMenuModeSelection setView={setView} />
|
||||
|
||||
{/* Right-side section with ToolCount and Model Selector together */}
|
||||
<div className="flex items-center mr-4 space-x-1">
|
||||
<div className="flex justify-between items-center transition-colors text-textSubtle relative text-xs align-middle">
|
||||
<div className="flex items-center pl-2">
|
||||
{/* Tool and Token count */}
|
||||
{<BottomMenuAlertPopover alerts={alerts} />}
|
||||
|
||||
{/* Model Selector Dropdown */}
|
||||
{settingsV2Enabled ? (
|
||||
<ModelsBottomBar dropdownRef={dropdownRef} setView={setView} />
|
||||
@@ -262,13 +205,19 @@ export default function BottomMenu({
|
||||
}}
|
||||
>
|
||||
<span className="text-sm">Tools and Settings</span>
|
||||
<Sliders className="w-5 h-5 ml-2 rotate-90" />
|
||||
<Sliders className="w-4 h-4 ml-2 rotate-90" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Separator */}
|
||||
<div className="w-[1px] h-4 bg-borderSubtle mx-2" />
|
||||
|
||||
{/* Goose Mode Selector Dropdown */}
|
||||
<BottomMenuModeSelection setView={setView} />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import React, { useEffect, useRef, useState, useCallback } from 'react';
|
||||
import { getApiUrl, getSecretKey } from '../../config';
|
||||
import { ChevronDown, ChevronUp } from '../icons';
|
||||
import { all_goose_modes, ModeSelectionItem } from '../settings_v2/mode/ModeSelectionItem';
|
||||
import { useConfig } from '../ConfigContext';
|
||||
import { settingsV2Enabled } from '../../flags';
|
||||
import { View, ViewOptions } from '../App';
|
||||
import { View, ViewOptions } from '../../App';
|
||||
import { Orbit } from 'lucide-react';
|
||||
|
||||
interface BottomMenuModeSelectionProps {
|
||||
setView: (view: View, viewOptions?: ViewOptions) => void;
|
||||
@@ -119,23 +119,25 @@ export const BottomMenuModeSelection = ({ setView }: BottomMenuModeSelectionProp
|
||||
|
||||
return (
|
||||
<div className="relative flex items-center" ref={gooseModeDropdownRef}>
|
||||
<div
|
||||
className="flex items-center cursor-pointer"
|
||||
<button
|
||||
className="flex items-center justify-center text-textSubtle hover:text-textStandard h-6 [&_svg]:size-4"
|
||||
onClick={() => setIsGooseModeMenuOpen(!isGooseModeMenuOpen)}
|
||||
>
|
||||
<span className="truncate max-w-[170px] md:max-w-[200px] lg:max-w-[380px]">
|
||||
Goose Mode: {getValueByKey(gooseMode)}
|
||||
</span>
|
||||
{isGooseModeMenuOpen ? (
|
||||
<ChevronDown className="w-4 h-4 ml-1" />
|
||||
) : (
|
||||
<ChevronUp className="w-4 h-4 ml-1" />
|
||||
)}
|
||||
</div>
|
||||
<span className="pr-1.5">{getValueByKey(gooseMode).toLowerCase()}</span>
|
||||
<Orbit />
|
||||
{/*<span className="truncate max-w-[170px] md:max-w-[200px] lg:max-w-[380px]">*/}
|
||||
{/* Goose Mode: {getValueByKey(gooseMode)}*/}
|
||||
{/*</span>*/}
|
||||
{/*{isGooseModeMenuOpen ? (*/}
|
||||
{/* <ChevronDown className="w-4 h-4 ml-1" />*/}
|
||||
{/*) : (*/}
|
||||
{/* <ChevronUp className="w-4 h-4 ml-1" />*/}
|
||||
{/*)}*/}
|
||||
</button>
|
||||
|
||||
{/* Dropdown Menu */}
|
||||
{isGooseModeMenuOpen && (
|
||||
<div className="absolute bottom-[24px] pl-4 pt-2 right-0 w-[240px] bg-bgApp rounded-lg border border-borderSubtle">
|
||||
<div className="absolute bottom-[24px] right-0 w-[240px] py-2 bg-bgApp rounded-lg border border-borderSubtle">
|
||||
<div>
|
||||
{all_goose_modes.map((mode) => (
|
||||
<ModeSelectionItem
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { Popover, PopoverContent, PopoverPortal, PopoverTrigger } from '../ui/popover';
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import { ChatSmart, Idea, More, Refresh, Time, Send } from '../icons';
|
||||
import { ChatSmart, Idea, Refresh, Time, Send, Settings } from '../icons';
|
||||
import { FolderOpen, Moon, Sliders, Sun } from 'lucide-react';
|
||||
import { useConfig } from '../ConfigContext';
|
||||
import { settingsV2Enabled } from '../../flags';
|
||||
@@ -169,10 +169,10 @@ export default function MoreMenu({
|
||||
<PopoverTrigger asChild>
|
||||
<button
|
||||
data-testid="more-options-button"
|
||||
className={`z-[100] absolute top-2 right-4 w-[20px] h-[20px] transition-colors cursor-pointer no-drag hover:text-textProminent ${open ? 'text-textProminent' : 'text-textSubtle'}`}
|
||||
className={`z-[100] w-7 h-7 p-1 rounded-full border border-borderSubtle transition-colors cursor-pointer no-drag hover:text-textStandard hover:border-borderStandard ${open ? 'text-textStandard' : 'text-textSubtle'}`}
|
||||
role="button"
|
||||
>
|
||||
<More />
|
||||
<Settings />
|
||||
</button>
|
||||
</PopoverTrigger>
|
||||
|
||||
|
||||
@@ -1,22 +1,56 @@
|
||||
import { useState } from 'react';
|
||||
import MoreMenu from './MoreMenu';
|
||||
import React from 'react';
|
||||
import { View, ViewOptions } from '../../App';
|
||||
import type { View, ViewOptions } from '../../App';
|
||||
import { Document } from '../icons';
|
||||
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from '../ui/Tooltip';
|
||||
|
||||
export default function MoreMenuLayout({
|
||||
hasMessages,
|
||||
showMenu = true,
|
||||
setView,
|
||||
setIsGoosehintsModalOpen,
|
||||
}: {
|
||||
setView: (view: View, viewOptions?: ViewOptions) => void;
|
||||
setIsGoosehintsModalOpen: (isOpen: boolean) => void;
|
||||
hasMessages?: boolean;
|
||||
showMenu?: boolean;
|
||||
setView?: (view: View, viewOptions?: ViewOptions) => void;
|
||||
setIsGoosehintsModalOpen?: (isOpen: boolean) => void;
|
||||
}) {
|
||||
const [isTooltipOpen, setIsTooltipOpen] = useState(false);
|
||||
return (
|
||||
<div className="relative flex items-center h-[36px] w-full bg-bgSubtle border-b border-borderSubtle">
|
||||
<div className="flex-1"></div>
|
||||
<div className="flex items-center h-full">
|
||||
<div className="flex items-center justify-center h-full px-2 mr-2">
|
||||
<div
|
||||
className="relative flex items-center h-14 border-b border-borderSubtle w-full"
|
||||
style={{ WebkitAppRegion: 'drag' }}
|
||||
>
|
||||
{showMenu && (
|
||||
<div className="flex items-center justify-between w-full h-full pl-[86px] pr-4">
|
||||
<TooltipProvider>
|
||||
<Tooltip open={isTooltipOpen} onOpenChange={setIsTooltipOpen}>
|
||||
<TooltipTrigger asChild>
|
||||
<button
|
||||
className="z-[100] no-drag hover:cursor-pointer border border-subtle hover:border-borderStandard rounded-lg p-2 pr-3 text-textSubtle hover:text-textStandard text-sm flex items-center transition-colors [&>svg]:size-4 "
|
||||
onClick={async () => {
|
||||
if (hasMessages) {
|
||||
window.electron.directoryChooser();
|
||||
} else {
|
||||
window.electron.directoryChooser(true);
|
||||
}
|
||||
}}
|
||||
>
|
||||
<Document className="mr-1" />
|
||||
<div className="max-w-[200px] truncate [direction:rtl]">
|
||||
{window.appConfig.get('GOOSE_WORKING_DIR')}
|
||||
</div>
|
||||
</button>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent className="max-w-96 overflow-auto scrollbar-thin" side="top">
|
||||
{window.appConfig.get('GOOSE_WORKING_DIR') as string}
|
||||
</TooltipContent>
|
||||
</Tooltip>
|
||||
</TooltipProvider>
|
||||
|
||||
<MoreMenu setView={setView} setIsGoosehintsModalOpen={setIsGoosehintsModalOpen} />
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -17,6 +17,7 @@ import { createSharedSession } from '../../sharedSessions';
|
||||
import { Modal, ModalContent } from '../ui/modal';
|
||||
import { Button } from '../ui/button';
|
||||
import { toast } from 'react-toastify';
|
||||
import MoreMenuLayout from '../more_menu/MoreMenuLayout';
|
||||
|
||||
interface SessionHistoryViewProps {
|
||||
session: SessionDetails;
|
||||
@@ -109,7 +110,7 @@ const SessionHistoryView: React.FC<SessionHistoryViewProps> = ({
|
||||
|
||||
return (
|
||||
<div className="h-screen w-full flex flex-col">
|
||||
<div className="relative flex items-center h-[36px] w-full bg-bgSubtle"></div>
|
||||
<MoreMenuLayout showMenu={false} />
|
||||
|
||||
{/* Top Row - back, info, reopen thread (fixed) */}
|
||||
<SessionHeaderCard onBack={onBack}>
|
||||
|
||||
@@ -15,6 +15,7 @@ import BackButton from '../ui/BackButton';
|
||||
import { ScrollArea } from '../ui/scroll-area';
|
||||
import { View, ViewOptions } from '../../App';
|
||||
import { formatMessageTimestamp } from '../../utils/timeUtils';
|
||||
import MoreMenuLayout from '../more_menu/MoreMenuLayout';
|
||||
|
||||
interface SessionListViewProps {
|
||||
setView: (view: View, viewOptions?: ViewOptions) => void;
|
||||
@@ -48,7 +49,7 @@ const SessionListView: React.FC<SessionListViewProps> = ({ setView, onSelectSess
|
||||
|
||||
return (
|
||||
<div className="h-screen w-full">
|
||||
<div className="relative flex items-center h-[36px] w-full bg-bgSubtle"></div>
|
||||
<MoreMenuLayout showMenu={false} />
|
||||
|
||||
<ScrollArea className="h-full w-full">
|
||||
<div className="flex flex-col pb-24">
|
||||
|
||||
@@ -18,6 +18,7 @@ import { View, ViewOptions } from '../../App';
|
||||
import { ModeSelection } from './basic/ModeSelection';
|
||||
import SessionSharingSection from './session/SessionSharingSection';
|
||||
import { toastSuccess } from '../../toasts';
|
||||
import MoreMenuLayout from '../more_menu/MoreMenuLayout';
|
||||
|
||||
const EXTENSIONS_DESCRIPTION =
|
||||
'The Model Context Protocol (MCP) is a system that allows AI models to securely connect with local or remote resources using standard server setups. It works like a client-server setup and expands AI capabilities using three main components: Prompts, Resources, and Tools.';
|
||||
@@ -190,7 +191,7 @@ export default function SettingsView({
|
||||
|
||||
return (
|
||||
<div className="h-screen w-full">
|
||||
<div className="relative flex items-center h-[36px] w-full bg-bgSubtle"></div>
|
||||
<MoreMenuLayout showMenu={false} />
|
||||
|
||||
<ScrollArea className="h-full w-full">
|
||||
<div className="flex flex-col pb-24">
|
||||
|
||||
@@ -11,7 +11,7 @@ export interface GooseMode {
|
||||
export const all_goose_modes: GooseMode[] = [
|
||||
{
|
||||
key: 'auto',
|
||||
label: 'Completely Autonomous',
|
||||
label: 'Autonomous',
|
||||
description: 'Full file modification capabilities, edit, create, and delete files freely.',
|
||||
},
|
||||
{
|
||||
@@ -90,17 +90,14 @@ export function ModeSelectionItem({
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div
|
||||
className="flex items-center justify-between p-2 text-textStandard hover:bg-bgSubtle transition-colors"
|
||||
onClick={() => handleModeChange(mode.key)}
|
||||
>
|
||||
<div>
|
||||
<h3 className="text-sm font-light text-textStandard dark:text-gray-200">{mode.label}</h3>
|
||||
{showDescription && (
|
||||
<p className="text-xs text-textSubtle dark:text-gray-400 mt-[2px]">
|
||||
{mode.description}
|
||||
</p>
|
||||
)}
|
||||
<div className="group hover:cursor-pointer" onClick={() => handleModeChange(mode.key)}>
|
||||
<div className="flex items-center justify-between text-textStandard mb-4">
|
||||
<div className="flex">
|
||||
<h3 className="text-textStandard">{mode.label}</h3>
|
||||
{showDescription && (
|
||||
<p className="text-xs text-textSubtle mt-[2px]">{mode.description}</p>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
<div className="relative flex items-center gap-3">
|
||||
{!isApproveModeConfigure && (mode.key == 'approve' || mode.key == 'smart_approve') && (
|
||||
|
||||
@@ -6,6 +6,7 @@ import { SearchBar } from './Search';
|
||||
import { AddModelInline } from './AddModelInline';
|
||||
import { ScrollArea } from '../../ui/scroll-area';
|
||||
import type { View } from '../../../App';
|
||||
import MoreMenuLayout from '../../more_menu/MoreMenuLayout';
|
||||
|
||||
export default function MoreModelsView({
|
||||
onClose,
|
||||
@@ -16,7 +17,7 @@ export default function MoreModelsView({
|
||||
}) {
|
||||
return (
|
||||
<div className="h-screen w-full">
|
||||
<div className="relative flex items-center h-[36px] w-full bg-bgSubtle"></div>
|
||||
<MoreMenuLayout showMenu={false} />
|
||||
|
||||
<ScrollArea className="h-full w-full">
|
||||
<div className="px-8 pt-6 pb-4">
|
||||
|
||||
@@ -2,11 +2,12 @@ import React from 'react';
|
||||
import { ScrollArea } from '../../ui/scroll-area';
|
||||
import BackButton from '../../ui/BackButton';
|
||||
import { ConfigureProvidersGrid } from './ConfigureProvidersGrid';
|
||||
import MoreMenuLayout from '../../more_menu/MoreMenuLayout';
|
||||
|
||||
export default function ConfigureProvidersView({ onClose }: { onClose: () => void }) {
|
||||
return (
|
||||
<div className="h-screen w-full">
|
||||
<div className="relative flex items-center h-[36px] w-full bg-bgSubtle"></div>
|
||||
<MoreMenuLayout showMenu={false} />
|
||||
|
||||
<ScrollArea className="h-full w-full">
|
||||
<div className="px-8 pt-6 pb-4">
|
||||
|
||||
@@ -6,6 +6,7 @@ import ModelsSection from './models/ModelsSection';
|
||||
import { ModeSection } from './mode/ModeSection';
|
||||
import SessionSharingSection from './sessions/SessionSharingSection';
|
||||
import { ExtensionConfig } from '../../api';
|
||||
import MoreMenuLayout from '../more_menu/MoreMenuLayout';
|
||||
|
||||
export type SettingsViewOptions = {
|
||||
deepLinkConfig?: ExtensionConfig;
|
||||
@@ -23,7 +24,7 @@ export default function SettingsView({
|
||||
}) {
|
||||
return (
|
||||
<div className="h-screen w-full animate-[fadein_200ms_ease-in_forwards]">
|
||||
<div className="relative flex items-center h-[36px] w-full bg-bgSubtle"></div>
|
||||
<MoreMenuLayout showMenu={false} />
|
||||
|
||||
<ScrollArea className="h-full w-full">
|
||||
<div className="flex flex-col pb-24">
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import { Input } from '../../../ui/input';
|
||||
import { Select } from '../../../ui/Select';
|
||||
import React from 'react';
|
||||
|
||||
interface ExtensionInfoFieldsProps {
|
||||
name: string;
|
||||
|
||||
@@ -12,17 +12,17 @@ export interface GooseMode {
|
||||
export const all_goose_modes: GooseMode[] = [
|
||||
{
|
||||
key: 'auto',
|
||||
label: 'Completely autonomous',
|
||||
label: 'Autonomous',
|
||||
description: 'Full file modification capabilities, edit, create, and delete files freely.',
|
||||
},
|
||||
{
|
||||
key: 'approve',
|
||||
label: 'Manual approval',
|
||||
label: 'Manual',
|
||||
description: 'All tools, extensions and file modifications will require human approval',
|
||||
},
|
||||
{
|
||||
key: 'smart_approve',
|
||||
label: 'Smart approval',
|
||||
label: 'Smart',
|
||||
description: 'Intelligently determine which actions need approval based on risk level ',
|
||||
},
|
||||
{
|
||||
@@ -61,21 +61,19 @@ export function ModeSelectionItem({
|
||||
return (
|
||||
<div className="group hover:cursor-pointer">
|
||||
<div
|
||||
className="flex items-center justify-between text-textStandard mb-4"
|
||||
className="flex items-center justify-between text-textStandard py-2 px-4 hover:bg-bgSubtle"
|
||||
onClick={() => handleModeChange(mode.key)}
|
||||
>
|
||||
<div className="flex">
|
||||
<div>
|
||||
<h3 className="text-textStandard dark:text-gray-200">{mode.label}</h3>
|
||||
<h3 className="text-textStandard">{mode.label}</h3>
|
||||
{showDescription && (
|
||||
<p className="text-xs text-textSubtle dark:text-gray-400 mt-[2px]">
|
||||
{mode.description}
|
||||
</p>
|
||||
<p className="text-xs text-textSubtle mt-[2px]">{mode.description}</p>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="relative flex items-center gap-3 mr-4">
|
||||
<div className="relative flex items-center gap-2">
|
||||
{!isApproveModeConfigure && (mode.key == 'approve' || mode.key == 'smart_approve') && (
|
||||
<button
|
||||
onClick={() => {
|
||||
@@ -84,7 +82,7 @@ export function ModeSelectionItem({
|
||||
});
|
||||
}}
|
||||
>
|
||||
<Gear className="w-5 h-5 text-textSubtle hover:text-textStandard" />
|
||||
<Gear className="w-4 h-4 text-textSubtle hover:text-textStandard" />
|
||||
</button>
|
||||
)}
|
||||
<input
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import { ChevronDown, ChevronUp } from '../../../icons';
|
||||
import { Sliders } from 'lucide-react';
|
||||
import React, { useEffect, useState, useRef } from 'react';
|
||||
import { useConfig } from '../../../ConfigContext';
|
||||
@@ -69,10 +68,10 @@ export default function ModelsBottomBar({ dropdownRef, setView }: ModelsBottomBa
|
||||
}, [isModelMenuOpen]);
|
||||
|
||||
return (
|
||||
<div className="relative flex items-center ml-auto mr-4" ref={dropdownRef}>
|
||||
<div className="relative flex items-center" ref={dropdownRef}>
|
||||
<div ref={menuRef} className="relative">
|
||||
<div
|
||||
className="flex items-center cursor-pointer max-w-[180px] md:max-w-[200px] lg:max-w-[380px] min-w-0 group"
|
||||
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>
|
||||
@@ -92,23 +91,18 @@ export default function ModelsBottomBar({ dropdownRef, setView }: ModelsBottomBa
|
||||
)}
|
||||
</Tooltip>
|
||||
</TooltipProvider>
|
||||
{isModelMenuOpen ? (
|
||||
<ChevronDown className="w-4 h-4 ml-1 flex-shrink-0" />
|
||||
) : (
|
||||
<ChevronUp className="w-4 h-4 ml-1 flex-shrink-0" />
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Dropdown Menu */}
|
||||
{isModelMenuOpen && (
|
||||
<div className="absolute bottom-[24px] right-0 w-[300px] bg-bgApp rounded-lg border border-borderSubtle">
|
||||
<div className="absolute bottom-[24px] right-[-55px] w-[300px] bg-bgApp rounded-lg border border-borderSubtle">
|
||||
<div className="">
|
||||
<div className="text-sm text-textProminent mt-3 ml-2">Current:</div>
|
||||
<div className="text-sm text-textProminent mt-2 ml-2">Current:</div>
|
||||
<div className="flex items-center justify-between text-sm ml-2">
|
||||
{model} -- {provider}
|
||||
</div>
|
||||
<div
|
||||
className="flex items-center justify-between text-textStandard p-2 cursor-pointer hover:bg-bgStandard
|
||||
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);
|
||||
@@ -116,7 +110,7 @@ export default function ModelsBottomBar({ dropdownRef, setView }: ModelsBottomBa
|
||||
}}
|
||||
>
|
||||
<span className="text-sm">Change Model</span>
|
||||
<Sliders className="w-5 h-5 ml-2 rotate-90" />
|
||||
<Sliders className="w-4 h-4 ml-2 rotate-90" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import React, { useEffect, useState, useCallback } from 'react';
|
||||
import { useEffect, useState, useCallback } from 'react';
|
||||
import { ArrowLeftRight, ExternalLink } from 'lucide-react';
|
||||
|
||||
import Modal from '../../../Modal';
|
||||
|
||||
@@ -4,6 +4,7 @@ import BackButton from '../../ui/BackButton';
|
||||
import { FixedExtensionEntry, useConfig } from '../../ConfigContext';
|
||||
import { ChevronRight } from 'lucide-react';
|
||||
import PermissionModal from './PermissionModal';
|
||||
import MoreMenuLayout from '../../more_menu/MoreMenuLayout';
|
||||
|
||||
function RuleItem({ title, description }: { title: string; description: string }) {
|
||||
const [isModalOpen, setIsModalOpen] = useState(false);
|
||||
@@ -76,7 +77,7 @@ export default function PermissionSettingsView({ onClose }: { onClose: () => voi
|
||||
|
||||
return (
|
||||
<div className="h-screen w-full animate-[fadein_200ms_ease-in_forwards]">
|
||||
<div className="relative flex items-center h-[36px] w-full bg-bgSubtle"></div>
|
||||
<MoreMenuLayout showMenu={false} />
|
||||
|
||||
<ScrollArea className="h-full w-full">
|
||||
<div className="flex flex-col pb-24">
|
||||
|
||||
@@ -173,7 +173,8 @@ export const PROVIDER_REGISTRY: ProviderRegistry[] = [
|
||||
details: {
|
||||
id: 'azure_openai',
|
||||
name: 'Azure OpenAI',
|
||||
description: 'Access Azure OpenAI models using API key or Azure credentials. If no API key is provided, Azure credential chain will be used.',
|
||||
description:
|
||||
'Access Azure OpenAI models using API key or Azure credentials. If no API key is provided, Azure credential chain will be used.',
|
||||
parameters: [
|
||||
{
|
||||
name: 'AZURE_OPENAI_API_KEY',
|
||||
|
||||
@@ -7,6 +7,7 @@ import { ProviderDetails } from '../../../api/types.gen';
|
||||
import { initializeSystem } from '../../../utils/providerUtils';
|
||||
import WelcomeGooseLogo from '../../WelcomeGooseLogo';
|
||||
import { toastService } from '../../../toasts';
|
||||
import MoreMenuLayout from '../../more_menu/MoreMenuLayout';
|
||||
|
||||
interface ProviderSettingsProps {
|
||||
onClose: () => void;
|
||||
@@ -88,7 +89,8 @@ export default function ProviderSettings({ onClose, isOnboarding }: ProviderSett
|
||||
|
||||
return (
|
||||
<div className="h-screen w-full flex flex-col">
|
||||
<div className="relative flex items-center h-[36px] w-full bg-bgSubtle"></div>
|
||||
<MoreMenuLayout showMenu={false} />
|
||||
|
||||
<ScrollArea className="flex-1 w-full">
|
||||
{isOnboarding && (
|
||||
<div className="group/logo flex justify-left pl-8">
|
||||
|
||||
@@ -97,12 +97,7 @@ const ScrollArea = React.forwardRef<ScrollAreaHandle, ScrollAreaProps>(
|
||||
data-scrolled={isScrolled}
|
||||
{...props}
|
||||
>
|
||||
<div
|
||||
className={cn(
|
||||
'absolute top-0 left-0 right-0 z-10 transition-all duration-200',
|
||||
isScrolled ? 'border-t border-borderSubtle' : 'border-t border-transparent'
|
||||
)}
|
||||
/>
|
||||
<div className={cn('absolute top-0 left-0 right-0 z-10 transition-all duration-200')} />
|
||||
<ScrollAreaPrimitive.Viewport
|
||||
ref={viewportRef}
|
||||
className="h-full w-full rounded-[inherit] [&>div]:!block"
|
||||
|
||||
@@ -334,7 +334,7 @@ const createChat = async (
|
||||
|
||||
const mainWindow = new BrowserWindow({
|
||||
titleBarStyle: process.platform === 'darwin' ? 'hidden' : 'default',
|
||||
trafficLightPosition: process.platform === 'darwin' ? { x: 16, y: 10 } : undefined,
|
||||
trafficLightPosition: process.platform === 'darwin' ? { x: 16, y: 20 } : undefined,
|
||||
vibrancy: process.platform === 'darwin' ? 'window' : undefined,
|
||||
frame: process.platform === 'darwin' ? false : true,
|
||||
width: 750,
|
||||
|
||||
Reference in New Issue
Block a user