Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
84 changes: 84 additions & 0 deletions frontend/src/app/connect.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
import { faNodeJs, faReact, Icon } from "@rivet-gg/icons";
import { useSearch } from "@tanstack/react-router";
import type { ComponentProps, Ref } from "react";
import {
Button,
Card,
CardContent,
CardHeader,
CardTitle,
DocsSheet,
} from "@/components";
import { ConnectionForm } from "@/components/connection-form";
import { docsLinks } from "@/content/data";

export function Connect({
onSubmit,
formRef,
}: {
formRef?: Ref<HTMLFormElement>;
onSubmit: ComponentProps<typeof ConnectionForm>["onSubmit"];
}) {
const search = useSearch({ from: "/_context" });
return (
<>
<Card className="sm:w-96 w-full mb-6">
<CardHeader>
<CardTitle>Getting Started</CardTitle>
</CardHeader>
<CardContent>
<p>Get started with one of our quick start guides:</p>
<div className="flex-1 flex flex-col gap-2 mt-4">
<div className="flex flex-row justify-stretch items-center gap-2">
<DocsSheet
path={docsLinks.gettingStarted.node}
title="Node.js & Bun Quickstart"
>
<Button
className="flex-1"
variant="outline"
startIcon={<Icon icon={faNodeJs} />}
>
Node.js & Bun
</Button>
</DocsSheet>
<DocsSheet
path={docsLinks.gettingStarted.react}
title="React Quickstart"
>
<Button
className="flex-1"
variant="outline"
startIcon={<Icon icon={faReact} />}
>
React
</Button>
</DocsSheet>
</div>
</div>
</CardContent>
</Card>

<Card className="sm:w-96">
<CardHeader>
<CardTitle>Connect to Project</CardTitle>
</CardHeader>
<CardContent>
<p className="mb-4">
Connect to your RivetKit project by entering the URL and
access token.
</p>

<ConnectionForm
ref={formRef}
defaultValues={{
username: search.u || "http://localhost:6420",
token: search.t || "",
}}
onSubmit={onSubmit}
/>
</CardContent>
</Card>
</>
);
}
36 changes: 12 additions & 24 deletions frontend/src/components/actors/actors-actor-details.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ import { ActorStopButton } from "./actor-stop-button";
import { ActorsSidebarToggleButton } from "./actors-sidebar-toggle-button";
import { useActorsView } from "./actors-view-context-provider";
import { ActorConsole } from "./console/actor-console";
import { GuardConnectableInspector } from "./guard-connectable-inspector";
import { GuardConnectableInspector, useInspectorGuard } from "./guard-connectable-inspector";
import { useManager } from "./manager-context";
import { ActorFeature, type ActorId } from "./queries";
import { ActorWorkerContextProvider } from "./worker/actor-worker-context";
Expand All @@ -48,29 +48,27 @@ export const ActorsActorDetails = memo(
const supportsConsole = features.includes(ActorFeature.Console);

return (

<GuardConnectableInspector actorId={actorId}>
<ActorDetailsSettingsProvider>
<div className="flex flex-col h-full flex-1">
<ActorTabs
features={features}
actorId={actorId}
tab={tab}
onTabChange={onTabChange}
// onExportLogs={onExportLogs}
// isExportingLogs={isExportingLogs}
/>

{supportsConsole ? (
<ActorWorkerContextProvider
actorId={actorId}
// notifyOnReconnect={features?.includes(
// ActorFeature.InspectReconnectNotification,
// )}
>
<ActorConsole actorId={actorId} />
</ActorWorkerContextProvider>
) : null}
</div>
</ActorDetailsSettingsProvider>
</GuardConnectableInspector>
);
},
);
Expand Down Expand Up @@ -121,6 +119,8 @@ export function ActorTabs({
const defaultTab = supportsState ? "state" : "logs";
const value = disabled ? undefined : tab || defaultTab;

const guardContent = useInspectorGuard();

return (
<Tabs
value={value}
Expand Down Expand Up @@ -220,9 +220,7 @@ export function ActorTabs({
className="min-h-0 flex-1 mt-0 h-full"
>
<Suspense fallback={<ActorLogsTab.Skeleton />}>
<GuardConnectableInspector actorId={actorId}>
<ActorLogsTab actorId={actorId} />
</GuardConnectableInspector>
{guardContent || <ActorLogsTab actorId={actorId} />}
</Suspense>
</TabsContent>
) : null}
Expand All @@ -239,49 +237,39 @@ export function ActorTabs({
value="connections"
className="min-h-0 flex-1 mt-0"
>
<GuardConnectableInspector actorId={actorId}>
<ActorConnectionsTab actorId={actorId} />
</GuardConnectableInspector>
{guardContent ||<ActorConnectionsTab actorId={actorId} />}
</TabsContent>
) : null}
{supportsEvents ? (
<TabsContent
value="events"
className="min-h-0 flex-1 mt-0"
>
<GuardConnectableInspector actorId={actorId}>
<ActorEventsTab actorId={actorId} />
</GuardConnectableInspector>
{guardContent || <ActorEventsTab actorId={actorId} />}
</TabsContent>
) : null}
{supportsDatabase ? (
<TabsContent
value="database"
className="min-h-0 min-w-0 flex-1 mt-0 h-full"
>
<GuardConnectableInspector actorId={actorId}>
<ActorDatabaseTab actorId={actorId} />
</GuardConnectableInspector>
{guardContent || <ActorDatabaseTab actorId={actorId} />}
</TabsContent>
) : null}
{supportsState ? (
<TabsContent
value="state"
className="min-h-0 flex-1 mt-0"
>
<GuardConnectableInspector actorId={actorId}>
<ActorStateTab actorId={actorId} />
</GuardConnectableInspector>
{guardContent || <ActorStateTab actorId={actorId} />}
</TabsContent>
) : null}
{supportsMetrics ? (
<TabsContent
value="metrics"
className="min-h-0 flex-1 mt-0 h-full"
>
<GuardConnectableInspector actorId={actorId}>
<ActorMetricsTab actorId={actorId} />
</GuardConnectableInspector>
{guardContent || <ActorMetricsTab actorId={actorId} />}
</TabsContent>
) : null}
</>
Expand Down
7 changes: 4 additions & 3 deletions frontend/src/components/actors/console/actor-console.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,8 @@ export function ActorConsole({ actorId }: ActorConsoleProps) {
const status = useActorWorkerStatus();
const managerQueries = useManager();
const actorQueries = useActor();
const { data: destroyedAt } = useQuery(
managerQueries.actorDestroyedAtQueryOptions(actorId),
const { data: { destroyedAt, sleepingAt } = {} } = useQuery(
managerQueries.actorWorkerQueryOptions(actorId),
);
const { isSuccess, isError, isLoading } = useQuery(
actorQueries.actorPingQueryOptions(actorId, {
Expand All @@ -31,7 +31,8 @@ export function ActorConsole({ actorId }: ActorConsoleProps) {
}),
);

const isBlocked = status.type !== "ready" || !isSuccess || !!destroyedAt;
const isBlocked =
status.type !== "ready" || !isSuccess || !!destroyedAt || !!sleepingAt;

const combinedStatus = isError
? "error"
Expand Down
82 changes: 56 additions & 26 deletions frontend/src/components/actors/guard-connectable-inspector.tsx
Original file line number Diff line number Diff line change
@@ -1,22 +1,26 @@
import { faPowerOff, faSpinnerThird, Icon } from "@rivet-gg/icons";
import { useMutation, useQuery, useSuspenseQuery } from "@tanstack/react-query";
import { useMatch } from "@tanstack/react-router";
import { type ReactNode, useMemo } from "react";
import { createContext, type ReactNode, useContext, useMemo } from "react";
import { useInspectorCredentials } from "@/app/credentials-context";
import { createEngineActorContext } from "@/queries/actor-engine";
import { createInspectorActorContext } from "@/queries/actor-inspector";
import {
type NamespaceNameId,
runnerByNameQueryOptions,
} from "@/queries/manager-engine";
import { DiscreteCopyButton } from "../copy-area";
import { getConfig } from "../lib/config";
import { Button } from "../ui/button";
import { useFiltersValue } from "./actor-filters-context";
import { ActorProvider } from "./actor-queries-context";
import { Info } from "./actor-state-tab";
import { useManager } from "./manager-context";
import type { ActorId } from "./queries";

const InspectorGuardContext = createContext<ReactNode | null>(null);

export const useInspectorGuard = () => useContext(InspectorGuardContext);

interface GuardConnectableInspectorProps {
actorId: ActorId;
children: ReactNode;
Expand All @@ -35,31 +39,56 @@ export function GuardConnectableInspector({
});

if (destroyedAt) {
return <Info>Unavailable for inactive Actors.</Info>;
return (
<InspectorGuardContext.Provider
value={<Info>Unavailable for inactive Actors.</Info>}
>
{children}
</InspectorGuardContext.Provider>
);
}

if (sleepingAt) {
if (filters.wakeOnSelect?.value?.[0] === "1") {
return (
<Info>
<AutoWakeUpActor actorId={actorId} />
</Info>
<InspectorGuardContext.Provider
value={
<Info>
<AutoWakeUpActor actorId={actorId} />
</Info>
}
>
{children}
</InspectorGuardContext.Provider>
);
}
return (
<Info>
<p>Unavailable for sleeping Actors.</p>
<WakeUpActorButton actorId={actorId} />
</Info>
<InspectorGuardContext.Provider
value={
<Info>
<p>Unavailable for sleeping Actors.</p>
<WakeUpActorButton actorId={actorId} />
</Info>
}
>
{children}
</InspectorGuardContext.Provider>
);
}

if (pendingAllocationAt && !startedAt) {
return (
<Info>
Cannot start Actor, runners are out of capacity. Add more
runners to run the Actor or increase runner capacity.
</Info>
<InspectorGuardContext.Provider
value={
<Info>
Cannot start Actor, runners are out of capacity. Add
more runners to run the Actor or increase runner
capacity.
</Info>
}
>
{children}
</InspectorGuardContext.Provider>
);
}

Expand All @@ -81,14 +110,7 @@ function ActorContextProvider(props: {
);
}

function ActorInspectorProvider({
actorId,
children,
}: {
actorId: ActorId;
children: ReactNode;
}) {
const { data } = useSuspenseQuery(useManager().actorQueryOptions(actorId));
function ActorInspectorProvider({ children }: { children: ReactNode }) {
const { credentials } = useInspectorCredentials();

if (!credentials?.url || !credentials?.token) {
Expand All @@ -98,9 +120,8 @@ function ActorInspectorProvider({
const actorContext = useMemo(() => {
return createInspectorActorContext({
...credentials,
name: data.name || "",
});
}, [credentials, data.name]);
}, [credentials]);

return <ActorProvider value={actorContext}>{children}</ActorProvider>;
}
Expand Down Expand Up @@ -133,7 +154,8 @@ function useActorEngineContext({ actorId }: { actorId: ActorId }) {
const { actor, runner } = useActorRunner({ actorId });

const actorContext = useMemo(() => {
return createEngineActorContext({
return createInspectorActorContext({
url: getConfig().apiUrl,
token: (runner?.metadata?.inspectorToken as string) || "",
});
}, [runner?.metadata?.inspectorToken]);
Expand All @@ -152,7 +174,15 @@ function ActorEngineProvider({

if (!runner || !actor.runner) {
return (
<NoRunnerInfo runner={runner?.name || actor.runner || "unknown"} />
<InspectorGuardContext.Provider
value={
<NoRunnerInfo
runner={runner?.name || actor.runner || "unknown"}
/>
}
>
{children}
</InspectorGuardContext.Provider>
);
}

Expand Down
1 change: 1 addition & 0 deletions frontend/src/components/actors/manager-context.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -296,6 +296,7 @@ const defaultContext = {
destroyedAt: data.destroyedAt
? new Date(data.destroyedAt)
: null,
sleepingAt: data.sleepingAt ? new Date(data.sleepingAt) : null,
startedAt: data.startedAt ? new Date(data.startedAt) : null,
}),
});
Expand Down
Loading
Loading