mirror of
https://github.com/aljazceru/goose.git
synced 2025-12-22 16:44:21 +01:00
feat: V1.0 (#734)
Co-authored-by: Michael Neale <michael.neale@gmail.com> Co-authored-by: Wendy Tang <wendytang@squareup.com> Co-authored-by: Jarrod Sibbison <72240382+jsibbison-square@users.noreply.github.com> Co-authored-by: Alex Hancock <alex.hancock@example.com> Co-authored-by: Alex Hancock <alexhancock@block.xyz> Co-authored-by: Lifei Zhou <lifei@squareup.com> Co-authored-by: Wes <141185334+wesrblock@users.noreply.github.com> Co-authored-by: Max Novich <maksymstepanenko1990@gmail.com> Co-authored-by: Zaki Ali <zaki@squareup.com> Co-authored-by: Salman Mohammed <smohammed@squareup.com> Co-authored-by: Kalvin C <kalvinnchau@users.noreply.github.com> Co-authored-by: Alec Thomas <alec@swapoff.org> Co-authored-by: lily-de <119957291+lily-de@users.noreply.github.com> Co-authored-by: kalvinnchau <kalvin@block.xyz> Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> Co-authored-by: Rizel Scarlett <rizel@squareup.com> Co-authored-by: bwrage <bwrage@squareup.com> Co-authored-by: Kalvin Chau <kalvin@squareup.com> Co-authored-by: Alice Hau <110418948+ahau-square@users.noreply.github.com> Co-authored-by: Alistair Gray <ajgray@stripe.com> Co-authored-by: Nahiyan Khan <nahiyan.khan@gmail.com> Co-authored-by: Alex Hancock <alexhancock@squareup.com> Co-authored-by: Nahiyan Khan <nahiyan@squareup.com> Co-authored-by: marcelle <1852848+laanak08@users.noreply.github.com> Co-authored-by: Yingjie He <yingjiehe@block.xyz> Co-authored-by: Yingjie He <yingjiehe@squareup.com> Co-authored-by: Lily Delalande <ldelalande@block.xyz> Co-authored-by: Adewale Abati <acekyd01@gmail.com> Co-authored-by: Ebony Louis <ebony774@gmail.com> Co-authored-by: Angie Jones <jones.angie@gmail.com> Co-authored-by: Ebony Louis <55366651+EbonyLouis@users.noreply.github.com>
This commit is contained in:
165
ui/desktop/src/components/settings/models/RecentModels.tsx
Normal file
165
ui/desktop/src/components/settings/models/RecentModels.tsx
Normal file
@@ -0,0 +1,165 @@
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import { Clock } from 'lucide-react';
|
||||
import { Model } from './ModelContext';
|
||||
import { useHandleModelSelection } from './utils';
|
||||
import { useModel } from './ModelContext';
|
||||
import { ModelRadioList } from './ModelRadioList';
|
||||
|
||||
const MAX_RECENT_MODELS = 3;
|
||||
|
||||
export function useRecentModels() {
|
||||
const [recentModels, setRecentModels] = useState<Model[]>([]);
|
||||
|
||||
useEffect(() => {
|
||||
const storedModels = localStorage.getItem('recentModels');
|
||||
if (storedModels) {
|
||||
setRecentModels(JSON.parse(storedModels));
|
||||
}
|
||||
}, []);
|
||||
|
||||
const addRecentModel = (model: Model) => {
|
||||
const modelWithTimestamp = { ...model, lastUsed: new Date().toISOString() }; // Add lastUsed field
|
||||
setRecentModels((prevModels) => {
|
||||
const updatedModels = [
|
||||
modelWithTimestamp,
|
||||
...prevModels.filter((m) => m.name !== model.name),
|
||||
].slice(0, MAX_RECENT_MODELS);
|
||||
|
||||
localStorage.setItem('recentModels', JSON.stringify(updatedModels));
|
||||
return updatedModels;
|
||||
});
|
||||
};
|
||||
|
||||
return { recentModels, addRecentModel };
|
||||
}
|
||||
|
||||
function getRelativeTimeString(date: string | Date): string {
|
||||
const now = new Date();
|
||||
const then = new Date(date);
|
||||
const diffInSeconds = Math.floor((now.getTime() - then.getTime()) / 1000);
|
||||
|
||||
if (diffInSeconds < 60) {
|
||||
return 'Just now';
|
||||
}
|
||||
|
||||
const diffInMinutes = Math.floor(diffInSeconds / 60);
|
||||
if (diffInMinutes < 60) {
|
||||
return `${diffInMinutes}m ago`;
|
||||
}
|
||||
|
||||
const diffInHours = Math.floor(diffInMinutes / 60);
|
||||
if (diffInHours < 24) {
|
||||
return `${diffInHours}h ago`;
|
||||
}
|
||||
|
||||
const diffInDays = Math.floor(diffInHours / 24);
|
||||
if (diffInDays < 7) {
|
||||
return `${diffInDays}d ago`;
|
||||
}
|
||||
|
||||
if (diffInDays < 30) {
|
||||
const weeks = Math.floor(diffInDays / 7);
|
||||
return `${weeks}w ago`;
|
||||
}
|
||||
|
||||
const months = Math.floor(diffInDays / 30);
|
||||
if (months < 12) {
|
||||
return `${months}mo ago`;
|
||||
}
|
||||
|
||||
const years = Math.floor(months / 12);
|
||||
return `${years}y ago`;
|
||||
}
|
||||
|
||||
export function RecentModels() {
|
||||
const { recentModels } = useRecentModels();
|
||||
const { currentModel } = useModel();
|
||||
const handleModelSelection = useHandleModelSelection();
|
||||
const [selectedModel, setSelectedModel] = useState<string | null>(null);
|
||||
|
||||
useEffect(() => {
|
||||
if (currentModel) {
|
||||
setSelectedModel(currentModel.name);
|
||||
}
|
||||
}, [currentModel]);
|
||||
|
||||
const handleRadioChange = async (model: Model) => {
|
||||
if (selectedModel === model.name) {
|
||||
console.log(`Model "${model.name}" is already active.`);
|
||||
return;
|
||||
}
|
||||
|
||||
setSelectedModel(model.name);
|
||||
await handleModelSelection(model, 'RecentModels');
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="space-y-2">
|
||||
{recentModels.map((model) => (
|
||||
<label key={model.name} className="flex justify-between items-center py-2 cursor-pointer">
|
||||
<div className="relative" onClick={() => handleRadioChange(model)}>
|
||||
<div className="flex flex-row items-center">
|
||||
<input
|
||||
type="radio"
|
||||
name="recentModels"
|
||||
value={model.name}
|
||||
checked={selectedModel === model.name}
|
||||
onChange={() => handleRadioChange(model)}
|
||||
className="peer sr-only"
|
||||
/>
|
||||
<div
|
||||
className="h-4 w-4 rounded-full border border-gray-400 dark:border-gray-500 mr-4
|
||||
peer-checked:border-[6px] peer-checked:border-black dark:peer-checked:border-white
|
||||
peer-checked:bg-white dark:peer-checked:bg-black
|
||||
transition-all duration-200 ease-in-out"
|
||||
></div>
|
||||
<div className="">
|
||||
<p className="text-sm text-textStandard">{model.name}</p>
|
||||
<p className="text-xs text-textSubtle">{model.provider}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex items-center text-sm text-textSubtle">
|
||||
<Clock className="w-4 h-4 mr-2" />
|
||||
{model.lastUsed ? getRelativeTimeString(model.lastUsed) : 'N/A'}
|
||||
</div>
|
||||
</label>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export function RecentModelsRadio() {
|
||||
return (
|
||||
<div className="space-y-2">
|
||||
<h2 className="text-md font-medium text-textStandard">Recently used</h2>
|
||||
<ModelRadioList
|
||||
renderItem={({ model, isSelected, onSelect }) => (
|
||||
<label key={model.name} className="flex items-center py-2 cursor-pointer">
|
||||
<div className="relative mr-4">
|
||||
<input
|
||||
type="radio"
|
||||
name="recentModels"
|
||||
value={model.name}
|
||||
checked={isSelected}
|
||||
onChange={onSelect}
|
||||
className="peer sr-only"
|
||||
/>
|
||||
<div
|
||||
className="h-4 w-4 rounded-full border border-gray-400 dark:border-gray-500
|
||||
peer-checked:border-[6px] peer-checked:border-black dark:peer-checked:border-white
|
||||
peer-checked:bg-white dark:peer-checked:bg-black
|
||||
transition-all duration-200 ease-in-out"
|
||||
></div>
|
||||
</div>
|
||||
|
||||
<div className="">
|
||||
<p className="text-sm text-textStandard">{model.name}</p>
|
||||
<p className="text-xs text-textSubtle">{model.provider}</p>
|
||||
</div>
|
||||
</label>
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user