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
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,7 @@ export function PullDetailHeader({
</span>
<DiffBoxes additions={pr.additions} deletions={pr.deletions} />
</span>
{!pr.isMerged && !isReviewRequested && (
{!isReviewRequested && (
<Link
to="/$owner/$repo/review/$pullId"
params={{ owner, repo, pullId }}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { CommentIcon } from "@diffkit/icons";
import { CheckIcon, CommentIcon, CopyIcon } from "@diffkit/icons";
import { Markdown } from "@diffkit/ui/components/markdown";
import { vercelDark, vercelLight } from "@diffkit/ui/lib/shiki-themes";
import { cn } from "@diffkit/ui/lib/utils";
Expand Down Expand Up @@ -274,12 +274,15 @@ function FileHeader({
isCollapsed: boolean;
onToggleCollapse: () => void;
}) {
const [copied, setCopied] = useState(false);

return (
<div className="sticky top-2 z-10 flex items-center gap-2 rounded-lg border bg-surface-1 px-3 py-2">
<div className="sticky top-2 z-10 flex items-center gap-2 rounded-lg border bg-surface-1 px-3 py-2 select-none">
<button
type="button"
onClick={onToggleCollapse}
className="flex items-center gap-1.5 text-muted-foreground transition-colors hover:text-foreground"
className="flex shrink-0 items-center text-muted-foreground transition-colors hover:text-foreground"
aria-label={isCollapsed ? "Expand file" : "Collapse file"}
>
<svg
aria-hidden="true"
Expand All @@ -294,7 +297,12 @@ function FileHeader({
</svg>
</button>

<span className="truncate font-mono text-xs font-medium">
{/* biome-ignore lint/a11y/noStaticElementInteractions: span stops propagation so text selection doesn't trigger collapse */}
{/* biome-ignore lint/a11y/useKeyWithClickEvents: keyboard users use the collapse button; this only prevents mouse click bubbling */}
<span
className="min-w-0 flex-1 cursor-text truncate font-mono text-xs font-medium select-text"
onClick={(e) => e.stopPropagation()}
>
{file.previousFilename && file.previousFilename !== file.filename ? (
<>
<span className="text-muted-foreground line-through">
Expand All @@ -308,6 +316,25 @@ function FileHeader({
)}
</span>

<button
type="button"
onClick={(e) => {
e.stopPropagation();
void navigator.clipboard.writeText(file.filename).then(() => {
setCopied(true);
setTimeout(() => setCopied(false), 1500);
});
}}
className="shrink-0 text-muted-foreground transition-colors hover:text-foreground"
aria-label="Copy file path"
>
{copied ? (
<CheckIcon size={12} strokeWidth={2.5} />
) : (
<CopyIcon size={12} strokeWidth={2} />
)}
</button>

<span className="ml-auto flex items-center gap-2 font-mono text-xs tabular-nums">
{file.additions > 0 && (
<span className="font-medium text-green-500">+{file.additions}</span>
Expand Down
20 changes: 19 additions & 1 deletion apps/dashboard/src/lib/tab-store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,30 @@ export interface Tab {

export const TABS_STORAGE_KEY = "diffkit:tabs";

const VALID_TAB_TYPES = new Set<string>(["pull", "issue", "review"]);

function isValidTab(t: unknown): t is Tab {
return (
t !== null &&
typeof t === "object" &&
typeof (t as Tab).id === "string" &&
VALID_TAB_TYPES.has((t as Tab).type)
);
}

export function readStoredTabs(): Tab[] {
if (typeof window === "undefined") return [];
try {
const raw = localStorage.getItem(TABS_STORAGE_KEY);
return raw ? JSON.parse(raw) : [];
if (!raw) return [];
const parsed: unknown = JSON.parse(raw);
if (!Array.isArray(parsed) || !parsed.every(isValidTab)) {
localStorage.removeItem(TABS_STORAGE_KEY);
return [];
}
return parsed;
} catch {
localStorage.removeItem(TABS_STORAGE_KEY);
return [];
}
}
Expand Down
42 changes: 0 additions & 42 deletions apps/dashboard/src/routeTree.gen.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,7 @@
// Additionally, you should also exclude this file from your linter and/or formatter to prevent it from being checked or modified.

import { Route as rootRouteImport } from './routes/__root'
import { Route as SitemapDotxmlRouteImport } from './routes/sitemap[.]xml'
import { Route as SetupRouteImport } from './routes/setup'
import { Route as RobotsDottxtRouteImport } from './routes/robots[.]txt'
import { Route as LoginRouteImport } from './routes/login'
import { Route as ProtectedRouteImport } from './routes/_protected'
import { Route as ProtectedIndexRouteImport } from './routes/_protected/index'
Expand All @@ -30,21 +28,11 @@ import { Route as ProtectedOwnerRepoReviewPullIdRouteImport } from './routes/_pr
import { Route as ProtectedOwnerRepoPullPullIdRouteImport } from './routes/_protected/$owner/$repo/pull.$pullId'
import { Route as ProtectedOwnerRepoIssuesIssueIdRouteImport } from './routes/_protected/$owner/$repo/issues.$issueId'

const SitemapDotxmlRoute = SitemapDotxmlRouteImport.update({
id: '/sitemap.xml',
path: '/sitemap.xml',
getParentRoute: () => rootRouteImport,
} as any)
const SetupRoute = SetupRouteImport.update({
id: '/setup',
path: '/setup',
getParentRoute: () => rootRouteImport,
} as any)
const RobotsDottxtRoute = RobotsDottxtRouteImport.update({
id: '/robots.txt',
path: '/robots.txt',
getParentRoute: () => rootRouteImport,
} as any)
const LoginRoute = LoginRouteImport.update({
id: '/login',
path: '/login',
Expand Down Expand Up @@ -137,9 +125,7 @@ const ProtectedOwnerRepoIssuesIssueIdRoute =
export interface FileRoutesByFullPath {
'/': typeof ProtectedIndexRoute
'/login': typeof LoginRoute
'/robots.txt': typeof RobotsDottxtRoute
'/setup': typeof SetupRoute
'/sitemap.xml': typeof SitemapDotxmlRoute
'/issues': typeof ProtectedIssuesRoute
'/pulls': typeof ProtectedPullsRoute
'/reviews': typeof ProtectedReviewsRoute
Expand All @@ -157,9 +143,7 @@ export interface FileRoutesByFullPath {
}
export interface FileRoutesByTo {
'/login': typeof LoginRoute
'/robots.txt': typeof RobotsDottxtRoute
'/setup': typeof SetupRoute
'/sitemap.xml': typeof SitemapDotxmlRoute
'/issues': typeof ProtectedIssuesRoute
'/pulls': typeof ProtectedPullsRoute
'/reviews': typeof ProtectedReviewsRoute
Expand All @@ -179,9 +163,7 @@ export interface FileRoutesById {
__root__: typeof rootRouteImport
'/_protected': typeof ProtectedRouteWithChildren
'/login': typeof LoginRoute
'/robots.txt': typeof RobotsDottxtRoute
'/setup': typeof SetupRoute
'/sitemap.xml': typeof SitemapDotxmlRoute
'/_protected/issues': typeof ProtectedIssuesRoute
'/_protected/pulls': typeof ProtectedPullsRoute
'/_protected/reviews': typeof ProtectedReviewsRoute
Expand All @@ -203,9 +185,7 @@ export interface FileRouteTypes {
fullPaths:
| '/'
| '/login'
| '/robots.txt'
| '/setup'
| '/sitemap.xml'
| '/issues'
| '/pulls'
| '/reviews'
Expand All @@ -223,9 +203,7 @@ export interface FileRouteTypes {
fileRoutesByTo: FileRoutesByTo
to:
| '/login'
| '/robots.txt'
| '/setup'
| '/sitemap.xml'
| '/issues'
| '/pulls'
| '/reviews'
Expand All @@ -244,9 +222,7 @@ export interface FileRouteTypes {
| '__root__'
| '/_protected'
| '/login'
| '/robots.txt'
| '/setup'
| '/sitemap.xml'
| '/_protected/issues'
| '/_protected/pulls'
| '/_protected/reviews'
Expand All @@ -267,9 +243,7 @@ export interface FileRouteTypes {
export interface RootRouteChildren {
ProtectedRoute: typeof ProtectedRouteWithChildren
LoginRoute: typeof LoginRoute
RobotsDottxtRoute: typeof RobotsDottxtRoute
SetupRoute: typeof SetupRoute
SitemapDotxmlRoute: typeof SitemapDotxmlRoute
ApiAuthSplatRoute: typeof ApiAuthSplatRoute
ApiWebhooksGithubRoute: typeof ApiWebhooksGithubRoute
ApiGithubAppAuthorizeRoute: typeof ApiGithubAppAuthorizeRoute
Expand All @@ -278,27 +252,13 @@ export interface RootRouteChildren {

declare module '@tanstack/react-router' {
interface FileRoutesByPath {
'/sitemap.xml': {
id: '/sitemap.xml'
path: '/sitemap.xml'
fullPath: '/sitemap.xml'
preLoaderRoute: typeof SitemapDotxmlRouteImport
parentRoute: typeof rootRouteImport
}
'/setup': {
id: '/setup'
path: '/setup'
fullPath: '/setup'
preLoaderRoute: typeof SetupRouteImport
parentRoute: typeof rootRouteImport
}
'/robots.txt': {
id: '/robots.txt'
path: '/robots.txt'
fullPath: '/robots.txt'
preLoaderRoute: typeof RobotsDottxtRouteImport
parentRoute: typeof rootRouteImport
}
'/login': {
id: '/login'
path: '/login'
Expand Down Expand Up @@ -465,9 +425,7 @@ const ProtectedRouteWithChildren = ProtectedRoute._addFileChildren(
const rootRouteChildren: RootRouteChildren = {
ProtectedRoute: ProtectedRouteWithChildren,
LoginRoute: LoginRoute,
RobotsDottxtRoute: RobotsDottxtRoute,
SetupRoute: SetupRoute,
SitemapDotxmlRoute: SitemapDotxmlRoute,
ApiAuthSplatRoute: ApiAuthSplatRoute,
ApiWebhooksGithubRoute: ApiWebhooksGithubRoute,
ApiGithubAppAuthorizeRoute: ApiGithubAppAuthorizeRoute,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,11 +28,15 @@ export const Route = createFileRoute(
// Never block navigation — fire prefetch and let the component
// show cached data instantly or a skeleton while loading.
void context.queryClient.prefetchQuery(pageOptions);

return { issueTitle: cachedData?.detail?.title ?? null };
},
head: ({ match, params }) =>
buildSeo({
path: match.pathname,
title: formatPageTitle(`Issue #${params.issueId}`),
title: formatPageTitle(
match.loaderData?.issueTitle ?? `Issue #${params.issueId}`,
),
description: `Private GitHub issue #${params.issueId} in ${params.owner}/${params.repo}.`,
robots: "noindex",
}),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,8 @@ export const Route = createFileRoute("/_protected/$owner/$repo/pull/$pullId")({

// Clean up broken cache entries (no detail)
const cachedData = context.queryClient.getQueryData(pageOptions.queryKey);
if (cachedData !== undefined && !cachedData?.detail) {
const isBrokenEntry = cachedData !== undefined && !cachedData?.detail;
if (isBrokenEntry) {
context.queryClient.removeQueries({
queryKey: pageOptions.queryKey,
exact: true,
Expand All @@ -30,11 +31,17 @@ export const Route = createFileRoute("/_protected/$owner/$repo/pull/$pullId")({
// show cached data instantly or a skeleton while loading.
void context.queryClient.prefetchQuery(pageOptions);
void context.queryClient.prefetchQuery(githubViewerQueryOptions(scope));

return {
prTitle: isBrokenEntry ? null : (cachedData?.detail?.title ?? null),
};
},
head: ({ match, params }) =>
buildSeo({
path: match.pathname,
title: formatPageTitle(`PR #${params.pullId}`),
title: formatPageTitle(
match.loaderData?.prTitle ?? `PR #${params.pullId}`,
),
description: `Private pull request #${params.pullId} in ${params.owner}/${params.repo}.`,
robots: "noindex",
}),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,9 @@ export const Route = createFileRoute("/_protected/$owner/$repo/review/$pullId")(
const cachedPageData = context.queryClient.getQueryData(
pageOptions.queryKey,
);
if (cachedPageData !== undefined && !cachedPageData?.detail) {
const isBrokenEntry =
cachedPageData !== undefined && !cachedPageData?.detail;
if (isBrokenEntry) {
context.queryClient.removeQueries({
queryKey: pageOptions.queryKey,
exact: true,
Expand All @@ -46,11 +48,19 @@ export const Route = createFileRoute("/_protected/$owner/$repo/review/$pullId")(
data: { ...input, page: 1, perPage: PULL_FILES_PAGE_SIZE },
});
}

return {
prTitle: isBrokenEntry ? null : (cachedPageData?.detail?.title ?? null),
};
},
head: ({ match, params }) =>
buildSeo({
path: match.pathname,
title: formatPageTitle(`Review PR #${params.pullId}`),
title: formatPageTitle(
match.loaderData?.prTitle
? `Review: ${match.loaderData.prTitle}`
: `Review PR #${params.pullId}`,
),
description: `Private code review workspace for pull request #${params.pullId} in ${params.owner}/${params.repo}.`,
robots: "noindex",
}),
Expand Down
24 changes: 0 additions & 24 deletions apps/dashboard/src/routes/robots[.]txt.ts

This file was deleted.

40 changes: 0 additions & 40 deletions apps/dashboard/src/routes/sitemap[.]xml.ts

This file was deleted.

1 change: 1 addition & 0 deletions packages/icons/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -65,3 +65,4 @@ export {
} from "@hugeicons/react";
export { GitHubLogo, GitHubWordmarkLogo } from "./brand-logos";
export { PenIcon } from "./pen-icon";
export { SeparatorHorizontalIcon } from "./separator-horizontal-icon";
Loading
Loading