mirror of
https://github.com/danny-avila/LibreChat.git
synced 2025-12-16 16:30:15 +01:00
⌨️ refactor: Favorite Item Selection & Keyboard Navigation/Focus Improvements (#10952)
* refactor: Reuse conversation switching logic from useSelectMention hook for Favorite Items - Added onSelectEndpoint prop to FavoriteItem for improved endpoint selection handling. - Refactored conversation initiation logic to utilize the new prop instead of direct navigation. - Updated FavoritesList to pass onSelectEndpoint to FavoriteItem, streamlining the interaction flow. - Replaced EndpointIcon with MinimalIcon for a cleaner UI representation of favorite models. * refactor: Enhance FavoriteItem and FavoritesList for improved accessibility and interaction - Added onRemoveFocus prop to FavoriteItem for better focus management after item removal. - Refactored event handling in FavoriteItem to support keyboard interactions for accessibility. - Updated FavoritesList to utilize the new onRemoveFocus prop, ensuring focus shifts appropriately after removing favorites. - Enhanced aria-labels and roles for better screen reader support and user experience. * refactor: Enhance EndpointModelItem for improved accessibility and interaction - Added useRef and useState hooks to manage active state and focus behavior. - Implemented MutationObserver to track changes in the data-active-item attribute for better accessibility. - Refactored favorite button handling to improve interaction and accessibility. - Updated button tabIndex based on active state to enhance keyboard navigation. * chore: Update Radix UI dependencies in package-lock and package.json files - Upgraded @radix-ui/react-alert-dialog and @radix-ui/react-dialog to version 1.1.15 across client and packages/client. - Updated related dependencies for improved compatibility and performance. - Removed outdated debug module references from package-lock.json. * refactor: Improve accessibility and interaction in conversation options - Added event handling to prevent unintended actions when renaming conversations. - Updated ConvoOptions to use Ariakit components for better accessibility and interaction. - Refactored button handlers for sharing and deleting conversations for clarity and consistency. - Enhanced dialog components with proper aria attributes and improved structure for better screen reader support. * refactor: Improve nested dialog accessibility for deleting shared link - Eliminated the setShareDialogOpen prop from both ShareButton and SharedLinkButton components to streamline the code. - Updated the delete mutation success handler in SharedLinkButton to improve focus management for accessibility. - Enhanced the OGDialog component in SharedLinkButton with a triggerRef for better interaction.
This commit is contained in:
parent
5b0cce2e2a
commit
4d7e6b4a58
12 changed files with 670 additions and 219 deletions
|
|
@ -39,10 +39,10 @@
|
||||||
"@marsidev/react-turnstile": "^1.1.0",
|
"@marsidev/react-turnstile": "^1.1.0",
|
||||||
"@mcp-ui/client": "^5.7.0",
|
"@mcp-ui/client": "^5.7.0",
|
||||||
"@radix-ui/react-accordion": "^1.1.2",
|
"@radix-ui/react-accordion": "^1.1.2",
|
||||||
"@radix-ui/react-alert-dialog": "^1.0.2",
|
"@radix-ui/react-alert-dialog": "^1.1.15",
|
||||||
"@radix-ui/react-checkbox": "^1.0.3",
|
"@radix-ui/react-checkbox": "^1.0.3",
|
||||||
"@radix-ui/react-collapsible": "^1.0.3",
|
"@radix-ui/react-collapsible": "^1.0.3",
|
||||||
"@radix-ui/react-dialog": "^1.0.2",
|
"@radix-ui/react-dialog": "^1.1.15",
|
||||||
"@radix-ui/react-dropdown-menu": "^2.1.1",
|
"@radix-ui/react-dropdown-menu": "^2.1.1",
|
||||||
"@radix-ui/react-hover-card": "^1.0.5",
|
"@radix-ui/react-hover-card": "^1.0.5",
|
||||||
"@radix-ui/react-icons": "^1.3.0",
|
"@radix-ui/react-icons": "^1.3.0",
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
import React from 'react';
|
import React, { useRef, useState, useEffect } from 'react';
|
||||||
import { EarthIcon, Pin, PinOff } from 'lucide-react';
|
import { EarthIcon, Pin, PinOff } from 'lucide-react';
|
||||||
import { isAgentsEndpoint, isAssistantsEndpoint } from 'librechat-data-provider';
|
import { isAgentsEndpoint, isAssistantsEndpoint } from 'librechat-data-provider';
|
||||||
import { useModelSelectorContext } from '../ModelSelectorContext';
|
import { useModelSelectorContext } from '../ModelSelectorContext';
|
||||||
|
|
@ -18,6 +18,26 @@ export function EndpointModelItem({ modelId, endpoint, isSelected }: EndpointMod
|
||||||
const { handleSelectModel } = useModelSelectorContext();
|
const { handleSelectModel } = useModelSelectorContext();
|
||||||
const { isFavoriteModel, toggleFavoriteModel, isFavoriteAgent, toggleFavoriteAgent } =
|
const { isFavoriteModel, toggleFavoriteModel, isFavoriteAgent, toggleFavoriteAgent } =
|
||||||
useFavorites();
|
useFavorites();
|
||||||
|
|
||||||
|
const itemRef = useRef<HTMLDivElement>(null);
|
||||||
|
const [isActive, setIsActive] = useState(false);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const element = itemRef.current;
|
||||||
|
if (!element) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const observer = new MutationObserver(() => {
|
||||||
|
setIsActive(element.hasAttribute('data-active-item'));
|
||||||
|
});
|
||||||
|
|
||||||
|
observer.observe(element, { attributes: true, attributeFilter: ['data-active-item'] });
|
||||||
|
setIsActive(element.hasAttribute('data-active-item'));
|
||||||
|
|
||||||
|
return () => observer.disconnect();
|
||||||
|
}, []);
|
||||||
|
|
||||||
let isGlobal = false;
|
let isGlobal = false;
|
||||||
let modelName = modelId;
|
let modelName = modelId;
|
||||||
const avatarUrl = endpoint?.modelIcons?.[modelId ?? ''] || null;
|
const avatarUrl = endpoint?.modelIcons?.[modelId ?? ''] || null;
|
||||||
|
|
@ -42,8 +62,7 @@ export function EndpointModelItem({ modelId, endpoint, isSelected }: EndpointMod
|
||||||
? isFavoriteAgent(modelId ?? '')
|
? isFavoriteAgent(modelId ?? '')
|
||||||
: isFavoriteModel(modelId ?? '', endpoint.value);
|
: isFavoriteModel(modelId ?? '', endpoint.value);
|
||||||
|
|
||||||
const handleFavoriteClick = (e: React.MouseEvent) => {
|
const handleFavoriteToggle = () => {
|
||||||
e.stopPropagation();
|
|
||||||
if (!modelId) {
|
if (!modelId) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
@ -55,6 +74,11 @@ export function EndpointModelItem({ modelId, endpoint, isSelected }: EndpointMod
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const handleFavoriteClick = (e: React.MouseEvent) => {
|
||||||
|
e.stopPropagation();
|
||||||
|
handleFavoriteToggle();
|
||||||
|
};
|
||||||
|
|
||||||
const renderAvatar = () => {
|
const renderAvatar = () => {
|
||||||
const isAgentOrAssistant =
|
const isAgentOrAssistant =
|
||||||
isAgentsEndpoint(endpoint.value) || isAssistantsEndpoint(endpoint.value);
|
isAgentsEndpoint(endpoint.value) || isAssistantsEndpoint(endpoint.value);
|
||||||
|
|
@ -84,6 +108,7 @@ export function EndpointModelItem({ modelId, endpoint, isSelected }: EndpointMod
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<MenuItem
|
<MenuItem
|
||||||
|
ref={itemRef}
|
||||||
key={modelId}
|
key={modelId}
|
||||||
onClick={() => handleSelectModel(endpoint, modelId ?? '')}
|
onClick={() => handleSelectModel(endpoint, modelId ?? '')}
|
||||||
className="group flex w-full cursor-pointer items-center justify-between rounded-lg px-2 text-sm"
|
className="group flex w-full cursor-pointer items-center justify-between rounded-lg px-2 text-sm"
|
||||||
|
|
@ -94,20 +119,18 @@ export function EndpointModelItem({ modelId, endpoint, isSelected }: EndpointMod
|
||||||
{isGlobal && <EarthIcon className="ml-1 size-4 text-surface-submit" />}
|
{isGlobal && <EarthIcon className="ml-1 size-4 text-surface-submit" />}
|
||||||
</div>
|
</div>
|
||||||
<button
|
<button
|
||||||
|
tabIndex={isActive ? 0 : -1}
|
||||||
onClick={handleFavoriteClick}
|
onClick={handleFavoriteClick}
|
||||||
aria-label={isFavorite ? localize('com_ui_unpin') : localize('com_ui_pin')}
|
aria-label={isFavorite ? localize('com_ui_unpin') : localize('com_ui_pin')}
|
||||||
className={cn(
|
className={cn(
|
||||||
'rounded-md p-1 hover:bg-surface-hover',
|
'rounded-md p-1 hover:bg-surface-hover',
|
||||||
isFavorite ? 'visible' : 'invisible group-hover:visible',
|
isFavorite ? 'visible' : 'invisible group-hover:visible group-data-[active-item]:visible',
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
{isFavorite ? (
|
{isFavorite ? (
|
||||||
<PinOff className="h-4 w-4 text-text-secondary" />
|
<PinOff className="h-4 w-4 text-text-secondary" />
|
||||||
) : (
|
) : (
|
||||||
<Pin
|
<Pin className="h-4 w-4 text-text-secondary" aria-hidden="true" />
|
||||||
className="h-4 w-4 text-text-secondary"
|
|
||||||
aria-hidden="true"
|
|
||||||
/>
|
|
||||||
)}
|
)}
|
||||||
</button>
|
</button>
|
||||||
{isSelected && (
|
{isSelected && (
|
||||||
|
|
|
||||||
|
|
@ -155,6 +155,9 @@ export default function Conversation({ conversation, retainView, toggleNav }: Co
|
||||||
if (renaming) {
|
if (renaming) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
if (e.target !== e.currentTarget) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (e.key === 'Enter' || e.key === ' ') {
|
if (e.key === 'Enter' || e.key === ' ') {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
handleNavigation(false);
|
handleNavigation(false);
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
import { useState, useId, useRef, memo, useCallback, useMemo } from 'react';
|
import { useState, useId, useRef, memo, useCallback, useMemo } from 'react';
|
||||||
import * as Menu from '@ariakit/react/menu';
|
import * as Ariakit from '@ariakit/react';
|
||||||
import { useParams, useNavigate } from 'react-router-dom';
|
import { useParams, useNavigate } from 'react-router-dom';
|
||||||
import { QueryKeys } from 'librechat-data-provider';
|
import { QueryKeys } from 'librechat-data-provider';
|
||||||
import { useQueryClient } from '@tanstack/react-query';
|
import { useQueryClient } from '@tanstack/react-query';
|
||||||
|
|
@ -49,6 +49,7 @@ function ConvoOptions({
|
||||||
const { conversationId: currentConvoId } = useParams();
|
const { conversationId: currentConvoId } = useParams();
|
||||||
const { newConversation } = useNewConvo();
|
const { newConversation } = useNewConvo();
|
||||||
|
|
||||||
|
const menuId = useId();
|
||||||
const shareButtonRef = useRef<HTMLButtonElement>(null);
|
const shareButtonRef = useRef<HTMLButtonElement>(null);
|
||||||
const deleteButtonRef = useRef<HTMLButtonElement>(null);
|
const deleteButtonRef = useRef<HTMLButtonElement>(null);
|
||||||
const [showShareDialog, setShowShareDialog] = useState(false);
|
const [showShareDialog, setShowShareDialog] = useState(false);
|
||||||
|
|
@ -106,11 +107,11 @@ function ConvoOptions({
|
||||||
const isArchiveLoading = archiveConvoMutation.isLoading;
|
const isArchiveLoading = archiveConvoMutation.isLoading;
|
||||||
const isDeleteLoading = deleteMutation.isLoading;
|
const isDeleteLoading = deleteMutation.isLoading;
|
||||||
|
|
||||||
const handleShareClick = useCallback(() => {
|
const shareHandler = useCallback(() => {
|
||||||
setShowShareDialog(true);
|
setShowShareDialog(true);
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const handleDeleteClick = useCallback(() => {
|
const deleteHandler = useCallback(() => {
|
||||||
setShowDeleteDialog(true);
|
setShowDeleteDialog(true);
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
|
|
@ -189,13 +190,15 @@ function ConvoOptions({
|
||||||
() => [
|
() => [
|
||||||
{
|
{
|
||||||
label: localize('com_ui_share'),
|
label: localize('com_ui_share'),
|
||||||
onClick: handleShareClick,
|
onClick: shareHandler,
|
||||||
icon: <Share2 className="icon-sm mr-2 text-text-primary" aria-hidden="true" />,
|
icon: <Share2 className="icon-sm mr-2 text-text-primary" aria-hidden="true" />,
|
||||||
show: startupConfig && startupConfig.sharedLinksEnabled,
|
show: startupConfig && startupConfig.sharedLinksEnabled,
|
||||||
hideOnClick: false,
|
|
||||||
ref: shareButtonRef,
|
|
||||||
ariaHasPopup: 'dialog' as const,
|
ariaHasPopup: 'dialog' as const,
|
||||||
ariaControls: 'share-conversation-dialog',
|
ariaControls: 'share-conversation-dialog',
|
||||||
|
/** NOTE: THE FOLLOWING PROPS ARE REQUIRED FOR MENU ITEMS THAT OPEN DIALOGS */
|
||||||
|
hideOnClick: false,
|
||||||
|
ref: shareButtonRef,
|
||||||
|
render: (props) => <button {...props} />,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: localize('com_ui_rename'),
|
label: localize('com_ui_rename'),
|
||||||
|
|
@ -224,29 +227,29 @@ function ConvoOptions({
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: localize('com_ui_delete'),
|
label: localize('com_ui_delete'),
|
||||||
onClick: handleDeleteClick,
|
onClick: deleteHandler,
|
||||||
icon: <Trash className="icon-sm mr-2 text-text-primary" aria-hidden="true" />,
|
icon: <Trash className="icon-sm mr-2 text-text-primary" aria-hidden="true" />,
|
||||||
hideOnClick: false,
|
|
||||||
ref: deleteButtonRef,
|
|
||||||
ariaHasPopup: 'dialog' as const,
|
ariaHasPopup: 'dialog' as const,
|
||||||
ariaControls: 'delete-conversation-dialog',
|
ariaControls: 'delete-conversation-dialog',
|
||||||
|
/** NOTE: THE FOLLOWING PROPS ARE REQUIRED FOR MENU ITEMS THAT OPEN DIALOGS */
|
||||||
|
hideOnClick: false,
|
||||||
|
ref: deleteButtonRef,
|
||||||
|
render: (props) => <button {...props} />,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
localize,
|
localize,
|
||||||
handleShareClick,
|
shareHandler,
|
||||||
startupConfig,
|
startupConfig,
|
||||||
renameHandler,
|
renameHandler,
|
||||||
handleDuplicateClick,
|
deleteHandler,
|
||||||
|
isArchiveLoading,
|
||||||
isDuplicateLoading,
|
isDuplicateLoading,
|
||||||
handleArchiveClick,
|
handleArchiveClick,
|
||||||
isArchiveLoading,
|
handleDuplicateClick,
|
||||||
handleDeleteClick,
|
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
|
|
||||||
const menuId = useId();
|
|
||||||
|
|
||||||
const buttonClassName = cn(
|
const buttonClassName = cn(
|
||||||
'inline-flex h-7 w-7 items-center justify-center rounded-md border-none p-0 text-sm font-medium ring-ring-primary transition-all duration-200 ease-in-out focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-offset-2 disabled:opacity-50',
|
'inline-flex h-7 w-7 items-center justify-center rounded-md border-none p-0 text-sm font-medium ring-ring-primary transition-all duration-200 ease-in-out focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-offset-2 disabled:opacity-50',
|
||||||
isActiveConvo === true || isPopoverActive
|
isActiveConvo === true || isPopoverActive
|
||||||
|
|
@ -292,13 +295,13 @@ function ConvoOptions({
|
||||||
</span>
|
</span>
|
||||||
<DropdownPopup
|
<DropdownPopup
|
||||||
portal={true}
|
portal={true}
|
||||||
mountByState={true}
|
menuId={menuId}
|
||||||
|
focusLoop={true}
|
||||||
unmountOnHide={true}
|
unmountOnHide={true}
|
||||||
preserveTabOrder={true}
|
|
||||||
isOpen={isPopoverActive}
|
isOpen={isPopoverActive}
|
||||||
setIsOpen={setIsPopoverActive}
|
setIsOpen={setIsPopoverActive}
|
||||||
trigger={
|
trigger={
|
||||||
<Menu.MenuButton
|
<Ariakit.MenuButton
|
||||||
id={`conversation-menu-${conversationId}`}
|
id={`conversation-menu-${conversationId}`}
|
||||||
aria-label={localize('com_nav_convo_menu_options')}
|
aria-label={localize('com_nav_convo_menu_options')}
|
||||||
aria-readonly={undefined}
|
aria-readonly={undefined}
|
||||||
|
|
@ -318,10 +321,9 @@ function ConvoOptions({
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Ellipsis className="icon-md text-text-secondary" aria-hidden={true} />
|
<Ellipsis className="icon-md text-text-secondary" aria-hidden={true} />
|
||||||
</Menu.MenuButton>
|
</Ariakit.MenuButton>
|
||||||
}
|
}
|
||||||
items={dropdownItems}
|
items={dropdownItems}
|
||||||
menuId={menuId}
|
|
||||||
className="z-30"
|
className="z-30"
|
||||||
/>
|
/>
|
||||||
{showShareDialog && (
|
{showShareDialog && (
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,7 @@ import {
|
||||||
Button,
|
Button,
|
||||||
Spinner,
|
Spinner,
|
||||||
OGDialog,
|
OGDialog,
|
||||||
|
OGDialogClose,
|
||||||
OGDialogTitle,
|
OGDialogTitle,
|
||||||
OGDialogHeader,
|
OGDialogHeader,
|
||||||
OGDialogContent,
|
OGDialogContent,
|
||||||
|
|
@ -81,14 +82,14 @@ export function DeleteConversationDialog({
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<OGDialogContent
|
<OGDialogContent
|
||||||
title={localize('com_ui_delete_confirm', { title })}
|
|
||||||
className="w-11/12 max-w-md"
|
className="w-11/12 max-w-md"
|
||||||
showCloseButton={false}
|
showCloseButton={false}
|
||||||
|
aria-describedby="delete-conversation-description"
|
||||||
>
|
>
|
||||||
<OGDialogHeader>
|
<OGDialogHeader>
|
||||||
<OGDialogTitle>{localize('com_ui_delete_conversation')}</OGDialogTitle>
|
<OGDialogTitle>{localize('com_ui_delete_conversation')}</OGDialogTitle>
|
||||||
</OGDialogHeader>
|
</OGDialogHeader>
|
||||||
<div id="delete-conversation-dialog" className="w-full truncate">
|
<div id="delete-conversation-description" className="w-full truncate">
|
||||||
<Trans
|
<Trans
|
||||||
i18nKey="com_ui_delete_confirm_strong"
|
i18nKey="com_ui_delete_confirm_strong"
|
||||||
values={{ title }}
|
values={{ title }}
|
||||||
|
|
@ -96,9 +97,11 @@ export function DeleteConversationDialog({
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex justify-end gap-4 pt-4">
|
<div className="flex justify-end gap-4 pt-4">
|
||||||
<Button aria-label="cancel" variant="outline" onClick={() => setShowDeleteDialog(false)}>
|
<OGDialogClose asChild>
|
||||||
{localize('com_ui_cancel')}
|
<Button aria-label="cancel" variant="outline">
|
||||||
</Button>
|
{localize('com_ui_cancel')}
|
||||||
|
</Button>
|
||||||
|
</OGDialogClose>
|
||||||
<Button variant="destructive" onClick={confirmDelete} disabled={deleteMutation.isLoading}>
|
<Button variant="destructive" onClick={confirmDelete} disabled={deleteMutation.isLoading}>
|
||||||
{deleteMutation.isLoading ? <Spinner /> : localize('com_ui_delete')}
|
{deleteMutation.isLoading ? <Spinner /> : localize('com_ui_delete')}
|
||||||
</Button>
|
</Button>
|
||||||
|
|
|
||||||
|
|
@ -51,7 +51,6 @@ export default function ShareButton({
|
||||||
share={share}
|
share={share}
|
||||||
conversationId={conversationId}
|
conversationId={conversationId}
|
||||||
targetMessageId={latestMessage?.messageId}
|
targetMessageId={latestMessage?.messageId}
|
||||||
setShareDialogOpen={onOpenChange}
|
|
||||||
showQR={showQR}
|
showQR={showQR}
|
||||||
setShowQR={setShowQR}
|
setShowQR={setShowQR}
|
||||||
setSharedLink={setSharedLink}
|
setSharedLink={setSharedLink}
|
||||||
|
|
|
||||||
|
|
@ -1,14 +1,17 @@
|
||||||
import { useState, useCallback } from 'react';
|
import { useState, useCallback, useRef } from 'react';
|
||||||
import { Trans } from 'react-i18next';
|
import { Trans } from 'react-i18next';
|
||||||
import { QrCode, RotateCw, Trash2 } from 'lucide-react';
|
import { QrCode, RotateCw, Trash2 } from 'lucide-react';
|
||||||
import {
|
import {
|
||||||
Button,
|
|
||||||
OGDialog,
|
|
||||||
Spinner,
|
|
||||||
TooltipAnchor,
|
|
||||||
Label,
|
Label,
|
||||||
OGDialogTemplate,
|
Button,
|
||||||
|
Spinner,
|
||||||
|
OGDialog,
|
||||||
|
OGDialogClose,
|
||||||
|
TooltipAnchor,
|
||||||
|
OGDialogTitle,
|
||||||
|
OGDialogHeader,
|
||||||
useToastContext,
|
useToastContext,
|
||||||
|
OGDialogContent,
|
||||||
} from '@librechat/client';
|
} from '@librechat/client';
|
||||||
import type { TSharedLinkGetResponse } from 'librechat-data-provider';
|
import type { TSharedLinkGetResponse } from 'librechat-data-provider';
|
||||||
import {
|
import {
|
||||||
|
|
@ -23,7 +26,6 @@ export default function SharedLinkButton({
|
||||||
share,
|
share,
|
||||||
conversationId,
|
conversationId,
|
||||||
targetMessageId,
|
targetMessageId,
|
||||||
setShareDialogOpen,
|
|
||||||
showQR,
|
showQR,
|
||||||
setShowQR,
|
setShowQR,
|
||||||
setSharedLink,
|
setSharedLink,
|
||||||
|
|
@ -31,13 +33,13 @@ export default function SharedLinkButton({
|
||||||
share: TSharedLinkGetResponse | undefined;
|
share: TSharedLinkGetResponse | undefined;
|
||||||
conversationId: string;
|
conversationId: string;
|
||||||
targetMessageId?: string;
|
targetMessageId?: string;
|
||||||
setShareDialogOpen: React.Dispatch<React.SetStateAction<boolean>>;
|
|
||||||
showQR: boolean;
|
showQR: boolean;
|
||||||
setShowQR: (showQR: boolean) => void;
|
setShowQR: (showQR: boolean) => void;
|
||||||
setSharedLink: (sharedLink: string) => void;
|
setSharedLink: (sharedLink: string) => void;
|
||||||
}) {
|
}) {
|
||||||
const localize = useLocalize();
|
const localize = useLocalize();
|
||||||
const { showToast } = useToastContext();
|
const { showToast } = useToastContext();
|
||||||
|
const deleteButtonRef = useRef<HTMLButtonElement>(null);
|
||||||
const [showDeleteDialog, setShowDeleteDialog] = useState(false);
|
const [showDeleteDialog, setShowDeleteDialog] = useState(false);
|
||||||
const [announcement, setAnnouncement] = useState('');
|
const [announcement, setAnnouncement] = useState('');
|
||||||
const shareId = share?.shareId ?? '';
|
const shareId = share?.shareId ?? '';
|
||||||
|
|
@ -63,9 +65,16 @@ export default function SharedLinkButton({
|
||||||
});
|
});
|
||||||
|
|
||||||
const deleteMutation = useDeleteSharedLinkMutation({
|
const deleteMutation = useDeleteSharedLinkMutation({
|
||||||
onSuccess: async () => {
|
onSuccess: () => {
|
||||||
setShowDeleteDialog(false);
|
setShowDeleteDialog(false);
|
||||||
setShareDialogOpen(false);
|
setTimeout(() => {
|
||||||
|
const dialog = document
|
||||||
|
.getElementById('share-conversation-dialog')
|
||||||
|
?.closest('[role="dialog"]');
|
||||||
|
if (dialog instanceof HTMLElement) {
|
||||||
|
dialog.focus();
|
||||||
|
}
|
||||||
|
}, 0);
|
||||||
},
|
},
|
||||||
onError: (error) => {
|
onError: (error) => {
|
||||||
console.error('Delete error:', error);
|
console.error('Delete error:', error);
|
||||||
|
|
@ -175,6 +184,7 @@ export default function SharedLinkButton({
|
||||||
render={(props) => (
|
render={(props) => (
|
||||||
<Button
|
<Button
|
||||||
{...props}
|
{...props}
|
||||||
|
ref={deleteButtonRef}
|
||||||
onClick={() => setShowDeleteDialog(true)}
|
onClick={() => setShowDeleteDialog(true)}
|
||||||
variant="destructive"
|
variant="destructive"
|
||||||
aria-label={localize('com_ui_delete')}
|
aria-label={localize('com_ui_delete')}
|
||||||
|
|
@ -185,36 +195,43 @@ export default function SharedLinkButton({
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
<OGDialog open={showDeleteDialog} onOpenChange={setShowDeleteDialog}>
|
<OGDialog
|
||||||
<OGDialogTemplate
|
open={showDeleteDialog}
|
||||||
showCloseButton={false}
|
triggerRef={deleteButtonRef}
|
||||||
title={localize('com_ui_delete_shared_link_heading')}
|
onOpenChange={setShowDeleteDialog}
|
||||||
className="max-w-[450px]"
|
>
|
||||||
main={
|
<OGDialogContent className="max-w-[450px]" showCloseButton={false}>
|
||||||
<>
|
<OGDialogHeader>
|
||||||
<div className="flex w-full flex-col items-center gap-2">
|
<OGDialogTitle>{localize('com_ui_delete_shared_link_heading')}</OGDialogTitle>
|
||||||
<div className="grid w-full items-center gap-2">
|
</OGDialogHeader>
|
||||||
<Label
|
<div className="flex w-full flex-col items-center gap-2">
|
||||||
htmlFor="dialog-confirm-delete"
|
<div className="grid w-full items-center gap-2">
|
||||||
className="text-left text-sm font-medium"
|
<Label htmlFor="dialog-confirm-delete" className="text-left text-sm font-medium">
|
||||||
>
|
<Trans
|
||||||
<Trans
|
i18nKey="com_ui_delete_confirm_strong"
|
||||||
i18nKey="com_ui_delete_confirm_strong"
|
values={{ title: shareId }}
|
||||||
values={{ title: shareId }}
|
components={{ strong: <strong /> }}
|
||||||
components={{ strong: <strong /> }}
|
/>
|
||||||
/>
|
</Label>
|
||||||
</Label>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
<div className="flex justify-end gap-4 pt-4">
|
||||||
</>
|
<OGDialogClose asChild>
|
||||||
}
|
<Button variant="outline">{localize('com_ui_cancel')}</Button>
|
||||||
selection={{
|
</OGDialogClose>
|
||||||
selectHandler: handleDelete,
|
<Button
|
||||||
selectClasses:
|
variant="destructive"
|
||||||
'bg-red-700 dark:bg-red-600 hover:bg-red-800 dark:hover:bg-red-800 text-white',
|
onClick={handleDelete}
|
||||||
selectText: localize('com_ui_delete'),
|
disabled={deleteMutation.isLoading}
|
||||||
}}
|
>
|
||||||
/>
|
{deleteMutation.isLoading ? (
|
||||||
|
<Spinner className="size-4" />
|
||||||
|
) : (
|
||||||
|
localize('com_ui_delete')
|
||||||
|
)}
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</OGDialogContent>
|
||||||
</OGDialog>
|
</OGDialog>
|
||||||
</div>
|
</div>
|
||||||
</>
|
</>
|
||||||
|
|
|
||||||
|
|
@ -1,60 +1,59 @@
|
||||||
import React, { useState } from 'react';
|
import React, { useState } from 'react';
|
||||||
import * as Menu from '@ariakit/react/menu';
|
import * as Menu from '@ariakit/react/menu';
|
||||||
import { useNavigate } from 'react-router-dom';
|
|
||||||
import { Ellipsis, PinOff } from 'lucide-react';
|
import { Ellipsis, PinOff } from 'lucide-react';
|
||||||
import { DropdownPopup } from '@librechat/client';
|
import { DropdownPopup } from '@librechat/client';
|
||||||
import { EModelEndpoint } from 'librechat-data-provider';
|
import { EModelEndpoint } from 'librechat-data-provider';
|
||||||
import type { FavoriteModel } from '~/store/favorites';
|
import type { FavoriteModel } from '~/store/favorites';
|
||||||
import type t from 'librechat-data-provider';
|
import type t from 'librechat-data-provider';
|
||||||
import EndpointIcon from '~/components/Endpoints/EndpointIcon';
|
import MinimalIcon from '~/components/Endpoints/MinimalIcon';
|
||||||
import { useNewConvo, useFavorites, useLocalize } from '~/hooks';
|
import { useFavorites, useLocalize } from '~/hooks';
|
||||||
import { renderAgentAvatar, cn } from '~/utils';
|
import { renderAgentAvatar, cn } from '~/utils';
|
||||||
|
|
||||||
|
type Kwargs = {
|
||||||
|
model?: string;
|
||||||
|
agent_id?: string;
|
||||||
|
assistant_id?: string;
|
||||||
|
spec?: string | null;
|
||||||
|
};
|
||||||
|
|
||||||
type FavoriteItemProps = {
|
type FavoriteItemProps = {
|
||||||
item: t.Agent | FavoriteModel;
|
item: t.Agent | FavoriteModel;
|
||||||
type: 'agent' | 'model';
|
type: 'agent' | 'model';
|
||||||
|
onSelectEndpoint?: (endpoint?: EModelEndpoint | string | null, kwargs?: Kwargs) => void;
|
||||||
|
onRemoveFocus?: () => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
export default function FavoriteItem({ item, type }: FavoriteItemProps) {
|
export default function FavoriteItem({
|
||||||
const navigate = useNavigate();
|
item,
|
||||||
|
type,
|
||||||
|
onSelectEndpoint,
|
||||||
|
onRemoveFocus,
|
||||||
|
}: FavoriteItemProps) {
|
||||||
const localize = useLocalize();
|
const localize = useLocalize();
|
||||||
const { newConversation } = useNewConvo();
|
|
||||||
const { removeFavoriteAgent, removeFavoriteModel } = useFavorites();
|
const { removeFavoriteAgent, removeFavoriteModel } = useFavorites();
|
||||||
const [isPopoverActive, setIsPopoverActive] = useState(false);
|
const [isPopoverActive, setIsPopoverActive] = useState(false);
|
||||||
|
|
||||||
|
const handleSelect = () => {
|
||||||
|
if (type === 'agent') {
|
||||||
|
const agent = item as t.Agent;
|
||||||
|
onSelectEndpoint?.(EModelEndpoint.agents, { agent_id: agent.id });
|
||||||
|
} else {
|
||||||
|
const model = item as FavoriteModel;
|
||||||
|
onSelectEndpoint?.(model.endpoint, { model: model.model });
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const handleClick = (e: React.MouseEvent) => {
|
const handleClick = (e: React.MouseEvent) => {
|
||||||
if ((e.target as HTMLElement).closest('[data-testid="favorite-options-button"]')) {
|
if ((e.target as HTMLElement).closest('[data-testid="favorite-options-button"]')) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
handleSelect();
|
||||||
|
};
|
||||||
|
|
||||||
if (type === 'agent') {
|
const handleKeyDown = (e: React.KeyboardEvent) => {
|
||||||
const agent = item as t.Agent;
|
if (e.key === 'Enter' || e.key === ' ') {
|
||||||
newConversation({
|
e.preventDefault();
|
||||||
template: {
|
handleSelect();
|
||||||
...agent,
|
|
||||||
endpoint: EModelEndpoint.agents,
|
|
||||||
agent_id: agent.id,
|
|
||||||
},
|
|
||||||
preset: {
|
|
||||||
...agent,
|
|
||||||
endpoint: EModelEndpoint.agents,
|
|
||||||
agent_id: agent.id,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
navigate(`/c/new`);
|
|
||||||
} else {
|
|
||||||
const model = item as FavoriteModel;
|
|
||||||
newConversation({
|
|
||||||
template: {
|
|
||||||
endpoint: model.endpoint,
|
|
||||||
model: model.model,
|
|
||||||
},
|
|
||||||
preset: {
|
|
||||||
endpoint: model.endpoint,
|
|
||||||
model: model.model,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
navigate(`/c/new`);
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -67,6 +66,9 @@ export default function FavoriteItem({ item, type }: FavoriteItemProps) {
|
||||||
removeFavoriteModel(model.model, model.endpoint);
|
removeFavoriteModel(model.model, model.endpoint);
|
||||||
}
|
}
|
||||||
setIsPopoverActive(false);
|
setIsPopoverActive(false);
|
||||||
|
requestAnimationFrame(() => {
|
||||||
|
onRemoveFocus?.();
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const renderIcon = () => {
|
const renderIcon = () => {
|
||||||
|
|
@ -76,23 +78,22 @@ export default function FavoriteItem({ item, type }: FavoriteItemProps) {
|
||||||
const model = item as FavoriteModel;
|
const model = item as FavoriteModel;
|
||||||
return (
|
return (
|
||||||
<div className="mr-2 h-5 w-5">
|
<div className="mr-2 h-5 w-5">
|
||||||
<EndpointIcon
|
<MinimalIcon endpoint={model.endpoint} size={20} isCreatedByUser={false} />
|
||||||
conversation={{ endpoint: model.endpoint, model: model.model } as t.TConversation}
|
|
||||||
endpoint={model.endpoint}
|
|
||||||
model={model.model}
|
|
||||||
size={20}
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const getName = () => {
|
const getName = (): string => {
|
||||||
if (type === 'agent') {
|
if (type === 'agent') {
|
||||||
return (item as t.Agent).name;
|
return (item as t.Agent).name ?? '';
|
||||||
}
|
}
|
||||||
return (item as FavoriteModel).model;
|
return (item as FavoriteModel).model;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const name = getName();
|
||||||
|
const typeLabel = type === 'agent' ? localize('com_ui_agent') : localize('com_ui_model');
|
||||||
|
const ariaLabel = `${name} (${typeLabel})`;
|
||||||
|
|
||||||
const menuId = React.useId();
|
const menuId = React.useId();
|
||||||
|
|
||||||
const dropdownItems = [
|
const dropdownItems = [
|
||||||
|
|
@ -105,22 +106,28 @@ export default function FavoriteItem({ item, type }: FavoriteItemProps) {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
|
role="button"
|
||||||
|
tabIndex={0}
|
||||||
|
aria-label={ariaLabel}
|
||||||
className={cn(
|
className={cn(
|
||||||
'group relative flex w-full cursor-pointer items-center justify-between rounded-lg px-3 py-2 text-sm text-text-primary hover:bg-surface-active-alt',
|
'group relative flex w-full cursor-pointer items-center justify-between rounded-lg px-3 py-2 text-sm text-text-primary hover:bg-surface-active-alt',
|
||||||
isPopoverActive ? 'bg-surface-active-alt' : '',
|
isPopoverActive ? 'bg-surface-active-alt' : '',
|
||||||
)}
|
)}
|
||||||
onClick={handleClick}
|
onClick={handleClick}
|
||||||
|
onKeyDown={handleKeyDown}
|
||||||
data-testid="favorite-item"
|
data-testid="favorite-item"
|
||||||
>
|
>
|
||||||
<div className="flex flex-1 items-center truncate pr-6">
|
<div className="flex flex-1 items-center truncate pr-6">
|
||||||
{renderIcon()}
|
{renderIcon()}
|
||||||
<span className="truncate">{getName()}</span>
|
<span className="truncate">{name}</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div
|
<div
|
||||||
className={cn(
|
className={cn(
|
||||||
'absolute right-2 flex items-center',
|
'absolute right-2 flex items-center',
|
||||||
isPopoverActive ? 'opacity-100' : 'opacity-0 group-hover:opacity-100',
|
isPopoverActive
|
||||||
|
? 'opacity-100'
|
||||||
|
: 'opacity-0 group-focus-within:opacity-100 group-hover:opacity-100',
|
||||||
)}
|
)}
|
||||||
onClick={(e) => e.stopPropagation()}
|
onClick={(e) => e.stopPropagation()}
|
||||||
>
|
>
|
||||||
|
|
@ -132,13 +139,23 @@ export default function FavoriteItem({ item, type }: FavoriteItemProps) {
|
||||||
trigger={
|
trigger={
|
||||||
<Menu.MenuButton
|
<Menu.MenuButton
|
||||||
className={cn(
|
className={cn(
|
||||||
'flex h-7 w-7 items-center justify-center rounded-md',
|
'inline-flex h-7 w-7 items-center justify-center rounded-md border-none p-0 text-sm font-medium ring-ring-primary transition-all duration-200 ease-in-out focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-offset-2 disabled:opacity-50',
|
||||||
isPopoverActive ? 'bg-surface-active-alt' : '',
|
isPopoverActive
|
||||||
|
? 'opacity-100'
|
||||||
|
: 'opacity-0 focus:opacity-100 group-focus-within:opacity-100 group-hover:opacity-100 data-[open]:opacity-100',
|
||||||
)}
|
)}
|
||||||
aria-label={localize('com_ui_options')}
|
aria-label={localize('com_nav_convo_menu_options')}
|
||||||
data-testid="favorite-options-button"
|
data-testid="favorite-options-button"
|
||||||
|
onClick={(e: React.MouseEvent<HTMLButtonElement>) => {
|
||||||
|
e.stopPropagation();
|
||||||
|
}}
|
||||||
|
onKeyDown={(e: React.KeyboardEvent<HTMLButtonElement>) => {
|
||||||
|
if (e.key === 'Enter' || e.key === ' ') {
|
||||||
|
e.stopPropagation();
|
||||||
|
}
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
<Ellipsis className="h-4 w-4 text-text-secondary" />
|
<Ellipsis className="icon-md text-text-secondary" aria-hidden={true} />
|
||||||
</Menu.MenuButton>
|
</Menu.MenuButton>
|
||||||
}
|
}
|
||||||
items={dropdownItems}
|
items={dropdownItems}
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,10 @@ import { QueryKeys, dataService } from 'librechat-data-provider';
|
||||||
import { useQueries, useQueryClient } from '@tanstack/react-query';
|
import { useQueries, useQueryClient } from '@tanstack/react-query';
|
||||||
import type { InfiniteData } from '@tanstack/react-query';
|
import type { InfiniteData } from '@tanstack/react-query';
|
||||||
import type t from 'librechat-data-provider';
|
import type t from 'librechat-data-provider';
|
||||||
import { useFavorites, useLocalize, useShowMarketplace } from '~/hooks';
|
import { useFavorites, useLocalize, useShowMarketplace, useNewConvo } from '~/hooks';
|
||||||
|
import useSelectMention from '~/hooks/Input/useSelectMention';
|
||||||
|
import { useGetEndpointsQuery } from '~/data-provider';
|
||||||
|
import { useAssistantsMapContext } from '~/Providers';
|
||||||
import FavoriteItem from './FavoriteItem';
|
import FavoriteItem from './FavoriteItem';
|
||||||
import store from '~/store';
|
import store from '~/store';
|
||||||
|
|
||||||
|
|
@ -123,6 +126,23 @@ export default function FavoritesList({
|
||||||
const { favorites, reorderFavorites, isLoading: isFavoritesLoading } = useFavorites();
|
const { favorites, reorderFavorites, isLoading: isFavoritesLoading } = useFavorites();
|
||||||
const showAgentMarketplace = useShowMarketplace();
|
const showAgentMarketplace = useShowMarketplace();
|
||||||
|
|
||||||
|
const { newConversation } = useNewConvo();
|
||||||
|
const assistantsMap = useAssistantsMapContext();
|
||||||
|
const conversation = useRecoilValue(store.conversationByIndex(0));
|
||||||
|
const { data: endpointsConfig = {} as t.TEndpointsConfig } = useGetEndpointsQuery();
|
||||||
|
|
||||||
|
const { onSelectEndpoint } = useSelectMention({
|
||||||
|
modelSpecs: [],
|
||||||
|
conversation,
|
||||||
|
assistantsMap,
|
||||||
|
endpointsConfig,
|
||||||
|
newConversation,
|
||||||
|
returnHandlers: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
const marketplaceRef = useRef<HTMLDivElement>(null);
|
||||||
|
const listContainerRef = useRef<HTMLDivElement>(null);
|
||||||
|
|
||||||
const handleAgentMarketplace = useCallback(() => {
|
const handleAgentMarketplace = useCallback(() => {
|
||||||
navigate('/agents');
|
navigate('/agents');
|
||||||
if (isSmallScreen && toggleNav) {
|
if (isSmallScreen && toggleNav) {
|
||||||
|
|
@ -130,6 +150,24 @@ export default function FavoritesList({
|
||||||
}
|
}
|
||||||
}, [navigate, isSmallScreen, toggleNav]);
|
}, [navigate, isSmallScreen, toggleNav]);
|
||||||
|
|
||||||
|
const handleRemoveFocus = useCallback(() => {
|
||||||
|
if (marketplaceRef.current) {
|
||||||
|
marketplaceRef.current.focus();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const nextFavorite = listContainerRef.current?.querySelector<HTMLElement>(
|
||||||
|
'[data-testid="favorite-item"]',
|
||||||
|
);
|
||||||
|
if (nextFavorite) {
|
||||||
|
nextFavorite.focus();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const newChatButton = document.querySelector<HTMLElement>(
|
||||||
|
'[data-testid="nav-new-chat-button"]',
|
||||||
|
);
|
||||||
|
newChatButton?.focus();
|
||||||
|
}, []);
|
||||||
|
|
||||||
// Ensure favorites is always an array (could be corrupted in localStorage)
|
// Ensure favorites is always an array (could be corrupted in localStorage)
|
||||||
const safeFavorites = useMemo(() => (Array.isArray(favorites) ? favorites : []), [favorites]);
|
const safeFavorites = useMemo(() => (Array.isArray(favorites) ? favorites : []), [favorites]);
|
||||||
|
|
||||||
|
|
@ -228,7 +266,7 @@ export default function FavoritesList({
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="mb-2 flex flex-col">
|
<div className="mb-2 flex flex-col">
|
||||||
<div className="mt-1 flex flex-col gap-1">
|
<div ref={listContainerRef} className="mt-1 flex flex-col gap-1">
|
||||||
{/* Show skeletons for ALL items while agents are still loading */}
|
{/* Show skeletons for ALL items while agents are still loading */}
|
||||||
{isAgentsLoading ? (
|
{isAgentsLoading ? (
|
||||||
<>
|
<>
|
||||||
|
|
@ -244,8 +282,18 @@ export default function FavoritesList({
|
||||||
{/* Agent Marketplace button */}
|
{/* Agent Marketplace button */}
|
||||||
{showAgentMarketplace && (
|
{showAgentMarketplace && (
|
||||||
<div
|
<div
|
||||||
|
ref={marketplaceRef}
|
||||||
|
role="button"
|
||||||
|
tabIndex={0}
|
||||||
|
aria-label={localize('com_agents_marketplace')}
|
||||||
className="group relative flex w-full cursor-pointer items-center justify-between rounded-lg px-3 py-2 text-sm text-text-primary hover:bg-surface-active-alt"
|
className="group relative flex w-full cursor-pointer items-center justify-between rounded-lg px-3 py-2 text-sm text-text-primary hover:bg-surface-active-alt"
|
||||||
onClick={handleAgentMarketplace}
|
onClick={handleAgentMarketplace}
|
||||||
|
onKeyDown={(e) => {
|
||||||
|
if (e.key === 'Enter' || e.key === ' ') {
|
||||||
|
e.preventDefault();
|
||||||
|
handleAgentMarketplace();
|
||||||
|
}
|
||||||
|
}}
|
||||||
data-testid="nav-agents-marketplace-button"
|
data-testid="nav-agents-marketplace-button"
|
||||||
>
|
>
|
||||||
<div className="flex flex-1 items-center truncate pr-6">
|
<div className="flex flex-1 items-center truncate pr-6">
|
||||||
|
|
@ -270,7 +318,12 @@ export default function FavoritesList({
|
||||||
moveItem={moveItem}
|
moveItem={moveItem}
|
||||||
onDrop={handleDrop}
|
onDrop={handleDrop}
|
||||||
>
|
>
|
||||||
<FavoriteItem item={agent} type="agent" />
|
<FavoriteItem
|
||||||
|
item={agent}
|
||||||
|
type="agent"
|
||||||
|
onSelectEndpoint={onSelectEndpoint}
|
||||||
|
onRemoveFocus={handleRemoveFocus}
|
||||||
|
/>
|
||||||
</DraggableFavoriteItem>
|
</DraggableFavoriteItem>
|
||||||
);
|
);
|
||||||
} else if (fav.model && fav.endpoint) {
|
} else if (fav.model && fav.endpoint) {
|
||||||
|
|
@ -285,6 +338,8 @@ export default function FavoritesList({
|
||||||
<FavoriteItem
|
<FavoriteItem
|
||||||
item={{ model: fav.model, endpoint: fav.endpoint }}
|
item={{ model: fav.model, endpoint: fav.endpoint }}
|
||||||
type="model"
|
type="model"
|
||||||
|
onSelectEndpoint={onSelectEndpoint}
|
||||||
|
onRemoveFocus={handleRemoveFocus}
|
||||||
/>
|
/>
|
||||||
</DraggableFavoriteItem>
|
</DraggableFavoriteItem>
|
||||||
);
|
);
|
||||||
|
|
|
||||||
503
package-lock.json
generated
503
package-lock.json
generated
|
|
@ -1401,23 +1401,6 @@
|
||||||
"node": ">= 0.8.0"
|
"node": ">= 0.8.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"api/node_modules/debug": {
|
|
||||||
"version": "4.4.3",
|
|
||||||
"resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz",
|
|
||||||
"integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==",
|
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
|
||||||
"ms": "^2.1.3"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">=6.0"
|
|
||||||
},
|
|
||||||
"peerDependenciesMeta": {
|
|
||||||
"supports-color": {
|
|
||||||
"optional": true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"api/node_modules/express-rate-limit": {
|
"api/node_modules/express-rate-limit": {
|
||||||
"version": "8.2.1",
|
"version": "8.2.1",
|
||||||
"resolved": "https://registry.npmjs.org/express-rate-limit/-/express-rate-limit-8.2.1.tgz",
|
"resolved": "https://registry.npmjs.org/express-rate-limit/-/express-rate-limit-8.2.1.tgz",
|
||||||
|
|
@ -1628,10 +1611,10 @@
|
||||||
"@marsidev/react-turnstile": "^1.1.0",
|
"@marsidev/react-turnstile": "^1.1.0",
|
||||||
"@mcp-ui/client": "^5.7.0",
|
"@mcp-ui/client": "^5.7.0",
|
||||||
"@radix-ui/react-accordion": "^1.1.2",
|
"@radix-ui/react-accordion": "^1.1.2",
|
||||||
"@radix-ui/react-alert-dialog": "^1.0.2",
|
"@radix-ui/react-alert-dialog": "^1.1.15",
|
||||||
"@radix-ui/react-checkbox": "^1.0.3",
|
"@radix-ui/react-checkbox": "^1.0.3",
|
||||||
"@radix-ui/react-collapsible": "^1.0.3",
|
"@radix-ui/react-collapsible": "^1.0.3",
|
||||||
"@radix-ui/react-dialog": "^1.0.2",
|
"@radix-ui/react-dialog": "^1.1.15",
|
||||||
"@radix-ui/react-dropdown-menu": "^2.1.1",
|
"@radix-ui/react-dropdown-menu": "^2.1.1",
|
||||||
"@radix-ui/react-hover-card": "^1.0.5",
|
"@radix-ui/react-hover-card": "^1.0.5",
|
||||||
"@radix-ui/react-icons": "^1.3.0",
|
"@radix-ui/react-icons": "^1.3.0",
|
||||||
|
|
@ -20655,23 +20638,23 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@radix-ui/react-alert-dialog": {
|
"node_modules/@radix-ui/react-alert-dialog": {
|
||||||
"version": "1.0.5",
|
"version": "1.1.15",
|
||||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-alert-dialog/-/react-alert-dialog-1.0.5.tgz",
|
"resolved": "https://registry.npmjs.org/@radix-ui/react-alert-dialog/-/react-alert-dialog-1.1.15.tgz",
|
||||||
"integrity": "sha512-OrVIOcZL0tl6xibeuGt5/+UxoT2N27KCFOPjFyfXMnchxSHZ/OW7cCX2nGlIYJrbHK/fczPcFzAwvNBB6XBNMA==",
|
"integrity": "sha512-oTVLkEw5GpdRe29BqJ0LSDFWI3qu0vR1M0mUkOQWDIUnY/QIkLpgDMWuKxP94c2NAC2LGcgVhG1ImF3jkZ5wXw==",
|
||||||
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/runtime": "^7.13.10",
|
"@radix-ui/primitive": "1.1.3",
|
||||||
"@radix-ui/primitive": "1.0.1",
|
"@radix-ui/react-compose-refs": "1.1.2",
|
||||||
"@radix-ui/react-compose-refs": "1.0.1",
|
"@radix-ui/react-context": "1.1.2",
|
||||||
"@radix-ui/react-context": "1.0.1",
|
"@radix-ui/react-dialog": "1.1.15",
|
||||||
"@radix-ui/react-dialog": "1.0.5",
|
"@radix-ui/react-primitive": "2.1.3",
|
||||||
"@radix-ui/react-primitive": "1.0.3",
|
"@radix-ui/react-slot": "1.2.3"
|
||||||
"@radix-ui/react-slot": "1.0.2"
|
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"@types/react": "*",
|
"@types/react": "*",
|
||||||
"@types/react-dom": "*",
|
"@types/react-dom": "*",
|
||||||
"react": "^16.8 || ^17.0 || ^18.0",
|
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
|
||||||
"react-dom": "^16.8 || ^17.0 || ^18.0"
|
"react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
||||||
},
|
},
|
||||||
"peerDependenciesMeta": {
|
"peerDependenciesMeta": {
|
||||||
"@types/react": {
|
"@types/react": {
|
||||||
|
|
@ -20682,6 +20665,83 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@radix-ui/react-alert-dialog/node_modules/@radix-ui/primitive": {
|
||||||
|
"version": "1.1.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/@radix-ui/primitive/-/primitive-1.1.3.tgz",
|
||||||
|
"integrity": "sha512-JTF99U/6XIjCBo0wqkU5sK10glYe27MRRsfwoiq5zzOEZLHU3A3KCMa5X/azekYRCJ0HlwI0crAXS/5dEHTzDg==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
|
"node_modules/@radix-ui/react-alert-dialog/node_modules/@radix-ui/react-compose-refs": {
|
||||||
|
"version": "1.1.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/@radix-ui/react-compose-refs/-/react-compose-refs-1.1.2.tgz",
|
||||||
|
"integrity": "sha512-z4eqJvfiNnFMHIIvXP3CY57y2WJs5g2v3X0zm9mEJkrkNv4rDxu+sg9Jh8EkXyeqBkB7SOcboo9dMVqhyrACIg==",
|
||||||
|
"license": "MIT",
|
||||||
|
"peerDependencies": {
|
||||||
|
"@types/react": "*",
|
||||||
|
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"@types/react": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@radix-ui/react-alert-dialog/node_modules/@radix-ui/react-context": {
|
||||||
|
"version": "1.1.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/@radix-ui/react-context/-/react-context-1.1.2.tgz",
|
||||||
|
"integrity": "sha512-jCi/QKUM2r1Ju5a3J64TH2A5SpKAgh0LpknyqdQ4m6DCV0xJ2HG1xARRwNGPQfi1SLdLWZ1OJz6F4OMBBNiGJA==",
|
||||||
|
"license": "MIT",
|
||||||
|
"peerDependencies": {
|
||||||
|
"@types/react": "*",
|
||||||
|
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"@types/react": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@radix-ui/react-alert-dialog/node_modules/@radix-ui/react-primitive": {
|
||||||
|
"version": "2.1.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.1.3.tgz",
|
||||||
|
"integrity": "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@radix-ui/react-slot": "1.2.3"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@types/react": "*",
|
||||||
|
"@types/react-dom": "*",
|
||||||
|
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
|
||||||
|
"react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"@types/react": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"@types/react-dom": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@radix-ui/react-alert-dialog/node_modules/@radix-ui/react-slot": {
|
||||||
|
"version": "1.2.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.3.tgz",
|
||||||
|
"integrity": "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@radix-ui/react-compose-refs": "1.1.2"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@types/react": "*",
|
||||||
|
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"@types/react": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@radix-ui/react-arrow": {
|
"node_modules/@radix-ui/react-arrow": {
|
||||||
"version": "1.0.3",
|
"version": "1.0.3",
|
||||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-arrow/-/react-arrow-1.0.3.tgz",
|
"resolved": "https://registry.npmjs.org/@radix-ui/react-arrow/-/react-arrow-1.0.3.tgz",
|
||||||
|
|
@ -20979,31 +21039,31 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@radix-ui/react-dialog": {
|
"node_modules/@radix-ui/react-dialog": {
|
||||||
"version": "1.0.5",
|
"version": "1.1.15",
|
||||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-dialog/-/react-dialog-1.0.5.tgz",
|
"resolved": "https://registry.npmjs.org/@radix-ui/react-dialog/-/react-dialog-1.1.15.tgz",
|
||||||
"integrity": "sha512-GjWJX/AUpB703eEBanuBnIWdIXg6NvJFCXcNlSZk4xdszCdhrJgBoUd1cGk67vFO+WdA2pfI/plOpqz/5GUP6Q==",
|
"integrity": "sha512-TCglVRtzlffRNxRMEyR36DGBLJpeusFcgMVD9PZEzAKnUs1lKCgX5u9BmC2Yg+LL9MgZDugFFs1Vl+Jp4t/PGw==",
|
||||||
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/runtime": "^7.13.10",
|
"@radix-ui/primitive": "1.1.3",
|
||||||
"@radix-ui/primitive": "1.0.1",
|
"@radix-ui/react-compose-refs": "1.1.2",
|
||||||
"@radix-ui/react-compose-refs": "1.0.1",
|
"@radix-ui/react-context": "1.1.2",
|
||||||
"@radix-ui/react-context": "1.0.1",
|
"@radix-ui/react-dismissable-layer": "1.1.11",
|
||||||
"@radix-ui/react-dismissable-layer": "1.0.5",
|
"@radix-ui/react-focus-guards": "1.1.3",
|
||||||
"@radix-ui/react-focus-guards": "1.0.1",
|
"@radix-ui/react-focus-scope": "1.1.7",
|
||||||
"@radix-ui/react-focus-scope": "1.0.4",
|
"@radix-ui/react-id": "1.1.1",
|
||||||
"@radix-ui/react-id": "1.0.1",
|
"@radix-ui/react-portal": "1.1.9",
|
||||||
"@radix-ui/react-portal": "1.0.4",
|
"@radix-ui/react-presence": "1.1.5",
|
||||||
"@radix-ui/react-presence": "1.0.1",
|
"@radix-ui/react-primitive": "2.1.3",
|
||||||
"@radix-ui/react-primitive": "1.0.3",
|
"@radix-ui/react-slot": "1.2.3",
|
||||||
"@radix-ui/react-slot": "1.0.2",
|
"@radix-ui/react-use-controllable-state": "1.2.2",
|
||||||
"@radix-ui/react-use-controllable-state": "1.0.1",
|
"aria-hidden": "^1.2.4",
|
||||||
"aria-hidden": "^1.1.1",
|
"react-remove-scroll": "^2.6.3"
|
||||||
"react-remove-scroll": "2.5.5"
|
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"@types/react": "*",
|
"@types/react": "*",
|
||||||
"@types/react-dom": "*",
|
"@types/react-dom": "*",
|
||||||
"react": "^16.8 || ^17.0 || ^18.0",
|
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
|
||||||
"react-dom": "^16.8 || ^17.0 || ^18.0"
|
"react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
||||||
},
|
},
|
||||||
"peerDependenciesMeta": {
|
"peerDependenciesMeta": {
|
||||||
"@types/react": {
|
"@types/react": {
|
||||||
|
|
@ -21014,6 +21074,308 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@radix-ui/react-dialog/node_modules/@radix-ui/primitive": {
|
||||||
|
"version": "1.1.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/@radix-ui/primitive/-/primitive-1.1.3.tgz",
|
||||||
|
"integrity": "sha512-JTF99U/6XIjCBo0wqkU5sK10glYe27MRRsfwoiq5zzOEZLHU3A3KCMa5X/azekYRCJ0HlwI0crAXS/5dEHTzDg==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
|
"node_modules/@radix-ui/react-dialog/node_modules/@radix-ui/react-compose-refs": {
|
||||||
|
"version": "1.1.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/@radix-ui/react-compose-refs/-/react-compose-refs-1.1.2.tgz",
|
||||||
|
"integrity": "sha512-z4eqJvfiNnFMHIIvXP3CY57y2WJs5g2v3X0zm9mEJkrkNv4rDxu+sg9Jh8EkXyeqBkB7SOcboo9dMVqhyrACIg==",
|
||||||
|
"license": "MIT",
|
||||||
|
"peerDependencies": {
|
||||||
|
"@types/react": "*",
|
||||||
|
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"@types/react": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@radix-ui/react-dialog/node_modules/@radix-ui/react-context": {
|
||||||
|
"version": "1.1.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/@radix-ui/react-context/-/react-context-1.1.2.tgz",
|
||||||
|
"integrity": "sha512-jCi/QKUM2r1Ju5a3J64TH2A5SpKAgh0LpknyqdQ4m6DCV0xJ2HG1xARRwNGPQfi1SLdLWZ1OJz6F4OMBBNiGJA==",
|
||||||
|
"license": "MIT",
|
||||||
|
"peerDependencies": {
|
||||||
|
"@types/react": "*",
|
||||||
|
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"@types/react": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@radix-ui/react-dialog/node_modules/@radix-ui/react-dismissable-layer": {
|
||||||
|
"version": "1.1.11",
|
||||||
|
"resolved": "https://registry.npmjs.org/@radix-ui/react-dismissable-layer/-/react-dismissable-layer-1.1.11.tgz",
|
||||||
|
"integrity": "sha512-Nqcp+t5cTB8BinFkZgXiMJniQH0PsUt2k51FUhbdfeKvc4ACcG2uQniY/8+h1Yv6Kza4Q7lD7PQV0z0oicE0Mg==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@radix-ui/primitive": "1.1.3",
|
||||||
|
"@radix-ui/react-compose-refs": "1.1.2",
|
||||||
|
"@radix-ui/react-primitive": "2.1.3",
|
||||||
|
"@radix-ui/react-use-callback-ref": "1.1.1",
|
||||||
|
"@radix-ui/react-use-escape-keydown": "1.1.1"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@types/react": "*",
|
||||||
|
"@types/react-dom": "*",
|
||||||
|
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
|
||||||
|
"react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"@types/react": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"@types/react-dom": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@radix-ui/react-dialog/node_modules/@radix-ui/react-focus-guards": {
|
||||||
|
"version": "1.1.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/@radix-ui/react-focus-guards/-/react-focus-guards-1.1.3.tgz",
|
||||||
|
"integrity": "sha512-0rFg/Rj2Q62NCm62jZw0QX7a3sz6QCQU0LpZdNrJX8byRGaGVTqbrW9jAoIAHyMQqsNpeZ81YgSizOt5WXq0Pw==",
|
||||||
|
"license": "MIT",
|
||||||
|
"peerDependencies": {
|
||||||
|
"@types/react": "*",
|
||||||
|
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"@types/react": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@radix-ui/react-dialog/node_modules/@radix-ui/react-focus-scope": {
|
||||||
|
"version": "1.1.7",
|
||||||
|
"resolved": "https://registry.npmjs.org/@radix-ui/react-focus-scope/-/react-focus-scope-1.1.7.tgz",
|
||||||
|
"integrity": "sha512-t2ODlkXBQyn7jkl6TNaw/MtVEVvIGelJDCG41Okq/KwUsJBwQ4XVZsHAVUkK4mBv3ewiAS3PGuUWuY2BoK4ZUw==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@radix-ui/react-compose-refs": "1.1.2",
|
||||||
|
"@radix-ui/react-primitive": "2.1.3",
|
||||||
|
"@radix-ui/react-use-callback-ref": "1.1.1"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@types/react": "*",
|
||||||
|
"@types/react-dom": "*",
|
||||||
|
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
|
||||||
|
"react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"@types/react": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"@types/react-dom": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@radix-ui/react-dialog/node_modules/@radix-ui/react-id": {
|
||||||
|
"version": "1.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@radix-ui/react-id/-/react-id-1.1.1.tgz",
|
||||||
|
"integrity": "sha512-kGkGegYIdQsOb4XjsfM97rXsiHaBwco+hFI66oO4s9LU+PLAC5oJ7khdOVFxkhsmlbpUqDAvXw11CluXP+jkHg==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@radix-ui/react-use-layout-effect": "1.1.1"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@types/react": "*",
|
||||||
|
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"@types/react": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@radix-ui/react-dialog/node_modules/@radix-ui/react-portal": {
|
||||||
|
"version": "1.1.9",
|
||||||
|
"resolved": "https://registry.npmjs.org/@radix-ui/react-portal/-/react-portal-1.1.9.tgz",
|
||||||
|
"integrity": "sha512-bpIxvq03if6UNwXZ+HTK71JLh4APvnXntDc6XOX8UVq4XQOVl7lwok0AvIl+b8zgCw3fSaVTZMpAPPagXbKmHQ==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@radix-ui/react-primitive": "2.1.3",
|
||||||
|
"@radix-ui/react-use-layout-effect": "1.1.1"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@types/react": "*",
|
||||||
|
"@types/react-dom": "*",
|
||||||
|
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
|
||||||
|
"react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"@types/react": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"@types/react-dom": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@radix-ui/react-dialog/node_modules/@radix-ui/react-presence": {
|
||||||
|
"version": "1.1.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/@radix-ui/react-presence/-/react-presence-1.1.5.tgz",
|
||||||
|
"integrity": "sha512-/jfEwNDdQVBCNvjkGit4h6pMOzq8bHkopq458dPt2lMjx+eBQUohZNG9A7DtO/O5ukSbxuaNGXMjHicgwy6rQQ==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@radix-ui/react-compose-refs": "1.1.2",
|
||||||
|
"@radix-ui/react-use-layout-effect": "1.1.1"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@types/react": "*",
|
||||||
|
"@types/react-dom": "*",
|
||||||
|
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
|
||||||
|
"react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"@types/react": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"@types/react-dom": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@radix-ui/react-dialog/node_modules/@radix-ui/react-primitive": {
|
||||||
|
"version": "2.1.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.1.3.tgz",
|
||||||
|
"integrity": "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@radix-ui/react-slot": "1.2.3"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@types/react": "*",
|
||||||
|
"@types/react-dom": "*",
|
||||||
|
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
|
||||||
|
"react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"@types/react": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"@types/react-dom": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@radix-ui/react-dialog/node_modules/@radix-ui/react-slot": {
|
||||||
|
"version": "1.2.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.3.tgz",
|
||||||
|
"integrity": "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@radix-ui/react-compose-refs": "1.1.2"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@types/react": "*",
|
||||||
|
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"@types/react": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@radix-ui/react-dialog/node_modules/@radix-ui/react-use-callback-ref": {
|
||||||
|
"version": "1.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@radix-ui/react-use-callback-ref/-/react-use-callback-ref-1.1.1.tgz",
|
||||||
|
"integrity": "sha512-FkBMwD+qbGQeMu1cOHnuGB6x4yzPjho8ap5WtbEJ26umhgqVXbhekKUQO+hZEL1vU92a3wHwdp0HAcqAUF5iDg==",
|
||||||
|
"license": "MIT",
|
||||||
|
"peerDependencies": {
|
||||||
|
"@types/react": "*",
|
||||||
|
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"@types/react": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@radix-ui/react-dialog/node_modules/@radix-ui/react-use-controllable-state": {
|
||||||
|
"version": "1.2.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/@radix-ui/react-use-controllable-state/-/react-use-controllable-state-1.2.2.tgz",
|
||||||
|
"integrity": "sha512-BjasUjixPFdS+NKkypcyyN5Pmg83Olst0+c6vGov0diwTEo6mgdqVR6hxcEgFuh4QrAs7Rc+9KuGJ9TVCj0Zzg==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@radix-ui/react-use-effect-event": "0.0.2",
|
||||||
|
"@radix-ui/react-use-layout-effect": "1.1.1"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@types/react": "*",
|
||||||
|
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"@types/react": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@radix-ui/react-dialog/node_modules/@radix-ui/react-use-escape-keydown": {
|
||||||
|
"version": "1.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@radix-ui/react-use-escape-keydown/-/react-use-escape-keydown-1.1.1.tgz",
|
||||||
|
"integrity": "sha512-Il0+boE7w/XebUHyBjroE+DbByORGR9KKmITzbR7MyQ4akpORYP/ZmbhAr0DG7RmmBqoOnZdy2QlvajJ2QA59g==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@radix-ui/react-use-callback-ref": "1.1.1"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@types/react": "*",
|
||||||
|
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"@types/react": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@radix-ui/react-dialog/node_modules/@radix-ui/react-use-layout-effect": {
|
||||||
|
"version": "1.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@radix-ui/react-use-layout-effect/-/react-use-layout-effect-1.1.1.tgz",
|
||||||
|
"integrity": "sha512-RbJRS4UWQFkzHTTwVymMTUv8EqYhOp8dOOviLj2ugtTiXRaRQS7GLGxZTLL1jWhMeoSCf5zmcZkqTl9IiYfXcQ==",
|
||||||
|
"license": "MIT",
|
||||||
|
"peerDependencies": {
|
||||||
|
"@types/react": "*",
|
||||||
|
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"@types/react": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@radix-ui/react-dialog/node_modules/react-remove-scroll": {
|
||||||
|
"version": "2.7.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/react-remove-scroll/-/react-remove-scroll-2.7.2.tgz",
|
||||||
|
"integrity": "sha512-Iqb9NjCCTt6Hf+vOdNIZGdTiH1QSqr27H/Ek9sv/a97gfueI/5h1s3yRi1nngzMUaOOToin5dI1dXKdXiF+u0Q==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"react-remove-scroll-bar": "^2.3.7",
|
||||||
|
"react-style-singleton": "^2.2.3",
|
||||||
|
"tslib": "^2.1.0",
|
||||||
|
"use-callback-ref": "^1.3.3",
|
||||||
|
"use-sidecar": "^1.1.3"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=10"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@types/react": "*",
|
||||||
|
"react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"@types/react": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@radix-ui/react-direction": {
|
"node_modules/@radix-ui/react-direction": {
|
||||||
"version": "1.0.1",
|
"version": "1.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-direction/-/react-direction-1.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/@radix-ui/react-direction/-/react-direction-1.0.1.tgz",
|
||||||
|
|
@ -28686,23 +29048,6 @@
|
||||||
"url": "https://opencollective.com/express"
|
"url": "https://opencollective.com/express"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/body-parser/node_modules/debug": {
|
|
||||||
"version": "4.4.3",
|
|
||||||
"resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz",
|
|
||||||
"integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==",
|
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
|
||||||
"ms": "^2.1.3"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">=6.0"
|
|
||||||
},
|
|
||||||
"peerDependenciesMeta": {
|
|
||||||
"supports-color": {
|
|
||||||
"optional": true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/body-parser/node_modules/media-typer": {
|
"node_modules/body-parser/node_modules/media-typer": {
|
||||||
"version": "1.1.0",
|
"version": "1.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/media-typer/-/media-typer-1.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/media-typer/-/media-typer-1.1.0.tgz",
|
||||||
|
|
@ -48867,10 +49212,10 @@
|
||||||
"@dicebear/core": "^9.2.2",
|
"@dicebear/core": "^9.2.2",
|
||||||
"@headlessui/react": "^2.1.2",
|
"@headlessui/react": "^2.1.2",
|
||||||
"@radix-ui/react-accordion": "^1.2.11",
|
"@radix-ui/react-accordion": "^1.2.11",
|
||||||
"@radix-ui/react-alert-dialog": "^1.0.2",
|
"@radix-ui/react-alert-dialog": "^1.1.15",
|
||||||
"@radix-ui/react-checkbox": "^1.0.3",
|
"@radix-ui/react-checkbox": "^1.0.3",
|
||||||
"@radix-ui/react-collapsible": "^1.1.11",
|
"@radix-ui/react-collapsible": "^1.1.11",
|
||||||
"@radix-ui/react-dialog": "^1.0.2",
|
"@radix-ui/react-dialog": "^1.1.15",
|
||||||
"@radix-ui/react-dropdown-menu": "^2.1.1",
|
"@radix-ui/react-dropdown-menu": "^2.1.1",
|
||||||
"@radix-ui/react-hover-card": "^1.0.5",
|
"@radix-ui/react-hover-card": "^1.0.5",
|
||||||
"@radix-ui/react-icons": "^1.3.0",
|
"@radix-ui/react-icons": "^1.3.0",
|
||||||
|
|
@ -50615,24 +50960,6 @@
|
||||||
"node": ">=18"
|
"node": ">=18"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"packages/client/node_modules/debug": {
|
|
||||||
"version": "4.4.3",
|
|
||||||
"resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz",
|
|
||||||
"integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==",
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
|
||||||
"ms": "^2.1.3"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">=6.0"
|
|
||||||
},
|
|
||||||
"peerDependenciesMeta": {
|
|
||||||
"supports-color": {
|
|
||||||
"optional": true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"packages/client/node_modules/decimal.js": {
|
"packages/client/node_modules/decimal.js": {
|
||||||
"version": "10.6.0",
|
"version": "10.6.0",
|
||||||
"resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.6.0.tgz",
|
"resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.6.0.tgz",
|
||||||
|
|
|
||||||
|
|
@ -35,10 +35,10 @@
|
||||||
"@dicebear/core": "^9.2.2",
|
"@dicebear/core": "^9.2.2",
|
||||||
"@headlessui/react": "^2.1.2",
|
"@headlessui/react": "^2.1.2",
|
||||||
"@radix-ui/react-accordion": "^1.2.11",
|
"@radix-ui/react-accordion": "^1.2.11",
|
||||||
"@radix-ui/react-alert-dialog": "^1.0.2",
|
"@radix-ui/react-alert-dialog": "^1.1.15",
|
||||||
"@radix-ui/react-checkbox": "^1.0.3",
|
"@radix-ui/react-checkbox": "^1.0.3",
|
||||||
"@radix-ui/react-collapsible": "^1.1.11",
|
"@radix-ui/react-collapsible": "^1.1.11",
|
||||||
"@radix-ui/react-dialog": "^1.0.2",
|
"@radix-ui/react-dialog": "^1.1.15",
|
||||||
"@radix-ui/react-dropdown-menu": "^2.1.1",
|
"@radix-ui/react-dropdown-menu": "^2.1.1",
|
||||||
"@radix-ui/react-hover-card": "^1.0.5",
|
"@radix-ui/react-hover-card": "^1.0.5",
|
||||||
"@radix-ui/react-icons": "^1.3.0",
|
"@radix-ui/react-icons": "^1.3.0",
|
||||||
|
|
|
||||||
|
|
@ -40,10 +40,15 @@ export interface ButtonProps
|
||||||
}
|
}
|
||||||
|
|
||||||
const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
|
const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
|
||||||
({ className, variant, size, asChild = false, ...props }, ref) => {
|
({ className, variant, size, asChild = false, type = 'button', ...props }, ref) => {
|
||||||
const Comp = asChild ? Slot : 'button';
|
const Comp = asChild ? Slot : 'button';
|
||||||
return (
|
return (
|
||||||
<Comp className={cn(buttonVariants({ variant, size, className }))} ref={ref} {...props} />
|
<Comp
|
||||||
|
type={asChild ? undefined : type}
|
||||||
|
className={cn(buttonVariants({ variant, size, className }))}
|
||||||
|
ref={ref}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue