diff --git a/frontend/package.json b/frontend/package.json index 00ba6c9ba8..06adbec282 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -118,6 +118,7 @@ "tailwindcss-animate": "^1.0.7", "ts-pattern": "^5.8.0", "typescript": "^5.5.4", + "typescript-plugin-css-modules": "^5.2.0", "usehooks-ts": "^3.1.0", "vite": "^5.2.0", "vite-plugin-favicons-inject": "^2.2.0", diff --git a/frontend/src/app/actor-builds-list.tsx b/frontend/src/app/actor-builds-list.tsx index 190b79dfd8..9044c9b8d9 100644 --- a/frontend/src/app/actor-builds-list.tsx +++ b/frontend/src/app/actor-builds-list.tsx @@ -4,8 +4,13 @@ import { Icon, } from "@rivet-gg/icons"; import { useInfiniteQuery } from "@tanstack/react-query"; -import { Link, useNavigate } from "@tanstack/react-router"; +import { + Link, + type LinkComponentProps, + useNavigate, +} from "@tanstack/react-router"; import { Fragment } from "react"; +import { match } from "ts-pattern"; import { Button, cn, Skeleton } from "@/components"; import { ACTORS_PER_PAGE, useManager } from "@/components/actors"; import { VisibilitySensor } from "@/components/visibility-sensor"; @@ -14,7 +19,7 @@ export function ActorBuildsList() { const { data, isLoading, hasNextPage, fetchNextPage, isFetchingNextPage } = useInfiniteQuery(useManager().buildsQueryOptions()); - const navigate = useNavigate({ from: "/" }); + const navigate = useNavigate(); return (
@@ -41,10 +46,15 @@ export function ActorBuildsList() { size="sm" onClick={() => { navigate({ - to: - __APP_TYPE__ === "engine" - ? "/ns/$namespace" - : "/", + to: match(__APP_TYPE__) + .with("engine", () => "/ns/$namespace") + .with( + "cloud", + () => + "/orgs/$organization/projects/$project/ns/$namespace", + ) + .otherwise(() => "/"), + search: (old) => ({ ...old, n: [build.name], diff --git a/frontend/src/app/context-switcher.tsx b/frontend/src/app/context-switcher.tsx new file mode 100644 index 0000000000..b37cc0c12a --- /dev/null +++ b/frontend/src/app/context-switcher.tsx @@ -0,0 +1,558 @@ +import { useClerk, useOrganizationList } from "@clerk/clerk-react"; +import { AvatarImage } from "@radix-ui/react-avatar"; +import { faPlusCircle, Icon } from "@rivet-gg/icons"; +import { useInfiniteQuery, useQuery } from "@tanstack/react-query"; +import { useMatchRoute, useNavigate, useParams } from "@tanstack/react-router"; +import { useState } from "react"; +import { + Avatar, + Button, + Command, + CommandEmpty, + CommandGroup, + CommandInput, + CommandItem, + CommandList, + cn, + Popover, + PopoverContent, + PopoverTrigger, + Skeleton, +} from "@/components"; +import { useManager } from "@/components/actors"; +import { SafeHover } from "@/components/safe-hover"; +import { VisibilitySensor } from "@/components/visibility-sensor"; +import { + namespaceQueryOptions, + organizationQueryOptions, + projectQueryOptions, + projectsQueryOptions, +} from "@/queries/manager-cloud"; + +export function ContextSwitcher() { + const [isOpen, setIsOpen] = useState(false); + + return ( + + + + + + setIsOpen(false)} /> + + + ); +} + +function Breadcrumbs() { + const match = useMatchRoute(); + + const matchNamespace = match({ + to: "/orgs/$organization/projects/$project/ns/$namespace", + fuzzy: true, + }); + if (matchNamespace) { + return ( + <> + + + + + ); + } + + const matchProject = match({ + to: "/orgs/$organization/projects/$project", + }); + + if (matchProject) { + return ( + <> + + + + ); + } + + const matchOrg = match({ + to: "/orgs/$organization", + }); + + if (matchOrg) { + return ; + } +} + +function OrganizationBreadcrumb({ + org, + className, +}: { + org: string; + className?: string; +}) { + const { isLoading, data } = useQuery(organizationQueryOptions({ org })); + if (isLoading) { + return ; + } + + return ( +
+ + + + {data?.name} +
+ ); +} + +function ProjectBreadcrumb({ + project, + className, +}: { + project: string; + className?: string; +}) { + const { isLoading, data } = useQuery(projectQueryOptions({ project })); + if (isLoading) { + return ; + } + + return {data?.name}; +} + +function NamespaceBreadcrumb({ + namespace, + project, + className, +}: { + namespace: string; + project: string; + className?: string; +}) { + const { isLoading, data } = useQuery( + namespaceQueryOptions({ project, namespace }), + ); + if (isLoading) { + return ; + } + + return {data?.name}; +} + +function Content({ onClose }: { onClose?: () => void }) { + const params = useParams({ strict: false }); + const { + userMemberships: { + data: userMemberships = [], + isLoading, + hasNextPage, + fetchNext, + }, + } = useOrganizationList({ + userMemberships: { + infinite: true, + }, + }); + const clerk = useClerk(); + + const [currentOrgHover, setCurrentOrgHover] = useState( + params.organization || null, + ); + + const [currentProjectHover, setCurrentProjectHover] = useState< + string | null + >(params.project || null); + + const navigate = useNavigate(); + + return ( +
+
+ + + + + {!isLoading ? ( + + No organizations found. + + + ) : null} + {userMemberships.map((membership) => ( + + { + clerk.setActive({ + organization: + membership.organization.id, + }); + navigate({ + to: "/orgs/$organization", + params: { + organization: + membership.organization + .id, + }, + }); + onClose?.(); + }} + value={membership.organization.id} + onMouseEnter={() => { + setCurrentOrgHover( + membership.organization.id, + ); + setCurrentProjectHover(null); + }} + keywords={[ + membership.organization.name, + ]} + className="static cursor-pointer" + > + {membership.organization.name} + + + ))} + {isLoading ? ( + <> + + + + + + + ) : null} + { + setCurrentOrgHover(null); + setCurrentProjectHover(null); + }} + onFocus={() => { + setCurrentOrgHover(null); + setCurrentProjectHover(null); + }} + onSelect={() => { + clerk.openCreateOrganization(); + }} + > + + Create Organization + + + {hasNextPage ? ( + + ) : null} + + + +
+ {currentOrgHover ? ( + + ) : null} + {currentProjectHover && currentOrgHover ? ( + + ) : null} +
+ ); +} + +function ProjectList({ + organization, + onClose, + onHover, +}: { + organization: string; + onClose?: () => void; + onHover?: (project: string | null) => void; +}) { + const { data, hasNextPage, isLoading, isFetchingNextPage, fetchNextPage } = + useInfiniteQuery(projectsQueryOptions({ organization: organization })); + const navigate = useNavigate(); + const clerk = useClerk(); + const project = useParams({ + strict: false, + select(params) { + return params.project; + }, + }); + + return ( +
+ + + + + {!isLoading ? ( + + No projects found. + + + ) : null} + + {data?.map((project) => ( + + { + clerk.setActive({ + organization, + }); + navigate({ + to: "/orgs/$organization/projects/$project", + params: { + organization: organization, + project: project.name, + }, + }); + onClose?.(); + }} + onMouseEnter={() => { + onHover?.(project.name); + }} + onFocus={() => { + onHover?.(project.name); + }} + > + + {project.displayName} + + + + ))} + {isLoading || isFetchingNextPage ? ( + <> + + + + + + + ) : null} + + { + onHover?.(null); + navigate({ + to: ".", + search: (old) => ({ + ...old, + modal: "create-project", + }), + }); + }} + > + + Create Project + + + {hasNextPage ? ( + + ) : null} + + + +
+ ); +} + +function ListItemSkeleton() { + return ( +
+ +
+ ); +} + +function NamespaceList({ + organization, + project, + onClose, +}: { + organization: string; + project: string; + onClose?: () => void; +}) { + const { data, hasNextPage, isLoading, isFetchingNextPage, fetchNextPage } = + useInfiniteQuery(useManager().projectNamespacesQueryOptions(project)); + const navigate = useNavigate(); + const clerk = useClerk(); + const namespace = useParams({ + strict: false, + select(params) { + return params.namespace; + }, + }); + + return ( +
+ + + + + {!isLoading ? ( + + No namespaces found. + + + ) : null} + + {data?.map((namespace) => ( + + { + clerk.setActive({ + organization, + }); + navigate({ + to: "/orgs/$organization/projects/$project/ns/$namespace", + params: { + organization: organization, + project: project, + namespace: namespace.name, + }, + }); + onClose?.(); + }} + > + + {namespace.displayName} + + + + ))} + {isLoading || isFetchingNextPage ? ( + <> + + + + + + + ) : null} + + { + navigate({ + to: ".", + search: (old) => ({ + ...old, + modal: "create-ns", + }), + }); + }} + > + + Create Namespace + + + {hasNextPage ? ( + + ) : null} + + + +
+ ); +} diff --git a/frontend/src/app/dialogs/create-namespace-dialog.tsx b/frontend/src/app/dialogs/create-namespace-dialog.tsx index f0e890d2c9..d5b17f46f2 100644 --- a/frontend/src/app/dialogs/create-namespace-dialog.tsx +++ b/frontend/src/app/dialogs/create-namespace-dialog.tsx @@ -1,41 +1,60 @@ import { useMutation, useQueryClient } from "@tanstack/react-query"; -import { useNavigate } from "@tanstack/react-router"; +import { useNavigate, useParams } from "@tanstack/react-router"; import * as CreateNamespaceForm from "@/app/forms/create-namespace-form"; import { DialogFooter, DialogHeader, DialogTitle, Flex } from "@/components"; +import { useManager } from "@/components/actors"; import { convertStringToId } from "@/lib/utils"; -import { - managerClient, - namespacesQueryOptions, -} from "@/queries/manager-engine"; -export default function CreateNamespacesDialogContent() { +const useCreateNamespace = () => { const queryClient = useQueryClient(); const navigate = useNavigate(); - const { mutateAsync } = useMutation({ - mutationFn: async (data: { displayName: string; nameId: string }) => { - const response = await managerClient.namespaces.create({ - displayName: data.displayName, - name: data.nameId, - }); - - return response; - }, - onSuccess: async (data) => { - await queryClient.invalidateQueries(namespacesQueryOptions()); - navigate({ - to: "/ns/$namespace", - params: { namespace: data.namespace.name }, - }); - }, - }); + const params = useParams({ strict: false }); + + const manager = useManager(); + + return useMutation( + manager.createNamespaceMutationOptions({ + onSuccess: async (data) => { + // Invalidate all queries to ensure fresh data + await queryClient.invalidateQueries( + manager.namespacesQueryOptions(), + ); + + if (__APP_TYPE__ === "cloud") { + if (!params.project || !params.organization) { + throw new Error("Missing required parameters"); + } + // Navigate to the newly created namespace + navigate({ + to: "/orgs/$organization/projects/$project/ns/$namespace", + params: { + organization: params.organization, + project: params.project, + namespace: data.name, + }, + }); + return; + } + + navigate({ + to: "/ns/$namespace", + params: { namespace: data.name }, + }); + }, + }), + ); +}; + +export default function CreateNamespacesDialogContent() { + const { mutateAsync } = useCreateNamespace(); return ( { await mutateAsync({ displayName: values.name, - nameId: values.slug || convertStringToId(values.name), + name: values.slug || convertStringToId(values.name), }); }} defaultValues={{ name: "", slug: "" }} diff --git a/frontend/src/app/dialogs/create-project-dialog.tsx b/frontend/src/app/dialogs/create-project-dialog.tsx index d4cbb8a252..213a4901f5 100644 --- a/frontend/src/app/dialogs/create-project-dialog.tsx +++ b/frontend/src/app/dialogs/create-project-dialog.tsx @@ -1,5 +1,5 @@ import { useMutation, useQueryClient } from "@tanstack/react-query"; -import { useNavigate } from "@tanstack/react-router"; +import { useNavigate, useParams } from "@tanstack/react-router"; import * as CreateProjectForm from "@/app/forms/create-project-form"; import { DialogFooter, DialogHeader, DialogTitle, Flex } from "@/components"; import { convertStringToId } from "@/lib/utils"; @@ -7,21 +7,22 @@ import { createProjectMutationOptions, projectsQueryOptions, } from "@/queries/manager-cloud"; -import { - managerClient, - namespacesQueryOptions, -} from "@/queries/manager-engine"; export default function CreateProjectDialogContent() { const queryClient = useQueryClient(); const navigate = useNavigate(); + const params = useParams({ strict: false }); const { mutateAsync } = useMutation( createProjectMutationOptions({ onSuccess: async (values) => { - await queryClient.invalidateQueries({ - ...projectsQueryOptions(), - }); + if (params.organization) { + await queryClient.invalidateQueries({ + ...projectsQueryOptions({ + organization: params.organization, + }), + }); + } navigate({ to: "/orgs/$organization/projects/$project", params: { diff --git a/frontend/src/app/layout.tsx b/frontend/src/app/layout.tsx index 37ffabec4d..61d8da5dbc 100644 --- a/frontend/src/app/layout.tsx +++ b/frontend/src/app/layout.tsx @@ -1,4 +1,4 @@ -import { OrganizationSwitcher, useClerk } from "@clerk/clerk-react"; +import { useClerk } from "@clerk/clerk-react"; import { faArrowUpRight, faLink, @@ -7,12 +7,7 @@ import { Icon, } from "@rivet-gg/icons"; import { useQuery } from "@tanstack/react-query"; -import { - Link, - useMatch, - useMatchRoute, - useNavigate, -} from "@tanstack/react-router"; +import { Link, useMatchRoute, useNavigate } from "@tanstack/react-router"; import { type ComponentProps, createContext, @@ -28,6 +23,8 @@ import { import type { ImperativePanelGroupHandle } from "react-resizable-panels"; import { match } from "ts-pattern"; import { + Avatar, + AvatarImage, Button, type ButtonProps, cn, @@ -44,6 +41,7 @@ import type { HeaderLinkProps } from "@/components/header/header-link"; import { ensureTrailingSlash } from "@/lib/utils"; import type { NamespaceNameId } from "@/queries/manager-engine"; import { ActorBuildsList } from "./actor-builds-list"; +import { ContextSwitcher } from "./context-switcher"; import { useInspectorCredentials } from "./credentials-context"; import { NamespaceSelect } from "./namespace-select"; @@ -176,6 +174,9 @@ const Sidebar = ({
+ {match(__APP_TYPE__) + .with("cloud", () => ) + .otherwise(() => null)} - + -
- - Projects - - { - clerk.openUserProfile({ - __experimental_startPath: "/billing", - }); - }} - > - Billing - - { - clerk.openUserProfile(); - }} - > - Settings - -
+
); } + +function CloudSidebarContent() { + const match = useMatchRoute(); + + const clerk = useClerk(); + + const matchNamespace = match({ + to: "/orgs/$organization/projects/$project/ns/$namespace", + }); + + if (matchNamespace) { + return ( +
+ + Connect + +
+ + Instances + + +
+
+ ); + } + + const matchOrganization = match({ + to: "/orgs/$organization", + }); + + if (matchOrganization) { + return ( +
+ + Projects + + { + clerk.openOrganizationProfile({ + __experimental_startPath: "/organization-billing", + }); + }} + > + Billing + + { + clerk.openOrganizationProfile(); + }} + > + Settings + +
+ ); + } +} + +function CloudSidebarFooter() { + const clerk = useClerk(); + + return ( + + ); +} diff --git a/frontend/src/app/namespace-select.tsx b/frontend/src/app/namespace-select.tsx index 843a4e798a..91ac6b70b6 100644 --- a/frontend/src/app/namespace-select.tsx +++ b/frontend/src/app/namespace-select.tsx @@ -1,7 +1,7 @@ import { SelectGroup } from "@radix-ui/react-select"; import { faCirclePlus, faSpinnerThird, Icon } from "@rivet-gg/icons"; import { useInfiniteQuery } from "@tanstack/react-query"; -import { type ComponentProps, useCallback, useEffect, useRef } from "react"; +import { type ComponentProps, useCallback } from "react"; import { Flex, Select, @@ -12,8 +12,8 @@ import { SelectTrigger, SelectValue, } from "@/components"; +import { useManager } from "@/components/actors"; import { VisibilitySensor } from "@/components/visibility-sensor"; -import { namespacesQueryOptions } from "@/queries/manager-engine"; interface NamespaceSelectProps extends ComponentProps { showCreate?: boolean; @@ -31,7 +31,7 @@ export function NamespaceSelect({ ...props }: NamespaceSelectProps) { const { data, hasNextPage, fetchNextPage, isFetchingNextPage } = - useInfiniteQuery(namespacesQueryOptions()); + useInfiniteQuery(useManager().namespacesQueryOptions()); const handleValueChange = useCallback( (value: string) => { @@ -53,10 +53,7 @@ export function NamespaceSelect({ Namespaces {data?.map((namespace) => ( - + {namespace.displayName} ))} diff --git a/frontend/src/app/namespaces-page.tsx b/frontend/src/app/namespaces-page.tsx new file mode 100644 index 0000000000..0f46779066 --- /dev/null +++ b/frontend/src/app/namespaces-page.tsx @@ -0,0 +1,186 @@ +import { faExternalLink, faPlus, faRefresh, Icon } from "@rivet-gg/icons"; +import { useInfiniteQuery } from "@tanstack/react-query"; +import { Link, type LinkComponentProps } from "@tanstack/react-router"; +import { match } from "ts-pattern"; +import { + Button, + DiscreteCopyButton, + H1, + Skeleton, + Table, + TableBody, + TableCell, + TableHead, + TableHeader, + TableRow, + Text, + WithTooltip, +} from "@/components"; +import { useManager } from "@/components/actors"; + +export function NamespacesPage({ from }: { from: LinkComponentProps["from"] }) { + const { + data: namespaces, + isRefetching, + hasNextPage, + fetchNextPage, + isLoading, + refetch, + } = useInfiniteQuery(useManager().namespacesQueryOptions()); + + return ( +
+
+

Namespaces

+
+ + ({ + ...old, + modal: "create-ns", + })} + > + + + + } + /> + refetch()} + > + + + } + /> +
+
+ +
+ +
+
+
+ + + + ID + Name + Created + + + + + {!isLoading && namespaces?.length === 0 ? ( + + + + There's no namespaces matching + criteria. + + + + ) : null} + {isLoading ? ( + <> + + + + + + + + + + ) : null} + {namespaces?.map((namespace) => ( + + + + {namespace.name} + + + + + {namespace.displayName} + + + + {new Date( + namespace.createdAt, + ).toLocaleString()} + + + + + + ))} + + {!isLoading && hasNextPage ? ( + + + + + + ) : null} + +
+
+
+
+
+ ); +} + +function RowSkeleton() { + return ( + + + + + + + + + + + + ); +} diff --git a/frontend/src/components/actors/guard-connectable-inspector.tsx b/frontend/src/components/actors/guard-connectable-inspector.tsx index 3305f4f6f0..52226eb5a4 100644 --- a/frontend/src/components/actors/guard-connectable-inspector.tsx +++ b/frontend/src/components/actors/guard-connectable-inspector.tsx @@ -135,10 +135,14 @@ function useActorRunner({ actorId }: { actorId: ActorId }) { ); const match = useMatch({ - from: "/_layout/ns/$namespace", + from: + __APP_TYPE__ === "engine" + ? "/_layout/ns/$namespace" + : "/_layout/orgs/$organization/projects/$project/ns/$namespace/", + shouldThrow: false, }); - if (!match.params.namespace || !actor.runner) { + if (!match?.params.namespace || !actor.runner) { throw new Error("Actor is missing required fields"); } diff --git a/frontend/src/components/actors/manager-context.tsx b/frontend/src/components/actors/manager-context.tsx index fef73597ec..6ee5e10fd3 100644 --- a/frontend/src/components/actors/manager-context.tsx +++ b/frontend/src/components/actors/manager-context.tsx @@ -16,6 +16,7 @@ import { type Build, type CrashPolicy, getActorStatus, + type Namespace, type Region, } from "./queries"; @@ -51,12 +52,13 @@ export type ActorQueryOptions = z.infer; export const ACTORS_PER_PAGE = 10; type PaginatedResponse = { - pagination: { cursor?: string | null }; + pagination: { cursor?: string }; } & Record; type PaginatedActorResponse = PaginatedResponse; type PaginatedBuildsResponse = PaginatedResponse; type PaginatedRegionsResponse = PaginatedResponse; +type PaginatedNamespacesResponse = PaginatedResponse; type CreateActor = Omit & { runnerNameSelector: string; @@ -115,6 +117,30 @@ const defaultContext = { }); }, + namespacesQueryOptions() { + return infiniteQueryOptions({ + queryKey: ["namespaces"], + initialPageParam: undefined as string | undefined, + queryFn: async () => { + return {} as PaginatedNamespacesResponse; + }, + getNextPageParam: () => undefined, + select: (data) => data.pages.flatMap((page) => page.namespaces), + }); + }, + + projectNamespacesQueryOptions(projectId: string) { + return infiniteQueryOptions({ + queryKey: ["namespaces", projectId], + initialPageParam: undefined as string | undefined, + queryFn: async () => { + return {} as PaginatedNamespacesResponse; + }, + getNextPageParam: () => undefined, + select: (data) => data.pages.flatMap((page) => page.namespaces), + }); + }, + buildsCountQueryOptions() { return infiniteQueryOptions({ ...this.buildsQueryOptions(), @@ -278,10 +304,9 @@ const defaultContext = { actorRuntimeQueryOptions(actorId: ActorId) { return queryOptions({ ...this.actorQueryOptions(actorId), - select: ({ runtime, lifecycle, resources, tags }) => ({ + select: ({ runtime, lifecycle, tags }) => ({ runtime, lifecycle, - resources, tags, }), }); @@ -351,6 +376,28 @@ const defaultContext = { }, }; }, + createNamespaceMutationOptions( + opts: Pick< + MutationOptions< + Namespace, + Error, + { name: string; displayName: string } + >, + "onSuccess" + >, + ) { + return { + ...opts, + mutationKey: ["createNamespace"], + mutationFn: async (_) => { + return {} as Namespace; + }, + } satisfies MutationOptions< + Namespace, + Error, + { name: string; displayName: string } + >; + }, }; export type ManagerContext = typeof defaultContext; diff --git a/frontend/src/components/actors/queries/index.ts b/frontend/src/components/actors/queries/index.ts index 693c8993a0..ca557fc41e 100644 --- a/frontend/src/components/actors/queries/index.ts +++ b/frontend/src/components/actors/queries/index.ts @@ -86,6 +86,13 @@ export type Region = { name: string; }; +export type Namespace = { + id: string; + name: string; + displayName: string; + createdAt: string; +}; + export * from "./actor"; export type ActorStatus = diff --git a/frontend/src/components/safe-hover.tsx b/frontend/src/components/safe-hover.tsx new file mode 100644 index 0000000000..6c7a87071f --- /dev/null +++ b/frontend/src/components/safe-hover.tsx @@ -0,0 +1,45 @@ +import { Slot } from "@radix-ui/react-slot"; +import { type MouseEventHandler, useCallback } from "react"; +import * as styles from "./styles/safe-hover.module.css"; + +export function SafeHover({ + children, + offset = 0, +}: { + children: React.ReactNode; + offset?: number; +}) { + const onMouseEnter: MouseEventHandler = useCallback( + (e) => { + const el = e.currentTarget as HTMLElement; + const parentRect = ( + el.parentNode as HTMLElement + )?.getBoundingClientRect(); + el.style.setProperty( + "--safe-y0", + `${el.getBoundingClientRect().top - parentRect.top + offset}px`, + ); + + el.style.setProperty( + "--safe-y1", + `${el.getBoundingClientRect().bottom - parentRect.top + offset}px`, + ); + }, + [offset], + ); + + const onMouseMove: MouseEventHandler = useCallback((e) => { + const el = e.currentTarget as HTMLElement; + el.style.setProperty("--safe-x", `${e.nativeEvent.offsetX}px`); + }, []); + + return ( + + {children} + + ); +} diff --git a/frontend/src/components/styles/safe-hover.module.css b/frontend/src/components/styles/safe-hover.module.css new file mode 100644 index 0000000000..8878c572b7 --- /dev/null +++ b/frontend/src/components/styles/safe-hover.module.css @@ -0,0 +1,23 @@ +.safeHover { + @apply static; +} + +.safeHover:hover::before { + @apply absolute inset-0; + content: ''; + clip-path: polygon(var(--safe-x, 0) var(--safe-y0, 0), 100% 0, 100% 100%, var(--safe-x, 0) var(--safe-y1, 0)); + pointer-events: none; + z-index: 1; + + animation: 0s .3s forwards delayed-pointer-events; +} + +@keyframes delayed-pointer-events { + 0% { + pointer-events: none + } + + to { + pointer-events: auto + } +} \ No newline at end of file diff --git a/frontend/src/queries/manager-cloud.ts b/frontend/src/queries/manager-cloud.ts index ffefdc0fec..047da7fb03 100644 --- a/frontend/src/queries/manager-cloud.ts +++ b/frontend/src/queries/manager-cloud.ts @@ -24,6 +24,7 @@ import { } from "@/components/actors/manager-context"; import { clerk } from "@/lib/auth"; import { cloudEnv } from "@/lib/env"; +import { queryClient } from "./global"; const client = new RivetClient({ baseUrl: () => getConfig().apiUrl, @@ -34,19 +35,54 @@ const cloudClient = new CloudRivetClient({ baseUrl: () => cloudEnv().VITE_APP_CLOUD_API_URL, environment: "", token: async () => { - console.log(await clerk.session?.getToken()); return (await clerk.session?.getToken()) || ""; }, }); -export { client as managerClient }; +export { cloudClient as managerCloudClient }; export const createCloudManagerContext = ({ namespace, + project, }: { namespace: string; + project: string; }) => { const def = createDefaultManagerContext(); + + const namespacesQueryOptions = (project: string) => { + return infiniteQueryOptions({ + queryKey: [project, "namespaces"], + initialPageParam: undefined as string | undefined, + queryFn: async ({ pageParam, signal: abortSignal }) => { + const data = await cloudClient.namespaces.list( + project, + { + limit: ACTORS_PER_PAGE, + cursor: pageParam ?? undefined, + }, + { abortSignal }, + ); + return { + pagination: data.pagination, + namespaces: data.namespaces.map((ns) => ({ + id: ns.id, + name: ns.name, + displayName: ns.displayName, + createdAt: ns.createdAt, + })), + }; + }, + getNextPageParam: (lastPage) => { + if (lastPage.namespaces.length < ACTORS_PER_PAGE) { + return undefined; + } + return lastPage.pagination.cursor; + }, + select: (data) => data.pages.flatMap((page) => page.namespaces), + }); + }; + return { ...def, features: { @@ -169,9 +205,6 @@ export const createCloudManagerContext = ({ return { ...data, - pagination: { - cursor: data.pagination.cursor || null, - }, actors: data.actors.map((actor) => transformActor(actor), ), @@ -245,19 +278,35 @@ export const createCloudManagerContext = ({ }, }; }, + namespacesQueryOptions() { + return namespacesQueryOptions(project); + }, + projectNamespacesQueryOptions(projectId) { + return namespacesQueryOptions(projectId); + }, + createNamespaceMutationOptions(opts) { + return { + ...opts, + mutationKey: [project, "namespaces"], + mutationFn: async (data) => { + return await cloudClient.namespaces.create(project, data); + }, + }; + }, } satisfies ManagerContext; }; export const NamespaceNameId = z.string().brand(); export type NamespaceNameId = z.infer; -export const projectsQueryOptions = ({ orgId }: { orgId: string }) => { +export const projectsQueryOptions = (opts: { organization: string }) => { return infiniteQueryOptions({ - queryKey: [orgId, "projects"], + queryKey: [opts, "projects"], initialPageParam: undefined as string | undefined, queryFn: async ({ signal: abortSignal, pageParam }) => { const data = await cloudClient.projects.list( { + org: opts.organization, cursor: pageParam ?? undefined, limit: ACTORS_PER_PAGE, }, @@ -277,6 +326,28 @@ export const projectsQueryOptions = ({ orgId }: { orgId: string }) => { }); }; +export const projectQueryOptions = (opts: { project: string }) => { + return queryOptions({ + queryKey: ["project", opts.project], + enabled: !!opts.project, + queryFn: async ({ signal: abortSignal }) => { + const data = await cloudClient.projects.get(opts.project, { + abortSignal, + }); + return data; + }, + }); +}; + +export const organizationQueryOptions = (opts: { org: string }) => { + return queryOptions({ + queryKey: ["organization", opts.org], + queryFn: async () => { + return clerk.getOrganization(opts.org); + }, + }); +}; + export const runnersQueryOptions = (opts: { namespace: NamespaceNameId }) => { return infiniteQueryOptions({ queryKey: [opts.namespace, "runners"], @@ -351,42 +422,26 @@ export const runnerNamesQueryOptions = (opts: { }); }; -export const namespacesQueryOptions = () => { - return infiniteQueryOptions({ - queryKey: ["namespaces"], - initialPageParam: undefined as string | undefined, - queryFn: async ({ pageParam, signal: abortSignal }) => { - const data = await client.namespaces.list( - { - limit: ACTORS_PER_PAGE, - cursor: pageParam ?? undefined, - }, - { abortSignal }, - ); - return data; - }, - getNextPageParam: (lastPage) => { - if (lastPage.namespaces.length < ACTORS_PER_PAGE) { - return undefined; - } - return lastPage.pagination.cursor; - }, - select: (data) => data.pages.flatMap((page) => page.namespaces), - }); -}; - -export const namespaceQueryOptions = ( - namespace: NamespaceNameId | undefined, -) => { +export const namespaceQueryOptions = ({ + project, + namespace, +}: { + project: string; + namespace: string; +}) => { return queryOptions({ - queryKey: ["namespace", namespace], + queryKey: [project, "namespace", namespace], enabled: !!namespace, queryFn: namespace ? async ({ signal: abortSignal }) => { - const data = await client.namespaces.get(namespace, { - abortSignal, - }); - return data.namespace; + const data = await cloudClient.namespaces.get( + project, + namespace, + { + abortSignal, + }, + ); + return data; } : skipToken, }); diff --git a/frontend/src/queries/manager-engine.ts b/frontend/src/queries/manager-engine.ts index 0c9864d703..925309d3f3 100644 --- a/frontend/src/queries/manager-engine.ts +++ b/frontend/src/queries/manager-engine.ts @@ -184,9 +184,6 @@ export const createEngineManagerContext = ({ return { ...data, - pagination: { - cursor: data.pagination.cursor || null, - }, actors: data.actors.map((actor) => transformActor(actor), ), @@ -280,6 +277,59 @@ export const createEngineManagerContext = ({ }, }; }, + namespacesQueryOptions() { + return infiniteQueryOptions({ + queryKey: ["namespaces"], + initialPageParam: undefined as string | undefined, + queryFn: async ({ pageParam, signal: abortSignal }) => { + const data = await client.namespaces.list( + { + limit: ACTORS_PER_PAGE, + cursor: pageParam ?? undefined, + }, + { abortSignal }, + ); + return { + ...data, + namespaces: data.namespaces.map((ns) => ({ + id: ns.namespaceId, + displayName: ns.displayName, + name: ns.name, + createdAt: new Date(ns.createTs).toISOString(), + })), + }; + }, + getNextPageParam: (lastPage) => { + if (lastPage.namespaces.length < ACTORS_PER_PAGE) { + return undefined; + } + return lastPage.pagination.cursor; + }, + select: (data) => data.pages.flatMap((page) => page.namespaces), + retry: shouldRetryAllExpect403, + meta: { + mightRequireAuth: true, + }, + }); + }, + createNamespaceMutationOptions(opts) { + return { + ...opts, + mutationKey: ["namespaces"], + mutationFn: async (data) => { + const response = await client.namespaces.create(data); + + return { + id: response.namespace.namespaceId, + name: response.namespace.name, + displayName: response.namespace.displayName, + createdAt: new Date( + response.namespace.createTs, + ).toISOString(), + }; + }, + }; + }, } satisfies ManagerContext; }; @@ -402,35 +452,6 @@ export const runnerNamesQueryOptions = (opts: { }); }; -export const namespacesQueryOptions = () => { - return infiniteQueryOptions({ - queryKey: ["namespaces"], - initialPageParam: undefined as string | undefined, - queryFn: async ({ pageParam, signal: abortSignal }) => { - const data = await client.namespaces.list( - { - limit: ACTORS_PER_PAGE, - cursor: pageParam ?? undefined, - }, - { abortSignal }, - ); - return data; - }, - getNextPageParam: (lastPage) => { - if (lastPage?.namespaces?.length < ACTORS_PER_PAGE) { - return undefined; - } - return lastPage.pagination.cursor; - }, - select: (data) => data.pages.flatMap((page) => page.namespaces), - throwOnError: noThrow, - retry: shouldRetryAllExpect403, - meta: { - mightRequireAuth: true, - }, - }); -}; - export const namespaceQueryOptions = ( namespace: NamespaceNameId | undefined, ) => { diff --git a/frontend/src/routeTree.gen.ts b/frontend/src/routeTree.gen.ts index 56afa0cb05..00a2f83986 100644 --- a/frontend/src/routeTree.gen.ts +++ b/frontend/src/routeTree.gen.ts @@ -17,6 +17,8 @@ import { Route as LayoutOrgsOrganizationIndexRouteImport } from './routes/_layou import { Route as LayoutNsNamespaceIndexRouteImport } from './routes/_layout/ns.$namespace/index' import { Route as LayoutNsNamespaceRunnersRouteImport } from './routes/_layout/ns.$namespace/runners' import { Route as LayoutOrgsOrganizationProjectsProjectIndexRouteImport } from './routes/_layout/orgs.$organization/projects.$project/index' +import { Route as LayoutOrgsOrganizationProjectsProjectNsNamespaceIndexRouteImport } from './routes/_layout/orgs.$organization/projects.$project/ns.$namespace/index' +import { Route as LayoutOrgsOrganizationProjectsProjectNsNamespaceConnectRouteImport } from './routes/_layout/orgs.$organization/projects.$project/ns.$namespace/connect' const LayoutRoute = LayoutRouteImport.update({ id: '/_layout', @@ -60,6 +62,18 @@ const LayoutOrgsOrganizationProjectsProjectIndexRoute = path: '/orgs/$organization/projects/$project/', getParentRoute: () => LayoutRoute, } as any) +const LayoutOrgsOrganizationProjectsProjectNsNamespaceIndexRoute = + LayoutOrgsOrganizationProjectsProjectNsNamespaceIndexRouteImport.update({ + id: '/orgs/$organization/projects/$project/ns/$namespace/', + path: '/orgs/$organization/projects/$project/ns/$namespace/', + getParentRoute: () => LayoutRoute, + } as any) +const LayoutOrgsOrganizationProjectsProjectNsNamespaceConnectRoute = + LayoutOrgsOrganizationProjectsProjectNsNamespaceConnectRouteImport.update({ + id: '/orgs/$organization/projects/$project/ns/$namespace/connect', + path: '/orgs/$organization/projects/$project/ns/$namespace/connect', + getParentRoute: () => LayoutRoute, + } as any) export interface FileRoutesByFullPath { '/namespaces': typeof LayoutNamespacesRoute @@ -69,6 +83,8 @@ export interface FileRoutesByFullPath { '/ns/$namespace/': typeof LayoutNsNamespaceIndexRoute '/orgs/$organization': typeof LayoutOrgsOrganizationIndexRoute '/orgs/$organization/projects/$project': typeof LayoutOrgsOrganizationProjectsProjectIndexRoute + '/orgs/$organization/projects/$project/ns/$namespace/connect': typeof LayoutOrgsOrganizationProjectsProjectNsNamespaceConnectRoute + '/orgs/$organization/projects/$project/ns/$namespace': typeof LayoutOrgsOrganizationProjectsProjectNsNamespaceIndexRoute } export interface FileRoutesByTo { '/namespaces': typeof LayoutNamespacesRoute @@ -77,6 +93,8 @@ export interface FileRoutesByTo { '/ns/$namespace': typeof LayoutNsNamespaceIndexRoute '/orgs/$organization': typeof LayoutOrgsOrganizationIndexRoute '/orgs/$organization/projects/$project': typeof LayoutOrgsOrganizationProjectsProjectIndexRoute + '/orgs/$organization/projects/$project/ns/$namespace/connect': typeof LayoutOrgsOrganizationProjectsProjectNsNamespaceConnectRoute + '/orgs/$organization/projects/$project/ns/$namespace': typeof LayoutOrgsOrganizationProjectsProjectNsNamespaceIndexRoute } export interface FileRoutesById { __root__: typeof rootRouteImport @@ -88,6 +106,8 @@ export interface FileRoutesById { '/_layout/ns/$namespace/': typeof LayoutNsNamespaceIndexRoute '/_layout/orgs/$organization/': typeof LayoutOrgsOrganizationIndexRoute '/_layout/orgs/$organization/projects/$project/': typeof LayoutOrgsOrganizationProjectsProjectIndexRoute + '/_layout/orgs/$organization/projects/$project/ns/$namespace/connect': typeof LayoutOrgsOrganizationProjectsProjectNsNamespaceConnectRoute + '/_layout/orgs/$organization/projects/$project/ns/$namespace/': typeof LayoutOrgsOrganizationProjectsProjectNsNamespaceIndexRoute } export interface FileRouteTypes { fileRoutesByFullPath: FileRoutesByFullPath @@ -99,6 +119,8 @@ export interface FileRouteTypes { | '/ns/$namespace/' | '/orgs/$organization' | '/orgs/$organization/projects/$project' + | '/orgs/$organization/projects/$project/ns/$namespace/connect' + | '/orgs/$organization/projects/$project/ns/$namespace' fileRoutesByTo: FileRoutesByTo to: | '/namespaces' @@ -107,6 +129,8 @@ export interface FileRouteTypes { | '/ns/$namespace' | '/orgs/$organization' | '/orgs/$organization/projects/$project' + | '/orgs/$organization/projects/$project/ns/$namespace/connect' + | '/orgs/$organization/projects/$project/ns/$namespace' id: | '__root__' | '/_layout' @@ -117,6 +141,8 @@ export interface FileRouteTypes { | '/_layout/ns/$namespace/' | '/_layout/orgs/$organization/' | '/_layout/orgs/$organization/projects/$project/' + | '/_layout/orgs/$organization/projects/$project/ns/$namespace/connect' + | '/_layout/orgs/$organization/projects/$project/ns/$namespace/' fileRoutesById: FileRoutesById } export interface RootRouteChildren { @@ -181,6 +207,20 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof LayoutOrgsOrganizationProjectsProjectIndexRouteImport parentRoute: typeof LayoutRoute } + '/_layout/orgs/$organization/projects/$project/ns/$namespace/': { + id: '/_layout/orgs/$organization/projects/$project/ns/$namespace/' + path: '/orgs/$organization/projects/$project/ns/$namespace' + fullPath: '/orgs/$organization/projects/$project/ns/$namespace' + preLoaderRoute: typeof LayoutOrgsOrganizationProjectsProjectNsNamespaceIndexRouteImport + parentRoute: typeof LayoutRoute + } + '/_layout/orgs/$organization/projects/$project/ns/$namespace/connect': { + id: '/_layout/orgs/$organization/projects/$project/ns/$namespace/connect' + path: '/orgs/$organization/projects/$project/ns/$namespace/connect' + fullPath: '/orgs/$organization/projects/$project/ns/$namespace/connect' + preLoaderRoute: typeof LayoutOrgsOrganizationProjectsProjectNsNamespaceConnectRouteImport + parentRoute: typeof LayoutRoute + } } } @@ -203,6 +243,8 @@ interface LayoutRouteChildren { LayoutNsNamespaceRoute: typeof LayoutNsNamespaceRouteWithChildren LayoutOrgsOrganizationIndexRoute: typeof LayoutOrgsOrganizationIndexRoute LayoutOrgsOrganizationProjectsProjectIndexRoute: typeof LayoutOrgsOrganizationProjectsProjectIndexRoute + LayoutOrgsOrganizationProjectsProjectNsNamespaceConnectRoute: typeof LayoutOrgsOrganizationProjectsProjectNsNamespaceConnectRoute + LayoutOrgsOrganizationProjectsProjectNsNamespaceIndexRoute: typeof LayoutOrgsOrganizationProjectsProjectNsNamespaceIndexRoute } const LayoutRouteChildren: LayoutRouteChildren = { @@ -212,6 +254,10 @@ const LayoutRouteChildren: LayoutRouteChildren = { LayoutOrgsOrganizationIndexRoute: LayoutOrgsOrganizationIndexRoute, LayoutOrgsOrganizationProjectsProjectIndexRoute: LayoutOrgsOrganizationProjectsProjectIndexRoute, + LayoutOrgsOrganizationProjectsProjectNsNamespaceConnectRoute: + LayoutOrgsOrganizationProjectsProjectNsNamespaceConnectRoute, + LayoutOrgsOrganizationProjectsProjectNsNamespaceIndexRoute: + LayoutOrgsOrganizationProjectsProjectNsNamespaceIndexRoute, } const LayoutRouteWithChildren = diff --git a/frontend/src/routes/_layout.tsx b/frontend/src/routes/_layout.tsx index 70985195aa..e90613b4f0 100644 --- a/frontend/src/routes/_layout.tsx +++ b/frontend/src/routes/_layout.tsx @@ -11,6 +11,7 @@ import { Outlet, useMatch, useNavigate, + useParams, } from "@tanstack/react-router"; import { zodValidator } from "@tanstack/zod-adapter"; import { C } from "node_modules/@clerk/clerk-react/dist/useAuth-BVxIa9U7.mjs"; @@ -49,6 +50,7 @@ import { RootLayoutContextProvider } from "@/components/actors/root-layout-conte import { ConnectionForm } from "@/components/connection-form"; import { ModalRenderer } from "@/components/modal-renderer"; import { docsLinks } from "@/content/data"; +import { createCloudManagerContext } from "@/queries/manager-cloud"; import { createEngineManagerContext, type NamespaceNameId, @@ -410,26 +412,26 @@ function CloudRouteComponent() { - - + - ); } -function NavigateToLatestOrganization() { - const { isLoaded, organization } = useOrganization(); - - if (!isLoaded || !organization) { - return null; - } +function CloudContextContent() { + const { namespace, project } = useParams({ strict: false }); + const managerContext = useMemo(() => { + return createCloudManagerContext({ + project: project || "", + namespace: namespace || "", + }); + }, [namespace, project]); return ( - + + + + ); } @@ -438,6 +440,8 @@ function CloudModals() { const search = Route.useSearch(); const CreateProjectDialog = useDialog.CreateProject.Dialog; + const CreateNamespaceDialog = useDialog.CreateNamespace.Dialog; + return ( <> + { + if (!value) { + navigate({ + to: ".", + search: (old) => ({ + ...old, + modal: undefined, + }), + }); + } + }, + }} + /> ); } diff --git a/frontend/src/routes/_layout/index.tsx b/frontend/src/routes/_layout/index.tsx index 315421b54d..0447611c1c 100644 --- a/frontend/src/routes/_layout/index.tsx +++ b/frontend/src/routes/_layout/index.tsx @@ -3,19 +3,21 @@ import { useInfiniteQuery } from "@tanstack/react-query"; import { createFileRoute, Navigate } from "@tanstack/react-router"; import { match } from "ts-pattern"; import { Card, CardContent, CardHeader, CardTitle } from "@/components"; -import { namespacesQueryOptions } from "@/queries/manager-engine"; +import { useManager } from "@/components/actors"; import { RouteComponent as NamespaceRouteComponent } from "./ns.$namespace/index"; export const Route = createFileRoute("/_layout/")({ component: match(__APP_TYPE__) .with("engine", () => RouteComponent) .with("inspector", () => NamespaceRouteComponent) - .with("cloud", () => () => CloudRouteComponent) + .with("cloud", () => CloudRouteComponent) .exhaustive(), }); function RouteComponent() { - const { data: namespaces } = useInfiniteQuery(namespacesQueryOptions()); + const { data: namespaces } = useInfiniteQuery( + useManager().namespacesQueryOptions(), + ); if (namespaces && namespaces?.length > 0) { return ( @@ -53,6 +55,7 @@ function CloudRouteComponent() { ); } diff --git a/frontend/src/routes/_layout/namespaces.tsx b/frontend/src/routes/_layout/namespaces.tsx index 5cfa4132a6..e7c47109ea 100644 --- a/frontend/src/routes/_layout/namespaces.tsx +++ b/frontend/src/routes/_layout/namespaces.tsx @@ -1,184 +1,15 @@ -import { faExternalLink, faPlus, faRefresh, Icon } from "@rivet-gg/icons"; -import { useInfiniteQuery } from "@tanstack/react-query"; -import { createFileRoute, Link } from "@tanstack/react-router"; -import { - Button, - DiscreteCopyButton, - H1, - Skeleton, - Table, - TableBody, - TableCell, - TableHead, - TableHeader, - TableRow, - Text, - WithTooltip, -} from "@/components"; -import { namespacesQueryOptions } from "@/queries/manager-engine"; +import { createFileRoute, notFound } from "@tanstack/react-router"; +import { match } from "ts-pattern"; +import { NamespacesPage } from "@/app/namespaces-page"; export const Route = createFileRoute("/_layout/namespaces")({ - component: RouteComponent, + component: match(__APP_TYPE__) + .with("engine", () => RouteComponent) + .otherwise(() => () => { + throw notFound(); + }), }); function RouteComponent() { - const { - data: namespaces, - isRefetching, - hasNextPage, - fetchNextPage, - isLoading, - refetch, - } = useInfiniteQuery(namespacesQueryOptions()); - - return ( -
-
-

Namespaces

-
- - ({ - ...old, - modal: "create-ns", - })} - > - - - - } - /> - refetch()} - > - - - } - /> -
-
- -
- -
-
-
- - - - ID - Name - Created - - - - - {!isLoading && namespaces?.length === 0 ? ( - - - - There's no namespaces matching - criteria. - - - - ) : null} - {isLoading ? ( - <> - - - - - - - - - - ) : null} - {namespaces?.map((namespace) => ( - - - - {namespace.name} - - - - - {namespace.displayName} - - - - {new Date( - namespace.createTs, - ).toLocaleString()} - - - - - - ))} - - {!isLoading && hasNextPage ? ( - - - - - - ) : null} - -
-
-
-
-
- ); -} - -function RowSkeleton() { - return ( - - - - - - - - - - - - ); + return ; } diff --git a/frontend/src/routes/_layout/ns.$namespace.tsx b/frontend/src/routes/_layout/ns.$namespace.tsx index 1f031948be..75bc49cefa 100644 --- a/frontend/src/routes/_layout/ns.$namespace.tsx +++ b/frontend/src/routes/_layout/ns.$namespace.tsx @@ -1,9 +1,14 @@ -import { createFileRoute, Outlet } from "@tanstack/react-router"; +import { createFileRoute, notFound, Outlet } from "@tanstack/react-router"; import { zodValidator } from "@tanstack/zod-adapter"; +import { match } from "ts-pattern"; import z from "zod"; export const Route = createFileRoute("/_layout/ns/$namespace")({ - component: RouteComponent, + component: match(__APP_TYPE__) + .with("engine", () => RouteComponent) + .otherwise(() => () => { + throw notFound(); + }), validateSearch: zodValidator( z.object({ n: z.array(z.string()).optional(), diff --git a/frontend/src/routes/_layout/orgs.$organization/index.tsx b/frontend/src/routes/_layout/orgs.$organization/index.tsx index 48e7692d14..2a1f6889ed 100644 --- a/frontend/src/routes/_layout/orgs.$organization/index.tsx +++ b/frontend/src/routes/_layout/orgs.$organization/index.tsx @@ -10,7 +10,7 @@ import { projectsQueryOptions } from "@/queries/manager-cloud"; export const Route = createFileRoute("/_layout/orgs/$organization/")({ component: match(__APP_TYPE__) .with("cloud", () => RouteComponent) - .otherwise(() => { + .otherwise(() => () => { throw notFound(); }), }); @@ -26,7 +26,7 @@ function RouteComponent() { isFetchingNextPage, isLoading, refetch, - } = useInfiniteQuery(projectsQueryOptions({ orgId: organization })); + } = useInfiniteQuery(projectsQueryOptions({ organization })); return (
diff --git a/frontend/src/routes/_layout/orgs.$organization/projects.$project/index.tsx b/frontend/src/routes/_layout/orgs.$organization/projects.$project/index.tsx index be2d7e01c1..243d59e4f2 100644 --- a/frontend/src/routes/_layout/orgs.$organization/projects.$project/index.tsx +++ b/frontend/src/routes/_layout/orgs.$organization/projects.$project/index.tsx @@ -1,129 +1,17 @@ -import type { Rivet } from "@rivet-gg/cloud"; -import { - faAdd, - faArrowRight, - faChevronRight, - faRefresh, - Icon, -} from "@rivet-gg/icons"; -import { useInfiniteQuery } from "@tanstack/react-query"; -import { createFileRoute, Link, notFound } from "@tanstack/react-router"; +import { createFileRoute, notFound } from "@tanstack/react-router"; import { match } from "ts-pattern"; -import { Button, H1, Skeleton, Text, WithTooltip } from "@/components"; -import { VisibilitySensor } from "@/components/visibility-sensor"; -import { projectsQueryOptions } from "@/queries/manager-cloud"; +import { NamespacesPage } from "@/app/namespaces-page"; export const Route = createFileRoute( "/_layout/orgs/$organization/projects/$project/", )({ component: match(__APP_TYPE__) .with("cloud", () => RouteComponent) - .otherwise(() => { + .otherwise(() => () => { throw notFound(); }), }); function RouteComponent() { - const { - data: projects, - isRefetching, - isSuccess, - hasNextPage, - fetchNextPage, - isFetchingNextPage, - isLoading, - isError, - refetch, - } = useInfiniteQuery(projectsQueryOptions()); - - return ( -
-
-

Projects

-
- refetch()} - > - - - } - /> -
-
- -
- -
-
-
- {isSuccess - ? projects.map((project) => ( - - )) - : null} - - ({ - ...old, - modal: "create-project", - })} - > -
- - - Create New Project - - - -
- - - {hasNextPage ? ( - - ) : null} - - {isLoading || isFetchingNextPage ? ( - <> - - - - - - - - ) : null} -
-
-
-
- ); -} - -function TileSkeleton() { - return ( -
- - - -
- ); -} - -function Row(project: Rivet.Project) { - return ( -
- {project.name} - {/* - {project.} - */} - -
- ); + return ; } diff --git a/frontend/src/routes/_layout/orgs.$organization/projects.$project/ns.$namespace/connect.tsx b/frontend/src/routes/_layout/orgs.$organization/projects.$project/ns.$namespace/connect.tsx new file mode 100644 index 0000000000..8db37544dd --- /dev/null +++ b/frontend/src/routes/_layout/orgs.$organization/projects.$project/ns.$namespace/connect.tsx @@ -0,0 +1,21 @@ +import { createFileRoute, notFound } from "@tanstack/react-router"; +import { match } from "ts-pattern"; + +export const Route = createFileRoute( + "/_layout/orgs/$organization/projects/$project/ns/$namespace/connect", +)({ + component: match(__APP_TYPE__) + .with("cloud", () => RouteComponent) + .otherwise(() => () => { + throw notFound(); + }), +}); + +function RouteComponent() { + return ( +
+ Hello + "/_layout/orgs/$organization/projects/$project/ns/$namespace/connect"! +
+ ); +} diff --git a/frontend/src/routes/_layout/orgs.$organization/projects.$project/ns.$namespace/index.tsx b/frontend/src/routes/_layout/orgs.$organization/projects.$project/ns.$namespace/index.tsx new file mode 100644 index 0000000000..d6c631ddc4 --- /dev/null +++ b/frontend/src/routes/_layout/orgs.$organization/projects.$project/ns.$namespace/index.tsx @@ -0,0 +1,47 @@ +import { useSuspenseInfiniteQuery } from "@tanstack/react-query"; +import { + createFileRoute, + Navigate, + notFound, + useSearch, +} from "@tanstack/react-router"; +import { match } from "ts-pattern"; +import { Actors } from "@/app/actors"; +import { useManager } from "@/components/actors"; + +export const Route = createFileRoute( + "/_layout/orgs/$organization/projects/$project/ns/$namespace/", +)({ + component: match(__APP_TYPE__) + .with("cloud", () => RouteComponent) + .otherwise(() => () => { + throw notFound(); + }), +}); + +export function RouteComponent() { + const { actorId, n } = useSearch({ from: "/_layout" }); + + return ( + <> + + {!n ? : null} + + ); +} + +function BuildPrefiller() { + const { data } = useSuspenseInfiniteQuery( + useManager().buildsQueryOptions(), + ); + + if (data[0]) { + return ( + ({ ...search, n: [data[0].name] })} + /> + ); + } + return null; +} diff --git a/frontend/tsconfig.json b/frontend/tsconfig.json index 29e61c24ee..d38c4ab055 100644 --- a/frontend/tsconfig.json +++ b/frontend/tsconfig.json @@ -12,7 +12,8 @@ "baseUrl": ".", "paths": { "@/*": ["./src/*"] - } + }, + "plugins": [{ "name": "typescript-plugin-css-modules" }] }, "include": ["src", "vite.inspector.config.ts", "vite.engine.config.ts"] } diff --git a/frontend/vendor/rivet-cloud.tgz b/frontend/vendor/rivet-cloud.tgz index 4f0118347a..5047f93a47 100644 --- a/frontend/vendor/rivet-cloud.tgz +++ b/frontend/vendor/rivet-cloud.tgz @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:a057b5bfc514b5f078d61eb5523cc61771617d2348ab57c7fc2d719731b5838c -size 176433 +oid sha256:bb1fcfffe70989a9fe9a23bd4cf60eb75eca951f468e0830bf2b5b6cdfc80ed6 +size 184211 diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 33ae3544af..df72b918f0 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -167,7 +167,7 @@ importers: version: file:frontend/vendor/rivet-cloud.tgz '@rivet-gg/icons': specifier: file:./vendor/rivet-icons.tgz - version: file:frontend/vendor/rivet-icons.tgz(@fortawesome/fontawesome-svg-core@6.7.2)(@fortawesome/free-brands-svg-icons@6.7.2)(@fortawesome/free-solid-svg-icons@6.7.2)(@fortawesome/react-fontawesome@0.2.3(@fortawesome/fontawesome-svg-core@6.7.2)(react@19.1.1))(@types/node@20.19.9)(babel-plugin-macros@3.1.0)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)(terser@5.43.1) + version: file:frontend/vendor/rivet-icons.tgz(@fortawesome/fontawesome-svg-core@6.7.2)(@fortawesome/free-brands-svg-icons@6.7.2)(@fortawesome/free-solid-svg-icons@6.7.2)(@fortawesome/react-fontawesome@0.2.3(@fortawesome/fontawesome-svg-core@6.7.2)(react@19.1.1))(@types/node@20.19.9)(babel-plugin-macros@3.1.0)(less@4.4.1)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)(sass@1.93.2)(stylus@0.62.0)(terser@5.43.1) '@rivetkit/engine-api-full': specifier: workspace:* version: link:../sdks/typescript/api-full @@ -218,7 +218,7 @@ importers: version: 1.131.14(@tanstack/react-router@1.131.14(react-dom@19.1.1(react@19.1.1))(react@19.1.1))(@tanstack/router-core@1.131.14)(csstype@3.1.3)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)(solid-js@1.9.9)(tiny-invariant@1.3.3) '@tanstack/router-plugin': specifier: ^1.114.25 - version: 1.131.15(@tanstack/react-router@1.131.14(react-dom@19.1.1(react@19.1.1))(react@19.1.1))(vite@5.4.19(@types/node@20.19.9)(terser@5.43.1))(webpack@5.101.3(esbuild@0.25.8)) + version: 1.131.15(@tanstack/react-router@1.131.14(react-dom@19.1.1(react@19.1.1))(react@19.1.1))(vite@5.4.19(@types/node@20.19.9)(less@4.4.1)(sass@1.93.2)(stylus@0.62.0)(terser@5.43.1))(webpack@5.101.3(esbuild@0.25.8)) '@tanstack/store': specifier: ^0.7.2 version: 0.7.2 @@ -254,7 +254,7 @@ importers: version: 4.25.1(@babel/runtime@7.28.3)(@codemirror/autocomplete@6.18.6)(@codemirror/language@6.11.3)(@codemirror/lint@6.8.5)(@codemirror/search@6.5.11)(@codemirror/state@6.5.2)(@codemirror/theme-one-dark@6.1.3)(@codemirror/view@6.38.1)(codemirror@6.0.2)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) '@vitejs/plugin-react': specifier: ^4.2.1 - version: 4.7.0(vite@5.4.19(@types/node@20.19.9)(terser@5.43.1)) + version: 4.7.0(vite@5.4.19(@types/node@20.19.9)(less@4.4.1)(sass@1.93.2)(stylus@0.62.0)(terser@5.43.1)) actor-core: specifier: ^0.6.2 version: 0.6.3(ws@8.18.3) @@ -351,18 +351,21 @@ importers: typescript: specifier: ^5.5.4 version: 5.8.2 + typescript-plugin-css-modules: + specifier: ^5.2.0 + version: 5.2.0(typescript@5.8.2) usehooks-ts: specifier: ^3.1.0 version: 3.1.1(react@19.1.1) vite: specifier: ^5.2.0 - version: 5.4.19(@types/node@20.19.9)(terser@5.43.1) + version: 5.4.19(@types/node@20.19.9)(less@4.4.1)(sass@1.93.2)(stylus@0.62.0)(terser@5.43.1) vite-plugin-favicons-inject: specifier: ^2.2.0 version: 2.2.0 vite-tsconfig-paths: specifier: ^5.1.4 - version: 5.1.4(typescript@5.8.2)(vite@5.4.19(@types/node@20.19.9)(terser@5.43.1)) + version: 5.1.4(typescript@5.8.2)(vite@5.4.19(@types/node@20.19.9)(less@4.4.1)(sass@1.93.2)(stylus@0.62.0)(terser@5.43.1)) zod: specifier: ^3.24 version: 3.25.76 @@ -567,7 +570,7 @@ importers: version: 18.3.7(@types/react@18.3.23) '@vitejs/plugin-react': specifier: ^4.2.1 - version: 4.7.0(vite@5.4.19(@types/node@20.19.9)(terser@5.43.1)) + version: 4.7.0(vite@5.4.19(@types/node@20.19.9)(less@4.4.1)(sass@1.93.2)(stylus@0.62.0)(terser@5.43.1)) autoprefixer: specifier: ^10.4.19 version: 10.4.21(postcss@8.5.6) @@ -579,10 +582,10 @@ importers: version: 3.4.17 vite: specifier: ^5.2.0 - version: 5.4.19(@types/node@20.19.9)(terser@5.43.1) + version: 5.4.19(@types/node@20.19.9)(less@4.4.1)(sass@1.93.2)(stylus@0.62.0)(terser@5.43.1) vite-plugin-dts: specifier: ^3.8.1 - version: 3.9.1(@types/node@20.19.9)(rollup@4.46.2)(typescript@5.8.2)(vite@5.4.19(@types/node@20.19.9)(terser@5.43.1)) + version: 3.9.1(@types/node@20.19.9)(rollup@4.46.2)(typescript@5.8.2)(vite@5.4.19(@types/node@20.19.9)(less@4.4.1)(sass@1.93.2)(stylus@0.62.0)(terser@5.43.1)) frontend/packages/icons: dependencies: @@ -612,7 +615,7 @@ importers: version: 19.1.1(react@19.1.1) vite: specifier: ^5.2.0 - version: 5.4.19(@types/node@24.3.0)(terser@5.43.1) + version: 5.4.19(@types/node@24.3.0)(less@4.4.1)(sass@1.93.2)(stylus@0.62.0)(terser@5.43.1) devDependencies: esbuild: specifier: ^0.25.0 @@ -727,7 +730,7 @@ importers: version: 5.8.2 vitest: specifier: ^1.6.0 - version: 1.6.1(@types/node@22.17.0)(terser@5.43.1) + version: 1.6.1(@types/node@22.17.0)(less@4.4.1)(sass@1.93.2)(stylus@0.62.0)(terser@5.43.1) sdks/typescript/runner-protocol: dependencies: @@ -786,7 +789,7 @@ importers: version: 5.8.2 vitest: specifier: ^1.6.0 - version: 1.6.1(@types/node@22.17.0)(terser@5.43.1) + version: 1.6.1(@types/node@22.17.0)(less@4.4.1)(sass@1.93.2)(stylus@0.62.0)(terser@5.43.1) site: dependencies: @@ -825,7 +828,7 @@ importers: version: 15.5.2(@mdx-js/loader@3.1.1(webpack@5.101.3(esbuild@0.25.8)))(@mdx-js/react@3.1.1(@types/react@18.3.23)(react@19.1.1)) '@next/third-parties': specifier: latest - version: 15.5.2(next@15.5.2(babel-plugin-macros@3.1.0)(react-dom@19.1.1(react@19.1.1))(react@19.1.1))(react@19.1.1) + version: 15.5.2(next@15.5.2(babel-plugin-macros@3.1.0)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)(sass@1.93.2))(react@19.1.1) '@rivet-gg/api': specifier: ^0.0.1-rc19 version: 0.0.1-rc8 @@ -906,13 +909,13 @@ importers: version: 0.1.4 next: specifier: ^15.2.4 - version: 15.5.2(babel-plugin-macros@3.1.0)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + version: 15.5.2(babel-plugin-macros@3.1.0)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)(sass@1.93.2) next-router-mock: specifier: ^0.9.7 - version: 0.9.13(next@15.5.2(babel-plugin-macros@3.1.0)(react-dom@19.1.1(react@19.1.1))(react@19.1.1))(react@19.1.1) + version: 0.9.13(next@15.5.2(babel-plugin-macros@3.1.0)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)(sass@1.93.2))(react@19.1.1) next-sitemap: specifier: ^4.2.3 - version: 4.2.3(next@15.5.2(babel-plugin-macros@3.1.0)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)) + version: 4.2.3(next@15.5.2(babel-plugin-macros@3.1.0)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)(sass@1.93.2)) postcss: specifier: ^8.4.24 version: 8.5.6 @@ -1024,13 +1027,16 @@ importers: version: 5.8.2 vite-node: specifier: ^2.1.1 - version: 2.1.9(@types/node@24.3.0)(terser@5.43.1) + version: 2.1.9(@types/node@24.3.0)(less@4.4.1)(sass@1.93.2)(stylus@0.62.0)(terser@5.43.1) yaml: specifier: ^2.3.1 version: 2.8.1 packages: + '@adobe/css-tools@4.3.3': + resolution: {integrity: sha512-rE0Pygv0sEZ4vBWHlAgJLGDU7Pm8xoO6p3wsEceb7GYAjScrOHpEo8KK/eVkAcnSM+slAEtXjA2JpdjLp4fJQQ==} + '@adraffy/ens-normalize@1.11.1': resolution: {integrity: sha512-nhCBV3quEgesuf7c7KYfperqSS14T8bYuvJ8PcLJp6znkZpFc0AuW4qBtr8eKVyPPe/8RSr7sglCWPU5eaxwKQ==} @@ -2442,6 +2448,88 @@ packages: resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==} engines: {node: '>= 8'} + '@parcel/watcher-android-arm64@2.5.1': + resolution: {integrity: sha512-KF8+j9nNbUN8vzOFDpRMsaKBHZ/mcjEjMToVMJOhTozkDonQFFrRcfdLWn6yWKCmJKmdVxSgHiYvTCef4/qcBA==} + engines: {node: '>= 10.0.0'} + cpu: [arm64] + os: [android] + + '@parcel/watcher-darwin-arm64@2.5.1': + resolution: {integrity: sha512-eAzPv5osDmZyBhou8PoF4i6RQXAfeKL9tjb3QzYuccXFMQU0ruIc/POh30ePnaOyD1UXdlKguHBmsTs53tVoPw==} + engines: {node: '>= 10.0.0'} + cpu: [arm64] + os: [darwin] + + '@parcel/watcher-darwin-x64@2.5.1': + resolution: {integrity: sha512-1ZXDthrnNmwv10A0/3AJNZ9JGlzrF82i3gNQcWOzd7nJ8aj+ILyW1MTxVk35Db0u91oD5Nlk9MBiujMlwmeXZg==} + engines: {node: '>= 10.0.0'} + cpu: [x64] + os: [darwin] + + '@parcel/watcher-freebsd-x64@2.5.1': + resolution: {integrity: sha512-SI4eljM7Flp9yPuKi8W0ird8TI/JK6CSxju3NojVI6BjHsTyK7zxA9urjVjEKJ5MBYC+bLmMcbAWlZ+rFkLpJQ==} + engines: {node: '>= 10.0.0'} + cpu: [x64] + os: [freebsd] + + '@parcel/watcher-linux-arm-glibc@2.5.1': + resolution: {integrity: sha512-RCdZlEyTs8geyBkkcnPWvtXLY44BCeZKmGYRtSgtwwnHR4dxfHRG3gR99XdMEdQ7KeiDdasJwwvNSF5jKtDwdA==} + engines: {node: '>= 10.0.0'} + cpu: [arm] + os: [linux] + + '@parcel/watcher-linux-arm-musl@2.5.1': + resolution: {integrity: sha512-6E+m/Mm1t1yhB8X412stiKFG3XykmgdIOqhjWj+VL8oHkKABfu/gjFj8DvLrYVHSBNC+/u5PeNrujiSQ1zwd1Q==} + engines: {node: '>= 10.0.0'} + cpu: [arm] + os: [linux] + + '@parcel/watcher-linux-arm64-glibc@2.5.1': + resolution: {integrity: sha512-LrGp+f02yU3BN9A+DGuY3v3bmnFUggAITBGriZHUREfNEzZh/GO06FF5u2kx8x+GBEUYfyTGamol4j3m9ANe8w==} + engines: {node: '>= 10.0.0'} + cpu: [arm64] + os: [linux] + + '@parcel/watcher-linux-arm64-musl@2.5.1': + resolution: {integrity: sha512-cFOjABi92pMYRXS7AcQv9/M1YuKRw8SZniCDw0ssQb/noPkRzA+HBDkwmyOJYp5wXcsTrhxO0zq1U11cK9jsFg==} + engines: {node: '>= 10.0.0'} + cpu: [arm64] + os: [linux] + + '@parcel/watcher-linux-x64-glibc@2.5.1': + resolution: {integrity: sha512-GcESn8NZySmfwlTsIur+49yDqSny2IhPeZfXunQi48DMugKeZ7uy1FX83pO0X22sHntJ4Ub+9k34XQCX+oHt2A==} + engines: {node: '>= 10.0.0'} + cpu: [x64] + os: [linux] + + '@parcel/watcher-linux-x64-musl@2.5.1': + resolution: {integrity: sha512-n0E2EQbatQ3bXhcH2D1XIAANAcTZkQICBPVaxMeaCVBtOpBZpWJuf7LwyWPSBDITb7In8mqQgJ7gH8CILCURXg==} + engines: {node: '>= 10.0.0'} + cpu: [x64] + os: [linux] + + '@parcel/watcher-win32-arm64@2.5.1': + resolution: {integrity: sha512-RFzklRvmc3PkjKjry3hLF9wD7ppR4AKcWNzH7kXR7GUe0Igb3Nz8fyPwtZCSquGrhU5HhUNDr/mKBqj7tqA2Vw==} + engines: {node: '>= 10.0.0'} + cpu: [arm64] + os: [win32] + + '@parcel/watcher-win32-ia32@2.5.1': + resolution: {integrity: sha512-c2KkcVN+NJmuA7CGlaGD1qJh1cLfDnQsHjE89E60vUEMlqduHGCdCLJCID5geFVM0dOtA3ZiIO8BoEQmzQVfpQ==} + engines: {node: '>= 10.0.0'} + cpu: [ia32] + os: [win32] + + '@parcel/watcher-win32-x64@2.5.1': + resolution: {integrity: sha512-9lHBdJITeNR++EvSQVUcaZoWupyHfXe1jZvGZ06O/5MflPcuPLtEphScIBL+AiCWBO46tDSHzWyD0uDmmZqsgA==} + engines: {node: '>= 10.0.0'} + cpu: [x64] + os: [win32] + + '@parcel/watcher@2.5.1': + resolution: {integrity: sha512-dfUnCxiN9H4ap84DvD2ubjw+3vUNpstxa0TneY/Paat8a3R4uQZDLSvWjmznAY/DoahqTHl9V46HF/Zs3F29pg==} + engines: {node: '>= 10.0.0'} + '@pkgjs/parseargs@0.11.0': resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==} engines: {node: '>=14'} @@ -3034,7 +3122,7 @@ packages: resolution: {integrity: sha512-aGJVImxsmz8fLLzeZHUlFRJ/7Y/xBrke9bOvMpooVaJpHor/XmiP19QeEtB2hmQUOPlgS3dz5o8UtCZ5+LcQGg==} '@rivet-gg/cloud@file:frontend/vendor/rivet-cloud.tgz': - resolution: {integrity: sha512-PbR+zVclhb53/mxqtRk6uGDparY2d3u72QZ34fPIAW0D8vp2QBcEeWqBZ92m+0SXsXy0a63R1tUmqrAmXrBSlg==, tarball: file:frontend/vendor/rivet-cloud.tgz} + resolution: {integrity: sha512-zh9YFz5/NaJKSGR/FB8g5vaSBs2YZbsUbiB2rY1cQQw56StMefBrBMYwrrVtef3KDhr7DyZJ17O4b8ok54i3Yw==, tarball: file:frontend/vendor/rivet-cloud.tgz} version: 0.0.0 '@rivet-gg/icons@file:frontend/vendor/rivet-icons.tgz': @@ -3651,6 +3739,12 @@ packages: '@types/parse-json@4.0.2': resolution: {integrity: sha512-dISoDXWWQwUquiKsyZ4Ng+HX2KsPL7LyHKHQwgGFEA3IaKac4Obd+h2a/a6waisAoepJlBcx9paWqjA8/HVjCw==} + '@types/postcss-modules-local-by-default@4.0.2': + resolution: {integrity: sha512-CtYCcD+L+trB3reJPny+bKWKMzPfxEyQpKIwit7kErnOexf5/faaGpkFy4I5AwbV4hp1sk7/aTg0tt0B67VkLQ==} + + '@types/postcss-modules-scope@3.0.4': + resolution: {integrity: sha512-//ygSisVq9kVI0sqx3UPLzWIMCmtSVrzdljtuaAEJtGoGnpjBikZ2sXO5MpH9SnWX9HRfXxHifDAXcQjupWnIQ==} + '@types/prop-types@15.7.15': resolution: {integrity: sha512-F6bEyamV9jKGAFBEmlQnesRPGOQqS2+Uwi0Em15xenOxHaf2hv6L8YCVn3rPdPJOiJfPiCnLIRyvwVaqMY3MIw==} @@ -4373,6 +4467,9 @@ packages: resolution: {integrity: sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==} engines: {node: '>= 0.6'} + copy-anything@2.0.6: + resolution: {integrity: sha512-1j20GZTsvKNkc4BY3NpMOM8tt///wY3FpIzozTOFO2ffuZcV61nojHXVKIy3WM+7ADCy5FVhdZYHYDdgTU0yJw==} + copy-to-clipboard@3.3.3: resolution: {integrity: sha512-2KV8NhB5JqC3ky0r9PMCAZKbUHSwtEo4CwCs0KXgruG43gX5PMqDEBbVU4OUzw2MuAWUfsuFmWvEKG5QRfSnJA==} @@ -4558,6 +4655,11 @@ packages: resolution: {integrity: sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==} engines: {node: '>=6'} + detect-libc@1.0.3: + resolution: {integrity: sha512-pGjwhsmsp4kL2RTz08wcOlGN83otlqHeD/Z5T8GXZB+/YcpQ/dgo+lbU8ZsGxV0HIvqqxo9l7mqYwyYMD9bKDg==} + engines: {node: '>=0.10'} + hasBin: true + detect-libc@2.0.4: resolution: {integrity: sha512-3UDv+G9CsCKO1WKMGw9fwq/SWJYbI0c5Y7LU1AXYoDdbhE2AHQ6N6Nb34sG8Fj7T5APy8qXDCKuuIHd1BR0tVA==} engines: {node: '>=8'} @@ -4646,6 +4748,10 @@ packages: resolution: {integrity: sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==} engines: {node: '>=0.12'} + errno@0.1.8: + resolution: {integrity: sha512-dJ6oBr5SQ1VSd9qkk7ByRgb/1SH4JZjCHSW/mr63/QcXO9zLVxvJ6Oy13nio03rxpSnVDDjFor75SjVeZWPW/A==} + hasBin: true + error-ex@1.3.4: resolution: {integrity: sha512-sqQamAnR14VgCr1A618A3sGrygcpK+HEbenA/HiEAkkUwcZIIB/tgWqHFxWgOyDh4nB4JCRimh79dR5Ywc9MDQ==} @@ -5263,6 +5369,16 @@ packages: resolution: {integrity: sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==} engines: {node: '>=0.10.0'} + iconv-lite@0.6.3: + resolution: {integrity: sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==} + engines: {node: '>=0.10.0'} + + icss-utils@5.1.0: + resolution: {integrity: sha512-soFhflCVWLfRNOPU3iv5Z9VUdT44xFRbzjLsEzSr5AQmgqPMTHdU3PMT1Cf1ssx8fLNJDA1juftYl+PUcv3MqA==} + engines: {node: ^10 || ^12 || >= 14} + peerDependencies: + postcss: ^8.1.0 + idb-keyval@6.2.1: resolution: {integrity: sha512-8Sb3veuYCyrZL+VBt9LJfZjLUPWVvqn8tG28VqYNFCo43KHcKuq+b4EiXGeuaLAQWL2YmyDgMp2aSpH9JHsEQg==} @@ -5273,9 +5389,17 @@ packages: resolution: {integrity: sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==} engines: {node: '>= 4'} + image-size@0.5.5: + resolution: {integrity: sha512-6TDAlDPZxUFCv+fuOkIoXT/V/f3Qbq8e37p+YOiYrUv3v9cc3/6x78VdfPgFVaB9dZYeLUfKgHRebpkm/oP2VQ==} + engines: {node: '>=0.10.0'} + hasBin: true + immediate@3.0.6: resolution: {integrity: sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ==} + immutable@5.1.3: + resolution: {integrity: sha512-+chQdDfvscSF1SJqv2gn4SRO2ZyS3xL3r7IW/wWEEzrzLisnOlKiQu5ytC/BVNcS15C39WT2Hg/bjKjDMcu+zg==} + import-fresh@3.3.1: resolution: {integrity: sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==} engines: {node: '>=6'} @@ -5467,6 +5591,9 @@ packages: resolution: {integrity: sha512-mfcwb6IzQyOKTs84CQMrOwW4gQcaTOAWJ0zzJCl2WSPDrWk/OzDaImWFH3djXhb24g4eudZfLRozAvPGw4d9hQ==} engines: {node: '>= 0.4'} + is-what@3.14.1: + resolution: {integrity: sha512-sNxgpk9793nzSs7bA6JQJGeIuRBQhAaNGG77kzYQgMkrID+lS6SlK07K5LaptscDlSaIgH+GPFzf+d75FVxozA==} + isarray@1.0.0: resolution: {integrity: sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==} @@ -5657,6 +5784,11 @@ packages: resolution: {integrity: sha512-huMg+mGp6wHPjkaLdchuOvxVRMzvz6OVdhivatiH2Qn47O5Zm46jwzbVPYIanX6N/8ZTjGLBxv8tZ0KYmKt/Jg==} hasBin: true + less@4.4.1: + resolution: {integrity: sha512-X9HKyiXPi0f/ed0XhgUlBeFfxrlDP3xR4M7768Zl+WXLUViuL9AOPPJP4nCV0tgRWvTYvpNmN0SFhZOQzy16PA==} + engines: {node: '>=14'} + hasBin: true + levn@0.4.1: resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==} engines: {node: '>= 0.8.0'} @@ -5664,6 +5796,10 @@ packages: lie@3.3.0: resolution: {integrity: sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ==} + lilconfig@2.1.0: + resolution: {integrity: sha512-utWOt/GHzuUxnLKxB6dk81RoOeoNeHgbrXiuGk4yyF5qlRz+iIVWu56E2fqGHFrXz0QNUhLB/8nKqvRH66JKGQ==} + engines: {node: '>=10'} + lilconfig@3.1.3: resolution: {integrity: sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw==} engines: {node: '>=14'} @@ -5700,6 +5836,9 @@ packages: resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==} engines: {node: '>=10'} + lodash.camelcase@4.3.0: + resolution: {integrity: sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==} + lodash.castarray@4.4.0: resolution: {integrity: sha512-aVx8ztPv7/2ULbArGJ2Y42bG1mEQ5mGjpdvrbJcJFU3TbYybe+QlLS4pst9zV52ymy2in1KpFPiZnAOATxD4+Q==} @@ -5762,6 +5901,10 @@ packages: resolution: {integrity: sha512-ISQTe55T2ao7XtlAStud6qwYPZjE4GK1S/BeVPus4jrq6JuOnQ00YKQC581RWhR122W7msZV263KzVeLoqidyQ==} engines: {node: '>=12'} + make-dir@2.1.0: + resolution: {integrity: sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==} + engines: {node: '>=6'} + markdown-extensions@2.0.0: resolution: {integrity: sha512-o5vL7aDWatOTX8LzaS1WMoaoxIiLRQJuIKKe2wAw6IeULDHaqbiqiggmx+pKvZDb1Sj+pE46Sn1T7lCqfFtg1Q==} engines: {node: '>=16'} @@ -5975,6 +6118,11 @@ packages: resolution: {integrity: sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==} engines: {node: '>= 0.6'} + mime@1.6.0: + resolution: {integrity: sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==} + engines: {node: '>=4'} + hasBin: true + mime@4.0.7: resolution: {integrity: sha512-2OfDPL+e03E0LrXaGYOtTFIYhiuzep94NSsuhrNULq+stylcJedcHdzHtz0atMUuGwJfFYs0YL5xeC/Ca2x0eQ==} engines: {node: '>=16'} @@ -6057,6 +6205,11 @@ packages: natural-compare@1.4.0: resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==} + needle@3.3.1: + resolution: {integrity: sha512-6k0YULvhpw+RoLNiQCRKOl09Rv1dPLr8hHnVjHqdolKwDrdNyk+Hmrthi4lIGPPz3r39dLx0hsF5s40sZ3Us4Q==} + engines: {node: '>= 4.4.x'} + hasBin: true + negotiator@0.6.3: resolution: {integrity: sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==} engines: {node: '>= 0.6'} @@ -6105,6 +6258,9 @@ packages: node-addon-api@6.1.0: resolution: {integrity: sha512-+eawOlIgy680F0kBzPUNFhMZGtJ1YmqM6l4+Crf4IkImjYrO/mqPwRMh352g23uIaQKFItcQ64I7KMaJxHgAVA==} + node-addon-api@7.1.1: + resolution: {integrity: sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==} + node-fetch@2.7.0: resolution: {integrity: sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==} engines: {node: 4.x || >=6.0.0} @@ -6257,6 +6413,10 @@ packages: resolution: {integrity: sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==} engines: {node: '>=8'} + parse-node-version@1.0.1: + resolution: {integrity: sha512-3YHlOa/JgH6Mnpr05jP9eDG254US9ek25LyIxZlDItp2iJtwyaXQb57lBYLdT3MowkUFYEV2XXNAYIPlESvJlA==} + engines: {node: '>= 0.10'} + parse5@6.0.1: resolution: {integrity: sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==} @@ -6317,6 +6477,10 @@ packages: resolution: {integrity: sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==} engines: {node: '>=0.10.0'} + pify@4.0.1: + resolution: {integrity: sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==} + engines: {node: '>=6'} + pino-abstract-transport@2.0.0: resolution: {integrity: sha512-F63x5tizV6WCh4R6RHyi2Ml+M70DNRXt/+HANowMflpgGFMAym/VKm6G7ZOQRjqN7XbGxK1Lg9t6ZrtzOaivMw==} @@ -6356,6 +6520,18 @@ packages: peerDependencies: postcss: ^8.4.21 + postcss-load-config@3.1.4: + resolution: {integrity: sha512-6DiM4E7v4coTE4uzA8U//WhtPwyhiim3eyjEMFCnUpzbrkK9wJHgKDT2mR+HbtSrd/NubVaYTOpSpjUl8NQeRg==} + engines: {node: '>= 10'} + peerDependencies: + postcss: '>=8.0.9' + ts-node: '>=9.0.0' + peerDependenciesMeta: + postcss: + optional: true + ts-node: + optional: true + postcss-load-config@4.0.2: resolution: {integrity: sha512-bSVhyJGL00wMVoPUzAVAnbEoWyqRxkjv64tUl427SKnPrENtq6hJwUojroMz2VB+Q1edmi4IfrAPpami5VVgMQ==} engines: {node: '>= 14'} @@ -6386,6 +6562,24 @@ packages: yaml: optional: true + postcss-modules-extract-imports@3.1.0: + resolution: {integrity: sha512-k3kNe0aNFQDAZGbin48pL2VNidTF0w4/eASDsxlyspobzU3wZQLOGj7L9gfRe0Jo9/4uud09DsjFNH7winGv8Q==} + engines: {node: ^10 || ^12 || >= 14} + peerDependencies: + postcss: ^8.1.0 + + postcss-modules-local-by-default@4.2.0: + resolution: {integrity: sha512-5kcJm/zk+GJDSfw+V/42fJ5fhjL5YbFDl8nVdXkJPLLW+Vf9mTD5Xe0wqIaDnLuL2U6cDNpTr+UQ+v2HWIBhzw==} + engines: {node: ^10 || ^12 || >= 14} + peerDependencies: + postcss: ^8.1.0 + + postcss-modules-scope@3.2.1: + resolution: {integrity: sha512-m9jZstCVaqGjTAuny8MdgE88scJnCiQSlSrOWcTQgM2t32UBe+MUmFSO5t7VMSfAf/FJKImAxBav8ooCHJXCJA==} + engines: {node: ^10 || ^12 || >= 14} + peerDependencies: + postcss: ^8.1.0 + postcss-nested@6.2.0: resolution: {integrity: sha512-HQbt28KulC5AJzG+cZtj9kvKB93CFCdLvog1WFLf1D+xmMvPGlBstkpTEZfK5+AN9hfJocyBFCNiqyS48bpgzQ==} engines: {node: '>=12.0'} @@ -6400,6 +6594,10 @@ packages: resolution: {integrity: sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==} engines: {node: '>=4'} + postcss-selector-parser@7.1.0: + resolution: {integrity: sha512-8sLjZwK0R+JlxlYcTuVnyT2v+htpdrjDOKuMcOVdYjt52Lh8hWRYpxBPoKx/Zg+bcjc3wx6fmQevMmUztS/ccA==} + engines: {node: '>=4'} + postcss-value-parser@4.2.0: resolution: {integrity: sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==} @@ -6534,6 +6732,9 @@ packages: proxy-from-env@1.1.0: resolution: {integrity: sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==} + prr@1.0.1: + resolution: {integrity: sha512-yPw4Sng1gWghHQWj0B3ZggWUm4qVbPwPFcRG8KyxiU7J2OHFSoEHKS+EZ3fv5l1t9CyCiop6l/ZYeWbrgoQejw==} + pump@3.0.3: resolution: {integrity: sha512-todwxLMY7/heScKmntwQG8CXVkWUOdYxIvY2s0VWAAMh/nd8SoYiRaKjlr7+iCs984f2P8zvrfWcDDYVb73NfA==} @@ -6798,6 +6999,9 @@ packages: resolution: {integrity: sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==} engines: {node: '>=0.10.0'} + reserved-words@0.1.2: + resolution: {integrity: sha512-0S5SrIUJ9LfpbVl4Yzij6VipUdafHrOTzvmfazSw/jeZrZtQK303OPZW+obtkaw7jQlTQppy0UvZWm9872PbRw==} + resolve-from@4.0.0: resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==} engines: {node: '>=4'} @@ -6886,6 +7090,14 @@ packages: safer-buffer@2.1.2: resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==} + sass@1.93.2: + resolution: {integrity: sha512-t+YPtOQHpGW1QWsh1CHQ5cPIr9lbbGZLZnbihP/D/qZj/yuV68m8qarcV17nvkOX81BCrvzAlq2klCQFZghyTg==} + engines: {node: '>=14.0.0'} + hasBin: true + + sax@1.3.0: + resolution: {integrity: sha512-0s+oAmw9zLl1V1cS9BtZN7JAd0cW5e0QH4W3LWEK6a4LaLEA2OTpGYWDY+6XasBLtz6wkm3u1xRw95mRuJ59WA==} + sax@1.4.1: resolution: {integrity: sha512-+aWOz7yVScEGoKNd4PA10LZ8sk0A/z5+nXQG5giUO5rprX9jgYsTdov9qCchZiPIZezbZH+jRut8nPodFAX4Jg==} @@ -6900,6 +7112,10 @@ packages: resolution: {integrity: sha512-Gn/JaSk/Mt9gYubxTtSn/QCV4em9mpAPiR1rqy/Ocu19u/G9J5WWdNoUT4SiV6mFC3y6cxyFcFwdzPM3FgxGAQ==} engines: {node: '>= 10.13.0'} + semver@5.7.2: + resolution: {integrity: sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==} + hasBin: true + semver@6.3.1: resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==} hasBin: true @@ -7174,6 +7390,10 @@ packages: stylis@4.2.0: resolution: {integrity: sha512-Orov6g6BB1sDfYgzWfTHDOxamtX1bE/zo104Dh9e6fqJ3PooipYyfJ0pUmrZO2wAvO8YbEyeFrkV91XTsGMSrw==} + stylus@0.62.0: + resolution: {integrity: sha512-v3YCf31atbwJQIMtPNX8hcQ+okD4NQaTuKGUWfII8eaqn+3otrbttGL1zSMZAAtiPsBztQnujVBugg/cXFUpyg==} + hasBin: true + sucrase@3.35.0: resolution: {integrity: sha512-8EbVDiu9iN/nESwxeSxDKe0dunta1GOlHufmSSXxMD2z2/tMZpDMpvXQGsc+ajGo8y2uYUmixaSRUc/QPoQ0GA==} engines: {node: '>=16 || 14 >=14.17'} @@ -7349,6 +7569,10 @@ packages: tsconfig-paths@3.15.0: resolution: {integrity: sha512-2Ac2RgzDe/cn48GvOe3M+o82pEFewD3UPbyoUHHdKasHwJKjds4fLXWf/Ux5kATBKN20oaFGu+jbElp1pos0mg==} + tsconfig-paths@4.2.0: + resolution: {integrity: sha512-NoZ4roiN7LnbKn9QqE1amc9DJfzvZXxF4xDavcOWt1BPkdx+m+0gJuPM+S0vCe7zTJMYUP0R8pO2XMr+Y8oLIg==} + engines: {node: '>=6'} + tslib@1.14.1: resolution: {integrity: sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==} @@ -7454,6 +7678,11 @@ packages: resolution: {integrity: sha512-3KS2b+kL7fsuk/eJZ7EQdnEmQoaho/r6KUef7hxvltNA5DR8NAUM+8wJMbJyZ4G9/7i3v5zPBIMN5aybAh2/Jg==} engines: {node: '>= 0.4'} + typescript-plugin-css-modules@5.2.0: + resolution: {integrity: sha512-c5pAU5d+m3GciDr/WhkFldz1NIEGBafuP/3xhFt9BEXS2gmn/LvjkoZ11vEBIuP8LkXfPNhOt1BUhM5efFuwOw==} + peerDependencies: + typescript: '>=4.0.0' + typescript@5.4.2: resolution: {integrity: sha512-+2/g0Fds1ERlP6JsakQQDXjZdZMM+rqpamFZJEKh4kwTIn3iDkgKtby0CeNd5ATNZ4Ry1ax15TMx0W2V+miizQ==} engines: {node: '>=14.17'} @@ -7923,6 +8152,9 @@ packages: snapshots: + '@adobe/css-tools@4.3.3': + optional: true + '@adraffy/ens-normalize@1.11.1': {} '@alloc/quick-lru@5.2.0': {} @@ -9287,9 +9519,9 @@ snapshots: '@next/swc-win32-x64-msvc@15.5.2': optional: true - '@next/third-parties@15.5.2(next@15.5.2(babel-plugin-macros@3.1.0)(react-dom@19.1.1(react@19.1.1))(react@19.1.1))(react@19.1.1)': + '@next/third-parties@15.5.2(next@15.5.2(babel-plugin-macros@3.1.0)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)(sass@1.93.2))(react@19.1.1)': dependencies: - next: 15.5.2(babel-plugin-macros@3.1.0)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + next: 15.5.2(babel-plugin-macros@3.1.0)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)(sass@1.93.2) react: 19.1.1 third-party-capital: 1.0.20 @@ -9319,6 +9551,67 @@ snapshots: '@nodelib/fs.scandir': 2.1.5 fastq: 1.19.1 + '@parcel/watcher-android-arm64@2.5.1': + optional: true + + '@parcel/watcher-darwin-arm64@2.5.1': + optional: true + + '@parcel/watcher-darwin-x64@2.5.1': + optional: true + + '@parcel/watcher-freebsd-x64@2.5.1': + optional: true + + '@parcel/watcher-linux-arm-glibc@2.5.1': + optional: true + + '@parcel/watcher-linux-arm-musl@2.5.1': + optional: true + + '@parcel/watcher-linux-arm64-glibc@2.5.1': + optional: true + + '@parcel/watcher-linux-arm64-musl@2.5.1': + optional: true + + '@parcel/watcher-linux-x64-glibc@2.5.1': + optional: true + + '@parcel/watcher-linux-x64-musl@2.5.1': + optional: true + + '@parcel/watcher-win32-arm64@2.5.1': + optional: true + + '@parcel/watcher-win32-ia32@2.5.1': + optional: true + + '@parcel/watcher-win32-x64@2.5.1': + optional: true + + '@parcel/watcher@2.5.1': + dependencies: + detect-libc: 1.0.3 + is-glob: 4.0.3 + micromatch: 4.0.8 + node-addon-api: 7.1.1 + optionalDependencies: + '@parcel/watcher-android-arm64': 2.5.1 + '@parcel/watcher-darwin-arm64': 2.5.1 + '@parcel/watcher-darwin-x64': 2.5.1 + '@parcel/watcher-freebsd-x64': 2.5.1 + '@parcel/watcher-linux-arm-glibc': 2.5.1 + '@parcel/watcher-linux-arm-musl': 2.5.1 + '@parcel/watcher-linux-arm64-glibc': 2.5.1 + '@parcel/watcher-linux-arm64-musl': 2.5.1 + '@parcel/watcher-linux-x64-glibc': 2.5.1 + '@parcel/watcher-linux-x64-musl': 2.5.1 + '@parcel/watcher-win32-arm64': 2.5.1 + '@parcel/watcher-win32-ia32': 2.5.1 + '@parcel/watcher-win32-x64': 2.5.1 + optional: true + '@pkgjs/parseargs@0.11.0': optional: true @@ -9968,7 +10261,7 @@ snapshots: transitivePeerDependencies: - encoding - '@rivet-gg/icons@file:frontend/vendor/rivet-icons.tgz(@fortawesome/fontawesome-svg-core@6.7.2)(@fortawesome/free-brands-svg-icons@6.7.2)(@fortawesome/free-solid-svg-icons@6.7.2)(@fortawesome/react-fontawesome@0.2.3(@fortawesome/fontawesome-svg-core@6.7.2)(react@19.1.1))(@types/node@20.19.9)(babel-plugin-macros@3.1.0)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)(terser@5.43.1)': + '@rivet-gg/icons@file:frontend/vendor/rivet-icons.tgz(@fortawesome/fontawesome-svg-core@6.7.2)(@fortawesome/free-brands-svg-icons@6.7.2)(@fortawesome/free-solid-svg-icons@6.7.2)(@fortawesome/react-fontawesome@0.2.3(@fortawesome/fontawesome-svg-core@6.7.2)(react@19.1.1))(@types/node@20.19.9)(babel-plugin-macros@3.1.0)(less@4.4.1)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)(sass@1.93.2)(stylus@0.62.0)(terser@5.43.1)': dependencies: '@fortawesome/fontawesome-svg-core': 6.7.2 '@fortawesome/free-brands-svg-icons': 6.7.2 @@ -9978,7 +10271,7 @@ snapshots: local-pkg: 0.5.1 react: 19.1.1 react-dom: 19.1.1(react@19.1.1) - vite: 5.4.19(@types/node@20.19.9)(terser@5.43.1) + vite: 5.4.19(@types/node@20.19.9)(less@4.4.1)(sass@1.93.2)(stylus@0.62.0)(terser@5.43.1) transitivePeerDependencies: - '@types/node' - babel-plugin-macros @@ -10514,7 +10807,7 @@ snapshots: transitivePeerDependencies: - supports-color - '@tanstack/router-plugin@1.131.15(@tanstack/react-router@1.131.14(react-dom@19.1.1(react@19.1.1))(react@19.1.1))(vite@5.4.19(@types/node@20.19.9)(terser@5.43.1))(webpack@5.101.3(esbuild@0.25.8))': + '@tanstack/router-plugin@1.131.15(@tanstack/react-router@1.131.14(react-dom@19.1.1(react@19.1.1))(react@19.1.1))(vite@5.4.19(@types/node@20.19.9)(less@4.4.1)(sass@1.93.2)(stylus@0.62.0)(terser@5.43.1))(webpack@5.101.3(esbuild@0.25.8))': dependencies: '@babel/core': 7.28.3 '@babel/plugin-syntax-jsx': 7.27.1(@babel/core@7.28.3) @@ -10532,7 +10825,7 @@ snapshots: zod: 3.25.76 optionalDependencies: '@tanstack/react-router': 1.131.14(react-dom@19.1.1(react@19.1.1))(react@19.1.1) - vite: 5.4.19(@types/node@20.19.9)(terser@5.43.1) + vite: 5.4.19(@types/node@20.19.9)(less@4.4.1)(sass@1.93.2)(stylus@0.62.0)(terser@5.43.1) webpack: 5.101.3(esbuild@0.25.8) transitivePeerDependencies: - supports-color @@ -10701,6 +10994,14 @@ snapshots: '@types/parse-json@4.0.2': {} + '@types/postcss-modules-local-by-default@4.0.2': + dependencies: + postcss: 8.5.6 + + '@types/postcss-modules-scope@3.0.4': + dependencies: + postcss: 8.5.6 + '@types/prop-types@15.7.15': {} '@types/qs@6.9.8': {} @@ -10815,7 +11116,7 @@ snapshots: '@ungap/structured-clone@1.3.0': {} - '@vitejs/plugin-react@4.7.0(vite@5.4.19(@types/node@20.19.9)(terser@5.43.1))': + '@vitejs/plugin-react@4.7.0(vite@5.4.19(@types/node@20.19.9)(less@4.4.1)(sass@1.93.2)(stylus@0.62.0)(terser@5.43.1))': dependencies: '@babel/core': 7.28.3 '@babel/plugin-transform-react-jsx-self': 7.27.1(@babel/core@7.28.3) @@ -10823,7 +11124,7 @@ snapshots: '@rolldown/pluginutils': 1.0.0-beta.27 '@types/babel__core': 7.20.5 react-refresh: 0.17.0 - vite: 5.4.19(@types/node@20.19.9)(terser@5.43.1) + vite: 5.4.19(@types/node@20.19.9)(less@4.4.1)(sass@1.93.2)(stylus@0.62.0)(terser@5.43.1) transitivePeerDependencies: - supports-color @@ -11510,6 +11811,10 @@ snapshots: cookie@0.7.2: {} + copy-anything@2.0.6: + dependencies: + is-what: 3.14.1 + copy-to-clipboard@3.3.3: dependencies: toggle-selection: 1.0.6 @@ -11669,6 +11974,9 @@ snapshots: dequal@2.0.3: {} + detect-libc@1.0.3: + optional: true + detect-libc@2.0.4: {} detect-node-es@1.1.0: {} @@ -11763,6 +12071,11 @@ snapshots: entities@4.5.0: {} + errno@0.1.8: + dependencies: + prr: 1.0.1 + optional: true + error-ex@1.3.4: dependencies: is-arrayish: 0.2.1 @@ -12698,14 +13011,28 @@ snapshots: dependencies: safer-buffer: 2.1.2 + iconv-lite@0.6.3: + dependencies: + safer-buffer: 2.1.2 + optional: true + + icss-utils@5.1.0(postcss@8.5.6): + dependencies: + postcss: 8.5.6 + idb-keyval@6.2.1: {} ieee754@1.2.1: {} ignore@5.3.2: {} + image-size@0.5.5: + optional: true + immediate@3.0.6: {} + immutable@5.1.3: {} + import-fresh@3.3.1: dependencies: parent-module: 1.0.1 @@ -12885,6 +13212,8 @@ snapshots: call-bound: 1.0.4 get-intrinsic: 1.3.0 + is-what@3.14.1: {} + isarray@1.0.0: {} isarray@2.0.5: {} @@ -13040,6 +13369,20 @@ snapshots: lefthook-windows-arm64: 1.12.3 lefthook-windows-x64: 1.12.3 + less@4.4.1: + dependencies: + copy-anything: 2.0.6 + parse-node-version: 1.0.1 + tslib: 2.8.1 + optionalDependencies: + errno: 0.1.8 + graceful-fs: 4.2.11 + image-size: 0.5.5 + make-dir: 2.1.0 + mime: 1.6.0 + needle: 3.3.1 + source-map: 0.6.1 + levn@0.4.1: dependencies: prelude-ls: 1.2.1 @@ -13049,6 +13392,8 @@ snapshots: dependencies: immediate: 3.0.6 + lilconfig@2.1.0: {} + lilconfig@3.1.3: {} lines-and-columns@1.2.4: {} @@ -13088,6 +13433,8 @@ snapshots: dependencies: p-locate: 5.0.0 + lodash.camelcase@4.3.0: {} + lodash.castarray@4.4.0: {} lodash.debounce@4.0.8: {} @@ -13138,6 +13485,12 @@ snapshots: dependencies: '@jridgewell/sourcemap-codec': 1.5.5 + make-dir@2.1.0: + dependencies: + pify: 4.0.1 + semver: 5.7.2 + optional: true + markdown-extensions@2.0.0: {} markdown-table@3.0.4: {} @@ -13634,6 +13987,9 @@ snapshots: dependencies: mime-db: 1.52.0 + mime@1.6.0: + optional: true + mime@4.0.7: {} mimic-fn@4.0.0: {} @@ -13699,24 +14055,30 @@ snapshots: natural-compare@1.4.0: {} + needle@3.3.1: + dependencies: + iconv-lite: 0.6.3 + sax: 1.4.1 + optional: true + negotiator@0.6.3: {} neo-async@2.6.2: {} - next-router-mock@0.9.13(next@15.5.2(babel-plugin-macros@3.1.0)(react-dom@19.1.1(react@19.1.1))(react@19.1.1))(react@19.1.1): + next-router-mock@0.9.13(next@15.5.2(babel-plugin-macros@3.1.0)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)(sass@1.93.2))(react@19.1.1): dependencies: - next: 15.5.2(babel-plugin-macros@3.1.0)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + next: 15.5.2(babel-plugin-macros@3.1.0)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)(sass@1.93.2) react: 19.1.1 - next-sitemap@4.2.3(next@15.5.2(babel-plugin-macros@3.1.0)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)): + next-sitemap@4.2.3(next@15.5.2(babel-plugin-macros@3.1.0)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)(sass@1.93.2)): dependencies: '@corex/deepmerge': 4.0.43 '@next/env': 13.5.11 fast-glob: 3.3.3 minimist: 1.2.8 - next: 15.5.2(babel-plugin-macros@3.1.0)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + next: 15.5.2(babel-plugin-macros@3.1.0)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)(sass@1.93.2) - next@15.5.2(babel-plugin-macros@3.1.0)(react-dom@19.1.1(react@19.1.1))(react@19.1.1): + next@15.5.2(babel-plugin-macros@3.1.0)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)(sass@1.93.2): dependencies: '@next/env': 15.5.2 '@swc/helpers': 0.5.15 @@ -13734,6 +14096,7 @@ snapshots: '@next/swc-linux-x64-musl': 15.5.2 '@next/swc-win32-arm64-msvc': 15.5.2 '@next/swc-win32-x64-msvc': 15.5.2 + sass: 1.93.2 sharp: 0.34.3 transitivePeerDependencies: - '@babel/core' @@ -13745,6 +14108,9 @@ snapshots: node-addon-api@6.1.0: {} + node-addon-api@7.1.1: + optional: true + node-fetch@2.7.0: dependencies: whatwg-url: 5.0.0 @@ -13927,6 +14293,8 @@ snapshots: json-parse-even-better-errors: 2.3.1 lines-and-columns: 1.2.4 + parse-node-version@1.0.1: {} + parse5@6.0.1: {} path-browserify@1.0.1: {} @@ -13968,6 +14336,9 @@ snapshots: pify@2.3.0: {} + pify@4.0.1: + optional: true + pino-abstract-transport@2.0.0: dependencies: split2: 4.2.0 @@ -14015,6 +14386,13 @@ snapshots: camelcase-css: 2.0.1 postcss: 8.5.6 + postcss-load-config@3.1.4(postcss@8.5.6): + dependencies: + lilconfig: 2.1.0 + yaml: 1.10.2 + optionalDependencies: + postcss: 8.5.6 + postcss-load-config@4.0.2(postcss@8.5.6): dependencies: lilconfig: 3.1.3 @@ -14031,6 +14409,22 @@ snapshots: tsx: 4.20.3 yaml: 2.8.1 + postcss-modules-extract-imports@3.1.0(postcss@8.5.6): + dependencies: + postcss: 8.5.6 + + postcss-modules-local-by-default@4.2.0(postcss@8.5.6): + dependencies: + icss-utils: 5.1.0(postcss@8.5.6) + postcss: 8.5.6 + postcss-selector-parser: 7.1.0 + postcss-value-parser: 4.2.0 + + postcss-modules-scope@3.2.1(postcss@8.5.6): + dependencies: + postcss: 8.5.6 + postcss-selector-parser: 7.1.0 + postcss-nested@6.2.0(postcss@8.5.6): dependencies: postcss: 8.5.6 @@ -14046,6 +14440,11 @@ snapshots: cssesc: 3.0.0 util-deprecate: 1.0.2 + postcss-selector-parser@7.1.0: + dependencies: + cssesc: 3.0.0 + util-deprecate: 1.0.2 + postcss-value-parser@4.2.0: {} postcss@8.4.31: @@ -14124,6 +14523,9 @@ snapshots: proxy-from-env@1.1.0: {} + prr@1.0.1: + optional: true + pump@3.0.3: dependencies: end-of-stream: 1.4.5 @@ -14500,6 +14902,8 @@ snapshots: require-from-string@2.0.2: {} + reserved-words@0.1.2: {} + resolve-from@4.0.0: {} resolve-from@5.0.0: {} @@ -14611,6 +15015,17 @@ snapshots: safer-buffer@2.1.2: {} + sass@1.93.2: + dependencies: + chokidar: 4.0.3 + immutable: 5.1.3 + source-map-js: 1.2.1 + optionalDependencies: + '@parcel/watcher': 2.5.1 + + sax@1.3.0: + optional: true + sax@1.4.1: {} scheduler@0.26.0: {} @@ -14628,6 +15043,9 @@ snapshots: ajv-formats: 2.1.1(ajv@8.17.1) ajv-keywords: 5.1.0(ajv@8.17.1) + semver@5.7.2: + optional: true + semver@6.3.1: {} semver@7.5.4: @@ -15024,6 +15442,17 @@ snapshots: stylis@4.2.0: {} + stylus@0.62.0: + dependencies: + '@adobe/css-tools': 4.3.3 + debug: 4.4.1 + glob: 7.2.3 + sax: 1.3.0 + source-map: 0.7.6 + transitivePeerDependencies: + - supports-color + optional: true + sucrase@3.35.0: dependencies: '@jridgewell/gen-mapping': 0.3.13 @@ -15216,6 +15645,12 @@ snapshots: minimist: 1.2.8 strip-bom: 3.0.0 + tsconfig-paths@4.2.0: + dependencies: + json5: 2.2.3 + minimist: 1.2.8 + strip-bom: 3.0.0 + tslib@1.14.1: {} tslib@2.8.1: {} @@ -15393,6 +15828,30 @@ snapshots: possible-typed-array-names: 1.1.0 reflect.getprototypeof: 1.0.10 + typescript-plugin-css-modules@5.2.0(typescript@5.8.2): + dependencies: + '@types/postcss-modules-local-by-default': 4.0.2 + '@types/postcss-modules-scope': 3.0.4 + dotenv: 16.6.1 + icss-utils: 5.1.0(postcss@8.5.6) + less: 4.4.1 + lodash.camelcase: 4.3.0 + postcss: 8.5.6 + postcss-load-config: 3.1.4(postcss@8.5.6) + postcss-modules-extract-imports: 3.1.0(postcss@8.5.6) + postcss-modules-local-by-default: 4.2.0(postcss@8.5.6) + postcss-modules-scope: 3.2.1(postcss@8.5.6) + reserved-words: 0.1.2 + sass: 1.93.2 + source-map-js: 1.2.1 + tsconfig-paths: 4.2.0 + typescript: 5.8.2 + optionalDependencies: + stylus: 0.62.0 + transitivePeerDependencies: + - supports-color + - ts-node + typescript@5.4.2: {} typescript@5.8.2: {} @@ -15617,13 +16076,13 @@ snapshots: - utf-8-validate - zod - vite-node@1.6.1(@types/node@22.17.0)(terser@5.43.1): + vite-node@1.6.1(@types/node@22.17.0)(less@4.4.1)(sass@1.93.2)(stylus@0.62.0)(terser@5.43.1): dependencies: cac: 6.7.14 debug: 4.4.1 pathe: 1.1.2 picocolors: 1.1.1 - vite: 5.4.19(@types/node@22.17.0)(terser@5.43.1) + vite: 5.4.19(@types/node@22.17.0)(less@4.4.1)(sass@1.93.2)(stylus@0.62.0)(terser@5.43.1) transitivePeerDependencies: - '@types/node' - less @@ -15635,13 +16094,13 @@ snapshots: - supports-color - terser - vite-node@2.1.9(@types/node@24.3.0)(terser@5.43.1): + vite-node@2.1.9(@types/node@24.3.0)(less@4.4.1)(sass@1.93.2)(stylus@0.62.0)(terser@5.43.1): dependencies: cac: 6.7.14 debug: 4.4.1 es-module-lexer: 1.7.0 pathe: 1.1.2 - vite: 5.4.19(@types/node@24.3.0)(terser@5.43.1) + vite: 5.4.19(@types/node@24.3.0)(less@4.4.1)(sass@1.93.2)(stylus@0.62.0)(terser@5.43.1) transitivePeerDependencies: - '@types/node' - less @@ -15653,7 +16112,7 @@ snapshots: - supports-color - terser - vite-plugin-dts@3.9.1(@types/node@20.19.9)(rollup@4.46.2)(typescript@5.8.2)(vite@5.4.19(@types/node@20.19.9)(terser@5.43.1)): + vite-plugin-dts@3.9.1(@types/node@20.19.9)(rollup@4.46.2)(typescript@5.8.2)(vite@5.4.19(@types/node@20.19.9)(less@4.4.1)(sass@1.93.2)(stylus@0.62.0)(terser@5.43.1)): dependencies: '@microsoft/api-extractor': 7.43.0(@types/node@20.19.9) '@rollup/pluginutils': 5.2.0(rollup@4.46.2) @@ -15664,7 +16123,7 @@ snapshots: typescript: 5.8.2 vue-tsc: 1.8.27(typescript@5.8.2) optionalDependencies: - vite: 5.4.19(@types/node@20.19.9)(terser@5.43.1) + vite: 5.4.19(@types/node@20.19.9)(less@4.4.1)(sass@1.93.2)(stylus@0.62.0)(terser@5.43.1) transitivePeerDependencies: - '@types/node' - rollup @@ -15674,18 +16133,18 @@ snapshots: dependencies: favicons: 7.2.0 - vite-tsconfig-paths@5.1.4(typescript@5.8.2)(vite@5.4.19(@types/node@20.19.9)(terser@5.43.1)): + vite-tsconfig-paths@5.1.4(typescript@5.8.2)(vite@5.4.19(@types/node@20.19.9)(less@4.4.1)(sass@1.93.2)(stylus@0.62.0)(terser@5.43.1)): dependencies: debug: 4.4.1 globrex: 0.1.2 tsconfck: 3.1.6(typescript@5.8.2) optionalDependencies: - vite: 5.4.19(@types/node@20.19.9)(terser@5.43.1) + vite: 5.4.19(@types/node@20.19.9)(less@4.4.1)(sass@1.93.2)(stylus@0.62.0)(terser@5.43.1) transitivePeerDependencies: - supports-color - typescript - vite@5.4.19(@types/node@20.19.9)(terser@5.43.1): + vite@5.4.19(@types/node@20.19.9)(less@4.4.1)(sass@1.93.2)(stylus@0.62.0)(terser@5.43.1): dependencies: esbuild: 0.21.5 postcss: 8.5.6 @@ -15693,9 +16152,12 @@ snapshots: optionalDependencies: '@types/node': 20.19.9 fsevents: 2.3.3 + less: 4.4.1 + sass: 1.93.2 + stylus: 0.62.0 terser: 5.43.1 - vite@5.4.19(@types/node@22.17.0)(terser@5.43.1): + vite@5.4.19(@types/node@22.17.0)(less@4.4.1)(sass@1.93.2)(stylus@0.62.0)(terser@5.43.1): dependencies: esbuild: 0.21.5 postcss: 8.5.6 @@ -15703,9 +16165,12 @@ snapshots: optionalDependencies: '@types/node': 22.17.0 fsevents: 2.3.3 + less: 4.4.1 + sass: 1.93.2 + stylus: 0.62.0 terser: 5.43.1 - vite@5.4.19(@types/node@24.3.0)(terser@5.43.1): + vite@5.4.19(@types/node@24.3.0)(less@4.4.1)(sass@1.93.2)(stylus@0.62.0)(terser@5.43.1): dependencies: esbuild: 0.21.5 postcss: 8.5.6 @@ -15713,9 +16178,12 @@ snapshots: optionalDependencies: '@types/node': 24.3.0 fsevents: 2.3.3 + less: 4.4.1 + sass: 1.93.2 + stylus: 0.62.0 terser: 5.43.1 - vitest@1.6.1(@types/node@22.17.0)(terser@5.43.1): + vitest@1.6.1(@types/node@22.17.0)(less@4.4.1)(sass@1.93.2)(stylus@0.62.0)(terser@5.43.1): dependencies: '@vitest/expect': 1.6.1 '@vitest/runner': 1.6.1 @@ -15734,8 +16202,8 @@ snapshots: strip-literal: 2.1.1 tinybench: 2.9.0 tinypool: 0.8.4 - vite: 5.4.19(@types/node@22.17.0)(terser@5.43.1) - vite-node: 1.6.1(@types/node@22.17.0)(terser@5.43.1) + vite: 5.4.19(@types/node@22.17.0)(less@4.4.1)(sass@1.93.2)(stylus@0.62.0)(terser@5.43.1) + vite-node: 1.6.1(@types/node@22.17.0)(less@4.4.1)(sass@1.93.2)(stylus@0.62.0)(terser@5.43.1) why-is-node-running: 2.3.0 optionalDependencies: '@types/node': 22.17.0