Skip to content
Merged
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
61 changes: 55 additions & 6 deletions apps/loopover-ui/src/routeTree.gen.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,11 @@ import { Route as AppRouteImport } from './routes/app'
import { Route as ApiRouteImport } from './routes/api'
import { Route as AgentsRouteImport } from './routes/agents'
import { Route as IndexRouteImport } from './routes/index'
import { Route as InstallIndexRouteImport } from './routes/install.index'
import { Route as DocsIndexRouteImport } from './routes/docs.index'
import { Route as AppIndexRouteImport } from './routes/app.index'
import { Route as ApiIndexRouteImport } from './routes/api.index'
import { Route as InstallPermissionsRouteImport } from './routes/install.permissions'
import { Route as DocsUpstreamDriftRouteImport } from './routes/docs.upstream-drift'
import { Route as DocsTuningRouteImport } from './routes/docs.tuning'
import { Route as DocsTroubleshootingRouteImport } from './routes/docs.troubleshooting'
Expand Down Expand Up @@ -142,6 +144,11 @@ const IndexRoute = IndexRouteImport.update({
path: '/',
getParentRoute: () => rootRouteImport,
} as any)
const InstallIndexRoute = InstallIndexRouteImport.update({
id: '/',
path: '/',
getParentRoute: () => InstallRoute,
} as any)
const DocsIndexRoute = DocsIndexRouteImport.update({
id: '/',
path: '/',
Expand All @@ -157,6 +164,11 @@ const ApiIndexRoute = ApiIndexRouteImport.update({
path: '/',
getParentRoute: () => ApiRoute,
} as any)
const InstallPermissionsRoute = InstallPermissionsRouteImport.update({
id: '/permissions',
path: '/permissions',
getParentRoute: () => InstallRoute,
} as any)
const DocsUpstreamDriftRoute = DocsUpstreamDriftRouteImport.update({
id: '/upstream-drift',
path: '/upstream-drift',
Expand Down Expand Up @@ -498,7 +510,7 @@ export interface FileRoutesByFullPath {
'/changelog': typeof ChangelogRoute
'/docs': typeof DocsRouteWithChildren
'/extension': typeof ExtensionRoute
'/install': typeof InstallRoute
'/install': typeof InstallRouteWithChildren
'/maintainers': typeof MaintainersRoute
'/miners': typeof MinersRoute
'/roadmap': typeof RoadmapRoute
Expand Down Expand Up @@ -564,17 +576,18 @@ export interface FileRoutesByFullPath {
'/docs/troubleshooting': typeof DocsTroubleshootingRoute
'/docs/tuning': typeof DocsTuningRoute
'/docs/upstream-drift': typeof DocsUpstreamDriftRoute
'/install/permissions': typeof InstallPermissionsRoute
'/api/': typeof ApiIndexRoute
'/app/': typeof AppIndexRoute
'/docs/': typeof DocsIndexRoute
'/install/': typeof InstallIndexRoute
'/repos/$owner/$repo/quality': typeof ReposOwnerRepoQualityRoute
}
export interface FileRoutesByTo {
'/': typeof IndexRoute
'/agents': typeof AgentsRoute
'/changelog': typeof ChangelogRoute
'/extension': typeof ExtensionRoute
'/install': typeof InstallRoute
'/maintainers': typeof MaintainersRoute
'/miners': typeof MinersRoute
'/roadmap': typeof RoadmapRoute
Expand Down Expand Up @@ -640,9 +653,11 @@ export interface FileRoutesByTo {
'/docs/troubleshooting': typeof DocsTroubleshootingRoute
'/docs/tuning': typeof DocsTuningRoute
'/docs/upstream-drift': typeof DocsUpstreamDriftRoute
'/install/permissions': typeof InstallPermissionsRoute
'/api': typeof ApiIndexRoute
'/app': typeof AppIndexRoute
'/docs': typeof DocsIndexRoute
'/install': typeof InstallIndexRoute
'/repos/$owner/$repo/quality': typeof ReposOwnerRepoQualityRoute
}
export interface FileRoutesById {
Expand All @@ -654,7 +669,7 @@ export interface FileRoutesById {
'/changelog': typeof ChangelogRoute
'/docs': typeof DocsRouteWithChildren
'/extension': typeof ExtensionRoute
'/install': typeof InstallRoute
'/install': typeof InstallRouteWithChildren
'/maintainers': typeof MaintainersRoute
'/miners': typeof MinersRoute
'/roadmap': typeof RoadmapRoute
Expand Down Expand Up @@ -720,9 +735,11 @@ export interface FileRoutesById {
'/docs/troubleshooting': typeof DocsTroubleshootingRoute
'/docs/tuning': typeof DocsTuningRoute
'/docs/upstream-drift': typeof DocsUpstreamDriftRoute
'/install/permissions': typeof InstallPermissionsRoute
'/api/': typeof ApiIndexRoute
'/app/': typeof AppIndexRoute
'/docs/': typeof DocsIndexRoute
'/install/': typeof InstallIndexRoute
'/repos/$owner/$repo/quality': typeof ReposOwnerRepoQualityRoute
}
export interface FileRouteTypes {
Expand Down Expand Up @@ -801,17 +818,18 @@ export interface FileRouteTypes {
| '/docs/troubleshooting'
| '/docs/tuning'
| '/docs/upstream-drift'
| '/install/permissions'
| '/api/'
| '/app/'
| '/docs/'
| '/install/'
| '/repos/$owner/$repo/quality'
fileRoutesByTo: FileRoutesByTo
to:
| '/'
| '/agents'
| '/changelog'
| '/extension'
| '/install'
| '/maintainers'
| '/miners'
| '/roadmap'
Expand Down Expand Up @@ -877,9 +895,11 @@ export interface FileRouteTypes {
| '/docs/troubleshooting'
| '/docs/tuning'
| '/docs/upstream-drift'
| '/install/permissions'
| '/api'
| '/app'
| '/docs'
| '/install'
| '/repos/$owner/$repo/quality'
id:
| '__root__'
Expand Down Expand Up @@ -956,9 +976,11 @@ export interface FileRouteTypes {
| '/docs/troubleshooting'
| '/docs/tuning'
| '/docs/upstream-drift'
| '/install/permissions'
| '/api/'
| '/app/'
| '/docs/'
| '/install/'
| '/repos/$owner/$repo/quality'
fileRoutesById: FileRoutesById
}
Expand All @@ -970,7 +992,7 @@ export interface RootRouteChildren {
ChangelogRoute: typeof ChangelogRoute
DocsRoute: typeof DocsRouteWithChildren
ExtensionRoute: typeof ExtensionRoute
InstallRoute: typeof InstallRoute
InstallRoute: typeof InstallRouteWithChildren
MaintainersRoute: typeof MaintainersRoute
MinersRoute: typeof MinersRoute
RoadmapRoute: typeof RoadmapRoute
Expand Down Expand Up @@ -1056,6 +1078,13 @@ declare module '@tanstack/react-router' {
preLoaderRoute: typeof IndexRouteImport
parentRoute: typeof rootRouteImport
}
'/install/': {
id: '/install/'
path: '/'
fullPath: '/install/'
preLoaderRoute: typeof InstallIndexRouteImport
parentRoute: typeof InstallRoute
}
'/docs/': {
id: '/docs/'
path: '/'
Expand All @@ -1077,6 +1106,13 @@ declare module '@tanstack/react-router' {
preLoaderRoute: typeof ApiIndexRouteImport
parentRoute: typeof ApiRoute
}
'/install/permissions': {
id: '/install/permissions'
path: '/permissions'
fullPath: '/install/permissions'
preLoaderRoute: typeof InstallPermissionsRouteImport
parentRoute: typeof InstallRoute
}
'/docs/upstream-drift': {
id: '/docs/upstream-drift'
path: '/upstream-drift'
Expand Down Expand Up @@ -1675,6 +1711,19 @@ const DocsRouteChildren: DocsRouteChildren = {

const DocsRouteWithChildren = DocsRoute._addFileChildren(DocsRouteChildren)

interface InstallRouteChildren {
InstallPermissionsRoute: typeof InstallPermissionsRoute
InstallIndexRoute: typeof InstallIndexRoute
}

const InstallRouteChildren: InstallRouteChildren = {
InstallPermissionsRoute: InstallPermissionsRoute,
InstallIndexRoute: InstallIndexRoute,
}

const InstallRouteWithChildren =
InstallRoute._addFileChildren(InstallRouteChildren)

const rootRouteChildren: RootRouteChildren = {
IndexRoute: IndexRoute,
AgentsRoute: AgentsRoute,
Expand All @@ -1683,7 +1732,7 @@ const rootRouteChildren: RootRouteChildren = {
ChangelogRoute: ChangelogRoute,
DocsRoute: DocsRouteWithChildren,
ExtensionRoute: ExtensionRoute,
InstallRoute: InstallRoute,
InstallRoute: InstallRouteWithChildren,
MaintainersRoute: MaintainersRoute,
MinersRoute: MinersRoute,
RoadmapRoute: RoadmapRoute,
Expand Down
139 changes: 139 additions & 0 deletions apps/loopover-ui/src/routes/install.index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
import { createFileRoute, Link } from "@tanstack/react-router";
import { ArrowRight, UserPlus, Github, ShieldCheck } from "lucide-react";

import { Section, SectionTitle, Card, Callout, Eyebrow } from "@/components/site/primitives";

// Self-serve signup & App-install entry surface (part of #4802). This is the front door of the
// signup -> install -> confirm-scoped-permissions flow: it explains the three steps and routes a new
// customer to the existing GitHub App configuration guide, with a link to the scoped-permissions
// confirmation step (/install/permissions). It reads no secrets and changes no auth backend -- the
// install itself is completed on GitHub.

export const Route = createFileRoute("/install/")({
head: () => ({
meta: [
{ title: "Install LoopOver — self-serve setup" },
{
name: "description",
content:
"Sign up, install the LoopOver App on your own repository, and confirm the scoped permissions being granted — self-serve, no engineering step required.",
},
{ property: "og:title", content: "Install LoopOver — self-serve setup" },
{
property: "og:description",
content:
"A self-serve signup-through-install flow: connect your repository and grant scoped permissions.",
},
{ property: "og:url", content: "/install" },
],
links: [{ rel: "canonical", href: "/install" }],
}),
component: InstallPage,
});

const STEPS = [
{
icon: UserPlus,
title: "Sign up",
description:
"Create your account. No engineering involvement — you own the connection to your repository from the start.",
},
{
icon: Github,
title: "Install the App",
description:
"Add the LoopOver GitHub App to the repositories you want reviewed. You choose which repos it can see.",
},
{
icon: ShieldCheck,
title: "Confirm scoped permissions",
description:
"Review exactly which permissions you're granting before you finish — scoped to what review needs, nothing more.",
to: "/install/permissions" as const,
},
];

export function InstallPage() {
return (
<>
<Section className="pt-16 pb-12 sm:pt-24">
<div className="max-w-3xl">
<Eyebrow accent>Self-serve setup</Eyebrow>
<h1 className="mt-4 text-token-2xl font-medium tracking-tight text-foreground">
Connect your repository in three steps.
</h1>
<p className="mt-4 max-w-2xl text-token-md text-muted-foreground">
Sign up, install the LoopOver App on your own repository, and confirm the scoped
permissions being granted — without a manual or engineering step.
</p>
<div className="mt-7 flex flex-wrap items-center gap-2">
<Link
to="/docs/github-app"
className="inline-flex h-9 items-center justify-center gap-1.5 whitespace-nowrap rounded-token bg-coral px-4 text-token-sm font-medium text-primary-foreground transition-[filter,transform] duration-150 hover:brightness-110 active:scale-[0.98] focus-ring motion-reduce:transition-none motion-reduce:active:scale-100"
>
Install on GitHub
<ArrowRight className="size-3.5" />
</Link>
<Link
to="/docs/beta-onboarding"
className="inline-flex h-9 items-center justify-center gap-1.5 whitespace-nowrap rounded-token border border-border bg-transparent px-4 text-token-sm font-medium text-foreground transition-colors duration-150 hover:bg-accent focus-ring motion-reduce:transition-none"
>
Read the onboarding guide
</Link>
</div>
</div>
</Section>

<Section className="py-0">
<SectionTitle
eyebrow="How it works"
title="From signup to a connected repo"
description="Each step is self-serve. You stay in control of which repositories the App can access and what it's allowed to do."
/>
<div className="mt-8 grid gap-4 sm:grid-cols-3">
{STEPS.map((step, index) => {
const Icon = step.icon;
return (
<Card key={step.title}>
<div className="flex items-center gap-2 text-muted-foreground">
<Icon aria-hidden className="size-4" />
<span className="font-mono text-token-2xs uppercase tracking-wider">
Step {index + 1}
</span>
</div>
<h3 className="mt-3 text-token-md font-medium text-foreground">{step.title}</h3>
<p className="mt-1.5 text-token-sm text-muted-foreground">{step.description}</p>
{"to" in step && step.to ? (
<Link
to={step.to}
className="mt-3 inline-flex items-center gap-1 text-token-sm font-medium text-foreground underline underline-offset-2 focus-ring"
>
Review the exact scopes
<ArrowRight className="size-3.5" />
</Link>
) : null}
</Card>
);
})}
</div>
</Section>

<Section className="pt-12 pb-24">
<div className="max-w-3xl">
<Callout variant="safety" title="Scoped by design">
LoopOver requests only the permissions review needs, on only the repositories you
choose. You confirm the exact scopes on GitHub before the install completes, and you can
review or revoke them at any time from your repository's installed-Apps settings. See{" "}
<Link
to="/docs/privacy-security"
className="text-foreground underline underline-offset-2"
>
privacy &amp; security
</Link>{" "}
for what is and isn't stored.
</Callout>
</div>
</Section>
</>
);
}
Loading
Loading