mirror of
https://github.com/Significant-Gravitas/Auto-GPT.git
synced 2025-01-07 03:17:23 +08:00
draft-1
This commit is contained in:
parent
b44e6c5164
commit
e321d17151
@ -1,5 +1,6 @@
|
||||
import logging
|
||||
from typing import List
|
||||
import typing
|
||||
|
||||
import prisma.errors
|
||||
import prisma.models
|
||||
@ -15,17 +16,21 @@ logger = logging.getLogger(__name__)
|
||||
|
||||
async def get_library_agents(
|
||||
user_id: str,
|
||||
limit: int = 20,
|
||||
offset: int = 0,
|
||||
) -> List[backend.server.v2.library.model.LibraryAgent]:
|
||||
"""
|
||||
Returns all agents (AgentGraph) that belong to the user and all agents in their library (UserAgent table)
|
||||
Returns paginated agents (AgentGraph) that belong to the user and agents in their library (UserAgent table)
|
||||
"""
|
||||
logger.debug(f"Getting library agents for user {user_id}")
|
||||
logger.debug(f"Getting library agents for user {user_id} with limit {limit} offset {offset}")
|
||||
|
||||
try:
|
||||
# Get agents created by user with nodes and links
|
||||
user_created = await prisma.models.AgentGraph.prisma().find_many(
|
||||
where=prisma.types.AgentGraphWhereInput(userId=user_id, isActive=True),
|
||||
include=backend.data.includes.AGENT_GRAPH_INCLUDE,
|
||||
skip=offset,
|
||||
take=limit,
|
||||
)
|
||||
|
||||
# Get agents in user's library with nodes and links
|
||||
@ -47,6 +52,8 @@ async def get_library_agents(
|
||||
}
|
||||
}
|
||||
},
|
||||
skip=offset,
|
||||
take=limit,
|
||||
)
|
||||
|
||||
# Convert to Graph models first
|
||||
@ -94,6 +101,122 @@ async def get_library_agents(
|
||||
"Failed to fetch library agents"
|
||||
) from e
|
||||
|
||||
async def search_library_agents(
|
||||
user_id: str,
|
||||
search_term: str,
|
||||
sort_by: typing.Literal["most_recent", "highest_runtime", "most_runs", "alphabetical", "last_modified"],
|
||||
limit: int = 20,
|
||||
offset: int = 0,
|
||||
) -> List[backend.server.v2.library.model.LibraryAgent]:
|
||||
"""
|
||||
Searches paginated agents (AgentGraph) that belong to the user and agents in their library (UserAgent table)
|
||||
based on name or description containing the search term
|
||||
"""
|
||||
logger.debug(f"Searching library agents for user {user_id} with term '{search_term}', limit {limit} offset {offset}")
|
||||
|
||||
try:
|
||||
# Get sort field
|
||||
sort_order = "desc" if sort_by in ["most_recent", "highest_runtime", "most_runs", "last_modified"] else "asc"
|
||||
|
||||
sort_field = {
|
||||
"most_recent": "createdAt",
|
||||
"last_modified": "updatedAt",
|
||||
"highest_runtime": "totalRuntime",
|
||||
"most_runs": "runCount",
|
||||
"alphabetical": "name"
|
||||
}.get(sort_by, "updatedAt")
|
||||
|
||||
# Get user created agents matching search
|
||||
user_created = await prisma.models.AgentGraph.prisma().find_many(
|
||||
where=prisma.types.AgentGraphWhereInput(
|
||||
userId=user_id,
|
||||
isActive=True,
|
||||
OR=[
|
||||
{"name": {"contains": search_term, "mode": "insensitive"}},
|
||||
{"description": {"contains": search_term, "mode": "insensitive"}}
|
||||
]
|
||||
),
|
||||
include=backend.data.includes.AGENT_GRAPH_INCLUDE,
|
||||
order={sort_field: sort_order},
|
||||
skip=offset,
|
||||
take=limit,
|
||||
)
|
||||
|
||||
# Get library agents matching search
|
||||
library_agents = await prisma.models.UserAgent.prisma().find_many(
|
||||
where=prisma.types.UserAgentWhereInput(
|
||||
userId=user_id,
|
||||
isDeleted=False,
|
||||
isArchived=False,
|
||||
Agent={
|
||||
"is": {
|
||||
"OR": [
|
||||
{"name": {"contains": search_term, "mode": "insensitive"}},
|
||||
{"description": {"contains": search_term, "mode": "insensitive"}}
|
||||
]
|
||||
}
|
||||
}
|
||||
),
|
||||
include={
|
||||
"Agent": {
|
||||
"include": {
|
||||
"AgentNodes": {
|
||||
"include": {
|
||||
"Input": True,
|
||||
"Output": True,
|
||||
"Webhook": True,
|
||||
"AgentBlock": True,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
skip=offset,
|
||||
take=limit,
|
||||
)
|
||||
|
||||
# Convert to Graph models
|
||||
graphs = []
|
||||
|
||||
for agent in user_created:
|
||||
try:
|
||||
graphs.append(backend.data.graph.GraphModel.from_db(agent))
|
||||
except Exception as e:
|
||||
logger.error(f"Error processing searched user agent {agent.id}: {e}")
|
||||
continue
|
||||
|
||||
for agent in library_agents:
|
||||
if agent.Agent:
|
||||
try:
|
||||
graphs.append(backend.data.graph.GraphModel.from_db(agent.Agent))
|
||||
except Exception as e:
|
||||
logger.error(f"Error processing searched library agent {agent.agentId}: {e}")
|
||||
continue
|
||||
|
||||
# Convert to LibraryAgent models
|
||||
result = []
|
||||
for graph in graphs:
|
||||
result.append(
|
||||
backend.server.v2.library.model.LibraryAgent(
|
||||
id=graph.id,
|
||||
version=graph.version,
|
||||
is_active=graph.is_active,
|
||||
name=graph.name,
|
||||
description=graph.description,
|
||||
isCreatedByUser=any(a.id == graph.id for a in user_created),
|
||||
input_schema=graph.input_schema,
|
||||
output_schema=graph.output_schema,
|
||||
)
|
||||
)
|
||||
|
||||
logger.debug(f"Found {len(result)} library agents matching search")
|
||||
return result
|
||||
|
||||
except prisma.errors.PrismaError as e:
|
||||
logger.error(f"Database error searching library agents: {str(e)}")
|
||||
raise backend.server.v2.store.exceptions.DatabaseError(
|
||||
"Failed to search library agents"
|
||||
) from e
|
||||
|
||||
async def add_agent_to_library(store_listing_version_id: str, user_id: str) -> None:
|
||||
"""
|
||||
|
@ -4,7 +4,6 @@ import typing
|
||||
import autogpt_libs.auth.depends
|
||||
import autogpt_libs.auth.middleware
|
||||
import fastapi
|
||||
import prisma
|
||||
|
||||
import backend.data.graph
|
||||
import backend.integrations.creds_manager
|
||||
@ -28,14 +27,37 @@ integration_creds_manager = (
|
||||
async def get_library_agents(
|
||||
user_id: typing.Annotated[
|
||||
str, fastapi.Depends(autogpt_libs.auth.depends.get_user_id)
|
||||
]
|
||||
) -> typing.Sequence[backend.server.v2.library.model.LibraryAgent]:
|
||||
],
|
||||
pagination_token: str | None = fastapi.Query(None)
|
||||
) -> dict[str, typing.Any]:
|
||||
"""
|
||||
Get all agents in the user's library, including both created and saved agents.
|
||||
Get agents in the user's library with pagination (20 agents per page).
|
||||
|
||||
Args:
|
||||
user_id: ID of the authenticated user
|
||||
pagination_token: Token to get next page of results
|
||||
|
||||
Returns:
|
||||
Dictionary containing:
|
||||
- agents: List of agents for current page
|
||||
- next_token: Token to get next page (None if no more pages)
|
||||
"""
|
||||
try:
|
||||
agents = await backend.server.v2.library.db.get_library_agents(user_id)
|
||||
return agents
|
||||
page_size = 20
|
||||
agents = await backend.server.v2.library.db.get_library_agents(
|
||||
user_id,
|
||||
limit=page_size + 1,
|
||||
offset=int(pagination_token) if pagination_token else 0
|
||||
)
|
||||
|
||||
has_more = len(agents) > page_size
|
||||
agents = agents[:page_size]
|
||||
next_token = str(int(pagination_token or 0) + page_size) if has_more else None
|
||||
|
||||
return {
|
||||
"agents": agents,
|
||||
"next_token": next_token
|
||||
}
|
||||
except Exception:
|
||||
logger.exception("Exception occurred whilst getting library agents")
|
||||
raise fastapi.HTTPException(
|
||||
@ -43,6 +65,58 @@ async def get_library_agents(
|
||||
)
|
||||
|
||||
|
||||
# For searching and filtering the library agents
|
||||
@router.get(
|
||||
"/agents/search",
|
||||
tags=["library", "private"],
|
||||
dependencies=[fastapi.Depends(autogpt_libs.auth.middleware.auth_middleware)],
|
||||
)
|
||||
async def search_library_agents(
|
||||
user_id: typing.Annotated[
|
||||
str, fastapi.Depends(autogpt_libs.auth.depends.get_user_id)
|
||||
],
|
||||
search_term: str = fastapi.Query(..., description="Search term to filter agents"),
|
||||
sort_by: typing.Literal["most_recent", "highest_runtime", "most_runs", "alphabetical", "last_modified"] = fastapi.Query("most_recent", description="Sort results by criteria"),
|
||||
pagination_token: str | None = fastapi.Query(None)
|
||||
) -> dict[str, typing.Any]:
|
||||
"""
|
||||
Search for agents in the user's library with pagination (20 agents per page).
|
||||
|
||||
Args:
|
||||
user_id: ID of the authenticated user
|
||||
search_term: Term to search for in agent names/descriptions
|
||||
sort_by: How to sort results (most_recent, highest_runtime, most_runs, alphabetical, last_modified)
|
||||
pagination_token: Token to get next page of results
|
||||
|
||||
Returns:
|
||||
Dictionary containing:
|
||||
- agents: List of matching agents for current page
|
||||
- next_token: Token to get next page (None if no more pages)
|
||||
"""
|
||||
try:
|
||||
page_size = 20
|
||||
agents = await backend.server.v2.library.db.search_library_agents(
|
||||
user_id,
|
||||
search_term,
|
||||
sort_by=sort_by,
|
||||
limit=page_size + 1,
|
||||
offset=int(pagination_token) if pagination_token else 0
|
||||
)
|
||||
|
||||
has_more = len(agents) > page_size
|
||||
agents = agents[:page_size]
|
||||
next_token = str(int(pagination_token or 0) + page_size) if has_more else None
|
||||
|
||||
return {
|
||||
"agents": agents,
|
||||
"next_token": next_token
|
||||
}
|
||||
except Exception:
|
||||
logger.exception("Exception occurred whilst searching library agents")
|
||||
raise fastapi.HTTPException(
|
||||
status_code=500, detail="Failed to search library agents"
|
||||
)
|
||||
|
||||
@router.post(
|
||||
"/agents/{store_listing_version_id}",
|
||||
tags=["library", "private"],
|
||||
@ -70,49 +144,51 @@ async def add_agent_to_library(
|
||||
"""
|
||||
try:
|
||||
# Get the graph from the store listing
|
||||
store_listing_version = (
|
||||
await prisma.models.StoreListingVersion.prisma().find_unique(
|
||||
where={"id": store_listing_version_id}, include={"Agent": True}
|
||||
)
|
||||
)
|
||||
# store_listing_version = (
|
||||
# await prisma.models.StoreListingVersion.prisma().find_unique(
|
||||
# where={"id": store_listing_version_id}, include={"Agent": True}
|
||||
# )
|
||||
# )
|
||||
|
||||
if not store_listing_version or not store_listing_version.Agent:
|
||||
raise fastapi.HTTPException(
|
||||
status_code=404,
|
||||
detail=f"Store listing version {store_listing_version_id} not found",
|
||||
)
|
||||
# if not store_listing_version or not store_listing_version.Agent:
|
||||
# raise fastapi.HTTPException(
|
||||
# status_code=404,
|
||||
# detail=f"Store listing version {store_listing_version_id} not found",
|
||||
# )
|
||||
|
||||
agent = store_listing_version.Agent
|
||||
# agent = store_listing_version.Agent
|
||||
|
||||
if agent.userId == user_id:
|
||||
raise fastapi.HTTPException(
|
||||
status_code=400, detail="Cannot add own agent to library"
|
||||
)
|
||||
# if agent.userId == user_id:
|
||||
# raise fastapi.HTTPException(
|
||||
# status_code=400, detail="Cannot add own agent to library"
|
||||
# )
|
||||
|
||||
# Create a new graph from the template
|
||||
graph = await backend.data.graph.get_graph(
|
||||
agent.id, agent.version, template=True, user_id=user_id
|
||||
)
|
||||
# # Create a new graph from the template
|
||||
# graph = await backend.data.graph.get_graph(
|
||||
# agent.id, agent.version, template=True, user_id=user_id
|
||||
# )
|
||||
|
||||
if not graph:
|
||||
raise fastapi.HTTPException(
|
||||
status_code=404, detail=f"Agent {agent.id} not found"
|
||||
)
|
||||
# if not graph:
|
||||
# raise fastapi.HTTPException(
|
||||
# status_code=404, detail=f"Agent {agent.id} not found"
|
||||
# )
|
||||
|
||||
# Create a deep copy with new IDs
|
||||
graph.version = 1
|
||||
graph.is_template = False
|
||||
graph.is_active = True
|
||||
graph.reassign_ids(user_id=user_id, reassign_graph_id=True)
|
||||
# # Create a deep copy with new IDs
|
||||
# graph.version = 1
|
||||
# graph.is_template = False
|
||||
# graph.is_active = True
|
||||
# graph.reassign_ids(user_id=user_id, reassign_graph_id=True)
|
||||
|
||||
# Save the new graph
|
||||
graph = await backend.data.graph.create_graph(graph, user_id=user_id)
|
||||
graph = (
|
||||
await backend.integrations.webhooks.graph_lifecycle_hooks.on_graph_activate(
|
||||
graph,
|
||||
get_credentials=lambda id: integration_creds_manager.get(user_id, id),
|
||||
)
|
||||
)
|
||||
# # Save the new graph
|
||||
# graph = await backend.data.graph.create_graph(graph, user_id=user_id)
|
||||
# graph = (
|
||||
# await backend.integrations.webhooks.graph_lifecycle_hooks.on_graph_activate(
|
||||
# graph,
|
||||
# get_credentials=lambda id: integration_creds_manager.get(user_id, id),
|
||||
# )
|
||||
# )
|
||||
|
||||
await backend.server.v2.library.db.add_agent_to_library(store_listing_version_id=store_listing_version_id,user_id=user_id)
|
||||
|
||||
return fastapi.Response(status_code=201)
|
||||
|
||||
|
@ -19,6 +19,7 @@ const nextConfig = {
|
||||
typescript: {
|
||||
ignoreBuildErrors: true,
|
||||
},
|
||||
transpilePackages: ["geist"],
|
||||
};
|
||||
|
||||
export default withSentryConfig(nextConfig, {
|
||||
|
@ -62,6 +62,7 @@
|
||||
"framer-motion": "^11.15.0",
|
||||
"geist": "^1.3.1",
|
||||
"launchdarkly-react-client-sdk": "^3.6.0",
|
||||
"lodash.debounce": "^4.0.8",
|
||||
"lucide-react": "^0.468.0",
|
||||
"moment": "^2.30.1",
|
||||
"next": "^14.2.13",
|
||||
@ -69,6 +70,7 @@
|
||||
"react": "^18",
|
||||
"react-day-picker": "^9.4.4",
|
||||
"react-dom": "^18",
|
||||
"react-drag-drop-files": "^2.4.0",
|
||||
"react-hook-form": "^7.54.0",
|
||||
"react-icons": "^5.4.0",
|
||||
"react-markdown": "^9.0.1",
|
||||
@ -93,6 +95,7 @@
|
||||
"@storybook/react": "^8.3.5",
|
||||
"@storybook/test": "^8.3.5",
|
||||
"@storybook/test-runner": "^0.20.1",
|
||||
"@types/lodash": "^4.17.13",
|
||||
"@types/negotiator": "^0.6.3",
|
||||
"@types/node": "^22.9.0",
|
||||
"@types/react": "^18",
|
||||
|
@ -1,13 +1,34 @@
|
||||
"use client";
|
||||
|
||||
import LibraryActionHeader from "@/components/agptui/composite/LibraryActionHeader";
|
||||
import LibraryAgentListContainer from "@/components/agptui/composite/LibraryAgentListContainer";
|
||||
import { GraphMeta } from "@/lib/autogpt-server-api";
|
||||
import { useState } from "react";
|
||||
|
||||
/**
|
||||
* LibraryPage Component
|
||||
* Main component that manages the library interface including agent listing and actions
|
||||
*/
|
||||
|
||||
const LibraryPage = () => {
|
||||
const [agents, setAgents] = useState<GraphMeta[]>([]);
|
||||
const [agentLoading, setAgentLoading] = useState<boolean>(true);
|
||||
return (
|
||||
<main className="p-4">
|
||||
{/* Top section - includes notification, search and uploading mechansim */}
|
||||
<LibraryActionHeader />
|
||||
<main className="mx-auto w-screen max-w-[1600px] space-y-[16px] bg-neutral-50 p-4 px-2 dark:bg-neutral-900 sm:px-8 md:px-12">
|
||||
{/* Header section containing notifications, search functionality, agent count, filters and upload mechanism */}
|
||||
<LibraryActionHeader
|
||||
numberOfAgents={agents.length}
|
||||
setAgents={setAgents}
|
||||
setAgentLoading={setAgentLoading}
|
||||
/>
|
||||
|
||||
{/* Last section for Agent Lists, Agent counter and filter */}
|
||||
<div></div>
|
||||
{/* Content section displaying agent list with counter and filtering options */}
|
||||
<LibraryAgentListContainer
|
||||
setAgents={setAgents}
|
||||
agents={agents}
|
||||
agentLoading={agentLoading}
|
||||
setAgentLoading={setAgentLoading}
|
||||
/>
|
||||
</main>
|
||||
);
|
||||
};
|
||||
|
@ -125,3 +125,16 @@
|
||||
scrollbar-width: thin; /* For Firefox (sets a thin scrollbar) */
|
||||
scrollbar-color: transparent transparent; /* For Firefox (thumb and track colors) */
|
||||
}
|
||||
|
||||
body {
|
||||
overflow-x: hidden;
|
||||
}
|
||||
|
||||
.drop-style {
|
||||
border: dashed 2px #a3a3a3 !important;
|
||||
border-radius: 20px;
|
||||
}
|
||||
|
||||
.drop-style:hover {
|
||||
border: dashed 2px #525252 !important;
|
||||
}
|
||||
|
@ -4,6 +4,8 @@ import { Inter, Poppins } from "next/font/google";
|
||||
import { Providers } from "@/app/providers";
|
||||
import { cn } from "@/lib/utils";
|
||||
import { Navbar } from "@/components/agptui/Navbar";
|
||||
import { GeistSans } from "geist/font/sans";
|
||||
import { GeistMono } from "geist/font/mono";
|
||||
|
||||
import "./globals.css";
|
||||
import TallyPopupSimple from "@/components/TallyPopup";
|
||||
@ -11,7 +13,7 @@ import { GoogleAnalytics } from "@next/third-parties/google";
|
||||
import { Toaster } from "@/components/ui/toaster";
|
||||
import { IconType } from "@/components/ui/icons";
|
||||
|
||||
const inter = Inter({ subsets: ["latin"] });
|
||||
const inter = Inter({ subsets: ["latin"], variable: "--font-inter" });
|
||||
|
||||
const poppins = Poppins({
|
||||
subsets: ["latin"],
|
||||
@ -30,12 +32,14 @@ export default async function RootLayout({
|
||||
children: React.ReactNode;
|
||||
}>) {
|
||||
return (
|
||||
<html lang="en">
|
||||
<html
|
||||
lang="en"
|
||||
className={`${GeistSans.variable} ${GeistMono.variable} ${poppins.variable} ${inter.variable}`}
|
||||
>
|
||||
<body
|
||||
className={cn(
|
||||
"antialiased transition-colors",
|
||||
"bg-neutral-50 antialiased transition-colors",
|
||||
inter.className,
|
||||
poppins.className,
|
||||
)}
|
||||
>
|
||||
<Providers
|
||||
|
@ -38,7 +38,7 @@ const Monitor = () => {
|
||||
|
||||
const fetchAgents = useCallback(() => {
|
||||
api.listLibraryAgents().then((agent) => {
|
||||
setFlows(agent);
|
||||
setFlows(agent.agents);
|
||||
});
|
||||
api.getExecutions().then((executions) => {
|
||||
setExecutions(executions);
|
||||
|
@ -21,8 +21,8 @@ const buttonVariants = cva(
|
||||
"hover:bg-neutral-100 text-[#272727] dark:text-neutral-100 dark:hover:bg-neutral-700",
|
||||
link: "text-[#272727] underline-offset-4 hover:underline dark:text-neutral-100",
|
||||
library_outline:
|
||||
"rounded-[52px] hover:bg-[#262626] border border-zinc-700 hover:text-white",
|
||||
library_primary: "rounded-[52px] bg-[#262626] text-white",
|
||||
"rounded-[52px] hover:bg-[#262626] border border-zinc-700 hover:text-white font-sans",
|
||||
library_primary: "rounded-[52px] bg-[#262626] text-white font-sans",
|
||||
},
|
||||
size: {
|
||||
default:
|
||||
|
@ -0,0 +1,52 @@
|
||||
import { cn } from "@/lib/utils";
|
||||
import Link from "next/link";
|
||||
import { GraphMeta } from "@/lib/autogpt-server-api";
|
||||
|
||||
export const LibraryAgentCard = ({ id, name, isCreatedByUser }: GraphMeta) => {
|
||||
return (
|
||||
<div
|
||||
className={cn(
|
||||
"flex h-[158px] flex-col rounded-[14px] border border-[#E5E5E5] bg-white p-5 transition-all duration-300 ease-in-out hover:scale-[1.02]",
|
||||
!isCreatedByUser && "shadow-[0_-5px_0_0_rgb(196_181_253)]",
|
||||
)}
|
||||
>
|
||||
<div className="flex flex-1">
|
||||
<h3 className="font-inter flex-1 text-[18px] font-semibold leading-4">
|
||||
{name}
|
||||
</h3>
|
||||
{/* <span
|
||||
className={cn(
|
||||
"h-[14px] w-[14px] rounded-full",
|
||||
status == "Nothing running" && "bg-[#64748B]",
|
||||
status == "healthy" && "bg-[#22C55E]",
|
||||
status == "something wrong" && "bg-[#EF4444]",
|
||||
status == "waiting for trigger" && "bg-[#FBBF24]",
|
||||
)}
|
||||
></span> */}
|
||||
</div>
|
||||
|
||||
<div className="flex items-center justify-between">
|
||||
<div className="mt-6 flex gap-3">
|
||||
<Link
|
||||
href={`/agents/${id}`}
|
||||
className="font-inter text-[14px] font-[700] leading-[24px] text-neutral-800 hover:underline"
|
||||
>
|
||||
See runs
|
||||
</Link>
|
||||
|
||||
<Link
|
||||
href={`/build?flowID=${id}`}
|
||||
className="font-inter text-[14px] font-[700] leading-[24px] text-neutral-800 hover:underline"
|
||||
>
|
||||
Open in builder
|
||||
</Link>
|
||||
</div>
|
||||
{/* {output && (
|
||||
<div className="h-[24px] w-fit rounded-[45px] bg-neutral-600 px-[9px] py-[2px] font-sans text-[12px] font-[700] text-neutral-50">
|
||||
New output
|
||||
</div>
|
||||
)} */}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
@ -0,0 +1,68 @@
|
||||
import { GraphMeta } from "@/lib/autogpt-server-api";
|
||||
import { Dispatch, SetStateAction } from "react";
|
||||
import {
|
||||
Select,
|
||||
SelectContent,
|
||||
SelectGroup,
|
||||
SelectItem,
|
||||
SelectTrigger,
|
||||
SelectValue,
|
||||
} from "../ui/select";
|
||||
import { Filter } from "lucide-react";
|
||||
import { useBackendAPI } from "@/lib/autogpt-server-api/context";
|
||||
|
||||
type SortValue =
|
||||
| "most_recent"
|
||||
| "highest_runtime"
|
||||
| "most_runs"
|
||||
| "alphabetical"
|
||||
| "last_modified";
|
||||
|
||||
const LibraryAgentFilter = ({
|
||||
setAgents,
|
||||
setAgentLoading,
|
||||
}: {
|
||||
setAgents: Dispatch<SetStateAction<GraphMeta[]>>;
|
||||
setAgentLoading: Dispatch<SetStateAction<boolean>>;
|
||||
}) => {
|
||||
const api = useBackendAPI();
|
||||
const handleSortChange = async (value: SortValue) => {
|
||||
setAgentLoading(true);
|
||||
await new Promise((resolve) => setTimeout(resolve, 1000));
|
||||
let response = await api.librarySearchAgent("", undefined, value);
|
||||
setAgents(response.agents);
|
||||
setAgentLoading(false);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="flex items-center">
|
||||
<span className="hidden sm:inline">sort by</span>
|
||||
<Select onValueChange={handleSortChange}>
|
||||
<SelectTrigger className="ml-1 w-fit space-x-1 border-none pl-2 shadow-md">
|
||||
<Filter className="h-4 w-4 sm:hidden" />
|
||||
<SelectValue
|
||||
placeholder="most Recent"
|
||||
className={
|
||||
"font-sans text-[14px] font-[500] leading-[24px] text-neutral-600"
|
||||
}
|
||||
/>
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectGroup
|
||||
className={
|
||||
"font-sans text-[14px] font-[500] leading-[24px] text-neutral-600"
|
||||
}
|
||||
>
|
||||
<SelectItem value="most_recent">Most Recent</SelectItem>
|
||||
<SelectItem value="highest_runtime">Highest Runtime</SelectItem>
|
||||
<SelectItem value="most_runs">Most Runs</SelectItem>
|
||||
<SelectItem value="alphabetical">Alphabetical</SelectItem>
|
||||
<SelectItem value="last_modified">Last Modified</SelectItem>
|
||||
</SelectGroup>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default LibraryAgentFilter;
|
@ -0,0 +1,183 @@
|
||||
import { Button } from "../ui/button";
|
||||
import Image from "next/image";
|
||||
import { Separator } from "../ui/separator";
|
||||
import {
|
||||
CirclePlayIcon,
|
||||
ClipboardCopy,
|
||||
ImageIcon,
|
||||
Play,
|
||||
PlayCircle,
|
||||
Share2,
|
||||
X,
|
||||
} from "lucide-react";
|
||||
import { Dispatch, SetStateAction } from "react";
|
||||
|
||||
export interface NotificationCardData {
|
||||
type: "text" | "image" | "video" | "audio";
|
||||
title: string;
|
||||
id: string;
|
||||
content?: string;
|
||||
mediaUrl?: string;
|
||||
}
|
||||
|
||||
interface NotificationCardProps extends NotificationCardData {
|
||||
setNotifications: Dispatch<SetStateAction<NotificationCardData[] | null>>;
|
||||
}
|
||||
|
||||
const NotificationCard = ({
|
||||
type,
|
||||
title,
|
||||
id,
|
||||
content,
|
||||
mediaUrl,
|
||||
setNotifications,
|
||||
}: NotificationCardProps) => {
|
||||
const barHeights = Array.from({ length: 60 }, () =>
|
||||
Math.floor(Math.random() * (34 - 20 + 1) + 20),
|
||||
);
|
||||
|
||||
const handleClose = (e: React.MouseEvent<HTMLButtonElement>) => {
|
||||
e.preventDefault();
|
||||
setNotifications((prev) => {
|
||||
if (!prev) return null;
|
||||
return prev.filter((notification) => notification.id !== id);
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="w-[430px] space-y-[22px] rounded-[14px] border border-neutral-100 bg-neutral-50 p-[16px] pt-[12px]">
|
||||
<div className="flex items-center justify-between">
|
||||
{/* count */}
|
||||
<div className="flex items-center gap-[10px]">
|
||||
<p className="font-sans text-[12px] font-medium text-neutral-500">
|
||||
1/4
|
||||
</p>
|
||||
<p className="h-[26px] rounded-[45px] bg-green-100 px-[9px] py-[3px] font-sans text-[12px] font-medium text-green-800">
|
||||
Success
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* cross icon */}
|
||||
<Button
|
||||
variant="ghost"
|
||||
className="p-0 hover:bg-transparent"
|
||||
onClick={handleClose}
|
||||
>
|
||||
<X
|
||||
className="h-6 w-6 text-[#020617] hover:scale-105"
|
||||
strokeWidth={1.25}
|
||||
/>
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
<div className="space-y-[6px] p-0">
|
||||
<p className="font-sans text-[14px] font-medium leading-[20px] text-neutral-500">
|
||||
New Output Ready!
|
||||
</p>
|
||||
<h2 className="font-poppin text-[20px] font-medium leading-7 text-neutral-800">
|
||||
{title}
|
||||
</h2>
|
||||
{type === "text" && <Separator />}
|
||||
</div>
|
||||
|
||||
<div className="p-0">
|
||||
{type === "text" && (
|
||||
// Maybe in future we give markdown support
|
||||
<div className="mt-[-8px] line-clamp-6 font-sans text-sm font-[400px] text-neutral-600">
|
||||
{content}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{type === "image" &&
|
||||
(mediaUrl ? (
|
||||
<div className="relative h-[200px] w-full">
|
||||
<Image
|
||||
src={mediaUrl}
|
||||
alt={title}
|
||||
fill
|
||||
className="rounded-lg object-cover"
|
||||
/>
|
||||
</div>
|
||||
) : (
|
||||
<div className="flex h-[244px] w-full items-center justify-center rounded-lg bg-[#D9D9D9]">
|
||||
<ImageIcon
|
||||
className="h-[138px] w-[138px] text-neutral-400"
|
||||
strokeWidth={1}
|
||||
/>
|
||||
</div>
|
||||
))}
|
||||
|
||||
{type === "video" && (
|
||||
<div className="space-y-4">
|
||||
{mediaUrl ? (
|
||||
<video src={mediaUrl} controls className="w-full rounded-lg" />
|
||||
) : (
|
||||
<div className="flex h-[219px] w-[398px] items-center justify-center rounded-lg bg-[#D9D9D9]">
|
||||
<PlayCircle
|
||||
className="h-16 w-16 text-neutral-500"
|
||||
strokeWidth={1}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{type === "audio" && (
|
||||
<div className="flex gap-2">
|
||||
<CirclePlayIcon
|
||||
className="h-10 w-10 rounded-full bg-neutral-800 text-white"
|
||||
strokeWidth={1}
|
||||
/>
|
||||
<div className="flex flex-1 items-center justify-between">
|
||||
{/* <audio src={mediaUrl} controls className="w-full" /> */}
|
||||
{barHeights.map((h, i) => {
|
||||
return (
|
||||
<div
|
||||
key={i}
|
||||
className={`rounded-[8px] bg-neutral-500`}
|
||||
style={{
|
||||
height: `${h}px`,
|
||||
width: "3px",
|
||||
}}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className="flex justify-between gap-2 p-0">
|
||||
<div className="space-x-3">
|
||||
<Button
|
||||
variant="outline"
|
||||
onClick={() => {
|
||||
navigator.share({
|
||||
title,
|
||||
text: content,
|
||||
url: mediaUrl,
|
||||
});
|
||||
}}
|
||||
className="h-10 w-10 rounded-full border-neutral-800 p-0"
|
||||
>
|
||||
<Share2 className="h-5 w-5" strokeWidth={1} />
|
||||
</Button>
|
||||
<Button
|
||||
variant="outline"
|
||||
onClick={() =>
|
||||
navigator.clipboard.writeText(content || mediaUrl || "")
|
||||
}
|
||||
className="h-10 w-10 rounded-full border-neutral-800 p-0"
|
||||
>
|
||||
<ClipboardCopy className="h-5 w-5" strokeWidth={1} />
|
||||
</Button>
|
||||
</div>
|
||||
<Button className="h-[40px] rounded-[52px] bg-neutral-800 px-4 py-2">
|
||||
See run
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default NotificationCard;
|
@ -4,25 +4,63 @@ import {
|
||||
DropdownMenuContent,
|
||||
DropdownMenuItem,
|
||||
DropdownMenuLabel,
|
||||
DropdownMenuSeparator,
|
||||
DropdownMenuTrigger,
|
||||
} from "@/components/ui/dropdown-menu";
|
||||
import { Button } from "./Button";
|
||||
import { BellIcon, X } from "lucide-react";
|
||||
import { motion, useAnimationControls } from "framer-motion";
|
||||
import { useState } from "react";
|
||||
import {
|
||||
Card,
|
||||
CardContent,
|
||||
CardDescription,
|
||||
CardFooter,
|
||||
CardHeader,
|
||||
CardTitle,
|
||||
} from "../ui/card";
|
||||
import { motion, useAnimationControls, useScroll } from "framer-motion";
|
||||
import { useState, useEffect } from "react";
|
||||
import LibraryNotificationCard, {
|
||||
NotificationCardData,
|
||||
} from "./LibraryNotificationCard";
|
||||
import { cn } from "@/lib/utils";
|
||||
|
||||
export const LibraryNotificationDropdown = () => {
|
||||
const controls = useAnimationControls();
|
||||
const [open, setOpen] = useState(false);
|
||||
const [notifications, setNotifications] = useState<
|
||||
NotificationCardData[] | null
|
||||
>(null);
|
||||
const { scrollY } = useScroll();
|
||||
const [scrollPosition, setScrollPosition] = useState(0);
|
||||
|
||||
useEffect(() => {
|
||||
const unsubscribe = scrollY.onChange((currentY) => {
|
||||
setScrollPosition(currentY);
|
||||
});
|
||||
return () => unsubscribe();
|
||||
}, [scrollY]);
|
||||
|
||||
const initialNotificationData = [
|
||||
{
|
||||
type: "audio" as "audio",
|
||||
title: "Audio Processing Complete",
|
||||
id: "4",
|
||||
},
|
||||
{
|
||||
type: "text" as "text",
|
||||
title: "LinkedIn Post Generator: YouTube to Professional Content",
|
||||
id: "1",
|
||||
content:
|
||||
"As artificial intelligence (AI) continues to evolve, it's increasingly clear that AI isn't just a trend—it's reshaping the way we work, innovate, and solve complex problems. However, for many professionals, the question remains: How can I leverage AI to drive meaningful results in my own field? In this article, we'll explore how AI can empower businesses and individuals alike to be more efficient, make better decisions, and unlock new opportunities. Whether you're in tech, finance, healthcare, or any other industry, understanding the potential of AI can set you apart.",
|
||||
},
|
||||
{
|
||||
type: "image" as "image",
|
||||
title: "New Image Upload",
|
||||
id: "2",
|
||||
},
|
||||
{
|
||||
type: "video" as "video",
|
||||
title: "Video Processing Complete",
|
||||
id: "3",
|
||||
},
|
||||
] as NotificationCardData[];
|
||||
|
||||
useEffect(() => {
|
||||
if (initialNotificationData) {
|
||||
setNotifications(initialNotificationData);
|
||||
}
|
||||
}, []);
|
||||
|
||||
const handleHoverStart = () => {
|
||||
controls.start({
|
||||
@ -30,25 +68,49 @@ export const LibraryNotificationDropdown = () => {
|
||||
transition: { duration: 0.5 },
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<DropdownMenu open={open} onOpenChange={setOpen}>
|
||||
<DropdownMenuTrigger asChild>
|
||||
<DropdownMenuTrigger className="sm:flex-1" asChild>
|
||||
<Button
|
||||
variant={open ? "library_primary" : "library_outline"}
|
||||
size="library"
|
||||
onMouseEnter={handleHoverStart}
|
||||
onMouseLeave={handleHoverStart}
|
||||
className="w-[161px]"
|
||||
className={cn(
|
||||
"z-50 max-w-[161px] transition-all duration-200 ease-in-out",
|
||||
scrollY.get() > 30 ? "w-fit max-w-fit" : "w-fit sm:w-[161px]",
|
||||
)}
|
||||
>
|
||||
<motion.div animate={controls}>
|
||||
<BellIcon className="mr-2 h-5 w-5" strokeWidth={2} />
|
||||
<BellIcon
|
||||
className={cn(
|
||||
"h-5 w-5 transition-all duration-200 ease-in-out",
|
||||
scrollY.get() <= 30 && "sm:mr-2",
|
||||
)}
|
||||
strokeWidth={2}
|
||||
/>
|
||||
</motion.div>
|
||||
Your updates
|
||||
<span className="ml-2 text-[14px]">2</span>
|
||||
{scrollY.get() <= 30 && (
|
||||
<motion.div
|
||||
initial={{ opacity: 1 }}
|
||||
animate={{ opacity: 1 }}
|
||||
exit={{ opacity: 0 }}
|
||||
className="hidden items-center transition-opacity duration-300 sm:inline-flex"
|
||||
>
|
||||
Your updates
|
||||
<span className="ml-2 text-[14px]">
|
||||
{notifications?.length || 0}
|
||||
</span>
|
||||
</motion.div>
|
||||
)}
|
||||
</Button>
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent className="scroll-none relative left-[16px] h-[80vh] w-fit overflow-y-auto rounded-[26px] bg-[#a1a1aa]/60 p-5">
|
||||
<DropdownMenuLabel className="mb-4 font-sans text-[18px] text-white">
|
||||
<DropdownMenuContent
|
||||
sideOffset={22}
|
||||
className="scroll-none relative left-[16px] h-[80vh] w-fit overflow-y-auto rounded-[26px] bg-[#C5C5CA] p-5"
|
||||
>
|
||||
<DropdownMenuLabel className="z-10 mb-4 font-sans text-[18px] text-white">
|
||||
Agent run updates
|
||||
</DropdownMenuLabel>
|
||||
<button
|
||||
@ -57,53 +119,23 @@ export const LibraryNotificationDropdown = () => {
|
||||
>
|
||||
<X className="h-6 w-6 text-white hover:text-white/60" />
|
||||
</button>
|
||||
<DropdownMenuItem>
|
||||
<LibraryNotificationCard />
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuItem>
|
||||
<LibraryNotificationCard />
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuItem>
|
||||
<LibraryNotificationCard />
|
||||
</DropdownMenuItem>
|
||||
<div className="space-y-[12px]">
|
||||
{notifications && notifications.length ? (
|
||||
notifications.map((notification) => (
|
||||
<DropdownMenuItem key={notification.id} className="p-0">
|
||||
<LibraryNotificationCard
|
||||
{...notification}
|
||||
setNotifications={setNotifications}
|
||||
/>
|
||||
</DropdownMenuItem>
|
||||
))
|
||||
) : (
|
||||
<div className="w-[464px] py-4 text-center text-white">
|
||||
No notifications present
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
);
|
||||
};
|
||||
|
||||
const LibraryNotificationCard = () => {
|
||||
return (
|
||||
<Card className="w-[424px] rounded-[14px] border border-neutral-100 p-[16px] pt-[12px]">
|
||||
<CardHeader>
|
||||
<CardTitle>Latest Agent Updates</CardTitle>
|
||||
<CardDescription>View your latest workflow changes</CardDescription>
|
||||
</CardHeader>
|
||||
|
||||
<CardContent>
|
||||
<div className="space-y-4">
|
||||
<div className="flex items-center gap-4">
|
||||
<div className="h-10 w-10 rounded-full bg-neutral-100" />
|
||||
<div>
|
||||
<p className="font-medium">Agent Run #1234</p>
|
||||
<p className="text-sm text-neutral-500">Updated 2 hours ago</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex items-center gap-4">
|
||||
<div className="h-10 w-10 rounded-full bg-neutral-100" />
|
||||
<div>
|
||||
<p className="font-medium">Workflow Changes</p>
|
||||
<p className="text-sm text-neutral-500">3 new changes detected</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</CardContent>
|
||||
|
||||
<CardFooter>
|
||||
<Button variant="outline" className="w-full">
|
||||
View All Updates
|
||||
</Button>
|
||||
</CardFooter>
|
||||
</Card>
|
||||
);
|
||||
};
|
||||
|
@ -0,0 +1,104 @@
|
||||
"use client";
|
||||
import { Search, X } from "lucide-react";
|
||||
import { Input } from "../ui/input";
|
||||
import { Dispatch, SetStateAction, useRef, useState, useCallback } from "react";
|
||||
import { motion, AnimatePresence } from "framer-motion";
|
||||
import { GraphMeta } from "@/lib/autogpt-server-api";
|
||||
import debounce from "lodash/debounce";
|
||||
import { useBackendAPI } from "@/lib/autogpt-server-api/context";
|
||||
|
||||
export const LibrarySearchBar = ({
|
||||
setAgents,
|
||||
setAgentLoading,
|
||||
}: {
|
||||
setAgents: Dispatch<SetStateAction<GraphMeta[]>>;
|
||||
setAgentLoading: Dispatch<SetStateAction<boolean>>;
|
||||
}) => {
|
||||
const inputRef = useRef<HTMLInputElement>(null);
|
||||
const [isFocused, setIsFocused] = useState(false);
|
||||
const api = useBackendAPI();
|
||||
|
||||
const debouncedSearch = useCallback(
|
||||
debounce(async (searchTerm: string) => {
|
||||
try {
|
||||
setAgentLoading(true);
|
||||
await new Promise((resolve) => setTimeout(resolve, 1000));
|
||||
const response = await api.librarySearchAgent(searchTerm);
|
||||
setAgents(response.agents);
|
||||
setAgentLoading(false);
|
||||
} catch (error) {
|
||||
console.error("Search failed:", error);
|
||||
}
|
||||
}, 300),
|
||||
[setAgents],
|
||||
);
|
||||
|
||||
const handleSearchInput = (e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
const searchTerm = e.target.value;
|
||||
debouncedSearch(searchTerm);
|
||||
};
|
||||
|
||||
return (
|
||||
<div
|
||||
onClick={() => inputRef.current?.focus()}
|
||||
className="relative z-[21] mx-auto flex h-[50px] w-full max-w-[500px] flex-1 cursor-pointer items-center rounded-[45px] bg-[#EDEDED] px-[24px] py-[10px]"
|
||||
>
|
||||
<div className="w-[30px] overflow-hidden">
|
||||
<AnimatePresence mode="wait">
|
||||
{!isFocused ? (
|
||||
<motion.div
|
||||
key="search"
|
||||
initial={{ x: -50 }}
|
||||
animate={{ x: 0 }}
|
||||
exit={{ x: -50 }}
|
||||
transition={{
|
||||
duration: 0.2,
|
||||
ease: "easeInOut",
|
||||
}}
|
||||
>
|
||||
<Search
|
||||
className="h-[29px] w-[29px] text-neutral-900"
|
||||
strokeWidth={1.25}
|
||||
/>
|
||||
</motion.div>
|
||||
) : (
|
||||
<motion.div
|
||||
key="close"
|
||||
initial={{ x: 50 }}
|
||||
animate={{ x: 0 }}
|
||||
exit={{ x: 50 }}
|
||||
transition={{
|
||||
duration: 0.2,
|
||||
ease: "easeInOut",
|
||||
}}
|
||||
>
|
||||
<X
|
||||
className="h-[29px] w-[29px] cursor-pointer text-neutral-900"
|
||||
strokeWidth={1.25}
|
||||
onClick={(e) => {
|
||||
if (inputRef.current) {
|
||||
debouncedSearch("");
|
||||
inputRef.current.value = "";
|
||||
inputRef.current.blur();
|
||||
e.preventDefault();
|
||||
}
|
||||
setIsFocused(false);
|
||||
}}
|
||||
/>
|
||||
</motion.div>
|
||||
)}
|
||||
</AnimatePresence>
|
||||
</div>
|
||||
|
||||
<Input
|
||||
ref={inputRef}
|
||||
onFocus={() => setIsFocused(true)}
|
||||
onBlur={() => !inputRef.current?.value && setIsFocused(false)}
|
||||
onChange={handleSearchInput}
|
||||
className="border-none font-sans text-[16px] font-normal leading-7 shadow-none focus:shadow-none"
|
||||
type="text"
|
||||
placeholder="Search agents"
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
};
|
@ -0,0 +1,168 @@
|
||||
"use client";
|
||||
import { Upload, X } from "lucide-react";
|
||||
import { Button } from "./Button";
|
||||
import { useEffect, useState } from "react";
|
||||
import { motion, useAnimation } from "framer-motion";
|
||||
import { cn } from "@/lib/utils";
|
||||
import {
|
||||
Dialog,
|
||||
DialogContent,
|
||||
DialogDescription,
|
||||
DialogHeader,
|
||||
DialogTitle,
|
||||
DialogTrigger,
|
||||
} from "../ui/dialog";
|
||||
import { Input } from "../ui/input";
|
||||
import { FileUploader } from "react-drag-drop-files";
|
||||
|
||||
const fileTypes = ["JSON"];
|
||||
|
||||
export const LibraryUploadAgent = () => {
|
||||
const [scrolled, setScrolled] = useState(false);
|
||||
const [file, setFile] = useState<File | null>(null);
|
||||
const [isDroped, setisDroped] = useState(false);
|
||||
const controls = useAnimation();
|
||||
const handleChange = (file: File) => {
|
||||
setTimeout(() => {
|
||||
setisDroped(false);
|
||||
}, 2000);
|
||||
setFile(file);
|
||||
setisDroped(false);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
const handleScroll = () => {
|
||||
if (window.scrollY > 30) {
|
||||
setScrolled(true);
|
||||
} else {
|
||||
setScrolled(false);
|
||||
}
|
||||
};
|
||||
|
||||
window.addEventListener("scroll", handleScroll);
|
||||
return () => window.removeEventListener("scroll", handleScroll);
|
||||
}, []);
|
||||
|
||||
const handleUpload = () => {
|
||||
// Add upload logic here
|
||||
if (file) {
|
||||
console.log("Uploading file:", file);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<Dialog>
|
||||
<DialogTrigger asChild>
|
||||
<Button
|
||||
variant="library_primary"
|
||||
size="library"
|
||||
className={cn(
|
||||
"max-w-[177px] transition-all duration-200 ease-in-out",
|
||||
scrolled ? "w-fit max-w-fit" : "w-fit sm:w-[177px]",
|
||||
)}
|
||||
>
|
||||
<motion.div animate={controls}>
|
||||
<Upload
|
||||
className={cn(
|
||||
"h-5 w-5 transition-all duration-200 ease-in-out",
|
||||
!scrolled && "sm:mr-2",
|
||||
)}
|
||||
/>
|
||||
</motion.div>
|
||||
{!scrolled && (
|
||||
<motion.div
|
||||
initial={{ opacity: 1 }}
|
||||
animate={{ opacity: 1 }}
|
||||
exit={{ opacity: 0 }}
|
||||
className="hidden items-center transition-opacity duration-300 sm:inline-flex"
|
||||
>
|
||||
Upload an agent
|
||||
</motion.div>
|
||||
)}
|
||||
</Button>
|
||||
</DialogTrigger>
|
||||
<DialogContent>
|
||||
<DialogHeader>
|
||||
<DialogTitle className="mb-8 text-center">Upload Agent </DialogTitle>
|
||||
</DialogHeader>
|
||||
|
||||
<div className="relative flex flex-col gap-4">
|
||||
<Input placeholder="Agent name" className="w-full rounded-[10px]" />
|
||||
<Input placeholder="Description" className="w-full rounded-[10px]" />
|
||||
|
||||
{file ? (
|
||||
<div className="flex rounded-[10px] border p-2 font-sans text-sm font-medium text-[#525252] outline-none">
|
||||
<span className="line-clamp-1">{file.name}</span>
|
||||
<Button
|
||||
onClick={() => setFile(null)}
|
||||
className="absolute left-[-10px] top-[-16px] mt-2 h-fit border-none bg-red-200 p-1"
|
||||
size="library"
|
||||
>
|
||||
<X
|
||||
className="m-0 h-[12px] w-[12px] text-red-600"
|
||||
strokeWidth={3}
|
||||
/>
|
||||
</Button>
|
||||
</div>
|
||||
) : (
|
||||
<FileUploader
|
||||
handleChange={handleChange}
|
||||
name="file"
|
||||
types={fileTypes}
|
||||
label={"Upload your agent here..!!"}
|
||||
uploadedLabel={"Uploading Successful"}
|
||||
required={true}
|
||||
hoverTitle={"Drop your agent here...!!"}
|
||||
maxSize={10}
|
||||
classes={"drop-style"}
|
||||
onDrop={() => {
|
||||
setisDroped(true);
|
||||
}}
|
||||
onSelect={() => {
|
||||
setisDroped(true);
|
||||
}}
|
||||
children={
|
||||
<div
|
||||
style={{
|
||||
minHeight: "150px",
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
justifyContent: "center",
|
||||
alignItems: "center",
|
||||
outline: "none",
|
||||
fontFamily: "var(--font-geist-sans)",
|
||||
color: "#525252",
|
||||
fontSize: "14px",
|
||||
fontWeight: "500",
|
||||
}}
|
||||
>
|
||||
{isDroped ? (
|
||||
<div className="flex items-center justify-center py-4">
|
||||
<div className="h-8 w-8 animate-spin rounded-full border-b-2 border-t-2 border-neutral-800"></div>
|
||||
</div>
|
||||
) : (
|
||||
<>
|
||||
<span>Drop your agent here</span>
|
||||
<span>or</span>
|
||||
<span>Click to upload</span>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
}
|
||||
/>
|
||||
)}
|
||||
|
||||
<Button
|
||||
onClick={handleUpload}
|
||||
variant="library_primary"
|
||||
size="library"
|
||||
className="mt-2 self-end"
|
||||
disabled={!file}
|
||||
>
|
||||
Upload Agent
|
||||
</Button>
|
||||
</div>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
);
|
||||
};
|
@ -57,7 +57,7 @@ export const Navbar = async ({ links, menuItemGroups }: NavbarProps) => {
|
||||
|
||||
return (
|
||||
<>
|
||||
<nav className="sticky top-0 z-50 mx-[16px] hidden h-16 w-full max-w-[1600px] items-center justify-between rounded-bl-2xl rounded-br-2xl border border-white/50 bg-white/5 py-3 pl-6 pr-3 backdrop-blur-[26px] dark:border-gray-700 dark:bg-gray-900 md:inline-flex">
|
||||
<nav className="sticky top-0 z-50 mx-[16px] hidden h-16 w-full max-w-[1600px] items-center justify-between rounded-bl-2xl rounded-br-2xl border border-white/50 bg-neutral-50 py-3 pl-6 pr-3 backdrop-blur-[26px] dark:border-gray-700 dark:bg-gray-900 md:inline-flex">
|
||||
<div className="flex items-center gap-11">
|
||||
<div className="relative h-10 w-[88.87px]">
|
||||
<IconAutoGPTLogo className="h-full w-full" />
|
||||
|
@ -1,26 +1,140 @@
|
||||
"use client";
|
||||
import { LibrarySearchBar } from "@/components/agptui/LibrarySearchBar";
|
||||
import { LibraryNotificationDropdown } from "../LibraryNotificationDropdown";
|
||||
import { LibraryUploadAgent } from "../LibraryUploadAgent";
|
||||
import { motion, useScroll, useTransform } from "framer-motion";
|
||||
import { cn } from "@/lib/utils";
|
||||
import {
|
||||
Dispatch,
|
||||
SetStateAction,
|
||||
useEffect,
|
||||
useState,
|
||||
useCallback,
|
||||
} from "react";
|
||||
import { GraphMeta } from "@/lib/autogpt-server-api";
|
||||
import LibraryAgentFilter from "../LibraryAgentFilter";
|
||||
|
||||
const LibraryActionHeader: React.FC = () => {
|
||||
return (
|
||||
<div className="flex w-screen items-center justify-between px-4 pt-6">
|
||||
<LibraryNotificationDropdown />
|
||||
<LibrarySearchBar />
|
||||
<LibraryUploadAgent />
|
||||
</div>
|
||||
interface LibraryActionHeaderProps {
|
||||
setAgents: Dispatch<SetStateAction<GraphMeta[]>>;
|
||||
setAgentLoading: Dispatch<SetStateAction<boolean>>;
|
||||
numberOfAgents: number;
|
||||
}
|
||||
|
||||
// Constants for header animation behavior
|
||||
const SCROLL_THRESHOLD = 30;
|
||||
const INITIAL_HEIGHT = 100;
|
||||
const COLLAPSED_HEIGHT = 50;
|
||||
const TRANSITION_DURATION = 0.3;
|
||||
|
||||
/**
|
||||
* LibraryActionHeader component - Renders a sticky header with search, notifications and filters
|
||||
* Animates and collapses based on scroll position
|
||||
*/
|
||||
const LibraryActionHeader: React.FC<LibraryActionHeaderProps> = ({
|
||||
setAgents,
|
||||
setAgentLoading,
|
||||
numberOfAgents,
|
||||
}) => {
|
||||
const { scrollY } = useScroll();
|
||||
const [scrollPosition, setScrollPosition] = useState(0);
|
||||
|
||||
const height = useTransform(
|
||||
scrollY,
|
||||
[0, 100],
|
||||
[INITIAL_HEIGHT, COLLAPSED_HEIGHT],
|
||||
);
|
||||
};
|
||||
|
||||
const LibrarySearchBar = () => {
|
||||
const handleScroll = useCallback((currentY: number) => {
|
||||
setScrollPosition(currentY);
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
const unsubscribe = scrollY.on("change", handleScroll);
|
||||
return () => unsubscribe();
|
||||
}, [scrollY, handleScroll]);
|
||||
|
||||
// Calculate animation offsets based on scroll position
|
||||
const getScrollAnimation = (offsetX: number, offsetY: number) => ({
|
||||
x: scrollPosition > SCROLL_THRESHOLD ? offsetX : 0,
|
||||
y: scrollPosition > SCROLL_THRESHOLD ? offsetY : 0,
|
||||
});
|
||||
|
||||
return (
|
||||
<div>
|
||||
SearchBar
|
||||
{/* Search bar content */}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
<>
|
||||
<div className="sticky top-16 z-[10] hidden items-start justify-between bg-neutral-50 pb-4 md:flex">
|
||||
<motion.div
|
||||
className={cn("relative flex-1 space-y-[32px]")}
|
||||
style={{ height }}
|
||||
transition={{ duration: TRANSITION_DURATION }}
|
||||
>
|
||||
<LibraryNotificationDropdown />
|
||||
|
||||
const LibraryUploadAgent = () => {
|
||||
return <div>Uploading Agent</div>;
|
||||
<motion.div
|
||||
className="flex items-center gap-[10px] p-2"
|
||||
animate={getScrollAnimation(60, -76)}
|
||||
>
|
||||
<span className="w-[96px] font-poppin text-[18px] font-semibold leading-[28px] text-neutral-800">
|
||||
My agents
|
||||
</span>
|
||||
<span className="w-[70px] font-sans text-[14px] font-normal leading-6">
|
||||
{numberOfAgents} agents
|
||||
</span>
|
||||
</motion.div>
|
||||
</motion.div>
|
||||
|
||||
<LibrarySearchBar
|
||||
setAgents={setAgents}
|
||||
setAgentLoading={setAgentLoading}
|
||||
/>
|
||||
<motion.div
|
||||
className="flex flex-1 flex-col items-end space-y-[32px]"
|
||||
style={{ height }}
|
||||
transition={{ duration: TRANSITION_DURATION }}
|
||||
>
|
||||
<LibraryUploadAgent />
|
||||
<motion.div
|
||||
className="flex items-center gap-[10px] pl-2 pr-2 font-sans text-[14px] font-[500] leading-[24px] text-neutral-600"
|
||||
animate={getScrollAnimation(-60, -68)}
|
||||
>
|
||||
<LibraryAgentFilter
|
||||
setAgents={setAgents}
|
||||
setAgentLoading={setAgentLoading}
|
||||
/>
|
||||
</motion.div>
|
||||
</motion.div>
|
||||
</div>
|
||||
|
||||
{/* Mobile and tablet */}
|
||||
<div className="flex flex-col gap-4 bg-neutral-50 p-4 pt-[52px] md:hidden">
|
||||
<div className="flex w-full justify-between">
|
||||
<LibraryNotificationDropdown />
|
||||
<LibraryUploadAgent />
|
||||
</div>
|
||||
|
||||
<div className="flex items-center justify-center">
|
||||
<LibrarySearchBar
|
||||
setAgents={setAgents}
|
||||
setAgentLoading={setAgentLoading}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="flex w-full justify-between">
|
||||
<div className="flex items-center gap-2">
|
||||
<span className="font-poppin text-[18px] font-semibold leading-[28px] text-neutral-800">
|
||||
My agents
|
||||
</span>
|
||||
<span className="font-sans text-[14px] font-normal leading-6">
|
||||
{numberOfAgents} agents
|
||||
</span>
|
||||
</div>
|
||||
<LibraryAgentFilter
|
||||
setAgents={setAgents}
|
||||
setAgentLoading={setAgentLoading}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default LibraryActionHeader;
|
||||
|
@ -0,0 +1,120 @@
|
||||
"use client";
|
||||
import {
|
||||
useEffect,
|
||||
useState,
|
||||
useCallback,
|
||||
Dispatch,
|
||||
SetStateAction,
|
||||
} from "react";
|
||||
import { LibraryAgentCard } from "../LibraryAgentCard";
|
||||
import { useBackendAPI } from "@/lib/autogpt-server-api/context";
|
||||
import { GraphMeta } from "@/lib/autogpt-server-api";
|
||||
import { useThreshold } from "@/hooks/useThreshold";
|
||||
|
||||
interface LibraryAgentListContainerProps {
|
||||
setAgents: Dispatch<SetStateAction<GraphMeta[]>>;
|
||||
agents: GraphMeta[];
|
||||
setAgentLoading: Dispatch<SetStateAction<boolean>>;
|
||||
agentLoading: boolean;
|
||||
}
|
||||
|
||||
export type AgentStatus =
|
||||
| "healthy"
|
||||
| "something wrong"
|
||||
| "waiting for trigger"
|
||||
| "Nothing running";
|
||||
|
||||
/**
|
||||
* LibraryAgentListContainer is a React component that displays a grid of library agents with infinite scroll functionality.
|
||||
*/
|
||||
|
||||
const LibraryAgentListContainer: React.FC<LibraryAgentListContainerProps> = ({
|
||||
setAgents,
|
||||
agents,
|
||||
setAgentLoading,
|
||||
agentLoading,
|
||||
}) => {
|
||||
const [nextToken, setNextToken] = useState<string | null>(null);
|
||||
const [loadingMore, setLoadingMore] = useState(false);
|
||||
|
||||
const api = useBackendAPI();
|
||||
|
||||
const fetchAgents = useCallback(
|
||||
async (paginationToken?: string) => {
|
||||
try {
|
||||
const response = await api.listLibraryAgents(paginationToken);
|
||||
setAgents((prevAgents) =>
|
||||
paginationToken
|
||||
? [...prevAgents, ...response.agents]
|
||||
: response.agents,
|
||||
);
|
||||
setNextToken(response.next_token);
|
||||
} finally {
|
||||
setAgentLoading(false);
|
||||
setLoadingMore(false);
|
||||
}
|
||||
},
|
||||
[api, setAgents, setAgentLoading],
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
fetchAgents();
|
||||
}, [fetchAgents]);
|
||||
|
||||
const handleInfiniteScroll = useCallback(
|
||||
(scrollY: number) => {
|
||||
if (!nextToken || loadingMore) return;
|
||||
|
||||
const { scrollHeight, clientHeight } = document.documentElement;
|
||||
const SCROLL_THRESHOLD = 20;
|
||||
const FETCH_DELAY = 1000;
|
||||
|
||||
if (scrollY + clientHeight >= scrollHeight - SCROLL_THRESHOLD) {
|
||||
setLoadingMore(true);
|
||||
setTimeout(() => fetchAgents(nextToken), FETCH_DELAY);
|
||||
}
|
||||
},
|
||||
[nextToken, loadingMore, fetchAgents],
|
||||
);
|
||||
|
||||
useThreshold(handleInfiniteScroll, 50);
|
||||
|
||||
const LoadingSpinner = () => (
|
||||
<div className="h-8 w-8 animate-spin rounded-full border-b-2 border-t-2 border-neutral-800" />
|
||||
);
|
||||
|
||||
return (
|
||||
<div className="space-y-[10px] p-2">
|
||||
{agentLoading ? (
|
||||
<div className="flex h-[200px] items-center justify-center">
|
||||
<LoadingSpinner />
|
||||
</div>
|
||||
) : (
|
||||
<>
|
||||
<div className="grid grid-cols-1 gap-3 sm:grid-cols-2 md:grid-cols-2 lg:grid-cols-3 xl:grid-cols-3">
|
||||
{agents?.map((agent) => (
|
||||
<LibraryAgentCard
|
||||
key={agent.id}
|
||||
id={agent.id}
|
||||
name={agent.name}
|
||||
isCreatedByUser={agent.isCreatedByUser}
|
||||
input_schema={agent.input_schema}
|
||||
output_schema={agent.output_schema}
|
||||
is_active={agent.is_active}
|
||||
version={agent.version}
|
||||
description={agent.description}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
{loadingMore && (
|
||||
<div className="flex items-center justify-center py-4">
|
||||
<LoadingSpinner />
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default LibraryAgentListContainer;
|
37
autogpt_platform/frontend/src/hooks/useThreshold.ts
Normal file
37
autogpt_platform/frontend/src/hooks/useThreshold.ts
Normal file
@ -0,0 +1,37 @@
|
||||
import { useEffect, useState } from "react";
|
||||
|
||||
interface ThresholdCallback<T> {
|
||||
(value: T): void;
|
||||
}
|
||||
|
||||
export const useThreshold = <T>(
|
||||
callback: ThresholdCallback<T>,
|
||||
threshold: number,
|
||||
): boolean => {
|
||||
const [prevValue, setPrevValue] = useState<T | null>(null);
|
||||
const [isThresholdMet, setIsThresholdMet] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
const handleScroll = () => {
|
||||
const { scrollY } = window;
|
||||
|
||||
if (scrollY >= threshold) {
|
||||
setIsThresholdMet(true);
|
||||
} else {
|
||||
setIsThresholdMet(false);
|
||||
}
|
||||
|
||||
if (scrollY >= threshold && (!prevValue || prevValue !== scrollY)) {
|
||||
callback(scrollY as T);
|
||||
setPrevValue(scrollY as T);
|
||||
}
|
||||
};
|
||||
|
||||
window.addEventListener("scroll", handleScroll);
|
||||
handleScroll();
|
||||
|
||||
return () => window.removeEventListener("scroll", handleScroll);
|
||||
}, [callback, threshold, prevValue]);
|
||||
|
||||
return isThresholdMet;
|
||||
};
|
@ -311,7 +311,6 @@ export default class BackendAPI {
|
||||
"/store/submissions/generate_image?agent_id=" + agent_id,
|
||||
);
|
||||
}
|
||||
c;
|
||||
deleteStoreSubmission(submission_id: string): Promise<boolean> {
|
||||
return this._request("DELETE", `/store/submissions/${submission_id}`);
|
||||
}
|
||||
@ -352,11 +351,34 @@ export default class BackendAPI {
|
||||
/////////// V2 LIBRARY API //////////////
|
||||
/////////////////////////////////////////
|
||||
|
||||
async listLibraryAgents(): Promise<GraphMeta[]> {
|
||||
return this._get("/library/agents");
|
||||
async listLibraryAgents(
|
||||
paginationToken?: string,
|
||||
): Promise<{ agents: GraphMeta[]; next_token: string | null }> {
|
||||
return this._get(
|
||||
"/library/agents",
|
||||
paginationToken ? { pagination_token: paginationToken } : undefined,
|
||||
);
|
||||
}
|
||||
|
||||
async librarySearchAgent(
|
||||
search: string,
|
||||
paginationToken?: string,
|
||||
filter?:
|
||||
| "most_recent"
|
||||
| "highest_runtime"
|
||||
| "most_runs"
|
||||
| "alphabetical"
|
||||
| "last_modified",
|
||||
): Promise<{ agents: GraphMeta[]; next_token: string | null }> {
|
||||
return this._get("/library/agents/search", {
|
||||
search_term: search,
|
||||
...(paginationToken && { pagination_token: paginationToken }),
|
||||
...(filter && { sort_by: filter }),
|
||||
});
|
||||
}
|
||||
|
||||
async addAgentToLibrary(storeListingVersionId: string): Promise<void> {
|
||||
console.log("Adding to the library");
|
||||
await this._request("POST", `/library/agents/${storeListingVersionId}`);
|
||||
}
|
||||
|
||||
|
@ -206,6 +206,7 @@ export type GraphMeta = {
|
||||
version: number;
|
||||
is_active: boolean;
|
||||
name: string;
|
||||
isCreatedByUser?: boolean;
|
||||
description: string;
|
||||
input_schema: BlockIOObjectSubSchema;
|
||||
output_schema: BlockIOObjectSubSchema;
|
||||
|
@ -19,6 +19,7 @@ const config = {
|
||||
// Include the custom font family
|
||||
neue: ['"PP Neue Montreal TT"', "sans-serif"],
|
||||
poppin: ["var(--font-poppins)"],
|
||||
inter: ["var(--font-inter)"],
|
||||
},
|
||||
colors: {
|
||||
border: "hsl(var(--border))",
|
||||
|
@ -1041,6 +1041,23 @@
|
||||
dependencies:
|
||||
tslib "^2.4.0"
|
||||
|
||||
"@emotion/is-prop-valid@1.2.2":
|
||||
version "1.2.2"
|
||||
resolved "https://registry.yarnpkg.com/@emotion/is-prop-valid/-/is-prop-valid-1.2.2.tgz#d4175076679c6a26faa92b03bb786f9e52612337"
|
||||
integrity sha512-uNsoYd37AFmaCdXlg6EYD1KaPOaRWRByMCYzbKUX4+hhMfrxdVSelShywL4JVaAeM/eHUOSprYBQls+/neX3pw==
|
||||
dependencies:
|
||||
"@emotion/memoize" "^0.8.1"
|
||||
|
||||
"@emotion/memoize@^0.8.1":
|
||||
version "0.8.1"
|
||||
resolved "https://registry.yarnpkg.com/@emotion/memoize/-/memoize-0.8.1.tgz#c1ddb040429c6d21d38cc945fe75c818cfb68e17"
|
||||
integrity sha512-W2P2c/VRW1/1tLox0mVUalvnWXxavmv/Oum2aPsRcoDJuob75FC3Y8FbpfLwUegRcxINtGUMPq0tFCvYNTBXNA==
|
||||
|
||||
"@emotion/unitless@0.8.1":
|
||||
version "0.8.1"
|
||||
resolved "https://registry.yarnpkg.com/@emotion/unitless/-/unitless-0.8.1.tgz#182b5a4704ef8ad91bde93f7a860a88fd92c79a3"
|
||||
integrity sha512-KOEGMu6dmJZtpadb476IsZBclKvILjopjUii3V+7MnXIQCYh8W3NgNcgwo21n9LXZX6EDIKvqfjYxXebDwxKmQ==
|
||||
|
||||
"@esbuild/aix-ppc64@0.24.0":
|
||||
version "0.24.0"
|
||||
resolved "https://registry.yarnpkg.com/@esbuild/aix-ppc64/-/aix-ppc64-0.24.0.tgz#b57697945b50e99007b4c2521507dc613d4a648c"
|
||||
@ -3685,6 +3702,11 @@
|
||||
resolved "https://registry.yarnpkg.com/@types/junit-report-builder/-/junit-report-builder-3.0.2.tgz#17cc131d14ceff59dcf14e5847bd971b96f2cbe0"
|
||||
integrity sha512-R5M+SYhMbwBeQcNXYWNCZkl09vkVfAtcPIaCGdzIkkbeaTrVbGQ7HVgi4s+EmM/M1K4ZuWQH0jGcvMvNePfxYA==
|
||||
|
||||
"@types/lodash@^4.17.13":
|
||||
version "4.17.13"
|
||||
resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.17.13.tgz#786e2d67cfd95e32862143abe7463a7f90c300eb"
|
||||
integrity sha512-lfx+dftrEZcdBPczf9d0Qv0x+j/rfNCMuC6OcfXmO8gkfeNAY88PgKUbvG56whcN23gc27yenwF6oJZXGFpYxg==
|
||||
|
||||
"@types/mdast@^4.0.0":
|
||||
version "4.0.4"
|
||||
resolved "https://registry.yarnpkg.com/@types/mdast/-/mdast-4.0.4.tgz#7ccf72edd2f1aa7dd3437e180c64373585804dd6"
|
||||
@ -3813,6 +3835,11 @@
|
||||
resolved "https://registry.yarnpkg.com/@types/statuses/-/statuses-2.0.5.tgz#f61ab46d5352fd73c863a1ea4e1cef3b0b51ae63"
|
||||
integrity sha512-jmIUGWrAiwu3dZpxntxieC+1n/5c3mjrImkmOSQ2NC5uP6cYO4aAZDdSmRcI5C1oiTmqlZGHC+/NmJrKogbP5A==
|
||||
|
||||
"@types/stylis@4.2.5":
|
||||
version "4.2.5"
|
||||
resolved "https://registry.yarnpkg.com/@types/stylis/-/stylis-4.2.5.tgz#1daa6456f40959d06157698a653a9ab0a70281df"
|
||||
integrity sha512-1Xve+NMN7FWjY14vLoY5tL3BVEQ/n42YLwaqJIPYhotZ9uBHt87VceMwWQpzmdEt2TNXIorIFG+YeCUUW7RInw==
|
||||
|
||||
"@types/tedious@^4.0.14":
|
||||
version "4.0.14"
|
||||
resolved "https://registry.yarnpkg.com/@types/tedious/-/tedious-4.0.14.tgz#868118e7a67808258c05158e9cad89ca58a2aec1"
|
||||
@ -4884,6 +4911,11 @@ camelcase@^6.2.0:
|
||||
resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-6.3.0.tgz#5685b95eb209ac9c0c177467778c9c84df58ba9a"
|
||||
integrity sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==
|
||||
|
||||
camelize@^1.0.0:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/camelize/-/camelize-1.0.1.tgz#89b7e16884056331a35d6b5ad064332c91daa6c3"
|
||||
integrity sha512-dU+Tx2fsypxTgtLoE36npi3UqcjSSMNYfkqgmoEhtZrraP5VWq0K7FkWVTYa8eMPtnU/G2txVsfdCJTn9uzpuQ==
|
||||
|
||||
caniuse-lite@^1.0.30001579, caniuse-lite@^1.0.30001669:
|
||||
version "1.0.30001688"
|
||||
resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001688.tgz#f9d3ede749f083ce0db4c13db9d828adaf2e8d0a"
|
||||
@ -5344,6 +5376,11 @@ crypto-browserify@^3.12.0:
|
||||
randombytes "^2.1.0"
|
||||
randomfill "^1.0.4"
|
||||
|
||||
css-color-keywords@^1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/css-color-keywords/-/css-color-keywords-1.0.0.tgz#fea2616dc676b2962686b3af8dbdbe180b244e05"
|
||||
integrity sha512-FyyrDHZKEjXDpNJYvVsV960FiqQyXc/LlYmsxl2BcdMb2WPx0OGRVgTg55rPSyLSNMqP52R9r8geSp7apN3Ofg==
|
||||
|
||||
css-loader@^6.7.1, css-loader@^6.7.3:
|
||||
version "6.11.0"
|
||||
resolved "https://registry.yarnpkg.com/css-loader/-/css-loader-6.11.0.tgz#33bae3bf6363d0a7c2cf9031c96c744ff54d85ba"
|
||||
@ -5369,6 +5406,15 @@ css-select@^4.1.3:
|
||||
domutils "^2.8.0"
|
||||
nth-check "^2.0.1"
|
||||
|
||||
css-to-react-native@3.2.0:
|
||||
version "3.2.0"
|
||||
resolved "https://registry.yarnpkg.com/css-to-react-native/-/css-to-react-native-3.2.0.tgz#cdd8099f71024e149e4f6fe17a7d46ecd55f1e32"
|
||||
integrity sha512-e8RKaLXMOFii+02mOlqwjbD00KSEKqblnpO9e++1aXS1fPQOpS1YoqdVHBqPjHNoxeF2mimzVqawm2KCbEdtHQ==
|
||||
dependencies:
|
||||
camelize "^1.0.0"
|
||||
css-color-keywords "^1.0.0"
|
||||
postcss-value-parser "^4.0.2"
|
||||
|
||||
css-what@^6.0.1:
|
||||
version "6.1.0"
|
||||
resolved "https://registry.yarnpkg.com/css-what/-/css-what-6.1.0.tgz#fb5effcf76f1ddea2c81bdfaa4de44e79bac70f4"
|
||||
@ -5384,7 +5430,7 @@ cssesc@^3.0.0:
|
||||
resolved "https://registry.yarnpkg.com/cssesc/-/cssesc-3.0.0.tgz#37741919903b868565e1c09ea747445cd18983ee"
|
||||
integrity sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==
|
||||
|
||||
csstype@^3.0.2:
|
||||
csstype@3.1.3, csstype@^3.0.2:
|
||||
version "3.1.3"
|
||||
resolved "https://registry.yarnpkg.com/csstype/-/csstype-3.1.3.tgz#d80ff294d114fb0e6ac500fbf85b60137d7eff81"
|
||||
integrity sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==
|
||||
@ -9643,7 +9689,7 @@ postcss-selector-parser@^7.0.0:
|
||||
cssesc "^3.0.0"
|
||||
util-deprecate "^1.0.2"
|
||||
|
||||
postcss-value-parser@^4.0.0, postcss-value-parser@^4.1.0, postcss-value-parser@^4.2.0:
|
||||
postcss-value-parser@^4.0.0, postcss-value-parser@^4.0.2, postcss-value-parser@^4.1.0, postcss-value-parser@^4.2.0:
|
||||
version "4.2.0"
|
||||
resolved "https://registry.yarnpkg.com/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz#723c09920836ba6d3e5af019f92bc0971c02e514"
|
||||
integrity sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==
|
||||
@ -9657,6 +9703,15 @@ postcss@8.4.31:
|
||||
picocolors "^1.0.0"
|
||||
source-map-js "^1.0.2"
|
||||
|
||||
postcss@8.4.38:
|
||||
version "8.4.38"
|
||||
resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.38.tgz#b387d533baf2054288e337066d81c6bee9db9e0e"
|
||||
integrity sha512-Wglpdk03BSfXkHoQa3b/oulrotAkwrlLDRSOb9D0bN86FdRyE9lppSp33aHNPgBa0JKCoB+drFLZkQoRRYae5A==
|
||||
dependencies:
|
||||
nanoid "^3.3.7"
|
||||
picocolors "^1.0.0"
|
||||
source-map-js "^1.2.0"
|
||||
|
||||
postcss@^8, postcss@^8.2.14, postcss@^8.4.33, postcss@^8.4.38, postcss@^8.4.47:
|
||||
version "8.4.49"
|
||||
resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.49.tgz#4ea479048ab059ab3ae61d082190fabfd994fe19"
|
||||
@ -9932,6 +9987,14 @@ react-docgen@^7.0.0:
|
||||
loose-envify "^1.1.0"
|
||||
scheduler "^0.23.2"
|
||||
|
||||
react-drag-drop-files@^2.4.0:
|
||||
version "2.4.0"
|
||||
resolved "https://registry.yarnpkg.com/react-drag-drop-files/-/react-drag-drop-files-2.4.0.tgz#d4c4f14cf3e76bb7fb2734aed2174e7120e56733"
|
||||
integrity sha512-MGPV3HVVnwXEXq3gQfLtSU3jz5j5jrabvGedokpiSEMoONrDHgYl/NpIOlfsqGQ4zBv1bzzv7qbKURZNOX32PA==
|
||||
dependencies:
|
||||
prop-types "^15.7.2"
|
||||
styled-components "^6.1.11"
|
||||
|
||||
react-hook-form@^7.54.0:
|
||||
version "7.54.1"
|
||||
resolved "https://registry.yarnpkg.com/react-hook-form/-/react-hook-form-7.54.1.tgz#e99c2a55a5e4859fb47a8f55adf66b34d6ac331d"
|
||||
@ -10524,6 +10587,11 @@ sha.js@^2.4.0, sha.js@^2.4.8:
|
||||
inherits "^2.0.1"
|
||||
safe-buffer "^5.0.1"
|
||||
|
||||
shallowequal@1.1.0:
|
||||
version "1.1.0"
|
||||
resolved "https://registry.yarnpkg.com/shallowequal/-/shallowequal-1.1.0.tgz#188d521de95b9087404fd4dcb68b13df0ae4e7f8"
|
||||
integrity sha512-y0m1JoUZSlPAjXVtPPW70aZWfIL/dSP7AFkRnniLCrK/8MDKog3TySTBmckD+RObVxH0v4Tox67+F14PdED2oQ==
|
||||
|
||||
sharp@^0.33.3:
|
||||
version "0.33.5"
|
||||
resolved "https://registry.yarnpkg.com/sharp/-/sharp-0.33.5.tgz#13e0e4130cc309d6a9497596715240b2ec0c594e"
|
||||
@ -10656,7 +10724,7 @@ slash@^5.0.0:
|
||||
resolved "https://registry.yarnpkg.com/slash/-/slash-5.1.0.tgz#be3adddcdf09ac38eebe8dcdc7b1a57a75b095ce"
|
||||
integrity sha512-ZA6oR3T/pEyuqwMgAKT0/hAv8oAXckzbkmR0UkUosQ+Mc4RxGoJkRmwHgHufaenlyAgE1Mxgpdcrf75y6XcnDg==
|
||||
|
||||
source-map-js@^1.0.2, source-map-js@^1.2.1:
|
||||
source-map-js@^1.0.2, source-map-js@^1.2.0, source-map-js@^1.2.1:
|
||||
version "1.2.1"
|
||||
resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-1.2.1.tgz#1ce5650fddd87abc099eda37dcff024c2667ae46"
|
||||
integrity sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==
|
||||
@ -10982,6 +11050,21 @@ style-to-object@^1.0.0:
|
||||
dependencies:
|
||||
inline-style-parser "0.2.4"
|
||||
|
||||
styled-components@^6.1.11:
|
||||
version "6.1.13"
|
||||
resolved "https://registry.yarnpkg.com/styled-components/-/styled-components-6.1.13.tgz#2d777750b773b31469bd79df754a32479e9f475e"
|
||||
integrity sha512-M0+N2xSnAtwcVAQeFEsGWFFxXDftHUD7XrKla06QbpUMmbmtFBMMTcKWvFXtWxuD5qQkB8iU5gk6QASlx2ZRMw==
|
||||
dependencies:
|
||||
"@emotion/is-prop-valid" "1.2.2"
|
||||
"@emotion/unitless" "0.8.1"
|
||||
"@types/stylis" "4.2.5"
|
||||
css-to-react-native "3.2.0"
|
||||
csstype "3.1.3"
|
||||
postcss "8.4.38"
|
||||
shallowequal "1.1.0"
|
||||
stylis "4.3.2"
|
||||
tslib "2.6.2"
|
||||
|
||||
styled-jsx@5.1.1:
|
||||
version "5.1.1"
|
||||
resolved "https://registry.yarnpkg.com/styled-jsx/-/styled-jsx-5.1.1.tgz#839a1c3aaacc4e735fed0781b8619ea5d0009d1f"
|
||||
@ -10996,6 +11079,11 @@ styled-jsx@^5.1.6:
|
||||
dependencies:
|
||||
client-only "0.0.1"
|
||||
|
||||
stylis@4.3.2:
|
||||
version "4.3.2"
|
||||
resolved "https://registry.yarnpkg.com/stylis/-/stylis-4.3.2.tgz#8f76b70777dd53eb669c6f58c997bf0a9972e444"
|
||||
integrity sha512-bhtUjWd/z6ltJiQwg0dUfxEJ+W+jdqQd8TbWLWyeIJHlnsqmGLRFFd8e5mA0AZi/zx90smXRlN66YMTcaSFifg==
|
||||
|
||||
sucrase@^3.35.0:
|
||||
version "3.35.0"
|
||||
resolved "https://registry.yarnpkg.com/sucrase/-/sucrase-3.35.0.tgz#57f17a3d7e19b36d8995f06679d121be914ae263"
|
||||
@ -11245,6 +11333,11 @@ tsconfig-paths@^4.0.0, tsconfig-paths@^4.1.2, tsconfig-paths@^4.2.0:
|
||||
minimist "^1.2.6"
|
||||
strip-bom "^3.0.0"
|
||||
|
||||
tslib@2.6.2:
|
||||
version "2.6.2"
|
||||
resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.6.2.tgz#703ac29425e7b37cd6fd456e92404d46d1f3e4ae"
|
||||
integrity sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==
|
||||
|
||||
tslib@^2.0.0, tslib@^2.0.1, tslib@^2.0.3, tslib@^2.1.0, tslib@^2.4.0:
|
||||
version "2.8.1"
|
||||
resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.8.1.tgz#612efe4ed235d567e8aba5f2a5fab70280ade83f"
|
||||
|
Loading…
Reference in New Issue
Block a user