feat(ui): let users hide context rail surfaces
A trailing configure button on the rail — outside the sortable list and the digit shortcuts — opens a dialog that toggles each surface. The choice is stored as the hidden set so newly added surfaces appear for everyone, and the rail and the mod+alt+digit switcher share the same visibility filter, so badges and shortcuts always agree. Hidden surfaces keep their data and stay reachable from the command palette.
This commit is contained in:
@@ -36,6 +36,7 @@ import { cn } from '@/lib/utils';
|
|||||||
import { useFeatureFlagsStore } from '@/stores/useFeatureFlagsStore';
|
import { useFeatureFlagsStore } from '@/stores/useFeatureFlagsStore';
|
||||||
import { useGitStatus } from '@/stores/useGitStore';
|
import { useGitStatus } from '@/stores/useGitStore';
|
||||||
import { normalizeContextPanelDirectoryKey, useUIStore } from '@/stores/useUIStore';
|
import { normalizeContextPanelDirectoryKey, useUIStore } from '@/stores/useUIStore';
|
||||||
|
import { ContextRailSurfacesDialog } from './ContextRailSurfacesDialog';
|
||||||
|
|
||||||
const RAIL_TOOLTIP_DELAY_MS = 150;
|
const RAIL_TOOLTIP_DELAY_MS = 150;
|
||||||
// Hold the surface-switch modifier for this long before revealing the order
|
// Hold the surface-switch modifier for this long before revealing the order
|
||||||
@@ -161,6 +162,7 @@ export const ContextPanelRail: React.FC = () => {
|
|||||||
const panelState = useUIStore((state) => (directoryKey ? state.contextPanelByDirectory[directoryKey] : undefined));
|
const panelState = useUIStore((state) => (directoryKey ? state.contextPanelByDirectory[directoryKey] : undefined));
|
||||||
const workStatusPanelVisible = useUIStore((state) => state.workStatusPanelVisible);
|
const workStatusPanelVisible = useUIStore((state) => state.workStatusPanelVisible);
|
||||||
const contextRailOrder = useUIStore((state) => state.contextRailOrder);
|
const contextRailOrder = useUIStore((state) => state.contextRailOrder);
|
||||||
|
const contextRailHiddenSurfaces = useUIStore((state) => state.contextRailHiddenSurfaces);
|
||||||
const setContextRailOrder = useUIStore((state) => state.setContextRailOrder);
|
const setContextRailOrder = useUIStore((state) => state.setContextRailOrder);
|
||||||
const openContextSurface = useUIStore((state) => state.openContextSurface);
|
const openContextSurface = useUIStore((state) => state.openContextSurface);
|
||||||
const shortcutOverrides = useUIStore((state) => state.shortcutOverrides);
|
const shortcutOverrides = useUIStore((state) => state.shortcutOverrides);
|
||||||
@@ -256,12 +258,15 @@ export const ContextPanelRail: React.FC = () => {
|
|||||||
const surfaces = React.useMemo(() => {
|
const surfaces = React.useMemo(() => {
|
||||||
return getVisibleContextRailSurfaces({
|
return getVisibleContextRailSurfaces({
|
||||||
railOrder: contextRailOrder,
|
railOrder: contextRailOrder,
|
||||||
|
hiddenSurfaces: contextRailHiddenSurfaces,
|
||||||
planModeEnabled,
|
planModeEnabled,
|
||||||
isVSCode: isVSCodeRuntime(),
|
isVSCode: isVSCodeRuntime(),
|
||||||
screenWidth,
|
screenWidth,
|
||||||
tabs,
|
tabs,
|
||||||
});
|
});
|
||||||
}, [contextRailOrder, planModeEnabled, screenWidth, tabs]);
|
}, [contextRailHiddenSurfaces, contextRailOrder, planModeEnabled, screenWidth, tabs]);
|
||||||
|
|
||||||
|
const [isSurfacesDialogOpen, setIsSurfacesDialogOpen] = React.useState(false);
|
||||||
|
|
||||||
const handleDragEnd = React.useCallback((event: DragEndEvent) => {
|
const handleDragEnd = React.useCallback((event: DragEndEvent) => {
|
||||||
const { active, over } = event;
|
const { active, over } = event;
|
||||||
@@ -331,6 +336,24 @@ export const ContextPanelRail: React.FC = () => {
|
|||||||
})}
|
})}
|
||||||
</SortableContext>
|
</SortableContext>
|
||||||
</DndContext>
|
</DndContext>
|
||||||
|
{/* Outside the sortable list on purpose: this button takes no digit,
|
||||||
|
cannot be dragged, and configures the rail rather than living on it. */}
|
||||||
|
<Tooltip delayDuration={RAIL_TOOLTIP_DELAY_MS}>
|
||||||
|
<TooltipTrigger asChild>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
aria-label={t('contextRail.configure.open')}
|
||||||
|
onClick={() => setIsSurfacesDialogOpen(true)}
|
||||||
|
className="flex h-9 w-9 items-center justify-center rounded-md text-muted-foreground/70 transition-colors hover:text-foreground"
|
||||||
|
>
|
||||||
|
<Icon name="equalizer-2" className="h-[18px] w-[18px]" />
|
||||||
|
</button>
|
||||||
|
</TooltipTrigger>
|
||||||
|
<TooltipContent side="left" sideOffset={8}>
|
||||||
|
{t('contextRail.configure.open')}
|
||||||
|
</TooltipContent>
|
||||||
|
</Tooltip>
|
||||||
|
<ContextRailSurfacesDialog open={isSurfacesDialogOpen} onOpenChange={setIsSurfacesDialogOpen} />
|
||||||
</nav>
|
</nav>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -0,0 +1,78 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import { useI18n } from '@/lib/i18n';
|
||||||
|
import { useUIStore } from '@/stores/useUIStore';
|
||||||
|
import { SettingsCheckboxRow } from '@/components/sections/shared/SettingsSection';
|
||||||
|
import { Button } from '@/components/ui/button';
|
||||||
|
import {
|
||||||
|
Dialog,
|
||||||
|
DialogContent,
|
||||||
|
DialogDescription,
|
||||||
|
DialogHeader,
|
||||||
|
DialogTitle,
|
||||||
|
} from '@/components/ui/dialog';
|
||||||
|
import { sortContextSurfaces } from '@/lib/surfaces/registry';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Which surfaces the context rail shows. Everything is on by default and the
|
||||||
|
* choice is stored as the *hidden* set, so a surface added in a later release
|
||||||
|
* appears for everyone rather than staying invisible to whoever had saved
|
||||||
|
* settings before it existed. Hidden surfaces also leave the digit shortcuts
|
||||||
|
* (the rail and the shortcut share one visibility filter).
|
||||||
|
*/
|
||||||
|
export const ContextRailSurfacesDialog: React.FC<{
|
||||||
|
open: boolean;
|
||||||
|
onOpenChange: (open: boolean) => void;
|
||||||
|
}> = ({ open, onOpenChange }) => {
|
||||||
|
const { t } = useI18n();
|
||||||
|
const contextRailOrder = useUIStore((state) => state.contextRailOrder);
|
||||||
|
const hidden = useUIStore((state) => state.contextRailHiddenSurfaces);
|
||||||
|
const setSurfaceVisible = useUIStore((state) => state.setContextRailSurfaceVisible);
|
||||||
|
const setHiddenSurfaces = useUIStore((state) => state.setContextRailHiddenSurfaces);
|
||||||
|
|
||||||
|
// The full registry in the user's rail order — including surfaces a runtime
|
||||||
|
// filter currently drops, so a choice made on desktop is editable anywhere.
|
||||||
|
const surfaces = React.useMemo(() => sortContextSurfaces(contextRailOrder), [contextRailOrder]);
|
||||||
|
|
||||||
|
const allVisible = hidden.length === 0;
|
||||||
|
const noneVisible = surfaces.every((surface) => hidden.includes(surface.id));
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Dialog open={open} onOpenChange={onOpenChange}>
|
||||||
|
<DialogContent className="max-w-md">
|
||||||
|
<DialogHeader>
|
||||||
|
<DialogTitle>{t('contextRail.configure.dialogTitle')}</DialogTitle>
|
||||||
|
<DialogDescription>{t('contextRail.configure.dialogDescription')}</DialogDescription>
|
||||||
|
</DialogHeader>
|
||||||
|
|
||||||
|
<div className="flex flex-col">
|
||||||
|
{surfaces.map((surface) => (
|
||||||
|
<SettingsCheckboxRow
|
||||||
|
key={surface.id}
|
||||||
|
settingsItem={`layout.context-rail.surface.${surface.id}`}
|
||||||
|
checked={!hidden.includes(surface.id)}
|
||||||
|
onChange={(checked) => setSurfaceVisible(surface.id, checked)}
|
||||||
|
label={t(surface.labelKey)}
|
||||||
|
ariaLabel={t(surface.labelKey)}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{!allVisible ? (
|
||||||
|
<div className="flex items-center justify-between border-t pt-3">
|
||||||
|
{noneVisible ? (
|
||||||
|
<span className="text-xs text-destructive">{t('contextRail.configure.noneWarning')}</span>
|
||||||
|
) : <span />}
|
||||||
|
<Button
|
||||||
|
variant="link"
|
||||||
|
size="xs"
|
||||||
|
onClick={() => setHiddenSurfaces([])}
|
||||||
|
className="normal-case text-muted-foreground hover:text-foreground"
|
||||||
|
>
|
||||||
|
{t('contextRail.configure.showAll')}
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
) : null}
|
||||||
|
</DialogContent>
|
||||||
|
</Dialog>
|
||||||
|
);
|
||||||
|
};
|
||||||
@@ -499,6 +499,7 @@ export const useKeyboardShortcuts = () => {
|
|||||||
const panel = state.contextPanelByDirectory[directory];
|
const panel = state.contextPanelByDirectory[directory];
|
||||||
const visibleSurfaces = getVisibleContextRailSurfaces({
|
const visibleSurfaces = getVisibleContextRailSurfaces({
|
||||||
railOrder: state.contextRailOrder,
|
railOrder: state.contextRailOrder,
|
||||||
|
hiddenSurfaces: state.contextRailHiddenSurfaces,
|
||||||
planModeEnabled: useFeatureFlagsStore.getState().planModeEnabled,
|
planModeEnabled: useFeatureFlagsStore.getState().planModeEnabled,
|
||||||
isVSCode: isVSCodeRuntime(),
|
isVSCode: isVSCodeRuntime(),
|
||||||
screenWidth: window.innerWidth,
|
screenWidth: window.innerWidth,
|
||||||
|
|||||||
@@ -2995,6 +2995,11 @@ export const dict = {
|
|||||||
'gitView.pr.segment.comments': 'Kommentare',
|
'gitView.pr.segment.comments': 'Kommentare',
|
||||||
'gitView.pr.comments.addAll': 'Alle hinzufügen',
|
'gitView.pr.comments.addAll': 'Alle hinzufügen',
|
||||||
'contextPanel.mode.pr': 'PR',
|
'contextPanel.mode.pr': 'PR',
|
||||||
|
'contextRail.configure.open': 'Panels konfigurieren',
|
||||||
|
'contextRail.configure.dialogTitle': 'Leisten-Panels',
|
||||||
|
'contextRail.configure.dialogDescription': 'Wähle, welche Panels die Leiste zeigt. Ausgeblendete Panels behalten ihre Daten und bleiben über die Befehlspalette erreichbar.',
|
||||||
|
'contextRail.configure.showAll': 'Alle anzeigen',
|
||||||
|
'contextRail.configure.noneWarning': 'Alle Panels sind ausgeblendet.',
|
||||||
'contextRail.aria.rail': 'Kontextleiste',
|
'contextRail.aria.rail': 'Kontextleiste',
|
||||||
'contextPanel.editorEmpty.title': 'Kein Kontext ausgewählt',
|
'contextPanel.editorEmpty.title': 'Kein Kontext ausgewählt',
|
||||||
'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.',
|
||||||
|
|||||||
@@ -1139,6 +1139,11 @@ export const dict = {
|
|||||||
'contextPanel.mode.context': 'Context',
|
'contextPanel.mode.context': 'Context',
|
||||||
'contextPanel.mode.preview': 'Preview',
|
'contextPanel.mode.preview': 'Preview',
|
||||||
'contextPanel.mode.browser': 'Browser',
|
'contextPanel.mode.browser': 'Browser',
|
||||||
|
'contextRail.configure.open': 'Configure panels',
|
||||||
|
'contextRail.configure.dialogTitle': 'Rail panels',
|
||||||
|
'contextRail.configure.dialogDescription': 'Choose which panels the rail shows. Hidden panels keep their data and stay reachable from the command palette.',
|
||||||
|
'contextRail.configure.showAll': 'Show all',
|
||||||
|
'contextRail.configure.noneWarning': 'All panels are hidden.',
|
||||||
'contextRail.aria.rail': 'Panel surfaces',
|
'contextRail.aria.rail': 'Panel surfaces',
|
||||||
'contextPanel.editorEmpty.title': 'No file open',
|
'contextPanel.editorEmpty.title': 'No file open',
|
||||||
'contextPanel.editorEmpty.description': 'Pick a file from the tree to start editing.',
|
'contextPanel.editorEmpty.description': 'Pick a file from the tree to start editing.',
|
||||||
|
|||||||
@@ -1140,6 +1140,11 @@ export const dict: Record<I18nKey, string> = {
|
|||||||
"contextPanel.mode.context": "Contexto",
|
"contextPanel.mode.context": "Contexto",
|
||||||
"contextPanel.mode.preview": "Vista previa",
|
"contextPanel.mode.preview": "Vista previa",
|
||||||
"contextPanel.mode.browser": "Navegador",
|
"contextPanel.mode.browser": "Navegador",
|
||||||
|
"contextRail.configure.open": "Configurar paneles",
|
||||||
|
"contextRail.configure.dialogTitle": "Paneles de la barra",
|
||||||
|
"contextRail.configure.dialogDescription": "Elige qué paneles muestra la barra. Los paneles ocultos conservan sus datos y siguen accesibles desde la paleta de comandos.",
|
||||||
|
"contextRail.configure.showAll": "Mostrar todos",
|
||||||
|
"contextRail.configure.noneWarning": "Todos los paneles están ocultos.",
|
||||||
"contextRail.aria.rail": "Superficies del panel",
|
"contextRail.aria.rail": "Superficies del panel",
|
||||||
"contextPanel.editorEmpty.title": "Ningún archivo abierto",
|
"contextPanel.editorEmpty.title": "Ningún archivo abierto",
|
||||||
"contextPanel.editorEmpty.description": "Elige un archivo del árbol para empezar a editar.",
|
"contextPanel.editorEmpty.description": "Elige un archivo del árbol para empezar a editar.",
|
||||||
|
|||||||
@@ -959,6 +959,11 @@ export const dict = {
|
|||||||
'contextPanel.mode.context': 'Contexte',
|
'contextPanel.mode.context': 'Contexte',
|
||||||
'contextPanel.mode.preview': 'Aperçu',
|
'contextPanel.mode.preview': 'Aperçu',
|
||||||
'contextPanel.mode.browser': 'Navigateur',
|
'contextPanel.mode.browser': 'Navigateur',
|
||||||
|
'contextRail.configure.open': 'Configurer les panneaux',
|
||||||
|
'contextRail.configure.dialogTitle': 'Panneaux de la barre',
|
||||||
|
'contextRail.configure.dialogDescription': 'Choisissez les panneaux affichés par la barre. Les panneaux masqués conservent leurs données et restent accessibles via la palette de commandes.',
|
||||||
|
'contextRail.configure.showAll': 'Tout afficher',
|
||||||
|
'contextRail.configure.noneWarning': 'Tous les panneaux sont masqués.',
|
||||||
'contextRail.aria.rail': 'Surfaces du panneau',
|
'contextRail.aria.rail': 'Surfaces du panneau',
|
||||||
'contextPanel.editorEmpty.title': 'Aucun fichier ouvert',
|
'contextPanel.editorEmpty.title': 'Aucun fichier ouvert',
|
||||||
'contextPanel.editorEmpty.description': 'Choisissez un fichier dans l’arborescence pour commencer.',
|
'contextPanel.editorEmpty.description': 'Choisissez un fichier dans l’arborescence pour commencer.',
|
||||||
|
|||||||
@@ -1136,6 +1136,11 @@ export const dict: Record<I18nKey, string> = {
|
|||||||
'contextPanel.mode.context': 'コンテキスト',
|
'contextPanel.mode.context': 'コンテキスト',
|
||||||
'contextPanel.mode.preview': 'プレビュー',
|
'contextPanel.mode.preview': 'プレビュー',
|
||||||
'contextPanel.mode.browser': 'ブラウザ',
|
'contextPanel.mode.browser': 'ブラウザ',
|
||||||
|
'contextRail.configure.open': 'パネルを設定',
|
||||||
|
'contextRail.configure.dialogTitle': 'レールのパネル',
|
||||||
|
'contextRail.configure.dialogDescription': 'レールに表示するパネルを選択します。非表示のパネルもデータは保持され、コマンドパレットから引き続き開けます。',
|
||||||
|
'contextRail.configure.showAll': 'すべて表示',
|
||||||
|
'contextRail.configure.noneWarning': 'すべてのパネルが非表示です。',
|
||||||
'contextRail.aria.rail': 'パネルサーフェス',
|
'contextRail.aria.rail': 'パネルサーフェス',
|
||||||
'contextPanel.editorEmpty.title': 'ファイルが開かれていません',
|
'contextPanel.editorEmpty.title': 'ファイルが開かれていません',
|
||||||
'contextPanel.editorEmpty.description': 'ツリーからファイルを選んで編集を始めましょう。',
|
'contextPanel.editorEmpty.description': 'ツリーからファイルを選んで編集を始めましょう。',
|
||||||
|
|||||||
@@ -1140,6 +1140,11 @@ export const dict: Record<I18nKey, string> = {
|
|||||||
'contextPanel.mode.context': '컨텍스트',
|
'contextPanel.mode.context': '컨텍스트',
|
||||||
'contextPanel.mode.preview': '미리보기',
|
'contextPanel.mode.preview': '미리보기',
|
||||||
'contextPanel.mode.browser': '브라우저',
|
'contextPanel.mode.browser': '브라우저',
|
||||||
|
'contextRail.configure.open': '패널 구성',
|
||||||
|
'contextRail.configure.dialogTitle': '레일 패널',
|
||||||
|
'contextRail.configure.dialogDescription': '레일에 표시할 패널을 선택하세요. 숨긴 패널의 데이터는 유지되며 명령 팔레트에서 계속 열 수 있습니다.',
|
||||||
|
'contextRail.configure.showAll': '모두 표시',
|
||||||
|
'contextRail.configure.noneWarning': '모든 패널이 숨겨져 있습니다.',
|
||||||
'contextRail.aria.rail': '패널 서피스',
|
'contextRail.aria.rail': '패널 서피스',
|
||||||
'contextPanel.editorEmpty.title': '열린 파일 없음',
|
'contextPanel.editorEmpty.title': '열린 파일 없음',
|
||||||
'contextPanel.editorEmpty.description': '트리에서 파일을 선택해 편집을 시작하세요.',
|
'contextPanel.editorEmpty.description': '트리에서 파일을 선택해 편집을 시작하세요.',
|
||||||
|
|||||||
@@ -1478,6 +1478,11 @@ export const dict: Record<I18nKey, string> = {
|
|||||||
'contextPanel.mode.pr': 'Pull Request',
|
'contextPanel.mode.pr': 'Pull Request',
|
||||||
'contextPanel.mode.preview': 'Podgląd',
|
'contextPanel.mode.preview': 'Podgląd',
|
||||||
'contextPanel.mode.browser': 'Przeglądarka',
|
'contextPanel.mode.browser': 'Przeglądarka',
|
||||||
|
'contextRail.configure.open': 'Konfiguruj panele',
|
||||||
|
'contextRail.configure.dialogTitle': 'Panele paska',
|
||||||
|
'contextRail.configure.dialogDescription': 'Wybierz, które panele pokazuje pasek. Ukryte panele zachowują dane i pozostają dostępne z palety poleceń.',
|
||||||
|
'contextRail.configure.showAll': 'Pokaż wszystkie',
|
||||||
|
'contextRail.configure.noneWarning': 'Wszystkie panele są ukryte.',
|
||||||
'contextRail.aria.rail': 'Powierzchnie panelu',
|
'contextRail.aria.rail': 'Powierzchnie panelu',
|
||||||
'contextPanel.editorEmpty.title': 'Brak otwartego pliku',
|
'contextPanel.editorEmpty.title': 'Brak otwartego pliku',
|
||||||
'contextPanel.editorEmpty.description': 'Wybierz plik z drzewa, aby rozpocząć edycję.',
|
'contextPanel.editorEmpty.description': 'Wybierz plik z drzewa, aby rozpocząć edycję.',
|
||||||
|
|||||||
@@ -1140,6 +1140,11 @@ export const dict: Record<I18nKey, string> = {
|
|||||||
"contextPanel.mode.context": "Contexto",
|
"contextPanel.mode.context": "Contexto",
|
||||||
"contextPanel.mode.preview": "Prévia",
|
"contextPanel.mode.preview": "Prévia",
|
||||||
"contextPanel.mode.browser": "Navegador",
|
"contextPanel.mode.browser": "Navegador",
|
||||||
|
"contextRail.configure.open": "Configurar painéis",
|
||||||
|
"contextRail.configure.dialogTitle": "Painéis da barra",
|
||||||
|
"contextRail.configure.dialogDescription": "Escolha quais painéis a barra mostra. Painéis ocultos mantêm seus dados e continuam acessíveis pela paleta de comandos.",
|
||||||
|
"contextRail.configure.showAll": "Mostrar todos",
|
||||||
|
"contextRail.configure.noneWarning": "Todos os painéis estão ocultos.",
|
||||||
"contextRail.aria.rail": "Superfícies do painel",
|
"contextRail.aria.rail": "Superfícies do painel",
|
||||||
"contextPanel.editorEmpty.title": "Nenhum arquivo aberto",
|
"contextPanel.editorEmpty.title": "Nenhum arquivo aberto",
|
||||||
"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.",
|
||||||
|
|||||||
@@ -1140,6 +1140,11 @@ export const dict: Record<I18nKey, string> = {
|
|||||||
"contextPanel.mode.context": "Контекст",
|
"contextPanel.mode.context": "Контекст",
|
||||||
"contextPanel.mode.preview": "Перегляд",
|
"contextPanel.mode.preview": "Перегляд",
|
||||||
"contextPanel.mode.browser": "Браузер",
|
"contextPanel.mode.browser": "Браузер",
|
||||||
|
"contextRail.configure.open": "Налаштувати панелі",
|
||||||
|
"contextRail.configure.dialogTitle": "Панелі рейки",
|
||||||
|
"contextRail.configure.dialogDescription": "Обери, які панелі показує рейка. Приховані панелі зберігають дані й доступні з палітри команд.",
|
||||||
|
"contextRail.configure.showAll": "Показати всі",
|
||||||
|
"contextRail.configure.noneWarning": "Усі панелі приховано.",
|
||||||
"contextRail.aria.rail": "Поверхні панелі",
|
"contextRail.aria.rail": "Поверхні панелі",
|
||||||
"contextPanel.editorEmpty.title": "Файл не відкрито",
|
"contextPanel.editorEmpty.title": "Файл не відкрито",
|
||||||
"contextPanel.editorEmpty.description": "Виберіть файл у дереві, щоб почати редагування.",
|
"contextPanel.editorEmpty.description": "Виберіть файл у дереві, щоб почати редагування.",
|
||||||
|
|||||||
@@ -1140,6 +1140,11 @@ export const dict: Record<I18nKey, string> = {
|
|||||||
'contextPanel.mode.context': '上下文',
|
'contextPanel.mode.context': '上下文',
|
||||||
'contextPanel.mode.preview': '预览',
|
'contextPanel.mode.preview': '预览',
|
||||||
'contextPanel.mode.browser': '浏览器',
|
'contextPanel.mode.browser': '浏览器',
|
||||||
|
'contextRail.configure.open': '配置面板',
|
||||||
|
'contextRail.configure.dialogTitle': '侧栏面板',
|
||||||
|
'contextRail.configure.dialogDescription': '选择侧栏显示哪些面板。隐藏的面板会保留数据,仍可通过命令面板打开。',
|
||||||
|
'contextRail.configure.showAll': '全部显示',
|
||||||
|
'contextRail.configure.noneWarning': '所有面板均已隐藏。',
|
||||||
'contextRail.aria.rail': '面板界面',
|
'contextRail.aria.rail': '面板界面',
|
||||||
'contextPanel.editorEmpty.title': '未打开文件',
|
'contextPanel.editorEmpty.title': '未打开文件',
|
||||||
'contextPanel.editorEmpty.description': '从文件树中选择一个文件开始编辑。',
|
'contextPanel.editorEmpty.description': '从文件树中选择一个文件开始编辑。',
|
||||||
|
|||||||
@@ -1152,6 +1152,11 @@ export const dict: Record<I18nKey, string> = {
|
|||||||
'contextPanel.mode.context': '上下文',
|
'contextPanel.mode.context': '上下文',
|
||||||
'contextPanel.mode.preview': '預覽',
|
'contextPanel.mode.preview': '預覽',
|
||||||
'contextPanel.mode.browser': '瀏覽器',
|
'contextPanel.mode.browser': '瀏覽器',
|
||||||
|
'contextRail.configure.open': '設定面板',
|
||||||
|
'contextRail.configure.dialogTitle': '側欄面板',
|
||||||
|
'contextRail.configure.dialogDescription': '選擇側欄顯示哪些面板。隱藏的面板會保留資料,仍可透過命令面板開啟。',
|
||||||
|
'contextRail.configure.showAll': '全部顯示',
|
||||||
|
'contextRail.configure.noneWarning': '所有面板皆已隱藏。',
|
||||||
'contextRail.aria.rail': '面板介面',
|
'contextRail.aria.rail': '面板介面',
|
||||||
'contextPanel.editorEmpty.title': '未開啟檔案',
|
'contextPanel.editorEmpty.title': '未開啟檔案',
|
||||||
'contextPanel.editorEmpty.description': '從檔案樹選擇檔案開始編輯。',
|
'contextPanel.editorEmpty.description': '從檔案樹選擇檔案開始編輯。',
|
||||||
|
|||||||
@@ -22,7 +22,10 @@ edge (`components/layout/ContextPanelRail.tsx`) and rendered by
|
|||||||
registry's default order and appends any missing surfaces.
|
registry's default order and appends any missing surfaces.
|
||||||
- `getVisibleContextRailSurfaces` is the single visibility filter shared by the
|
- `getVisibleContextRailSurfaces` is the single visibility filter shared by the
|
||||||
rail and the global surface-switch shortcut (`switch_context_surface` in
|
rail and the global surface-switch shortcut (`switch_context_surface` in
|
||||||
`lib/shortcuts.ts`): it drops the plan surface unless plan mode is enabled,
|
`lib/shortcuts`): it drops surfaces the user hid
|
||||||
|
(`useUIStore.contextRailHiddenSurfaces`, edited from the rail's trailing
|
||||||
|
configure button — `ContextRailSurfacesDialog`), drops the plan surface
|
||||||
|
unless plan mode is enabled,
|
||||||
drops the walkthrough on VS Code and below `WALKTHROUGH_MIN_WIDTH`, and hides
|
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
|
`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
|
it so the digit shown on a rail badge always maps to the same surface the
|
||||||
|
|||||||
@@ -187,6 +187,9 @@ export const sortContextSurfaces = (railOrder: readonly string[]): ContextSurfac
|
|||||||
|
|
||||||
type VisibleRailSurfacesOptions = {
|
type VisibleRailSurfacesOptions = {
|
||||||
railOrder: readonly string[];
|
railOrder: readonly string[];
|
||||||
|
/** Surfaces the user chose to hide from the rail (and from the digit
|
||||||
|
shortcuts, which share this filter). */
|
||||||
|
hiddenSurfaces?: readonly string[];
|
||||||
planModeEnabled: boolean;
|
planModeEnabled: boolean;
|
||||||
isVSCode: boolean;
|
isVSCode: boolean;
|
||||||
screenWidth: number;
|
screenWidth: number;
|
||||||
@@ -203,6 +206,9 @@ type VisibleRailSurfacesOptions = {
|
|||||||
*/
|
*/
|
||||||
export const getVisibleContextRailSurfaces = (options: VisibleRailSurfacesOptions): ContextSurfaceDescriptor[] => {
|
export const getVisibleContextRailSurfaces = (options: VisibleRailSurfacesOptions): ContextSurfaceDescriptor[] => {
|
||||||
return sortContextSurfaces(options.railOrder).filter((surface) => {
|
return sortContextSurfaces(options.railOrder).filter((surface) => {
|
||||||
|
if (options.hiddenSurfaces?.includes(surface.id)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
if (surface.id === 'plan' && !options.planModeEnabled) {
|
if (surface.id === 'plan' && !options.planModeEnabled) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -607,6 +607,9 @@ interface UIStore {
|
|||||||
hasManuallyResizedLeftSidebar: boolean;
|
hasManuallyResizedLeftSidebar: boolean;
|
||||||
contextPanelByDirectory: Record<string, ContextPanelDirectoryState>;
|
contextPanelByDirectory: Record<string, ContextPanelDirectoryState>;
|
||||||
contextRailOrder: string[];
|
contextRailOrder: string[];
|
||||||
|
/** Surface ids the user hid from the context rail; stored as the hidden set
|
||||||
|
so surfaces added later appear for everyone. */
|
||||||
|
contextRailHiddenSurfaces: string[];
|
||||||
contextEditorTreeVisible: boolean;
|
contextEditorTreeVisible: boolean;
|
||||||
contextEditorTreeWidth: number;
|
contextEditorTreeWidth: number;
|
||||||
notesPanelHeight: number;
|
notesPanelHeight: number;
|
||||||
@@ -828,6 +831,8 @@ interface UIStore {
|
|||||||
setWorkStatusOverlayOpen: (open: boolean) => void;
|
setWorkStatusOverlayOpen: (open: boolean) => void;
|
||||||
setWorkStatusSectionVisible: (sectionId: string, visible: boolean) => void;
|
setWorkStatusSectionVisible: (sectionId: string, visible: boolean) => void;
|
||||||
setWorkStatusHiddenSections: (sectionIds: string[]) => void;
|
setWorkStatusHiddenSections: (sectionIds: string[]) => void;
|
||||||
|
setContextRailSurfaceVisible: (surfaceId: string, visible: boolean) => void;
|
||||||
|
setContextRailHiddenSurfaces: (surfaceIds: string[]) => void;
|
||||||
setSessionSwitcherOpen: (open: boolean) => void;
|
setSessionSwitcherOpen: (open: boolean) => void;
|
||||||
setSessionDropdownOpen: (open: boolean) => void;
|
setSessionDropdownOpen: (open: boolean) => void;
|
||||||
setPendingDiffFile: (filePath: string | null, staged?: boolean, scope?: PendingDiffScope | null) => void;
|
setPendingDiffFile: (filePath: string | null, staged?: boolean, scope?: PendingDiffScope | null) => void;
|
||||||
@@ -990,6 +995,7 @@ export const useUIStore = create<UIStore>()(
|
|||||||
hasManuallyResizedLeftSidebar: false,
|
hasManuallyResizedLeftSidebar: false,
|
||||||
contextPanelByDirectory: {},
|
contextPanelByDirectory: {},
|
||||||
contextRailOrder: [],
|
contextRailOrder: [],
|
||||||
|
contextRailHiddenSurfaces: [],
|
||||||
contextEditorTreeVisible: true,
|
contextEditorTreeVisible: true,
|
||||||
contextEditorTreeWidth: 240,
|
contextEditorTreeWidth: 240,
|
||||||
notesPanelHeight: 112,
|
notesPanelHeight: 112,
|
||||||
@@ -1599,6 +1605,23 @@ export const useUIStore = create<UIStore>()(
|
|||||||
set({ workStatusHiddenSections: [...new Set(sectionIds)] });
|
set({ workStatusHiddenSections: [...new Set(sectionIds)] });
|
||||||
},
|
},
|
||||||
|
|
||||||
|
setContextRailSurfaceVisible: (surfaceId, visible) => {
|
||||||
|
set((state) => {
|
||||||
|
const hidden = state.contextRailHiddenSurfaces;
|
||||||
|
const isHidden = hidden.includes(surfaceId);
|
||||||
|
if (visible === !isHidden) return state;
|
||||||
|
return {
|
||||||
|
contextRailHiddenSurfaces: visible
|
||||||
|
? hidden.filter((entry) => entry !== surfaceId)
|
||||||
|
: [...hidden, surfaceId],
|
||||||
|
};
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
setContextRailHiddenSurfaces: (surfaceIds) => {
|
||||||
|
set({ contextRailHiddenSurfaces: [...new Set(surfaceIds)] });
|
||||||
|
},
|
||||||
|
|
||||||
|
|
||||||
setSessionSwitcherOpen: (open) => {
|
setSessionSwitcherOpen: (open) => {
|
||||||
if (get().isSessionSwitcherOpen === open) {
|
if (get().isSessionSwitcherOpen === open) {
|
||||||
@@ -2627,6 +2650,9 @@ export const useUIStore = create<UIStore>()(
|
|||||||
state.autoSaveEnabled = true;
|
state.autoSaveEnabled = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
state.contextRailHiddenSurfaces = Array.isArray(state.contextRailHiddenSurfaces)
|
||||||
|
? (state.contextRailHiddenSurfaces as unknown[]).filter((id): id is string => typeof id === 'string' && id.trim() !== '')
|
||||||
|
: [];
|
||||||
state.contextRailOrder = Array.isArray(state.contextRailOrder)
|
state.contextRailOrder = Array.isArray(state.contextRailOrder)
|
||||||
? (state.contextRailOrder as unknown[]).filter((id): id is string => typeof id === 'string' && id.trim() !== '')
|
? (state.contextRailOrder as unknown[]).filter((id): id is string => typeof id === 'string' && id.trim() !== '')
|
||||||
: [];
|
: [];
|
||||||
@@ -2639,6 +2665,7 @@ export const useUIStore = create<UIStore>()(
|
|||||||
sidebarWidth: state.sidebarWidth,
|
sidebarWidth: state.sidebarWidth,
|
||||||
contextPanelByDirectory: state.contextPanelByDirectory,
|
contextPanelByDirectory: state.contextPanelByDirectory,
|
||||||
contextRailOrder: state.contextRailOrder,
|
contextRailOrder: state.contextRailOrder,
|
||||||
|
contextRailHiddenSurfaces: state.contextRailHiddenSurfaces,
|
||||||
contextEditorTreeVisible: state.contextEditorTreeVisible,
|
contextEditorTreeVisible: state.contextEditorTreeVisible,
|
||||||
contextEditorTreeWidth: state.contextEditorTreeWidth,
|
contextEditorTreeWidth: state.contextEditorTreeWidth,
|
||||||
notesPanelHeight: state.notesPanelHeight,
|
notesPanelHeight: state.notesPanelHeight,
|
||||||
|
|||||||
Reference in New Issue
Block a user