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:
@@ -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>
|
||||
|
||||
@@ -1166,6 +1166,8 @@ export const dict = {
|
||||
'filesView.editor.enableLineWrap': 'Enable line wrap',
|
||||
'filesView.editor.findInFile': 'Find in file',
|
||||
'filesView.editor.goToLine': 'Go to line',
|
||||
'filesView.editor.switchToEditMode': 'Switch to edit mode',
|
||||
'filesView.editor.switchToPreviewMode': 'Switch to preview mode',
|
||||
'filesView.editor.switchToTextView': 'Switch to Text View',
|
||||
'filesView.editor.switchToTreeView': 'Switch to Tree View',
|
||||
'filesView.toast.copyFailed': 'Copy failed',
|
||||
|
||||
@@ -1132,6 +1132,8 @@ export const dict: Record<I18nKey, string> = {
|
||||
"filesView.editor.enableLineWrap": "Activar ajuste de línea",
|
||||
"filesView.editor.findInFile": "Buscar en el archivo",
|
||||
"filesView.editor.goToLine": "Ir a línea",
|
||||
"filesView.editor.switchToEditMode": "Cambiar al modo de edición",
|
||||
"filesView.editor.switchToPreviewMode": "Cambiar al modo de vista previa",
|
||||
"filesView.editor.switchToTextView": "Cambiar a vista de texto",
|
||||
"filesView.editor.switchToTreeView": "Cambiar a vista de árbol",
|
||||
"filesView.toast.copyFailed": "No se pudo copiar",
|
||||
|
||||
@@ -995,6 +995,8 @@ export const dict = {
|
||||
'filesView.editor.enableLineWrap': 'Activer le retour à la ligne',
|
||||
'filesView.editor.findInFile': 'Rechercher dans le fichier',
|
||||
'filesView.editor.goToLine': 'Aller à la ligne',
|
||||
'filesView.editor.switchToEditMode': 'Passer en mode édition',
|
||||
'filesView.editor.switchToPreviewMode': 'Passer en mode aperçu',
|
||||
'filesView.editor.switchToTextView': 'Passer à l\'affichage texte',
|
||||
'filesView.editor.switchToTreeView': 'Passer à l\'arborescence',
|
||||
'filesView.toast.copyFailed': 'Échec de la copie',
|
||||
|
||||
@@ -1162,6 +1162,8 @@ export const dict: Record<I18nKey, string> = {
|
||||
'filesView.editor.enableLineWrap': '行の折り返しを有効にする',
|
||||
'filesView.editor.findInFile': 'ファイル内を検索',
|
||||
'filesView.editor.goToLine': '指定行に移動',
|
||||
'filesView.editor.switchToEditMode': '編集モードに切り替え',
|
||||
'filesView.editor.switchToPreviewMode': 'プレビューモードに切り替え',
|
||||
'filesView.editor.switchToTextView': 'テキストビューに切り替え',
|
||||
'filesView.editor.switchToTreeView': 'ツリービューに切り替え',
|
||||
'filesView.toast.copyFailed': 'コピーに失敗しました',
|
||||
|
||||
@@ -1169,6 +1169,8 @@ export const dict: Record<I18nKey, string> = {
|
||||
'filesView.editor.enableLineWrap': '줄 바꿈 켜기',
|
||||
'filesView.editor.findInFile': '파일에서 찾기',
|
||||
'filesView.editor.goToLine': '줄로 이동',
|
||||
'filesView.editor.switchToEditMode': '편집 모드로 전환',
|
||||
'filesView.editor.switchToPreviewMode': '미리보기 모드로 전환',
|
||||
'filesView.editor.switchToTextView': '텍스트 보기로 전환',
|
||||
'filesView.editor.switchToTreeView': '트리 보기로 전환',
|
||||
'filesView.toast.copyFailed': '복사 실패',
|
||||
|
||||
@@ -1653,6 +1653,8 @@ export const dict: Record<I18nKey, string> = {
|
||||
'filesView.editor.fullscreen': 'Pełny ekran',
|
||||
'filesView.editor.goToLine': 'Przejdź do linii',
|
||||
'filesView.editor.htmlPreviewTitle': 'Podgląd HTML',
|
||||
'filesView.editor.switchToEditMode': 'Przełącz na tryb edycji',
|
||||
'filesView.editor.switchToPreviewMode': 'Przełącz na tryb podglądu',
|
||||
'filesView.diagram.closeDiagramView': 'Zamknij widok diagramu',
|
||||
'filesView.diagram.saveDiagram': 'Zapisz diagram',
|
||||
'filesView.editor.imageAltFallback': 'Obraz',
|
||||
|
||||
@@ -1132,6 +1132,8 @@ export const dict: Record<I18nKey, string> = {
|
||||
"filesView.editor.enableLineWrap": "Ativar ajuste de linha",
|
||||
"filesView.editor.findInFile": "Buscar no arquivo",
|
||||
"filesView.editor.goToLine": "Ir para linha",
|
||||
"filesView.editor.switchToEditMode": "Alternar para o modo de edição",
|
||||
"filesView.editor.switchToPreviewMode": "Alternar para o modo de visualização",
|
||||
"filesView.editor.switchToTextView": "Alternar para visualização de texto",
|
||||
"filesView.editor.switchToTreeView": "Alternar para visualização em árvore",
|
||||
"filesView.toast.copyFailed": "Não foi possível copiar",
|
||||
|
||||
@@ -1132,6 +1132,8 @@ export const dict: Record<I18nKey, string> = {
|
||||
"filesView.editor.enableLineWrap": "Увімкнути перенос рядків",
|
||||
"filesView.editor.findInFile": "Знайти у файлі",
|
||||
"filesView.editor.goToLine": "Перейти до рядка",
|
||||
"filesView.editor.switchToEditMode": "Перемкнутися в режим редагування",
|
||||
"filesView.editor.switchToPreviewMode": "Перемкнутися в режим попереднього перегляду",
|
||||
"filesView.editor.switchToTextView": "Перемкнутися на текстовий перегляд",
|
||||
"filesView.editor.switchToTreeView": "Перейти до перегляду дерева",
|
||||
"filesView.toast.copyFailed": "Помилка копіювання",
|
||||
|
||||
@@ -1132,6 +1132,8 @@ export const dict: Record<I18nKey, string> = {
|
||||
'filesView.editor.enableLineWrap': '开启自动换行',
|
||||
'filesView.editor.findInFile': '文件内查找',
|
||||
'filesView.editor.goToLine': '跳转到行',
|
||||
'filesView.editor.switchToEditMode': '切换到编辑模式',
|
||||
'filesView.editor.switchToPreviewMode': '切换到预览模式',
|
||||
'filesView.editor.switchToTextView': '切换到文本视图',
|
||||
'filesView.editor.switchToTreeView': '切换到树视图',
|
||||
'filesView.toast.copyFailed': '复制失败',
|
||||
|
||||
@@ -1143,6 +1143,8 @@ export const dict: Record<I18nKey, string> = {
|
||||
'filesView.editor.enableLineWrap': '開啟自動換行',
|
||||
'filesView.editor.findInFile': '檔案內尋找',
|
||||
'filesView.editor.goToLine': '跳轉到行',
|
||||
'filesView.editor.switchToEditMode': '切換到編輯模式',
|
||||
'filesView.editor.switchToPreviewMode': '切換到預覽模式',
|
||||
'filesView.editor.switchToTextView': '切換到文字檢視',
|
||||
'filesView.editor.switchToTreeView': '切換到樹狀檢視',
|
||||
'filesView.toast.copyFailed': '複製失敗',
|
||||
|
||||
Reference in New Issue
Block a user