Merge pull request #2679 from makeittech/feat/gh-2364-git-changes-count

feat(ui): show the changed-files count badge on the Git rail surface
This commit is contained in:
Serhii Dziupin
2026-08-05 17:06:24 +03:00
committed by GitHub
12 changed files with 112 additions and 20 deletions
@@ -49,17 +49,30 @@ type RailItemProps = {
showActivityDot: boolean; showActivityDot: boolean;
label: string; label: string;
description: string; description: string;
/** Numeric badge (e.g. the Git changed-files count); takes precedence over the activity dot. */
badgeCount?: number | null;
/** Accessible label that includes the badge count; falls back to `label`. */
badgeAriaLabel?: string | null;
/** Extra tooltip line describing the badge; rendered under the description. */
badgeDescription?: string | null;
orderNumber?: number | null; orderNumber?: number | null;
showOrderNumber?: boolean; showOrderNumber?: boolean;
onSelect: (surface: ContextSurfaceDescriptor) => void; onSelect: (surface: ContextSurfaceDescriptor) => void;
}; };
// The badge corner is 16px tall; cap large counts so the pill stays compact
// on the 36px rail button (matching the order-number badge's footprint).
const formatRailBadgeCount = (count: number): string => (count > 99 ? '99+' : String(count));
const ContextPanelRailItem: React.FC<RailItemProps> = ({ const ContextPanelRailItem: React.FC<RailItemProps> = ({
surface, surface,
isActive, isActive,
showActivityDot, showActivityDot,
label, label,
description, description,
badgeCount,
badgeAriaLabel,
badgeDescription,
orderNumber, orderNumber,
showOrderNumber, showOrderNumber,
onSelect, onSelect,
@@ -68,6 +81,8 @@ const ContextPanelRailItem: React.FC<RailItemProps> = ({
id: surface.id, id: surface.id,
}); });
const displayBadgeCount = badgeCount != null && badgeCount > 0 ? formatRailBadgeCount(badgeCount) : null;
return ( return (
<div <div
ref={setNodeRef} ref={setNodeRef}
@@ -81,7 +96,7 @@ const ContextPanelRailItem: React.FC<RailItemProps> = ({
{...attributes} {...attributes}
{...listeners} {...listeners}
onClick={() => onSelect(surface)} onClick={() => onSelect(surface)}
aria-label={label} aria-label={badgeAriaLabel ?? label}
aria-pressed={isActive} aria-pressed={isActive}
className={cn( className={cn(
'flex h-9 w-9 touch-none select-none items-center justify-center rounded-md transition-colors', 'flex h-9 w-9 touch-none select-none items-center justify-center rounded-md transition-colors',
@@ -95,12 +110,6 @@ const ContextPanelRailItem: React.FC<RailItemProps> = ({
) : ( ) : (
<Icon name={surface.icon} className="h-[18px] w-[18px]" /> <Icon name={surface.icon} className="h-[18px] w-[18px]" />
)} )}
{showActivityDot && !showOrderNumber ? (
<span
aria-hidden="true"
className="absolute right-1 top-1 h-1.5 w-1.5 rounded-full bg-[var(--status-info)]"
/>
) : null}
{showOrderNumber && orderNumber != null ? ( {showOrderNumber && orderNumber != null ? (
<span <span
aria-hidden="true" aria-hidden="true"
@@ -108,6 +117,18 @@ const ContextPanelRailItem: React.FC<RailItemProps> = ({
> >
{orderNumber === 10 ? '0' : orderNumber} {orderNumber === 10 ? '0' : orderNumber}
</span> </span>
) : displayBadgeCount ? (
<span
aria-hidden="true"
className="absolute right-0 top-0 flex h-4 min-w-4 items-center justify-center rounded-full bg-surface-muted px-1 text-[0.625rem] font-medium leading-none text-muted-foreground"
>
{displayBadgeCount}
</span>
) : showActivityDot ? (
<span
aria-hidden="true"
className="absolute right-1 top-1 h-1.5 w-1.5 rounded-full bg-[var(--status-info)]"
/>
) : null} ) : null}
</button> </button>
</TooltipTrigger> </TooltipTrigger>
@@ -115,6 +136,9 @@ const ContextPanelRailItem: React.FC<RailItemProps> = ({
<div className="flex flex-col gap-0.5"> <div className="flex flex-col gap-0.5">
<span>{label}</span> <span>{label}</span>
<span className="typography-micro text-muted-foreground">{description}</span> <span className="typography-micro text-muted-foreground">{description}</span>
{badgeDescription ? (
<span className="typography-micro text-muted-foreground">{badgeDescription}</span>
) : null}
</div> </div>
</TooltipContent> </TooltipContent>
</Tooltip> </Tooltip>
@@ -258,19 +282,43 @@ export const ContextPanelRail: React.FC = () => {
> >
<DndContext sensors={sensors} collisionDetection={closestCenter} onDragEnd={handleDragEnd}> <DndContext sensors={sensors} collisionDetection={closestCenter} onDragEnd={handleDragEnd}>
<SortableContext items={surfaces.map((surface) => surface.id)} strategy={verticalListSortingStrategy}> <SortableContext items={surfaces.map((surface) => surface.id)} strategy={verticalListSortingStrategy}>
{surfaces.map((surface, index) => ( {surfaces.map((surface, index) => {
const label = t(surface.labelKey);
// Git shows a numeric badge instead of the old activity dot.
// Other surfaces never inherit git's changed-files signal.
const gitChangedCount = surface.id === 'git' ? changedFilesCount : 0;
const badgeCount = gitChangedCount > 0 ? gitChangedCount : null;
return (
<ContextPanelRailItem <ContextPanelRailItem
key={surface.id} key={surface.id}
surface={surface} surface={surface}
isActive={activeMode === surface.mode} isActive={activeMode === surface.mode}
showActivityDot={surface.id === 'git' && changedFilesCount > 0} showActivityDot={false}
label={t(surface.labelKey)} label={label}
description={t(surface.descriptionKey)} description={t(surface.descriptionKey)}
badgeCount={badgeCount}
badgeAriaLabel={badgeCount !== null
? t(
badgeCount === 1
? 'contextRail.surface.git.changesCountAriaSingle'
: 'contextRail.surface.git.changesCountAriaPlural',
{ label, count: badgeCount },
)
: null}
badgeDescription={badgeCount !== null
? t(
badgeCount === 1
? 'contextRail.surface.git.changesCountTooltipSingle'
: 'contextRail.surface.git.changesCountTooltipPlural',
{ count: badgeCount },
)
: null}
orderNumber={index + 1} orderNumber={index + 1}
showOrderNumber={revealNumbers} showOrderNumber={revealNumbers}
onSelect={(selected) => openContextSurface(directoryKey, selected.mode)} onSelect={(selected) => openContextSurface(directoryKey, selected.mode)}
/> />
))} );
})}
</SortableContext> </SortableContext>
</DndContext> </DndContext>
</nav> </nav>
+4
View File
@@ -2828,6 +2828,10 @@ export const dict = {
'contextPanel.editorEmpty.description': 'Wählen Sie etwas aus der Seitenleiste aus, um Kontext anzuzeigen.', 'contextPanel.editorEmpty.description': 'Wählen Sie etwas aus der Seitenleiste aus, um Kontext anzuzeigen.',
'contextRail.surface.editor.description': 'Bearbeitungskontext', 'contextRail.surface.editor.description': 'Bearbeitungskontext',
'contextRail.surface.git.description': 'Git-Kontext', 'contextRail.surface.git.description': 'Git-Kontext',
'contextRail.surface.git.changesCountAriaSingle': '{label}, {count} geänderte Datei',
'contextRail.surface.git.changesCountAriaPlural': '{label}, {count} geänderte Dateien',
'contextRail.surface.git.changesCountTooltipSingle': '{count} geänderte Datei',
'contextRail.surface.git.changesCountTooltipPlural': '{count} geänderte Dateien',
'contextRail.surface.terminal.description': 'Terminal-Kontext', 'contextRail.surface.terminal.description': 'Terminal-Kontext',
'contextRail.surface.diff.description': 'Diff-Kontext', 'contextRail.surface.diff.description': 'Diff-Kontext',
'contextPanel.mode.walkthrough': 'Walkthrough', 'contextPanel.mode.walkthrough': 'Walkthrough',
+4
View File
@@ -1106,6 +1106,10 @@ export const dict = {
'contextPanel.editorEmpty.description': 'Pick a file from the tree to start editing.', 'contextPanel.editorEmpty.description': 'Pick a file from the tree to start editing.',
'contextRail.surface.editor.description': 'Edit project files', 'contextRail.surface.editor.description': 'Edit project files',
'contextRail.surface.git.description': 'Commits, branches, and pull requests', 'contextRail.surface.git.description': 'Commits, branches, and pull requests',
'contextRail.surface.git.changesCountAriaSingle': '{label}, {count} changed file',
'contextRail.surface.git.changesCountAriaPlural': '{label}, {count} changed files',
'contextRail.surface.git.changesCountTooltipSingle': '{count} changed file',
'contextRail.surface.git.changesCountTooltipPlural': '{count} changed files',
'contextRail.surface.terminal.description': 'Built-in terminal', 'contextRail.surface.terminal.description': 'Built-in terminal',
'contextRail.surface.diff.description': 'Review working changes', 'contextRail.surface.diff.description': 'Review working changes',
'contextPanel.mode.walkthrough': 'Walkthrough', 'contextPanel.mode.walkthrough': 'Walkthrough',
+4
View File
@@ -1107,6 +1107,10 @@ export const dict: Record<I18nKey, string> = {
"contextPanel.editorEmpty.description": "Elige un archivo del árbol para empezar a editar.", "contextPanel.editorEmpty.description": "Elige un archivo del árbol para empezar a editar.",
"contextRail.surface.editor.description": "Editar archivos del proyecto", "contextRail.surface.editor.description": "Editar archivos del proyecto",
"contextRail.surface.git.description": "Commits, ramas y pull requests", "contextRail.surface.git.description": "Commits, ramas y pull requests",
"contextRail.surface.git.changesCountAriaSingle": "{label}, {count} archivo modificado",
"contextRail.surface.git.changesCountAriaPlural": "{label}, {count} archivos modificados",
"contextRail.surface.git.changesCountTooltipSingle": "{count} archivo modificado",
"contextRail.surface.git.changesCountTooltipPlural": "{count} archivos modificados",
"contextRail.surface.terminal.description": "Terminal integrada", "contextRail.surface.terminal.description": "Terminal integrada",
"contextRail.surface.diff.description": "Revisar cambios en curso", "contextRail.surface.diff.description": "Revisar cambios en curso",
"contextPanel.mode.walkthrough": "Recorrido", "contextPanel.mode.walkthrough": "Recorrido",
+4
View File
@@ -931,6 +931,10 @@ export const dict = {
'contextPanel.editorEmpty.description': 'Choisissez un fichier dans l’arborescence pour commencer.', 'contextPanel.editorEmpty.description': 'Choisissez un fichier dans l’arborescence pour commencer.',
'contextRail.surface.editor.description': 'Modifier les fichiers du projet', 'contextRail.surface.editor.description': 'Modifier les fichiers du projet',
'contextRail.surface.git.description': 'Commits, branches et pull requests', 'contextRail.surface.git.description': 'Commits, branches et pull requests',
'contextRail.surface.git.changesCountAriaSingle': '{label}, {count} fichier modifié',
'contextRail.surface.git.changesCountAriaPlural': '{label}, {count} fichiers modifiés',
'contextRail.surface.git.changesCountTooltipSingle': '{count} fichier modifié',
'contextRail.surface.git.changesCountTooltipPlural': '{count} fichiers modifiés',
'contextRail.surface.terminal.description': 'Terminal intégré', 'contextRail.surface.terminal.description': 'Terminal intégré',
'contextRail.surface.diff.description': 'Passer en revue les modifications', 'contextRail.surface.diff.description': 'Passer en revue les modifications',
'contextPanel.mode.walkthrough': 'Parcours', 'contextPanel.mode.walkthrough': 'Parcours',
+4
View File
@@ -1103,6 +1103,10 @@ export const dict: Record<I18nKey, string> = {
'contextPanel.editorEmpty.description': 'ツリーからファイルを選んで編集を始めましょう。', 'contextPanel.editorEmpty.description': 'ツリーからファイルを選んで編集を始めましょう。',
'contextRail.surface.editor.description': 'プロジェクトのファイルを編集', 'contextRail.surface.editor.description': 'プロジェクトのファイルを編集',
'contextRail.surface.git.description': 'コミット・ブランチ・プルリクエスト', 'contextRail.surface.git.description': 'コミット・ブランチ・プルリクエスト',
'contextRail.surface.git.changesCountAriaSingle': '{label}、変更ファイル{count}件',
'contextRail.surface.git.changesCountAriaPlural': '{label}、変更ファイル{count}件',
'contextRail.surface.git.changesCountTooltipSingle': '変更ファイル{count}件',
'contextRail.surface.git.changesCountTooltipPlural': '変更ファイル{count}件',
'contextRail.surface.terminal.description': '内蔵ターミナル', 'contextRail.surface.terminal.description': '内蔵ターミナル',
'contextRail.surface.diff.description': '作業中の変更をレビュー', 'contextRail.surface.diff.description': '作業中の変更をレビュー',
'contextPanel.mode.walkthrough': 'ウォークスルー', 'contextPanel.mode.walkthrough': 'ウォークスルー',
+4
View File
@@ -1107,6 +1107,10 @@ export const dict: Record<I18nKey, string> = {
'contextPanel.editorEmpty.description': '트리에서 파일을 선택해 편집을 시작하세요.', 'contextPanel.editorEmpty.description': '트리에서 파일을 선택해 편집을 시작하세요.',
'contextRail.surface.editor.description': '프로젝트 파일 편집', 'contextRail.surface.editor.description': '프로젝트 파일 편집',
'contextRail.surface.git.description': '커밋, 브랜치, 풀 리퀘스트', 'contextRail.surface.git.description': '커밋, 브랜치, 풀 리퀘스트',
'contextRail.surface.git.changesCountAriaSingle': '{label}, 변경된 파일 {count}개',
'contextRail.surface.git.changesCountAriaPlural': '{label}, 변경된 파일 {count}개',
'contextRail.surface.git.changesCountTooltipSingle': '변경된 파일 {count}개',
'contextRail.surface.git.changesCountTooltipPlural': '변경된 파일 {count}개',
'contextRail.surface.terminal.description': '내장 터미널', 'contextRail.surface.terminal.description': '내장 터미널',
'contextRail.surface.diff.description': '작업 중인 변경 사항 검토', 'contextRail.surface.diff.description': '작업 중인 변경 사항 검토',
'contextPanel.mode.walkthrough': '워크스루', 'contextPanel.mode.walkthrough': '워크스루',
+4
View File
@@ -1419,6 +1419,10 @@ export const dict: Record<I18nKey, string> = {
'contextPanel.editorEmpty.description': 'Wybierz plik z drzewa, aby rozpocząć edycję.', 'contextPanel.editorEmpty.description': 'Wybierz plik z drzewa, aby rozpocząć edycję.',
'contextRail.surface.editor.description': 'Edytuj pliki projektu', 'contextRail.surface.editor.description': 'Edytuj pliki projektu',
'contextRail.surface.git.description': 'Commity, gałęzie i pull requesty', 'contextRail.surface.git.description': 'Commity, gałęzie i pull requesty',
'contextRail.surface.git.changesCountAriaSingle': '{label}, {count} zmieniony plik',
'contextRail.surface.git.changesCountAriaPlural': '{label}, {count} zmienionych plików',
'contextRail.surface.git.changesCountTooltipSingle': '{count} zmieniony plik',
'contextRail.surface.git.changesCountTooltipPlural': '{count} zmienionych plików',
'contextRail.surface.terminal.description': 'Wbudowany terminal', 'contextRail.surface.terminal.description': 'Wbudowany terminal',
'contextRail.surface.diff.description': 'Przeglądaj bieżące zmiany', 'contextRail.surface.diff.description': 'Przeglądaj bieżące zmiany',
'contextPanel.mode.walkthrough': 'Przewodnik', 'contextPanel.mode.walkthrough': 'Przewodnik',
@@ -1107,6 +1107,10 @@ export const dict: Record<I18nKey, string> = {
"contextPanel.editorEmpty.description": "Escolha um arquivo na árvore para começar a editar.", "contextPanel.editorEmpty.description": "Escolha um arquivo na árvore para começar a editar.",
"contextRail.surface.editor.description": "Editar arquivos do projeto", "contextRail.surface.editor.description": "Editar arquivos do projeto",
"contextRail.surface.git.description": "Commits, branches e pull requests", "contextRail.surface.git.description": "Commits, branches e pull requests",
"contextRail.surface.git.changesCountAriaSingle": "{label}, {count} arquivo modificado",
"contextRail.surface.git.changesCountAriaPlural": "{label}, {count} arquivos modificados",
"contextRail.surface.git.changesCountTooltipSingle": "{count} arquivo modificado",
"contextRail.surface.git.changesCountTooltipPlural": "{count} arquivos modificados",
"contextRail.surface.terminal.description": "Terminal integrado", "contextRail.surface.terminal.description": "Terminal integrado",
"contextRail.surface.diff.description": "Revisar alterações em andamento", "contextRail.surface.diff.description": "Revisar alterações em andamento",
"contextPanel.mode.walkthrough": "Percurso", "contextPanel.mode.walkthrough": "Percurso",
+4
View File
@@ -1107,6 +1107,10 @@ export const dict: Record<I18nKey, string> = {
"contextPanel.editorEmpty.description": "Виберіть файл у дереві, щоб почати редагування.", "contextPanel.editorEmpty.description": "Виберіть файл у дереві, щоб почати редагування.",
"contextRail.surface.editor.description": "Редагування файлів проєкту", "contextRail.surface.editor.description": "Редагування файлів проєкту",
"contextRail.surface.git.description": "Коміти, гілки та pull request-и", "contextRail.surface.git.description": "Коміти, гілки та pull request-и",
"contextRail.surface.git.changesCountAriaSingle": "{label}, {count} змінений файл",
"contextRail.surface.git.changesCountAriaPlural": "{label}, {count} змінених файлів",
"contextRail.surface.git.changesCountTooltipSingle": "{count} змінений файл",
"contextRail.surface.git.changesCountTooltipPlural": "{count} змінених файлів",
"contextRail.surface.terminal.description": "Вбудований термінал", "contextRail.surface.terminal.description": "Вбудований термінал",
"contextRail.surface.diff.description": "Перегляд поточних змін", "contextRail.surface.diff.description": "Перегляд поточних змін",
"contextPanel.mode.walkthrough": "Розбір", "contextPanel.mode.walkthrough": "Розбір",
@@ -1107,6 +1107,10 @@ export const dict: Record<I18nKey, string> = {
'contextPanel.editorEmpty.description': '从文件树中选择一个文件开始编辑。', 'contextPanel.editorEmpty.description': '从文件树中选择一个文件开始编辑。',
'contextRail.surface.editor.description': '编辑项目文件', 'contextRail.surface.editor.description': '编辑项目文件',
'contextRail.surface.git.description': '提交、分支和拉取请求', 'contextRail.surface.git.description': '提交、分支和拉取请求',
'contextRail.surface.git.changesCountAriaSingle': '{label},{count} 个更改的文件',
'contextRail.surface.git.changesCountAriaPlural': '{label},{count} 个更改的文件',
'contextRail.surface.git.changesCountTooltipSingle': '{count} 个更改的文件',
'contextRail.surface.git.changesCountTooltipPlural': '{count} 个更改的文件',
'contextRail.surface.terminal.description': '内置终端', 'contextRail.surface.terminal.description': '内置终端',
'contextRail.surface.diff.description': '查看工作区更改', 'contextRail.surface.diff.description': '查看工作区更改',
'contextPanel.mode.walkthrough': '导读', 'contextPanel.mode.walkthrough': '导读',
@@ -1119,6 +1119,10 @@ export const dict: Record<I18nKey, string> = {
'contextPanel.editorEmpty.description': '從檔案樹選擇檔案開始編輯。', 'contextPanel.editorEmpty.description': '從檔案樹選擇檔案開始編輯。',
'contextRail.surface.editor.description': '編輯專案檔案', 'contextRail.surface.editor.description': '編輯專案檔案',
'contextRail.surface.git.description': '提交、分支與拉取請求', 'contextRail.surface.git.description': '提交、分支與拉取請求',
'contextRail.surface.git.changesCountAriaSingle': '{label},{count} 個變更的檔案',
'contextRail.surface.git.changesCountAriaPlural': '{label},{count} 個變更的檔案',
'contextRail.surface.git.changesCountTooltipSingle': '{count} 個變更的檔案',
'contextRail.surface.git.changesCountTooltipPlural': '{count} 個變更的檔案',
'contextRail.surface.terminal.description': '內建終端機', 'contextRail.surface.terminal.description': '內建終端機',
'contextRail.surface.diff.description': '檢視工作區變更', 'contextRail.surface.diff.description': '檢視工作區變更',
'contextPanel.mode.walkthrough': '導讀', 'contextPanel.mode.walkthrough': '導讀',