feat(ui): gate the pull-request surface on GitHub, move the account into it, and GitHub sign-in into Integrations

The pull-request rail icon now appears only while GitHub is connected
(OAuth or gh CLI), like Linear; Linear sits after the walkthrough in the
default rail order. The GitHub account avatar and switcher leave the
header for the pull-request panel, where the walkthrough, refresh, and
account controls share one row and one height, and the account stays
visible on the panel's empty state. A manual refresh keeps its spinner on
screen long enough to read as work done.

GitHub sign-in moves from Settings → Git to Settings → Integrations →
Built-in integrations as a card before Linear; search and the connect
buttons follow it.
This commit is contained in:
Bohdan Triapitsyn
2026-08-30 14:17:12 +03:00
parent 9847ef179f
commit 65054a5f4a
20 changed files with 461 additions and 272 deletions
@@ -27,7 +27,9 @@ edge (`components/layout/ContextPanelRail.tsx`) and rendered by
configure button — `ContextRailSurfacesDialog`), drops the plan surface
unless plan mode is enabled,
drops the walkthrough on VS Code and below `WALKTHROUGH_MIN_WIDTH`, hides
Linear unless a workspace is connected, and hides `has-content` surfaces
Linear unless a workspace is connected, hides the pull-request surface
unless GitHub is connected (OAuth or `gh` CLI — signed in from Settings →
Integrations), and hides `has-content` surfaces
until a tab of their mode exists. Both consumers use it so the digit shown
on a rail badge always maps to the same surface the shortcut opens.
+10 -5
View File
@@ -13,6 +13,7 @@ const baseOptions = {
screenWidth: 1200,
tabs: [],
linearConnected: true,
githubConnected: true,
} as const;
describe('getVisibleContextRailSurfaces', () => {
@@ -65,12 +66,16 @@ describe('getVisibleContextRailSurfaces', () => {
expect(surfaces.slice(0, 2).map((surface) => surface.id)).toEqual(['git', 'context']);
});
test('places Linear after Pull Request in the default order', () => {
test('places Linear right after the walkthrough in the default order', () => {
const ids = getVisibleContextRailSurfaces(baseOptions).map((surface) => surface.id);
const pr = ids.indexOf('pr');
const linear = ids.indexOf('linear');
expect(pr).toBeGreaterThanOrEqual(0);
expect(linear).toBe(pr + 1);
const walkthrough = ids.indexOf('walkthrough');
expect(walkthrough).toBeGreaterThanOrEqual(0);
expect(ids.indexOf('linear')).toBe(walkthrough + 1);
});
test('hides the pull request surface until GitHub is connected', () => {
expect(getVisibleContextRailSurfaces({ ...baseOptions, githubConnected: false }).some((s) => s.id === 'pr')).toBe(false);
expect(getVisibleContextRailSurfaces({ ...baseOptions, githubConnected: true }).some((s) => s.id === 'pr')).toBe(true);
});
test('hides Linear until a workspace is connected', () => {
+16 -9
View File
@@ -66,15 +66,6 @@ export const CONTEXT_SURFACES: readonly ContextSurfaceDescriptor[] = [
labelKey: 'contextPanel.mode.pr',
availability: 'always',
},
{
id: 'linear',
descriptionKey: 'contextRail.surface.linear.description',
defaultWidthFraction: 0.45,
mode: 'linear',
icon: 'linear',
labelKey: 'contextPanel.mode.linear',
availability: 'always',
},
{
id: 'diff',
descriptionKey: 'contextRail.surface.diff.description',
@@ -93,6 +84,15 @@ export const CONTEXT_SURFACES: readonly ContextSurfaceDescriptor[] = [
labelKey: 'contextPanel.mode.walkthrough',
availability: 'always',
},
{
id: 'linear',
descriptionKey: 'contextRail.surface.linear.description',
defaultWidthFraction: 0.45,
mode: 'linear',
icon: 'linear',
labelKey: 'contextPanel.mode.linear',
availability: 'always',
},
{
id: 'editor',
descriptionKey: 'contextRail.surface.editor.description',
@@ -206,6 +206,10 @@ type VisibleRailSurfacesOptions = {
tabs: readonly { mode: ContextPanelMode }[];
/** Linear's rail icon stays off until a workspace is connected. */
linearConnected: boolean;
/** The pull-request rail icon stays off until GitHub is connected (OAuth
or a detected `gh` CLI login). GitHub is connected from Settings, so
hiding the surface removes no entry point. */
githubConnected: boolean;
};
/**
@@ -240,6 +244,9 @@ export const getVisibleContextRailSurfaces = (options: VisibleRailSurfacesOption
if (surface.id === 'linear' && !options.linearConnected) {
return false;
}
if (surface.id === 'pr' && !options.githubConnected) {
return false;
}
if (surface.availability === 'has-content') {
return options.tabs.some((tab) => tab.mode === surface.mode);
}