fix(ui): provider-aware context rail branding for GitLab
This commit is contained in:
@@ -24,7 +24,7 @@ import { useEffectiveDirectory } from '@/hooks/useEffectiveDirectory';
|
||||
import { cn } from '@/lib/utils';
|
||||
import { useI18n } from '@/lib/i18n';
|
||||
import { useBrowserFaviconStore } from '@/stores/useBrowserFaviconStore';
|
||||
import { useGitProvider } from '@/lib/gitProvider';
|
||||
import { useGitProvider, type GitProvider } from '@/lib/gitProvider';
|
||||
import { useFilesViewTabsStore } from '@/stores/useFilesViewTabsStore';
|
||||
import { useUIStore, type ContextPanelMode, type PendingDiffScope } from '@/stores/useUIStore';
|
||||
import { markSessionViewed } from '@/sync/notification-store';
|
||||
@@ -110,7 +110,8 @@ const getRelativePathLabel = (filePath: string | null, directory: string): strin
|
||||
|
||||
const getModeLabel = (
|
||||
mode: ContextPanelMode,
|
||||
t: TranslateFn
|
||||
t: TranslateFn,
|
||||
gitProvider: GitProvider | null,
|
||||
): string => {
|
||||
if (mode === 'chat') return t('contextPanel.mode.chat');
|
||||
if (mode === 'file') return t('contextPanel.mode.files');
|
||||
@@ -119,7 +120,7 @@ const getModeLabel = (
|
||||
if (mode === 'plan') return t('contextPanel.mode.plan');
|
||||
if (mode === 'browser') return t('contextPanel.mode.browser');
|
||||
if (mode === 'git') return t('layout.rightSidebar.git');
|
||||
if (mode === 'pr') return t('contextPanel.mode.pr');
|
||||
if (mode === 'pr') return gitProvider === 'gitlab' ? t('contextPanel.mode.mr') : t('contextPanel.mode.pr');
|
||||
if (mode === 'notes') return t('contextRail.surface.notes');
|
||||
if (mode === 'terminal') return t('layout.mainTab.terminal');
|
||||
return t('contextPanel.mode.context');
|
||||
@@ -146,7 +147,8 @@ const getFileNameFromPath = (path: string | null): string | null => {
|
||||
const getTabLabel = (
|
||||
tab: { mode: ContextPanelMode; label: string | null; targetPath: string | null; dedupeKey?: string; sessionTitleFallback?: string | null; stagedDiff?: boolean },
|
||||
sessionTitleById: ReadonlyMap<string, string>,
|
||||
t: TranslateFn
|
||||
t: TranslateFn,
|
||||
gitProvider: GitProvider | null,
|
||||
): string => {
|
||||
if (tab.mode === 'chat') {
|
||||
const sessionID = getSessionIDFromDedupeKey(tab.dedupeKey);
|
||||
@@ -185,7 +187,7 @@ const getTabLabel = (
|
||||
return t('contextPanel.mode.diff');
|
||||
}
|
||||
|
||||
return getModeLabel(tab.mode, t);
|
||||
return getModeLabel(tab.mode, t, gitProvider);
|
||||
};
|
||||
|
||||
const getTabIcon = (
|
||||
@@ -922,7 +924,7 @@ export const ContextPanel: React.FC = () => {
|
||||
);
|
||||
|
||||
const tabItems = React.useMemo(() => activeModeTabs.map((tab) => {
|
||||
const rawLabel = getTabLabel(tab, sessionTitleById, t);
|
||||
const rawLabel = getTabLabel(tab, sessionTitleById, t, gitProvider);
|
||||
const label = truncateTabLabel(rawLabel, CONTEXT_TAB_LABEL_MAX_CHARS);
|
||||
const tabPathLabel = getRelativePathLabel(tab.targetPath, effectiveDirectory);
|
||||
return {
|
||||
@@ -932,7 +934,7 @@ export const ContextPanel: React.FC = () => {
|
||||
title: tabPathLabel ? `${rawLabel}: ${tabPathLabel}` : rawLabel,
|
||||
closeLabel: t('contextPanel.tab.closeTabAria', { label }),
|
||||
};
|
||||
}), [activeModeTabs, effectiveDirectory, faviconByOrigin, sessionTitleById, t]);
|
||||
}), [activeModeTabs, effectiveDirectory, faviconByOrigin, gitProvider, sessionTitleById, t]);
|
||||
|
||||
const activeNonChatContent = activeTab?.mode === 'context'
|
||||
? <ContextPanelContent />
|
||||
@@ -1006,7 +1008,7 @@ export const ContextPanel: React.FC = () => {
|
||||
<div className="flex min-w-0 flex-1 items-center gap-1.5 px-3">
|
||||
{activeTab ? getTabIcon(activeTab, faviconByOrigin) : null}
|
||||
<span className="truncate typography-ui-label text-foreground">
|
||||
{activeTab ? getModeLabel(activeTab.mode, t) : null}
|
||||
{activeTab ? getModeLabel(activeTab.mode, t, gitProvider) : null}
|
||||
</span>
|
||||
</div>
|
||||
)}
|
||||
|
||||
@@ -17,11 +17,13 @@ import {
|
||||
import { CSS } from '@dnd-kit/utilities';
|
||||
|
||||
import { Icon } from '@/components/icon/Icon';
|
||||
import type { IconName } from '@/components/icon/icons';
|
||||
import { DiffViewIcon } from '@/components/icons/DiffIcon';
|
||||
import { Tooltip, TooltipContent, TooltipTrigger } from '@/components/ui/tooltip';
|
||||
import { useEffectiveDirectory } from '@/hooks/useEffectiveDirectory';
|
||||
import { useDeviceInfo } from '@/lib/device';
|
||||
import { isVSCodeRuntime } from '@/lib/desktop';
|
||||
import { useGitProvider } from '@/lib/gitProvider';
|
||||
import { useI18n } from '@/lib/i18n';
|
||||
import {
|
||||
getVisibleContextRailSurfaces,
|
||||
@@ -167,6 +169,9 @@ export const ContextPanelRail: React.FC = () => {
|
||||
const planModeEnabled = useFeatureFlagsStore((state) => state.planModeEnabled);
|
||||
const { screenWidth } = useDeviceInfo();
|
||||
const gitStatus = useGitStatus(directoryKey || null);
|
||||
// Provider-aware 'pr' branding: GitLab repositories get the MR descriptor
|
||||
// (icon + labels) on the rail instead of the GitHub pull-request branding.
|
||||
const gitProvider = useGitProvider(directoryKey);
|
||||
|
||||
const surfaceSwitchPrefix = React.useMemo(
|
||||
() => getEffectiveShortcutPrefix('switch_context_surface', shortcutOverrides),
|
||||
@@ -260,8 +265,9 @@ export const ContextPanelRail: React.FC = () => {
|
||||
isVSCode: isVSCodeRuntime(),
|
||||
screenWidth,
|
||||
tabs,
|
||||
gitProvider,
|
||||
});
|
||||
}, [contextRailOrder, planModeEnabled, screenWidth, tabs]);
|
||||
}, [contextRailOrder, gitProvider, planModeEnabled, screenWidth, tabs]);
|
||||
|
||||
const handleDragEnd = React.useCallback((event: DragEndEvent) => {
|
||||
const { active, over } = event;
|
||||
@@ -291,7 +297,17 @@ export const ContextPanelRail: React.FC = () => {
|
||||
<DndContext sensors={sensors} collisionDetection={closestCenter} onDragEnd={handleDragEnd}>
|
||||
<SortableContext items={surfaces.map((surface) => surface.id)} strategy={verticalListSortingStrategy}>
|
||||
{surfaces.map((surface, index) => {
|
||||
const label = t(surface.labelKey);
|
||||
// The 'pr' surface renders the GitLab MR view in GitLab repos, so
|
||||
// it borrows GitLab's merge-request branding instead of GitHub's.
|
||||
const gitlabMrSurface: ContextSurfaceDescriptor = surface.id === 'pr' && gitProvider === 'gitlab'
|
||||
? {
|
||||
...surface,
|
||||
icon: 'git-merge' as IconName,
|
||||
labelKey: 'contextPanel.mode.mr',
|
||||
descriptionKey: 'contextRail.surface.mr.description',
|
||||
}
|
||||
: surface;
|
||||
const label = t(gitlabMrSurface.labelKey);
|
||||
// Git shows a numeric badge instead of the old activity dot.
|
||||
// Other surfaces never inherit git's changed-files signal.
|
||||
// The work-status panel reports the same count in words a few
|
||||
@@ -301,11 +317,11 @@ export const ContextPanelRail: React.FC = () => {
|
||||
return (
|
||||
<ContextPanelRailItem
|
||||
key={surface.id}
|
||||
surface={surface}
|
||||
surface={gitlabMrSurface}
|
||||
isActive={activeMode === surface.mode}
|
||||
showActivityDot={false}
|
||||
label={label}
|
||||
description={t(surface.descriptionKey)}
|
||||
description={t(gitlabMrSurface.descriptionKey)}
|
||||
badgeCount={badgeCount}
|
||||
badgeAriaLabel={badgeCount !== null
|
||||
? t(
|
||||
|
||||
@@ -18,6 +18,7 @@ import {
|
||||
normalizeCombo,
|
||||
} from '@/lib/shortcuts';
|
||||
import { getVisibleContextRailSurfaces } from '@/lib/surfaces/registry';
|
||||
import { useGitProvider } from '@/lib/gitProvider';
|
||||
import { readEmbeddedThemeSearchParams } from '@/contexts/theme-embedded-bootstrap';
|
||||
import { useDirectoryStore } from '@/stores/useDirectoryStore';
|
||||
import { useProjectsStore } from '@/stores/useProjectsStore';
|
||||
@@ -39,6 +40,9 @@ export const useKeyboardShortcuts = () => {
|
||||
const toggleSidebar = useUIStore((s) => s.toggleSidebar);
|
||||
const currentShortcutDirectory = useDirectoryStore((s) => s.currentDirectory);
|
||||
const effectiveDirectory = useEffectiveDirectory();
|
||||
// Mirrors the rail's provider-aware 'pr' surface: the digit-shortcut list
|
||||
// must agree with the rail on whether the PR/MR surface is visible.
|
||||
const gitProvider = useGitProvider(effectiveDirectory);
|
||||
|
||||
// The terminal lives in the context panel; these mirror the rail behavior.
|
||||
const toggleTerminalSurface = React.useCallback(() => {
|
||||
@@ -505,6 +509,7 @@ export const useKeyboardShortcuts = () => {
|
||||
isVSCode: isVSCodeRuntime(),
|
||||
screenWidth: window.innerWidth,
|
||||
tabs: panelState?.tabs ?? [],
|
||||
gitProvider,
|
||||
});
|
||||
const target = visibleSurfaces[switchSurfaceDigit - 1];
|
||||
if (!target) {
|
||||
@@ -707,6 +712,7 @@ export const useKeyboardShortcuts = () => {
|
||||
currentSessionId,
|
||||
currentDirectory,
|
||||
effectiveDirectory,
|
||||
gitProvider,
|
||||
activeProject?.id,
|
||||
activeProject?.path,
|
||||
shortcutOverrides,
|
||||
|
||||
@@ -2979,6 +2979,7 @@ export const dict = {
|
||||
'gitView.pr.segment.comments': 'Kommentare',
|
||||
'gitView.pr.comments.addAll': 'Alle hinzufügen',
|
||||
'contextPanel.mode.pr': 'PR',
|
||||
'contextPanel.mode.mr': 'Merge-Request',
|
||||
'contextRail.aria.rail': 'Kontextleiste',
|
||||
'contextPanel.editorEmpty.title': 'Kein Kontext ausgewählt',
|
||||
'contextPanel.editorEmpty.description': 'Wählen Sie etwas aus der Seitenleiste aus, um Kontext anzuzeigen.',
|
||||
@@ -3093,6 +3094,7 @@ export const dict = {
|
||||
'walkthrough.blocked.structuredOutput.descriptionUnknownModel': 'Das kleine Modell unterstützt die strukturierten Antworten nicht, die ein Walkthrough benötigt.',
|
||||
'contextRail.surface.plan.description': 'Plankontext',
|
||||
'contextRail.surface.pr.description': 'PR-Kontext',
|
||||
'contextRail.surface.mr.description': 'MR-Kontext',
|
||||
'contextRail.surface.notes.description': 'Notizkontext',
|
||||
'contextRail.surface.context.description': 'Allgemeiner Kontext',
|
||||
'contextRail.surface.browser.description': 'Browserkontext',
|
||||
|
||||
@@ -1113,6 +1113,7 @@ export const dict = {
|
||||
'contextPanel.mode.workingDiff': 'Working Diff',
|
||||
'contextPanel.mode.plan': 'Plan',
|
||||
'contextPanel.mode.pr': 'Pull Request',
|
||||
'contextPanel.mode.mr': 'Merge request',
|
||||
'contextPanel.mode.context': 'Context',
|
||||
'contextPanel.mode.preview': 'Preview',
|
||||
'contextPanel.mode.browser': 'Browser',
|
||||
@@ -1230,6 +1231,7 @@ export const dict = {
|
||||
'walkthrough.blocked.structuredOutput.descriptionUnknownModel': 'The small model does not support the structured responses a walkthrough needs.',
|
||||
'contextRail.surface.plan.description': 'View the current plan',
|
||||
'contextRail.surface.pr.description': 'Create, review, and merge the pull request for the current branch',
|
||||
'contextRail.surface.mr.description': 'Create, review, and merge the merge request for the current branch',
|
||||
'contextRail.surface.notes.description': 'Notes, todos, and plans for the project',
|
||||
'contextRail.surface.context.description': 'Session context and token usage',
|
||||
'contextRail.surface.browser.description': 'Built-in web browser',
|
||||
|
||||
@@ -1114,6 +1114,7 @@ export const dict: Record<I18nKey, string> = {
|
||||
"contextPanel.mode.workingDiff": "Working Diff",
|
||||
"contextPanel.mode.plan": "Plan",
|
||||
"contextPanel.mode.pr": "Pull Request",
|
||||
"contextPanel.mode.mr": "Solicitud de fusión",
|
||||
"contextPanel.mode.context": "Contexto",
|
||||
"contextPanel.mode.preview": "Vista previa",
|
||||
"contextPanel.mode.browser": "Navegador",
|
||||
@@ -1231,6 +1232,7 @@ export const dict: Record<I18nKey, string> = {
|
||||
"walkthrough.blocked.structuredOutput.descriptionUnknownModel": "El modelo pequeño no admite las respuestas estructuradas que necesita un recorrido.",
|
||||
"contextRail.surface.plan.description": "Ver el plan actual",
|
||||
"contextRail.surface.pr.description": "Crea, revisa y fusiona el pull request de la rama actual",
|
||||
"contextRail.surface.mr.description": "Crea, revisa y fusiona la solicitud de fusión de la rama actual",
|
||||
"contextRail.surface.notes.description": "Notas, tareas y planes del proyecto",
|
||||
"contextRail.surface.context.description": "Contexto de la sesión y uso de tokens",
|
||||
"contextRail.surface.browser.description": "Navegador web integrado",
|
||||
|
||||
@@ -933,6 +933,7 @@ export const dict = {
|
||||
'contextPanel.mode.workingDiff': 'Différentiel de travail',
|
||||
'contextPanel.mode.plan': 'Plan',
|
||||
'contextPanel.mode.pr': 'Pull Request',
|
||||
'contextPanel.mode.mr': 'Demande de fusion',
|
||||
'contextPanel.mode.context': 'Contexte',
|
||||
'contextPanel.mode.preview': 'Aperçu',
|
||||
'contextPanel.mode.browser': 'Navigateur',
|
||||
@@ -1050,6 +1051,7 @@ export const dict = {
|
||||
'walkthrough.blocked.structuredOutput.descriptionUnknownModel': 'Le petit modèle ne prend pas en charge les réponses structurées nécessaires à un parcours.',
|
||||
'contextRail.surface.plan.description': 'Voir le plan actuel',
|
||||
'contextRail.surface.pr.description': 'Créer, relire et fusionner la pull request de la branche actuelle',
|
||||
'contextRail.surface.mr.description': 'Créer, relire et fusionner la demande de fusion de la branche actuelle',
|
||||
'contextRail.surface.notes.description': 'Notes, tâches et plans du projet',
|
||||
'contextRail.surface.context.description': 'Contexte de session et utilisation des tokens',
|
||||
'contextRail.surface.browser.description': 'Navigateur web intégré',
|
||||
|
||||
@@ -1110,6 +1110,7 @@ export const dict: Record<I18nKey, string> = {
|
||||
'contextPanel.mode.workingDiff': '作業中差分',
|
||||
'contextPanel.mode.plan': '計画',
|
||||
'contextPanel.mode.pr': 'プルリクエスト',
|
||||
'contextPanel.mode.mr': 'マージリクエスト',
|
||||
'contextPanel.mode.context': 'コンテキスト',
|
||||
'contextPanel.mode.preview': 'プレビュー',
|
||||
'contextPanel.mode.browser': 'ブラウザ',
|
||||
@@ -1227,6 +1228,7 @@ export const dict: Record<I18nKey, string> = {
|
||||
'walkthrough.blocked.structuredOutput.descriptionUnknownModel': 'スモールモデルはウォークスルーに必要な構造化応答をサポートしていません。',
|
||||
'contextRail.surface.plan.description': '現在のプランを表示',
|
||||
'contextRail.surface.pr.description': '現在のブランチのプルリクエストを作成・確認・マージ',
|
||||
'contextRail.surface.mr.description': '現在のブランチのマージリクエストを作成・確認・マージ',
|
||||
'contextRail.surface.notes.description': 'プロジェクトのノート・ToDo・プラン',
|
||||
'contextRail.surface.context.description': 'セッションのコンテキストとトークン使用量',
|
||||
'contextRail.surface.browser.description': '内蔵ウェブブラウザ',
|
||||
|
||||
@@ -1114,6 +1114,7 @@ export const dict: Record<I18nKey, string> = {
|
||||
'contextPanel.mode.workingDiff': 'Working Diff',
|
||||
'contextPanel.mode.plan': '계획',
|
||||
'contextPanel.mode.pr': '풀 리퀘스트',
|
||||
'contextPanel.mode.mr': '병합 요청',
|
||||
'contextPanel.mode.context': '컨텍스트',
|
||||
'contextPanel.mode.preview': '미리보기',
|
||||
'contextPanel.mode.browser': '브라우저',
|
||||
@@ -1231,6 +1232,7 @@ export const dict: Record<I18nKey, string> = {
|
||||
'walkthrough.blocked.structuredOutput.descriptionUnknownModel': '스몰 모델은 워크스루에 필요한 구조화된 응답을 지원하지 않습니다.',
|
||||
'contextRail.surface.plan.description': '현재 계획 보기',
|
||||
'contextRail.surface.pr.description': '현재 브랜치의 풀 리퀘스트를 생성, 검토, 병합',
|
||||
'contextRail.surface.mr.description': '현재 브랜치의 병합 요청을 생성, 검토, 병합',
|
||||
'contextRail.surface.notes.description': '프로젝트의 노트, 할 일, 계획',
|
||||
'contextRail.surface.context.description': '세션 컨텍스트 및 토큰 사용량',
|
||||
'contextRail.surface.browser.description': '내장 웹 브라우저',
|
||||
|
||||
@@ -1452,6 +1452,7 @@ export const dict: Record<I18nKey, string> = {
|
||||
'contextPanel.mode.files': 'Pliki',
|
||||
'contextPanel.mode.plan': 'Plan',
|
||||
'contextPanel.mode.pr': 'Pull Request',
|
||||
'contextPanel.mode.mr': 'Żądanie scalenia',
|
||||
'contextPanel.mode.preview': 'Podgląd',
|
||||
'contextPanel.mode.browser': 'Przeglądarka',
|
||||
'contextRail.aria.rail': 'Powierzchnie panelu',
|
||||
@@ -1568,6 +1569,7 @@ export const dict: Record<I18nKey, string> = {
|
||||
'walkthrough.blocked.structuredOutput.descriptionUnknownModel': 'Mały model nie obsługuje ustrukturyzowanych odpowiedzi wymaganych przez przewodnik.',
|
||||
'contextRail.surface.plan.description': 'Zobacz bieżący plan',
|
||||
'contextRail.surface.pr.description': 'Twórz, przeglądaj i scalaj pull request bieżącej gałęzi',
|
||||
'contextRail.surface.mr.description': 'Twórz, przeglądaj i scalaj żądanie scalenia bieżącej gałęzi',
|
||||
'contextRail.surface.notes.description': 'Notatki, zadania i plany projektu',
|
||||
'contextRail.surface.context.description': 'Kontekst sesji i zużycie tokenów',
|
||||
'contextRail.surface.browser.description': 'Wbudowana przeglądarka',
|
||||
|
||||
@@ -1114,6 +1114,7 @@ export const dict: Record<I18nKey, string> = {
|
||||
"contextPanel.mode.workingDiff": "Working Diff",
|
||||
"contextPanel.mode.plan": "Plano",
|
||||
"contextPanel.mode.pr": "Pull Request",
|
||||
"contextPanel.mode.mr": "Solicitação de merge",
|
||||
"contextPanel.mode.context": "Contexto",
|
||||
"contextPanel.mode.preview": "Prévia",
|
||||
"contextPanel.mode.browser": "Navegador",
|
||||
@@ -1231,6 +1232,7 @@ export const dict: Record<I18nKey, string> = {
|
||||
"walkthrough.blocked.structuredOutput.descriptionUnknownModel": "O modelo pequeno não suporta as respostas estruturadas que um percurso exige.",
|
||||
"contextRail.surface.plan.description": "Ver o plano atual",
|
||||
"contextRail.surface.pr.description": "Crie, revise e faça merge do pull request do branch atual",
|
||||
"contextRail.surface.mr.description": "Crie, revise e faça merge da solicitação de merge do branch atual",
|
||||
"contextRail.surface.notes.description": "Notas, tarefas e planos do projeto",
|
||||
"contextRail.surface.context.description": "Contexto da sessão e uso de tokens",
|
||||
"contextRail.surface.browser.description": "Navegador web integrado",
|
||||
|
||||
@@ -1114,6 +1114,7 @@ export const dict: Record<I18nKey, string> = {
|
||||
"contextPanel.mode.workingDiff": "Working Diff",
|
||||
"contextPanel.mode.plan": "План",
|
||||
"contextPanel.mode.pr": "Pull Request",
|
||||
"contextPanel.mode.mr": "Запит на злиття",
|
||||
"contextPanel.mode.context": "Контекст",
|
||||
"contextPanel.mode.preview": "Перегляд",
|
||||
"contextPanel.mode.browser": "Браузер",
|
||||
@@ -1231,6 +1232,7 @@ export const dict: Record<I18nKey, string> = {
|
||||
"walkthrough.blocked.structuredOutput.descriptionUnknownModel": "Small model не підтримує структуровані відповіді, потрібні для розбору.",
|
||||
"contextRail.surface.plan.description": "Перегляд поточного плану",
|
||||
"contextRail.surface.pr.description": "Створюйте, переглядайте та зливайте pull request поточної гілки",
|
||||
"contextRail.surface.mr.description": "Створюйте, переглядайте та зливайте запит на злиття поточної гілки",
|
||||
"contextRail.surface.notes.description": "Нотатки, задачі та плани проєкту",
|
||||
"contextRail.surface.context.description": "Контекст сесії та використання токенів",
|
||||
"contextRail.surface.browser.description": "Вбудований браузер",
|
||||
|
||||
@@ -1114,6 +1114,7 @@ export const dict: Record<I18nKey, string> = {
|
||||
'contextPanel.mode.workingDiff': 'Working Diff',
|
||||
'contextPanel.mode.plan': '计划',
|
||||
'contextPanel.mode.pr': '拉取请求',
|
||||
'contextPanel.mode.mr': '合并请求',
|
||||
'contextPanel.mode.context': '上下文',
|
||||
'contextPanel.mode.preview': '预览',
|
||||
'contextPanel.mode.browser': '浏览器',
|
||||
@@ -1231,6 +1232,7 @@ export const dict: Record<I18nKey, string> = {
|
||||
'walkthrough.blocked.structuredOutput.descriptionUnknownModel': '小模型不支持导读所需的结构化响应。',
|
||||
'contextRail.surface.plan.description': '查看当前计划',
|
||||
'contextRail.surface.pr.description': '创建、审查并合并当前分支的拉取请求',
|
||||
'contextRail.surface.mr.description': '创建、审查并合并当前分支的合并请求',
|
||||
'contextRail.surface.notes.description': '项目的笔记、待办和计划',
|
||||
'contextRail.surface.context.description': '会话上下文与令牌用量',
|
||||
'contextRail.surface.browser.description': '内置网页浏览器',
|
||||
|
||||
@@ -1126,6 +1126,7 @@ export const dict: Record<I18nKey, string> = {
|
||||
'contextPanel.mode.workingDiff': 'Working Diff',
|
||||
'contextPanel.mode.plan': '計畫',
|
||||
'contextPanel.mode.pr': '提取請求',
|
||||
'contextPanel.mode.mr': '合併請求',
|
||||
'contextPanel.mode.context': '上下文',
|
||||
'contextPanel.mode.preview': '預覽',
|
||||
'contextPanel.mode.browser': '瀏覽器',
|
||||
@@ -1243,6 +1244,7 @@ export const dict: Record<I18nKey, string> = {
|
||||
'walkthrough.blocked.structuredOutput.descriptionUnknownModel': '小模型不支援導讀所需的結構化回應。',
|
||||
'contextRail.surface.plan.description': '檢視目前計畫',
|
||||
'contextRail.surface.pr.description': '建立、審查並合併目前分支的提取請求',
|
||||
'contextRail.surface.mr.description': '建立、審查並合併目前分支的合併請求',
|
||||
'contextRail.surface.notes.description': '專案的筆記、待辦與計畫',
|
||||
'contextRail.surface.context.description': '工作階段情境與權杖用量',
|
||||
'contextRail.surface.browser.description': '內建網頁瀏覽器',
|
||||
|
||||
@@ -23,10 +23,11 @@ edge (`components/layout/ContextPanelRail.tsx`) and rendered by
|
||||
- `getVisibleContextRailSurfaces` is the single visibility filter shared by the
|
||||
rail and the global surface-switch shortcut (`switch_context_surface` in
|
||||
`lib/shortcuts.ts`): it drops the plan surface unless plan mode is enabled,
|
||||
drops the walkthrough on VS Code and below `WALKTHROUGH_MIN_WIDTH`, 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.
|
||||
drops the walkthrough on VS Code and below `WALKTHROUGH_MIN_WIDTH`, hides
|
||||
`has-content` surfaces until a tab of their mode exists, and drops the `pr`
|
||||
surface when the repository is on a git provider other than GitHub or GitLab
|
||||
(`gitProvider: 'other'`). Both consumers use it so the digit shown on a rail
|
||||
badge always maps to the same surface the shortcut opens.
|
||||
|
||||
## Adding a surface
|
||||
|
||||
|
||||
@@ -59,6 +59,17 @@ describe('getVisibleContextRailSurfaces', () => {
|
||||
expect(getVisibleContextRailSurfaces(baseOptions).some((s) => s.id === 'browser')).toBe(true);
|
||||
});
|
||||
|
||||
test('hides the pr surface for other git providers but keeps it for github/gitlab', () => {
|
||||
expect(getVisibleContextRailSurfaces({ ...baseOptions, gitProvider: 'other' }).some((s) => s.id === 'pr')).toBe(false);
|
||||
expect(getVisibleContextRailSurfaces({ ...baseOptions, gitProvider: 'gitlab' }).some((s) => s.id === 'pr')).toBe(true);
|
||||
expect(getVisibleContextRailSurfaces({ ...baseOptions, gitProvider: 'github' }).some((s) => s.id === 'pr')).toBe(true);
|
||||
});
|
||||
|
||||
test('keeps the pr surface while the git provider is unknown', () => {
|
||||
expect(getVisibleContextRailSurfaces({ ...baseOptions, gitProvider: null }).some((s) => s.id === 'pr')).toBe(true);
|
||||
expect(getVisibleContextRailSurfaces(baseOptions).some((s) => s.id === 'pr')).toBe(true);
|
||||
});
|
||||
|
||||
test('respects the persisted user rail order', () => {
|
||||
const surfaces = getVisibleContextRailSurfaces({ ...baseOptions, railOrder: ['git', 'context'] });
|
||||
expect(surfaces.slice(0, 2).map((surface) => surface.id)).toEqual(['git', 'context']);
|
||||
|
||||
@@ -188,6 +188,12 @@ type VisibleRailSurfacesOptions = {
|
||||
isVSCode: boolean;
|
||||
screenWidth: number;
|
||||
tabs: readonly { mode: ContextPanelMode }[];
|
||||
/**
|
||||
* The repository's git provider. The 'pr' surface renders the GitHub pull
|
||||
* request / GitLab merge request view; it is hidden for repositories on
|
||||
* any other provider. null (unknown, still resolving) keeps it visible.
|
||||
*/
|
||||
gitProvider?: 'github' | 'gitlab' | 'other' | null;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -203,6 +209,13 @@ export const getVisibleContextRailSurfaces = (options: VisibleRailSurfacesOption
|
||||
if (surface.id === 'plan' && !options.planModeEnabled) {
|
||||
return false;
|
||||
}
|
||||
// The 'pr' surface hosts the GitHub PR / GitLab MR view. Neither applies
|
||||
// to repositories on other providers, so the rail button (and its shortcut
|
||||
// digit) must not appear. Unknown (null) keeps it while the provider is
|
||||
// still resolving — it is not a reason to hide the surface.
|
||||
if (surface.id === 'pr' && options.gitProvider === 'other') {
|
||||
return false;
|
||||
}
|
||||
// The walkthrough needs room for a stop list beside real code, and its
|
||||
// diffs come from OpenChamber's Git routes, which VS Code does not serve.
|
||||
if (surface.id === 'walkthrough' && (options.isVSCode || options.screenWidth < WALKTHROUGH_MIN_WIDTH)) {
|
||||
|
||||
Reference in New Issue
Block a user