feat: bottom bar text truncation and tooltips (#2324)
This commit is contained in:
@@ -1,4 +1,4 @@
|
|||||||
import React, { useState, useEffect, useRef } from 'react';
|
import { useState, useEffect, useRef } from 'react';
|
||||||
import { useModel } from '../settings/models/ModelContext';
|
import { useModel } from '../settings/models/ModelContext';
|
||||||
import { Sliders } from 'lucide-react';
|
import { Sliders } from 'lucide-react';
|
||||||
import { AlertType, useAlerts } from '../alerts';
|
import { AlertType, useAlerts } from '../alerts';
|
||||||
@@ -12,6 +12,7 @@ import { BottomMenuModeSelection } from './BottomMenuModeSelection';
|
|||||||
import ModelsBottomBar from '../settings_v2/models/bottom_bar/ModelsBottomBar';
|
import ModelsBottomBar from '../settings_v2/models/bottom_bar/ModelsBottomBar';
|
||||||
import { useConfig } from '../ConfigContext';
|
import { useConfig } from '../ConfigContext';
|
||||||
import { getCurrentModelAndProvider } from '../settings_v2/models';
|
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_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 TOKEN_WARNING_THRESHOLD = 0.8; // warning shows at 80% of the token limit
|
||||||
@@ -33,6 +34,10 @@ export default function BottomMenu({
|
|||||||
const toolCount = useToolCount();
|
const toolCount = useToolCount();
|
||||||
const { getProviders, read } = useConfig();
|
const { getProviders, read } = useConfig();
|
||||||
const [tokenLimit, setTokenLimit] = useState<number>(TOKEN_LIMIT_DEFAULT);
|
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
|
// Load providers and get current model's token limit
|
||||||
const loadProviderDetails = async () => {
|
const loadProviderDetails = async () => {
|
||||||
@@ -137,6 +142,21 @@ export default function BottomMenu({
|
|||||||
};
|
};
|
||||||
}, [isModelMenuOpen]);
|
}, [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 (
|
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">
|
<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 */}
|
{/* Directory Chooser - Always visible */}
|
||||||
@@ -151,7 +171,23 @@ export default function BottomMenu({
|
|||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Document className="mr-1" />
|
<Document className="mr-1" />
|
||||||
Working in {window.appConfig.get('GOOSE_WORKING_DIR')}
|
<TooltipProvider>
|
||||||
|
<Tooltip open={isTooltipOpen} onOpenChange={setIsTooltipOpen}>
|
||||||
|
<TooltipTrigger asChild>
|
||||||
|
<span
|
||||||
|
ref={dirRef}
|
||||||
|
className="truncate max-w-[170px] md:max-w-[200px] lg:max-w-[380px] min-w-0 block"
|
||||||
|
>
|
||||||
|
Working in {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" />
|
<ChevronUp className="ml-1" />
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
|
|||||||
@@ -118,12 +118,14 @@ export const BottomMenuModeSelection = ({ setView }: BottomMenuModeSelectionProp
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="relative flex items-center ml-6" ref={gooseModeDropdownRef}>
|
<div className="relative flex items-center" ref={gooseModeDropdownRef}>
|
||||||
<div
|
<div
|
||||||
className="flex items-center cursor-pointer"
|
className="flex items-center cursor-pointer"
|
||||||
onClick={() => setIsGooseModeMenuOpen(!isGooseModeMenuOpen)}
|
onClick={() => setIsGooseModeMenuOpen(!isGooseModeMenuOpen)}
|
||||||
>
|
>
|
||||||
<span className="truncate w-[170px]">Goose Mode: {getValueByKey(gooseMode)}</span>
|
<span className="truncate max-w-[170px] md:max-w-[200px] lg:max-w-[380px]">
|
||||||
|
Goose Mode: {getValueByKey(gooseMode)}
|
||||||
|
</span>
|
||||||
{isGooseModeMenuOpen ? (
|
{isGooseModeMenuOpen ? (
|
||||||
<ChevronDown className="w-4 h-4 ml-1" />
|
<ChevronDown className="w-4 h-4 ml-1" />
|
||||||
) : (
|
) : (
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import { useConfig } from '../../../ConfigContext';
|
|||||||
import { getCurrentModelAndProviderForDisplay } from '../index';
|
import { getCurrentModelAndProviderForDisplay } from '../index';
|
||||||
import { AddModelModal } from '../subcomponents/AddModelModal';
|
import { AddModelModal } from '../subcomponents/AddModelModal';
|
||||||
import { View } from '../../../../App';
|
import { View } from '../../../../App';
|
||||||
|
import { Tooltip, TooltipTrigger, TooltipContent, TooltipProvider } from '../../../ui/Tooltip';
|
||||||
|
|
||||||
interface ModelsBottomBarProps {
|
interface ModelsBottomBarProps {
|
||||||
dropdownRef: React.RefObject<HTMLDivElement>;
|
dropdownRef: React.RefObject<HTMLDivElement>;
|
||||||
@@ -17,6 +18,10 @@ export default function ModelsBottomBar({ dropdownRef, setView }: ModelsBottomBa
|
|||||||
const [model, setModel] = useState<string>('');
|
const [model, setModel] = useState<string>('');
|
||||||
const [isAddModelModalOpen, setIsAddModelModalOpen] = useState(false);
|
const [isAddModelModalOpen, setIsAddModelModalOpen] = useState(false);
|
||||||
const menuRef = useRef<HTMLDivElement>(null);
|
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);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
(async () => {
|
(async () => {
|
||||||
@@ -24,11 +29,26 @@ export default function ModelsBottomBar({ dropdownRef, setView }: ModelsBottomBa
|
|||||||
readFromConfig: read,
|
readFromConfig: read,
|
||||||
getProviders,
|
getProviders,
|
||||||
});
|
});
|
||||||
setProvider(modelProvider.provider);
|
setProvider(modelProvider.provider as string | null);
|
||||||
setModel(modelProvider.model);
|
setModel(modelProvider.model as string);
|
||||||
})();
|
})();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const checkTruncation = () => {
|
||||||
|
if (modelRef.current) {
|
||||||
|
setIsModelTruncated(modelRef.current.scrollWidth > modelRef.current.clientWidth);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
checkTruncation();
|
||||||
|
window.addEventListener('resize', checkTruncation);
|
||||||
|
return () => window.removeEventListener('resize', checkTruncation);
|
||||||
|
}, [model]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setIsTooltipOpen(false);
|
||||||
|
}, [isModelTruncated]);
|
||||||
|
|
||||||
// Add click outside handler
|
// Add click outside handler
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
function handleClickOutside(event: MouseEvent) {
|
function handleClickOutside(event: MouseEvent) {
|
||||||
@@ -52,14 +72,30 @@ export default function ModelsBottomBar({ dropdownRef, setView }: ModelsBottomBa
|
|||||||
<div className="relative flex items-center ml-auto mr-4" ref={dropdownRef}>
|
<div className="relative flex items-center ml-auto mr-4" ref={dropdownRef}>
|
||||||
<div ref={menuRef} className="relative">
|
<div ref={menuRef} className="relative">
|
||||||
<div
|
<div
|
||||||
className="flex items-center cursor-pointer"
|
className="flex items-center cursor-pointer max-w-[180px] md:max-w-[200px] lg:max-w-[380px] min-w-0 group"
|
||||||
onClick={() => setIsModelMenuOpen(!isModelMenuOpen)}
|
onClick={() => setIsModelMenuOpen(!isModelMenuOpen)}
|
||||||
>
|
>
|
||||||
{model}
|
<TooltipProvider>
|
||||||
|
<Tooltip open={isTooltipOpen} onOpenChange={setIsTooltipOpen}>
|
||||||
|
<TooltipTrigger asChild>
|
||||||
|
<span
|
||||||
|
ref={modelRef}
|
||||||
|
className="truncate max-w-[130px] md:max-w-[200px] lg:max-w-[360px] min-w-0 block"
|
||||||
|
>
|
||||||
|
{model || 'Select Model'}
|
||||||
|
</span>
|
||||||
|
</TooltipTrigger>
|
||||||
|
{isModelTruncated && (
|
||||||
|
<TooltipContent className="max-w-96 overflow-auto scrollbar-thin" side="top">
|
||||||
|
{model || 'Select Model'}
|
||||||
|
</TooltipContent>
|
||||||
|
)}
|
||||||
|
</Tooltip>
|
||||||
|
</TooltipProvider>
|
||||||
{isModelMenuOpen ? (
|
{isModelMenuOpen ? (
|
||||||
<ChevronDown className="w-4 h-4 ml-1" />
|
<ChevronDown className="w-4 h-4 ml-1 flex-shrink-0" />
|
||||||
) : (
|
) : (
|
||||||
<ChevronUp className="w-4 h-4 ml-1" />
|
<ChevronUp className="w-4 h-4 ml-1 flex-shrink-0" />
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user