mirror of
https://github.com/harivansh-afk/sandbox-agent.git
synced 2026-04-17 18:02:36 +00:00
Rename Foundry handoffs to tasks (#239)
* Restore foundry onboarding stack * Consolidate foundry rename * Create foundry tasks without prompts * Rename Foundry handoffs to tasks
This commit is contained in:
parent
d30cc0bcc8
commit
d75e8c31d1
281 changed files with 9242 additions and 4356 deletions
342
foundry/packages/frontend/src/app/router.tsx
Normal file
342
foundry/packages/frontend/src/app/router.tsx
Normal file
|
|
@ -0,0 +1,342 @@
|
|||
import { type ReactNode, useEffect } from "react";
|
||||
import { setFrontendErrorContext } from "@sandbox-agent/foundry-frontend-errors/client";
|
||||
import type { FoundryBillingPlanId } from "@sandbox-agent/foundry-shared";
|
||||
import { Navigate, Outlet, createRootRoute, createRoute, createRouter, useRouterState } from "@tanstack/react-router";
|
||||
import { MockLayout } from "../components/mock-layout";
|
||||
import {
|
||||
MockHostedCheckoutPage,
|
||||
MockOrganizationBillingPage,
|
||||
MockOrganizationSelectorPage,
|
||||
MockOrganizationSettingsPage,
|
||||
MockSignInPage,
|
||||
} from "../components/mock-onboarding";
|
||||
import { defaultWorkspaceId, isMockFrontendClient } from "../lib/env";
|
||||
import { activeMockOrganization, getMockOrganizationById, isAppSnapshotBootstrapping, useMockAppClient, useMockAppSnapshot } from "../lib/mock-app";
|
||||
import { taskWorkbenchClient } from "../lib/workbench";
|
||||
|
||||
const rootRoute = createRootRoute({
|
||||
component: RootLayout,
|
||||
});
|
||||
|
||||
const indexRoute = createRoute({
|
||||
getParentRoute: () => rootRoute,
|
||||
path: "/",
|
||||
component: IndexRoute,
|
||||
});
|
||||
|
||||
const signInRoute = createRoute({
|
||||
getParentRoute: () => rootRoute,
|
||||
path: "/signin",
|
||||
component: SignInRoute,
|
||||
});
|
||||
|
||||
const organizationsRoute = createRoute({
|
||||
getParentRoute: () => rootRoute,
|
||||
path: "/organizations",
|
||||
component: OrganizationsRoute,
|
||||
});
|
||||
|
||||
const organizationSettingsRoute = createRoute({
|
||||
getParentRoute: () => rootRoute,
|
||||
path: "/organizations/$organizationId/settings",
|
||||
component: OrganizationSettingsRoute,
|
||||
});
|
||||
|
||||
const organizationBillingRoute = createRoute({
|
||||
getParentRoute: () => rootRoute,
|
||||
path: "/organizations/$organizationId/billing",
|
||||
component: OrganizationBillingRoute,
|
||||
});
|
||||
|
||||
const organizationCheckoutRoute = createRoute({
|
||||
getParentRoute: () => rootRoute,
|
||||
path: "/organizations/$organizationId/checkout/$planId",
|
||||
component: OrganizationCheckoutRoute,
|
||||
});
|
||||
|
||||
const workspaceRoute = createRoute({
|
||||
getParentRoute: () => rootRoute,
|
||||
path: "/workspaces/$workspaceId",
|
||||
component: WorkspaceLayoutRoute,
|
||||
});
|
||||
|
||||
const workspaceIndexRoute = createRoute({
|
||||
getParentRoute: () => workspaceRoute,
|
||||
path: "/",
|
||||
component: WorkspaceRoute,
|
||||
});
|
||||
|
||||
const taskRoute = createRoute({
|
||||
getParentRoute: () => workspaceRoute,
|
||||
path: "tasks/$taskId",
|
||||
validateSearch: (search: Record<string, unknown>) => ({
|
||||
sessionId: typeof search.sessionId === "string" && search.sessionId.trim().length > 0 ? search.sessionId : undefined,
|
||||
}),
|
||||
component: TaskRoute,
|
||||
});
|
||||
|
||||
const repoRoute = createRoute({
|
||||
getParentRoute: () => workspaceRoute,
|
||||
path: "repos/$repoId",
|
||||
component: RepoRoute,
|
||||
});
|
||||
|
||||
const routeTree = rootRoute.addChildren([
|
||||
indexRoute,
|
||||
signInRoute,
|
||||
organizationsRoute,
|
||||
organizationSettingsRoute,
|
||||
organizationBillingRoute,
|
||||
organizationCheckoutRoute,
|
||||
workspaceRoute.addChildren([workspaceIndexRoute, taskRoute, repoRoute]),
|
||||
]);
|
||||
|
||||
export const router = createRouter({ routeTree });
|
||||
|
||||
declare module "@tanstack/react-router" {
|
||||
interface Register {
|
||||
router: typeof router;
|
||||
}
|
||||
}
|
||||
|
||||
function WorkspaceLayoutRoute() {
|
||||
return <Outlet />;
|
||||
}
|
||||
|
||||
function AppLoadingScreen({ label }: { label: string }) {
|
||||
return (
|
||||
<div
|
||||
style={{
|
||||
minHeight: "100dvh",
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
justifyContent: "center",
|
||||
background:
|
||||
"radial-gradient(circle at top left, rgba(255, 79, 0, 0.16), transparent 28%), radial-gradient(circle at top right, rgba(24, 140, 255, 0.18), transparent 32%), #050505",
|
||||
color: "#ffffff",
|
||||
fontSize: "16px",
|
||||
fontWeight: 700,
|
||||
}}
|
||||
>
|
||||
{label}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function IndexRoute() {
|
||||
const snapshot = useMockAppSnapshot();
|
||||
if (!isMockFrontendClient && isAppSnapshotBootstrapping(snapshot)) {
|
||||
return <AppLoadingScreen label="Restoring Foundry session..." />;
|
||||
}
|
||||
if (snapshot.auth.status === "signed_out") {
|
||||
return <Navigate to="/signin" replace />;
|
||||
}
|
||||
|
||||
const activeOrganization = activeMockOrganization(snapshot);
|
||||
if (activeOrganization) {
|
||||
return <Navigate to="/workspaces/$workspaceId" params={{ workspaceId: activeOrganization.workspaceId }} replace />;
|
||||
}
|
||||
|
||||
return <Navigate to="/organizations" replace />;
|
||||
}
|
||||
|
||||
function SignInRoute() {
|
||||
const snapshot = useMockAppSnapshot();
|
||||
if (!isMockFrontendClient && isAppSnapshotBootstrapping(snapshot)) {
|
||||
return <AppLoadingScreen label="Restoring Foundry session..." />;
|
||||
}
|
||||
if (snapshot.auth.status === "signed_in") {
|
||||
return <IndexRoute />;
|
||||
}
|
||||
|
||||
return <MockSignInPage />;
|
||||
}
|
||||
|
||||
function OrganizationsRoute() {
|
||||
const snapshot = useMockAppSnapshot();
|
||||
if (!isMockFrontendClient && isAppSnapshotBootstrapping(snapshot)) {
|
||||
return <AppLoadingScreen label="Loading organizations..." />;
|
||||
}
|
||||
if (snapshot.auth.status === "signed_out") {
|
||||
return <Navigate to="/signin" replace />;
|
||||
}
|
||||
|
||||
return <MockOrganizationSelectorPage />;
|
||||
}
|
||||
|
||||
function OrganizationSettingsRoute() {
|
||||
const snapshot = useMockAppSnapshot();
|
||||
if (!isMockFrontendClient && isAppSnapshotBootstrapping(snapshot)) {
|
||||
return <AppLoadingScreen label="Loading organization settings..." />;
|
||||
}
|
||||
if (snapshot.auth.status === "signed_out") {
|
||||
return <Navigate to="/signin" replace />;
|
||||
}
|
||||
|
||||
const { organizationId } = organizationSettingsRoute.useParams();
|
||||
const organization = getMockOrganizationById(snapshot, organizationId);
|
||||
if (!organization) {
|
||||
return <Navigate to="/organizations" replace />;
|
||||
}
|
||||
|
||||
return <MockOrganizationSettingsPage organization={organization} />;
|
||||
}
|
||||
|
||||
function OrganizationBillingRoute() {
|
||||
const snapshot = useMockAppSnapshot();
|
||||
if (!isMockFrontendClient && isAppSnapshotBootstrapping(snapshot)) {
|
||||
return <AppLoadingScreen label="Loading billing..." />;
|
||||
}
|
||||
if (snapshot.auth.status === "signed_out") {
|
||||
return <Navigate to="/signin" replace />;
|
||||
}
|
||||
|
||||
const { organizationId } = organizationBillingRoute.useParams();
|
||||
const organization = getMockOrganizationById(snapshot, organizationId);
|
||||
if (!organization) {
|
||||
return <Navigate to="/organizations" replace />;
|
||||
}
|
||||
|
||||
return <MockOrganizationBillingPage organization={organization} />;
|
||||
}
|
||||
|
||||
function OrganizationCheckoutRoute() {
|
||||
const snapshot = useMockAppSnapshot();
|
||||
if (!isMockFrontendClient && isAppSnapshotBootstrapping(snapshot)) {
|
||||
return <AppLoadingScreen label="Loading checkout..." />;
|
||||
}
|
||||
if (snapshot.auth.status === "signed_out") {
|
||||
return <Navigate to="/signin" replace />;
|
||||
}
|
||||
|
||||
const { organizationId, planId } = organizationCheckoutRoute.useParams();
|
||||
const organization = getMockOrganizationById(snapshot, organizationId);
|
||||
if (!organization) {
|
||||
return <Navigate to="/organizations" replace />;
|
||||
}
|
||||
|
||||
return <MockHostedCheckoutPage organization={organization} planId={planId as FoundryBillingPlanId} />;
|
||||
}
|
||||
|
||||
function WorkspaceRoute() {
|
||||
const { workspaceId } = workspaceRoute.useParams();
|
||||
return (
|
||||
<AppWorkspaceGate workspaceId={workspaceId}>
|
||||
<WorkspaceView workspaceId={workspaceId} selectedTaskId={null} selectedSessionId={null} />
|
||||
</AppWorkspaceGate>
|
||||
);
|
||||
}
|
||||
|
||||
function WorkspaceView({
|
||||
workspaceId,
|
||||
selectedTaskId,
|
||||
selectedSessionId,
|
||||
}: {
|
||||
workspaceId: string;
|
||||
selectedTaskId: string | null;
|
||||
selectedSessionId: string | null;
|
||||
}) {
|
||||
useEffect(() => {
|
||||
setFrontendErrorContext({
|
||||
workspaceId,
|
||||
taskId: undefined,
|
||||
});
|
||||
}, [workspaceId]);
|
||||
|
||||
return <MockLayout workspaceId={workspaceId} selectedTaskId={selectedTaskId} selectedSessionId={selectedSessionId} />;
|
||||
}
|
||||
|
||||
function TaskRoute() {
|
||||
const { workspaceId, taskId } = taskRoute.useParams();
|
||||
const { sessionId } = taskRoute.useSearch();
|
||||
return (
|
||||
<AppWorkspaceGate workspaceId={workspaceId}>
|
||||
<TaskView workspaceId={workspaceId} taskId={taskId} sessionId={sessionId ?? null} />
|
||||
</AppWorkspaceGate>
|
||||
);
|
||||
}
|
||||
|
||||
function TaskView({ workspaceId, taskId, sessionId }: { workspaceId: string; taskId: string; sessionId: string | null }) {
|
||||
useEffect(() => {
|
||||
setFrontendErrorContext({
|
||||
workspaceId,
|
||||
taskId,
|
||||
repoId: undefined,
|
||||
});
|
||||
}, [taskId, workspaceId]);
|
||||
|
||||
return <MockLayout workspaceId={workspaceId} selectedTaskId={taskId} selectedSessionId={sessionId} />;
|
||||
}
|
||||
|
||||
function RepoRoute() {
|
||||
const { workspaceId, repoId } = repoRoute.useParams();
|
||||
return (
|
||||
<AppWorkspaceGate workspaceId={workspaceId}>
|
||||
<RepoRouteInner workspaceId={workspaceId} repoId={repoId} />
|
||||
</AppWorkspaceGate>
|
||||
);
|
||||
}
|
||||
|
||||
function AppWorkspaceGate({ workspaceId, children }: { workspaceId: string; children: ReactNode }) {
|
||||
const client = useMockAppClient();
|
||||
const snapshot = useMockAppSnapshot();
|
||||
const organization = snapshot.organizations.find((candidate) => candidate.workspaceId === workspaceId) ?? null;
|
||||
|
||||
useEffect(() => {
|
||||
if (organization && snapshot.activeOrganizationId !== organization.id) {
|
||||
void client.selectOrganization(organization.id);
|
||||
}
|
||||
}, [client, organization, snapshot.activeOrganizationId]);
|
||||
|
||||
if (!isMockFrontendClient && isAppSnapshotBootstrapping(snapshot)) {
|
||||
return <AppLoadingScreen label="Loading workspace..." />;
|
||||
}
|
||||
|
||||
if (snapshot.auth.status === "signed_out") {
|
||||
return <Navigate to="/signin" replace />;
|
||||
}
|
||||
|
||||
if (!organization) {
|
||||
return isMockFrontendClient ? <Navigate to="/organizations" replace /> : <Navigate to="/" replace />;
|
||||
}
|
||||
|
||||
return <>{children}</>;
|
||||
}
|
||||
|
||||
function RepoRouteInner({ workspaceId, repoId }: { workspaceId: string; repoId: string }) {
|
||||
useEffect(() => {
|
||||
setFrontendErrorContext({
|
||||
workspaceId,
|
||||
taskId: undefined,
|
||||
repoId,
|
||||
});
|
||||
}, [repoId, workspaceId]);
|
||||
const activeTaskId = taskWorkbenchClient.getSnapshot().tasks.find((task) => task.repoId === repoId)?.id;
|
||||
if (!activeTaskId) {
|
||||
return <Navigate to="/workspaces/$workspaceId" params={{ workspaceId }} replace />;
|
||||
}
|
||||
return <Navigate to="/workspaces/$workspaceId/tasks/$taskId" params={{ workspaceId, taskId: activeTaskId }} search={{ sessionId: undefined }} replace />;
|
||||
}
|
||||
|
||||
function RootLayout() {
|
||||
return (
|
||||
<>
|
||||
<RouteContextSync />
|
||||
<Outlet />
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
function RouteContextSync() {
|
||||
const location = useRouterState({
|
||||
select: (state) => state.location,
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
setFrontendErrorContext({
|
||||
route: `${location.pathname}${location.search}${location.hash}`,
|
||||
});
|
||||
}, [location.hash, location.pathname, location.search]);
|
||||
|
||||
return null;
|
||||
}
|
||||
18
foundry/packages/frontend/src/app/theme.ts
Normal file
18
foundry/packages/frontend/src/app/theme.ts
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
import { createDarkTheme, type Theme } from "baseui";
|
||||
|
||||
export const appTheme: Theme = createDarkTheme({
|
||||
colors: {
|
||||
primary: "#e4e4e7", // zinc-200
|
||||
accent: "#ff4f00", // orange accent (inspector)
|
||||
backgroundPrimary: "#09090b", // darkest — chat center panel
|
||||
backgroundSecondary: "#0f0f11", // slightly lighter — sidebars
|
||||
backgroundTertiary: "#0c0c0e", // center + right panel headers
|
||||
backgroundInversePrimary: "#fafafa",
|
||||
contentPrimary: "#ffffff", // white (inspector --text)
|
||||
contentSecondary: "#a1a1aa", // zinc-400 (inspector --muted)
|
||||
contentTertiary: "#71717a", // zinc-500
|
||||
contentInversePrimary: "#000000",
|
||||
borderOpaque: "rgba(255, 255, 255, 0.10)", // inspector --border
|
||||
borderTransparent: "rgba(255, 255, 255, 0.07)", // inspector --border-2
|
||||
},
|
||||
});
|
||||
Loading…
Add table
Add a link
Reference in a new issue