Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
3 changes: 2 additions & 1 deletion biome.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,8 @@
"!**/node_modules",
"!**/dist",
"!**/web-dist",
"!**/drizzle"
"!**/drizzle",
"!packages/web/src/routeTree.gen.ts"
]
},
"linter": {
Expand Down
2 changes: 1 addition & 1 deletion packages/cli/src/show.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ export async function show(jsonPath: string): Promise<void> {
routes: [...runRoutes(db), ...viewStateRoutes(db), ...diffRoutes(db)],
});
const { port } = handle;
const url = `http://${LOOPBACK_HOST}:${port}/#/runs/${runId}`;
const url = `http://${LOOPBACK_HOST}:${port}/runs/${encodeURIComponent(runId)}`;

process.stdout.write(`Listening on ${url}\n`);
process.stdout.write("Press Ctrl+C to exit.\n");
Expand Down
2 changes: 2 additions & 0 deletions packages/web/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
"@radix-ui/react-tooltip": "^1.2.8",
"@stage-cli/types": "workspace:*",
"@tanstack/react-query": "^5.100.7",
"@tanstack/react-router": "^1.169.1",
"class-variance-authority": "^0.7.1",
"clsx": "^2.1.1",
"lucide-react": "^0.562.0",
Expand All @@ -34,6 +35,7 @@
},
"devDependencies": {
"@tailwindcss/vite": "^4.1.18",
"@tanstack/router-plugin": "^1.167.32",
"@testing-library/react": "^16.3.2",
"@types/react": "^19.2.5",
"@types/react-dom": "^19.2.3",
Expand Down
27 changes: 0 additions & 27 deletions packages/web/src/App.tsx

This file was deleted.

24 changes: 24 additions & 0 deletions packages/web/src/app/__root.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import type { QueryClient } from "@tanstack/react-query";
import { createRootRouteWithContext, Outlet, redirect } from "@tanstack/react-router";

export const Route = createRootRouteWithContext<{
queryClient: QueryClient;
}>()({
beforeLoad: ({ location }) => {
if (location.hash.startsWith("#/runs/")) {
throw redirect({
to: location.hash.slice(1),
replace: true,
Comment thread
dastratakos marked this conversation as resolved.
Outdated
});
}
},
component: RootLayout,
});

function RootLayout() {
return (
<div className="flex min-h-screen flex-col bg-background text-foreground">
<Outlet />
</div>
);
}
23 changes: 23 additions & 0 deletions packages/web/src/app/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { createFileRoute } from "@tanstack/react-router";
import { Topbar } from "@/components/layout/topbar";

export const Route = createFileRoute("/")({
component: NoRunSelected,
});

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Legacy hash URL redirect is missing

Medium Severity

The PR description states it preserves "a legacy #/runs/... redirect," and the old useHashRunId hook is deleted, but no redirect from hash-based URLs to path-based URLs exists anywhere in the codebase. A user arriving at /#/runs/abc (e.g. from browser history or a cached link) lands on the / route and sees "No run selected" instead of being redirected to /runs/abc. The PR discussion references a fix in commit 041833a, but the redirect logic is absent from the final code.

Additional Locations (1)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit db957b3. Configure here.


function NoRunSelected() {
return (
<>
<Topbar runId={null} />
<div className="flex flex-1 items-center justify-center p-6">
<div className="max-w-md text-center">
<h1 className="font-semibold text-lg">No run selected</h1>
<p className="mt-2 text-muted-foreground text-sm">
The URL is missing a <code>/runs/&lt;runId&gt;</code> path. Open the app via{" "}
<code>stage-cli show &lt;path&gt;</code>.
</p>
</div>
</div>
</>
);
}
11 changes: 11 additions & 0 deletions packages/web/src/app/runs.$runId.files.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { createFileRoute } from "@tanstack/react-router";
import { FilesPage } from "@/routes/files-page";

export const Route = createFileRoute("/runs/$runId/files")({
component: FilesRoute,
});

function FilesRoute() {
const { runId } = Route.useParams();
return <FilesPage runId={runId} />;
}
31 changes: 31 additions & 0 deletions packages/web/src/app/runs.$runId.index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import { createFileRoute } from "@tanstack/react-router";
import { useMemo } from "react";
import { useChapters } from "@/lib/use-chapters";
import { useViewStateData } from "@/lib/use-view-state";
import { ChaptersIndexPage } from "@/routes/chapters-index-page";

export const Route = createFileRoute("/runs/$runId/")({
component: ChaptersRoute,
});

function ChaptersRoute() {
const { runId } = Route.useParams();
const { data, isLoading } = useChapters(runId);
const { chapterIdSet } = useViewStateData(runId);
const chapters = data?.chapters;
const viewedCount = useMemo(() => {
if (!chapters) return 0;
let n = 0;
for (const chapter of chapters) if (chapterIdSet.has(chapter.externalId)) n++;
return n;
}, [chapters, chapterIdSet]);
Comment thread
dastratakos marked this conversation as resolved.
Outdated
Comment thread
cursor[bot] marked this conversation as resolved.
Outdated

return (
<ChaptersIndexPage
chapters={chapters}
runId={runId}
viewedCount={viewedCount}
isLoading={isLoading}
/>
);
}
17 changes: 17 additions & 0 deletions packages/web/src/app/runs.$runId.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { createFileRoute } from "@tanstack/react-router";
import { Topbar } from "@/components/layout/topbar";
import { PullRequestLayout } from "@/routes/pull-request-layout";

export const Route = createFileRoute("/runs/$runId")({
component: RunLayout,
});

function RunLayout() {
const { runId } = Route.useParams();
return (
<>
<Topbar runId={runId} />
<PullRequestLayout runId={runId} />
</>
);
}
59 changes: 0 additions & 59 deletions packages/web/src/lib/__tests__/use-hash-run-id.test.tsx

This file was deleted.

22 changes: 0 additions & 22 deletions packages/web/src/lib/use-hash-run-id.ts

This file was deleted.

19 changes: 6 additions & 13 deletions packages/web/src/main.tsx
Original file line number Diff line number Diff line change
@@ -1,33 +1,26 @@
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
import { QueryClientProvider } from "@tanstack/react-query";
import { RouterProvider } from "@tanstack/react-router";
import { StrictMode } from "react";
import { createRoot } from "react-dom/client";
import { App } from "./App";
import { ThemeProvider } from "./lib/theme";
import { DiffSettingsProvider } from "./lib/use-diff-settings";
import { getQueryClient, getRouter } from "./router";
import "./styles/globals.css";

const rootElement = document.getElementById("root");
if (!rootElement) {
throw new Error("Root element #root not found");
}

const queryClient = new QueryClient({
defaultOptions: {
queries: {
// Two browser tabs on the same run see synchronized state via refetch on focus
// (acceptance criterion). 30s staleTime keeps a single tab from re-fetching too eagerly.
staleTime: 30_000,
refetchOnWindowFocus: true,
},
},
});
const router = getRouter();
const queryClient = getQueryClient();

createRoot(rootElement).render(
<StrictMode>
<ThemeProvider>
<QueryClientProvider client={queryClient}>
<DiffSettingsProvider>
<App />
<RouterProvider router={router} />
</DiffSettingsProvider>
</QueryClientProvider>
</ThemeProvider>
Expand Down
Loading
Loading