@@ -26,7 +26,7 @@ Node 22 is required. If you use asdf, `.tool-versions` pins the right version au
| `/` | Repo picker — auto-redirects for single repo |
| `/$repo/tree/$ref/...path` | Code browser — directory listing |
| `/$repo/blob/$ref/...path` | Code browser — file viewer |
-| `/$repo/commits/$ref` | Commit history |
+| `/$repo/commits/$ref?path=...` | Commit history (optionally scoped to a path) |
| `/$repo/commit/$hash` | Commit detail with collapsible file diffs |
| `/$repo/issues` | Issue list with search, filters, pagination |
| `/$repo/issues/new` | New issue form |
@@ -71,7 +71,7 @@ src/
Components are organized in three layers:
-- **`ui/`** — Generic primitives managed by shadcn CLI (`npx shadcn add`). No domain knowledge. Examples: button, input, avatar, badge, popover, separator, skeleton, textarea.
+- **`ui/`** — Generic primitives managed by shadcn CLI (`npx shadcn add`) or hand-written. No domain knowledge. Examples: button, input, avatar, badge, listbox (presentational compound components for dropdown menus), popover, separator, skeleton, textarea. Interactive dropdowns use `@floating-ui/react` hooks wired per-consumer with `Listbox.*` presentational primitives.
- **`shared/`** — App-level reusable components. These know about the domain (bug status, labels, identities) but contain no data fetching. They use **composition APIs** (compound components) and are typed against **colocated GraphQL fragments**. Examples: issue-row, label-badge, status-badge, status-tabs, comment-card, pagination, query-input, write-preview, empty-state, section-heading, issue-filters.
@@ -54,7 +54,7 @@ function CodeLayout() {
function handleRefSelect(newRef: GitRef) {
const refName = newRef.shortName;
if (viewMode === "commits") {
- void navigate({ to: "/$repo/commits/$ref", params: { repo, ref: refName } });
+ void navigate({ to: "/$repo/commits/$ref", params: { repo, ref: refName }, search: { path: currentPath || undefined } });
} else if (viewMode === "blob") {
void navigate({
to: "/$repo/blob/$ref/$",
@@ -92,6 +92,7 @@ function CodeLayout() {
<ButtonLink
to="/$repo/commits/$ref"
params={{ repo, ref: currentRef }}
+ search={{ path: currentPath || undefined }}
variant="outline"
size="sm"
>
@@ -1,17 +1,24 @@
-// Commit history view: /$repo/commits/$ref
+// Commit history view: /$repo/commits/$ref?path=...
import { createFileRoute } from "@tanstack/react-router";
+import * as v from "valibot";
import { CommitList } from "@/components/code/commit-list";
+const commitsSearchSchema = v.object({
+ path: v.fallback(v.optional(v.string()), undefined),
+});
+
export const Route = createFileRoute("/$repo/_code/commits/$ref")({
component: CommitsView,
beforeLoad: () => ({ viewMode: "commits" as const }),
+ validateSearch: (search) => v.parse(commitsSearchSchema, search),
});
function CommitsView() {
const { ref: currentRef } = Route.useParams();
const { ref: repoRef } = Route.useRouteContext();
+ const { path } = Route.useSearch();
- return <CommitList repo={repoRef} ref_={currentRef} />;
+ return <CommitList repo={repoRef} ref_={currentRef} path={path} />;
}