feat(files): expose markdown preview toggle (#2152)

* fix(files): expose markdown preview toggle

* fix(files): sync markdown preview icon state
This commit is contained in:
Greg Haynes
2026-07-13 01:46:03 +03:00
committed by GitHub
parent 9624d4b6f6
commit d4b75b268a
11 changed files with 110 additions and 42 deletions
+90 -42
View File
@@ -3302,18 +3302,35 @@ export const FilesView: React.FC<FilesViewProps> = ({ mode = 'full' }) => {
/>
)}
{(isMarkdown || isHtmlFile(selectedFile?.path ?? '')) && (
<PreviewToggleButton
currentMode={isMarkdown ? getMdViewMode() : getHtmlViewMode()}
onToggle={() => {
if (isHtmlFile(selectedFile?.path ?? '')) {
saveHtmlViewMode(getHtmlViewMode() === 'preview' ? 'edit' : 'preview');
} else {
saveMdViewMode(getMdViewMode() === 'preview' ? 'edit' : 'preview');
}
}}
/>
)}
{isMarkdown && (
withTooltip(
t(getMdViewMode() === 'preview' ? 'filesView.editor.switchToEditMode' : 'filesView.editor.switchToPreviewMode'),
<Button
variant="ghost"
size="sm"
onClick={() => saveMdViewMode(getMdViewMode() === 'preview' ? 'edit' : 'preview')}
className={cn(
'size-6 p-0 transition-colors hover:bg-[var(--interactive-hover)] focus-visible:bg-[var(--interactive-hover)] active:bg-[var(--interactive-hover)]',
getMdViewMode() === 'preview'
? 'bg-[var(--interactive-selection)] text-[var(--interactive-selection-foreground)] hover:bg-[var(--interactive-selection)] focus-visible:bg-[var(--interactive-selection)] active:bg-[var(--interactive-selection)]'
: 'text-muted-foreground opacity-65 hover:opacity-100'
)}
title={t(getMdViewMode() === 'preview' ? 'filesView.editor.switchToEditMode' : 'filesView.editor.switchToPreviewMode')}
aria-label={t(getMdViewMode() === 'preview' ? 'filesView.editor.switchToEditMode' : 'filesView.editor.switchToPreviewMode')}
>
<Icon name={getMdViewMode() === 'preview' ? 'eye' : 'eye-off'} className="size-4" />
</Button>
)
)}
{isHtmlFile(selectedFile?.path ?? '') && (
<PreviewToggleButton
currentMode={getHtmlViewMode()}
onToggle={() => {
saveHtmlViewMode(getHtmlViewMode() === 'preview' ? 'edit' : 'preview');
}}
/>
)}
{isMarkdown && getMdViewMode() === 'preview' && showMessageTTSButtons && (
<Tooltip>
@@ -3713,38 +3730,69 @@ export const FilesView: React.FC<FilesViewProps> = ({ mode = 'full' }) => {
<div className="flex-1 min-h-0 min-w-0 relative">
{selectedFile && !isSearchOpen && !(settingsExpandedEditorToolbar && !isMobile) && (
<div
ref={floatingToolbarRef}
className="absolute right-3 top-3 z-30"
onMouseEnter={() => setIsFloatingToolbarOpen(true)}
onMouseLeave={() => {
if (toolbarDropdownOpenCountRef.current > 0) return;
setIsFloatingToolbarOpen(false);
<div
ref={floatingToolbarRef}
className="absolute right-3 top-3 z-30"
onMouseEnter={() => {
if (isMarkdown) return;
setIsFloatingToolbarOpen(true);
}}
onMouseLeave={() => {
if (toolbarDropdownOpenCountRef.current > 0) return;
setIsFloatingToolbarOpen(false);
}}
>
{isFloatingToolbarOpen ? (
renderFloatingFileControls()
) : (
<Tooltip>
<TooltipTrigger asChild>
<span className="inline-flex">
<Button
variant="ghost"
size="sm"
onClick={() => setIsFloatingToolbarOpen(true)}
className="size-8 rounded-lg border border-[var(--interactive-border)] bg-[var(--surface-elevated)] p-0 text-muted-foreground shadow-sm hover:text-foreground"
aria-label={t('filesView.editor.showControlsAria')}
title={t('filesView.editor.controlsTitle')}
>
<Icon name="more-2-fill" className="size-4" />
</Button>
</span>
</TooltipTrigger>
<TooltipContent side="bottom" sideOffset={6}>{t('filesView.editor.controlsTitle')}</TooltipContent>
</Tooltip>
)}
</div>
)}
{isFloatingToolbarOpen ? (
renderFloatingFileControls()
) : (
<div className="flex items-center gap-1">
{isMarkdown ? (
<Tooltip>
<TooltipTrigger asChild>
<span className="inline-flex">
<Button
variant="ghost"
size="sm"
onClick={() => saveMdViewMode(getMdViewMode() === 'preview' ? 'edit' : 'preview')}
className={cn(
'size-8 rounded-lg border border-[var(--interactive-border)] bg-[var(--surface-elevated)] p-0 shadow-sm transition-colors',
getMdViewMode() === 'preview'
? 'bg-[var(--interactive-selection)] text-[var(--interactive-selection-foreground)] hover:bg-[var(--interactive-selection)]'
: 'text-muted-foreground hover:text-foreground'
)}
aria-label={t(getMdViewMode() === 'preview' ? 'filesView.editor.switchToEditMode' : 'filesView.editor.switchToPreviewMode')}
title={t(getMdViewMode() === 'preview' ? 'filesView.editor.switchToEditMode' : 'filesView.editor.switchToPreviewMode')}
>
<Icon name={getMdViewMode() === 'preview' ? 'eye' : 'eye-off'} className="size-4" />
</Button>
</span>
</TooltipTrigger>
<TooltipContent side="bottom" sideOffset={6}>
{t(getMdViewMode() === 'preview' ? 'filesView.editor.switchToEditMode' : 'filesView.editor.switchToPreviewMode')}
</TooltipContent>
</Tooltip>
) : null}
<Tooltip>
<TooltipTrigger asChild>
<span className="inline-flex">
<Button
variant="ghost"
size="sm"
onClick={() => setIsFloatingToolbarOpen(true)}
className="size-8 rounded-lg border border-[var(--interactive-border)] bg-[var(--surface-elevated)] p-0 text-muted-foreground shadow-sm hover:text-foreground"
aria-label={t('filesView.editor.showControlsAria')}
title={t('filesView.editor.controlsTitle')}
>
<Icon name="more-2-fill" className="size-4" />
</Button>
</span>
</TooltipTrigger>
<TooltipContent side="bottom" sideOffset={6}>{t('filesView.editor.controlsTitle')}</TooltipContent>
</Tooltip>
</div>
)}
</div>
)}
<ScrollableOverlay outerClassName="h-full min-w-0" className="h-full min-w-0">
{!selectedFile ? (
<div className="p-3 typography-ui text-muted-foreground">{t('filesView.editor.pickFileFromTree')}</div>