Commitae2a071
fix(nav): connect the orphaned repo-settings and account-settings tree
fix(nav): connect the orphaned repo-settings and account-settings tree RepoNav had "settings" in its active-tab type union but rendered no <a> for it — the entire repo-settings tree (mirrors, dep updater, audit log, webhooks, environments, pages, workflow secrets, protected tags, rulesets, automation, collaborators) was reachable only via the Cmd-K palette, never from normal navigation. Two pages already rendered RepoNav active="settings" correctly, confirming this was expected to exist, not scope creep. - components.tsx: add the missing Settings tab render; link the CommitList "Verified" badge to /settings/signing-keys (previously a dead-end badge with no path to manage your own signing key). - environments.tsx, pages.tsx: fix active="code" -> active="settings". - webhooks.tsx, mirrors.tsx, collaborators.tsx: add RepoNav (previously rendered no nav at all). - dep-updater.tsx: was rendering the wrong nav entirely (IssueNav, active="code") -- replaced with RepoNav active="settings". - repo-settings.tsx: the /settings index page itself was missing RepoNav too (found during verification, same bug, same root cause) -- fixed, and exported RepoSettingsSidebar for reuse. Account settings had the identical shape: SettingsSubnav only listed 4 of 11 settings pages, and 2 of those 4 (agents, deploy-targets) rendered no subnav themselves either. Extended SettingsSubnav with 2fa/passkeys/signing-keys/sessions/integrations and wired it into all 7 previously-orphaned pages (settings-agents, deploy-targets, settings-2fa, passkeys, signing-keys, settings-sessions, settings-integrations). Verified: grep confirms all files reference the nav component; full build-check on every touched file; existing route tests (green- ecosystem, signatures, collaborators, dep-updater, mirrors) all green; full suite 3073 pass / 4 pre-existing unrelated fails, no regressions. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
16 files changed+56−16ae2a0710134f4bcb371ea41275a92e9a6a2ab6b9
16 changed files+56−16
Modifiedsrc/routes/collaborators.tsx+2−1View fileUnifiedSplit
@@ -30,7 +30,7 @@ import {
3030 repoCollaborators,
3131} from "../db/schema";
3232import { Layout } from "../views/layout";
33import { RepoHeader } from "../views/components";
33import { RepoHeader, RepoNav } from "../views/components";
3434import { softAuth, requireAuth } from "../middleware/auth";
3535import type { AuthEnv } from "../middleware/auth";
3636import { generateInviteToken, hashInviteToken } from "../lib/invite-tokens";
@@ -601,6 +601,7 @@ collaboratorRoutes.get(
601601 return c.html(
602602 <Layout title={`Collaborators — ${ownerName}/${repoName}`} user={user}>
603603 <RepoHeader owner={ownerName} repo={repoName} />
604 <RepoNav owner={ownerName} repo={repoName} active="settings" />
604605 <div class="collab-wrap">
605606 <div class="collab-crumbs">
606607 <a href={`/${ownerName}/${repoName}/settings`}>
Modifiedsrc/routes/dep-updater.tsx+5−4View fileUnifiedSplit
@@ -9,8 +9,9 @@
99 * 2026 polish: scoped `.dep-*` class system mirrors the gradient hero and
1010 * card patterns from admin-integrations.tsx and admin-ops.tsx. Bump tables
1111 * use tabular-nums + mono for versions; PR refs + branch names are mono.
12 * RepoHeader + IssueNav above are untouched — only the content beneath
13 * the nav is restyled.
12 * RepoHeader + RepoNav above are untouched — only the content beneath
13 * the nav is restyled. (Was rendering the wrong nav component, IssueNav
14 * with active="code" — fixed to RepoNav active="settings".)
1415 */
1516
1617import { Hono } from "hono";
@@ -20,7 +21,7 @@ import { depUpdateRuns, repositories, users } from "../db/schema";
2021import { Layout } from "../views/layout";
2122import { RepoHeader } from "../views/components";
2223import { formatRelative } from "../views/ui";
23import { IssueNav } from "./issues";
24import { RepoNav } from "../views/components";
2425import { softAuth, requireAuth } from "../middleware/auth";
2526import type { AuthEnv } from "../middleware/auth";
2627import { runDepUpdateRun, type Bump } from "../lib/dep-updater";
@@ -629,7 +630,7 @@ depUpdater.get(
629630 return c.html(
630631 <Layout title={`Dep Updater — ${ownerName}/${repoName}`} user={user}>
631632 <RepoHeader owner={ownerName} repo={repoName} />
632 <IssueNav owner={ownerName} repo={repoName} active="code" />
633 <RepoNav owner={ownerName} repo={repoName} active="settings" />
633634 <div class="dep-wrap">
634635 <section class="dep-hero">
635636 <div class="dep-hero-orb" aria-hidden="true" />
Modifiedsrc/routes/deploy-targets.tsx+2−0View fileUnifiedSplit
@@ -16,6 +16,7 @@ import { and, desc, eq } from "drizzle-orm";
1616import { db } from "../db";
1717import { serverTargets } from "../db/schema";
1818import { Layout } from "../views/layout";
19import { SettingsSubnav } from "./settings";
1920import type { AuthEnv } from "../middleware/auth";
2021import { requireAuth } from "../middleware/auth";
2122import {
@@ -413,6 +414,7 @@ deployTargets.get("/settings/deploy-targets", async (c) => {
413414 return c.html(
414415 <Layout title="Deploy targets — settings" user={user}>
415416 <style dangerouslySetInnerHTML={{ __html: styles }} />
417 <SettingsSubnav active="deploy-targets" />
416418 <div class="dt-wrap">
417419 {/* ─── Hero ─── */}
418420 <div class="dt-hero">
Modifiedsrc/routes/environments.tsx+1−1View fileUnifiedSplit
@@ -574,7 +574,7 @@ r.get("/:owner/:repo/settings/environments", requireAuth, async (c) => {
574574 forkCount={repoRow.forkCount}
575575 currentUser={user.username}
576576 />
577 <RepoNav owner={owner} repo={repo} active="code" />
577 <RepoNav owner={owner} repo={repo} active="settings" />
578578
579579 <div class="envs-wrap">
580580 <section class="envs-head">
Modifiedsrc/routes/mirrors.tsx+2−1View fileUnifiedSplit
@@ -19,7 +19,7 @@ import { and, eq } from "drizzle-orm";
1919import { db } from "../db";
2020import { repositories, users } from "../db/schema";
2121import { Layout } from "../views/layout";
22import { RepoHeader } from "../views/components";
22import { RepoHeader, RepoNav } from "../views/components";
2323import { formatRelative } from "../views/ui";
2424import { softAuth, requireAuth } from "../middleware/auth";
2525import type { AuthEnv } from "../middleware/auth";
@@ -647,6 +647,7 @@ mirrors.get("/:owner/:repo/settings/mirror", requireAuth, async (c) => {
647647 return c.html(
648648 <Layout title={`Mirror — ${ownerName}/${repoName}`} user={user}>
649649 <RepoHeader owner={ownerName} repo={repoName} />
650 <RepoNav owner={ownerName} repo={repoName} active="settings" />
650651 <div class="mirror-wrap">
651652 <section class="mirror-hero">
652653 <div class="mirror-hero-orb" aria-hidden="true" />
Modifiedsrc/routes/pages.tsx+1−1View fileUnifiedSplit
@@ -658,7 +658,7 @@ pagesRoute.get(
658658 notificationCount={unread}
659659 >
660660 <RepoHeader owner={ownerName} repo={repoName} />
661 <RepoNav owner={ownerName} repo={repoName} active="code" />
661 <RepoNav owner={ownerName} repo={repoName} active="settings" />
662662 <div class="pages-wrap">
663663 <header class="pages-head">
664664 <div class="pages-head-text">
Modifiedsrc/routes/passkeys.tsx+2−0View fileUnifiedSplit
@@ -30,6 +30,7 @@ import { users, userPasskeys, sessions } from "../db/schema";
3030import type { AuthEnv } from "../middleware/auth";
3131import { requireAuth } from "../middleware/auth";
3232import { Layout } from "../views/layout";
33import { SettingsSubnav } from "./settings";
3334import {
3435 startRegistration,
3536 finishRegistration,
@@ -638,6 +639,7 @@ passkeys.get("/settings/passkeys", async (c) => {
638639
639640 return c.html(
640641 <Layout title="Passkeys" user={user}>
642 <SettingsSubnav active="passkeys" />
641643 <div class="pk-wrap">
642644 <section class="pk-hero">
643645 <div class="pk-hero-orb" aria-hidden="true" />
Modifiedsrc/routes/repo-settings.tsx+3−2View fileUnifiedSplit
@@ -7,7 +7,7 @@ import { eq, and } from "drizzle-orm";
77import { db } from "../db";
88import { repositories, users, repoTransfers, branchProtection } from "../db/schema";
99import { Layout } from "../views/layout";
10import { RepoHeader } from "../views/components";
10import { RepoHeader, RepoNav } from "../views/components";
1111import { softAuth, requireAuth } from "../middleware/auth";
1212import type { AuthEnv } from "../middleware/auth";
1313import { requireRepoAccess } from "../middleware/repo-access";
@@ -619,7 +619,7 @@ function Banner(props: { kind: "success" | "error"; text: string }) {
619619}
620620
621621/** Left-hand settings navigation sidebar. */
622function RepoSettingsSidebar(props: { owner: string; repo: string; currentPath: string }) {
622export function RepoSettingsSidebar(props: { owner: string; repo: string; currentPath: string }) {
623623 const { owner, repo, currentPath } = props;
624624 const base = `/${owner}/${repo}`;
625625
@@ -722,6 +722,7 @@ repoSettings.get("/:owner/:repo/settings", requireAuth, requireRepoAccess("admin
722722 return c.html(
723723 <Layout title={`Settings — ${ownerName}/${repoName}`} user={user}>
724724 <RepoHeader owner={ownerName} repo={repoName} />
725 <RepoNav owner={ownerName} repo={repoName} active="settings" />
725726 <style dangerouslySetInnerHTML={{ __html: repoSettingsStyles }} />
726727 <div class="repo-settings-container">
727728 <RepoSettingsHero owner={ownerName} repo={repoName} />
Modifiedsrc/routes/settings-2fa.tsx+2−0View fileUnifiedSplit
@@ -22,6 +22,7 @@ import { users, userTotp, userRecoveryCodes } from "../db/schema";
2222import type { AuthEnv } from "../middleware/auth";
2323import { requireAuth } from "../middleware/auth";
2424import { Layout } from "../views/layout";
25import { SettingsSubnav } from "./settings";
2526import { verifyPassword } from "../lib/auth";
2627import {
2728 generateTotpSecret,
@@ -562,6 +563,7 @@ settings2fa.get("/settings/2fa", async (c) => {
562563
563564 return c.html(
564565 <Layout title="Two-factor authentication" user={user}>
566 <SettingsSubnav active="2fa" />
565567 <div class="tfa-wrap">
566568 <section class="tfa-hero">
567569 <div class="tfa-hero-orb" aria-hidden="true" />
Modifiedsrc/routes/settings-agents.tsx+2−0View fileUnifiedSplit
@@ -12,6 +12,7 @@ import { raw } from "hono/html";
1212import { db } from "../db";
1313import { agentLeases, agentSessions } from "../db/schema";
1414import { Layout } from "../views/layout";
15import { SettingsSubnav } from "./settings";
1516import type { AuthEnv } from "../middleware/auth";
1617import { requireAuth } from "../middleware/auth";
1718import {
@@ -362,6 +363,7 @@ settingsAgents.get("/settings/agents", async (c) => {
362363
363364 return c.html(
364365 <Layout title="Agent sessions" user={user}>
366 <SettingsSubnav active="agents" />
365367 {raw(body)}
366368 </Layout>
367369 );
Modifiedsrc/routes/settings-integrations.tsx+2−0View fileUnifiedSplit
@@ -21,6 +21,7 @@ import { and, eq } from "drizzle-orm";
2121import { db } from "../db";
2222import { chatIntegrations, repositories } from "../db/schema";
2323import { Layout } from "../views/layout";
24import { SettingsSubnav } from "./settings";
2425import { softAuth, requireAuth } from "../middleware/auth";
2526import type { AuthEnv } from "../middleware/auth";
2627import { notifyChatChannels } from "../lib/chat-notifier";
@@ -380,6 +381,7 @@ r.get("/settings/integrations", async (c) => {
380381 return c.html(
381382 <Layout title="Chat integrations" user={user}>
382383 <style dangerouslySetInnerHTML={{ __html: chatiStyles }} />
384 <SettingsSubnav active="integrations" />
383385 <div class="chati-wrap">
384386 <div class="chati-hero">
385387 <div class="chati-hero-orb" aria-hidden="true" />
Modifiedsrc/routes/settings-sessions.tsx+2−0View fileUnifiedSplit
@@ -14,6 +14,7 @@ import { sessions } from "../db/schema";
1414import { requireAuth } from "../middleware/auth";
1515import type { AuthEnv } from "../middleware/auth";
1616import { Layout } from "../views/layout";
17import { SettingsSubnav } from "./settings";
1718
1819const settingsSessions = new Hono<AuthEnv>();
1920settingsSessions.use("/settings/sessions*", requireAuth);
@@ -117,6 +118,7 @@ settingsSessions.get("/settings/sessions", async (c) => {
117118 return c.html(
118119 <Layout title="Active sessions" user={user}>
119120 <style dangerouslySetInnerHTML={{ __html: sessionsStyles }} />
121 <SettingsSubnav active="sessions" />
120122 <div class="sessions-page">
121123 <div class="sessions-hero">
122124 <h1>Active sessions</h1>
Modifiedsrc/routes/settings.tsx+15−1View fileUnifiedSplit
@@ -599,12 +599,26 @@ function SettingsHero(props: {
599599}
600600
601601/** Pill-row sub-navigation, hand-built so we don't depend on shared components. */
602export type SettingsSubnavKey = "profile" | "keys" | "agents" | "deploy-targets";
602export type SettingsSubnavKey =
603 | "profile"
604 | "keys"
605 | "agents"
606 | "deploy-targets"
607 | "2fa"
608 | "passkeys"
609 | "signing-keys"
610 | "sessions"
611 | "integrations";
603612
604613export function SettingsSubnav(props: { active: SettingsSubnavKey }) {
605614 const items: Array<{ key: SettingsSubnavKey; href: string; label: string }> = [
606615 { key: "profile", href: "/settings", label: "Profile" },
607616 { key: "keys", href: "/settings/keys", label: "SSH keys" },
617 { key: "signing-keys", href: "/settings/signing-keys", label: "Signing keys" },
618 { key: "2fa", href: "/settings/2fa", label: "2FA" },
619 { key: "passkeys", href: "/settings/passkeys", label: "Passkeys" },
620 { key: "sessions", href: "/settings/sessions", label: "Sessions" },
621 { key: "integrations", href: "/settings/integrations", label: "Integrations" },
608622 { key: "agents", href: "/settings/agents", label: "Agents" },
609623 { key: "deploy-targets", href: "/settings/deploy-targets", label: "Deploy targets" },
610624 ];
Modifiedsrc/routes/signing-keys.tsx+2−0View fileUnifiedSplit
@@ -23,6 +23,7 @@
2323
2424import { Hono } from "hono";
2525import { Layout } from "../views/layout";
26import { SettingsSubnav } from "./settings";
2627import type { AuthEnv } from "../middleware/auth";
2728import { requireAuth } from "../middleware/auth";
2829import {
@@ -439,6 +440,7 @@ signingKeysRoutes.get("/settings/signing-keys", async (c) => {
439440 const now = new Date();
440441 return c.html(
441442 <Layout title="Signing keys" user={user}>
443 <SettingsSubnav active="signing-keys" />
442444 <div class="sk-wrap">
443445 <header class="sk-head">
444446 <div class="sk-eyebrow">
Modifiedsrc/routes/webhooks.tsx+2−1View fileUnifiedSplit
@@ -26,7 +26,7 @@ import {
2626 drainPendingDeliveries,
2727} from "../lib/webhook-delivery";
2828import { Layout } from "../views/layout";
29import { RepoHeader } from "../views/components";
29import { RepoHeader, RepoNav } from "../views/components";
3030import { softAuth, requireAuth } from "../middleware/auth";
3131import type { AuthEnv } from "../middleware/auth";
3232import { requireRepoAccess } from "../middleware/repo-access";
@@ -508,6 +508,7 @@ webhookRoutes.get(
508508 return c.html(
509509 <Layout title={`Webhooks — ${ownerName}/${repoName}`} user={user}>
510510 <RepoHeader owner={ownerName} repo={repoName} />
511 <RepoNav owner={ownerName} repo={repoName} active="settings" />
511512 <div class="wh-wrap">
512513 <header class="wh-head">
513514 <div class="wh-eyebrow">
Modifiedsrc/views/components.tsx+11−4View fileUnifiedSplit
@@ -267,6 +267,12 @@ export const RepoNav: FC<{
267267 >
268268 Security
269269 </a>
270 <a
271 href={`/${owner}/${repo}/settings`}
272 class={active === "settings" ? "active" : ""}
273 >
274 Settings
275 </a>
270276 <a
271277 href={`/${owner}/${repo}/cloud-deployments`}
272278 class={active === "deployments" ? "active" : ""}
@@ -516,12 +522,13 @@ export const CommitList: FC<{
516522 {commit.message}
517523 </a>
518524 {v?.verified && (
519 <span
520 title="Signed with a registered key"
521 style="margin-left:8px;font-size:10px;padding:1px 6px;border-radius:3px;background:var(--green,#2ea043);color:#fff;text-transform:uppercase;letter-spacing:.4px"
525 <a
526 href="/settings/signing-keys"
527 title="Signed with a registered key — manage your signing keys"
528 style="margin-left:8px;font-size:10px;padding:1px 6px;border-radius:3px;background:var(--green,#2ea043);color:#fff;text-transform:uppercase;letter-spacing:.4px;text-decoration:none"
522529 >
523530 Verified
524 </span>
531 </a>
525532 )}
526533 </div>
527534 <div class="commit-meta">
528535