feat(file-viewer): add html preview tab

This commit is contained in:
Haileyesus
2026-06-29 15:10:55 +03:00
parent 6b5506087c
commit dc3a928bed
2 changed files with 46 additions and 1 deletions

View File

@@ -1,8 +1,9 @@
import { EditorView } from '@codemirror/view'; import { EditorView } from '@codemirror/view';
import { unifiedMergeView } from '@codemirror/merge'; import { unifiedMergeView } from '@codemirror/merge';
import type { Extension } from '@codemirror/state'; import type { Extension } from '@codemirror/state';
import { useMemo, useState } from 'react'; import { useCallback, useMemo, useState } from 'react';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { usePaletteOps } from '../../../contexts/PaletteOpsContext'; import { usePaletteOps } from '../../../contexts/PaletteOpsContext';
import { useCodeEditorDocument } from '../hooks/useCodeEditorDocument'; import { useCodeEditorDocument } from '../hooks/useCodeEditorDocument';
import { useCodeEditorSettings } from '../hooks/useCodeEditorSettings'; import { useCodeEditorSettings } from '../hooks/useCodeEditorSettings';
@@ -11,6 +12,7 @@ import type { CodeEditorFile } from '../types/types';
import { createMinimapExtension, createScrollToFirstChunkExtension, getLanguageExtensions } from '../utils/editorExtensions'; import { createMinimapExtension, createScrollToFirstChunkExtension, getLanguageExtensions } from '../utils/editorExtensions';
import { getEditorStyles } from '../utils/editorStyles'; import { getEditorStyles } from '../utils/editorStyles';
import { createEditorToolbarPanelExtension } from '../utils/editorToolbarPanel'; import { createEditorToolbarPanelExtension } from '../utils/editorToolbarPanel';
import CodeEditorFooter from './subcomponents/CodeEditorFooter'; import CodeEditorFooter from './subcomponents/CodeEditorFooter';
import CodeEditorHeader from './subcomponents/CodeEditorHeader'; import CodeEditorHeader from './subcomponents/CodeEditorHeader';
import CodeEditorLoadingState from './subcomponents/CodeEditorLoadingState'; import CodeEditorLoadingState from './subcomponents/CodeEditorLoadingState';
@@ -73,6 +75,29 @@ export default function CodeEditor({
return extension === 'md' || extension === 'markdown'; return extension === 'md' || extension === 'markdown';
}, [file.name]); }, [file.name]);
const isHtmlPreviewFile = useMemo(() => {
const extension = file.name.split('.').pop()?.toLowerCase();
return extension === 'html' || extension === 'htm';
}, [file.name]);
const openHtmlPreview = useCallback(() => {
const previewWindow = window.open('', '_blank');
if (!previewWindow) return;
previewWindow.opener = null;
previewWindow.document.title = file.name;
previewWindow.document.body.style.margin = '0';
const iframe = previewWindow.document.createElement('iframe');
iframe.title = file.name;
iframe.sandbox.add('allow-forms', 'allow-modals', 'allow-popups', 'allow-scripts');
iframe.style.cssText = 'position:fixed;inset:0;width:100%;height:100%;border:0;background:white';
iframe.srcdoc = content;
previewWindow.document.body.appendChild(iframe);
}, [content, file.name]);
const minimapExtension = useMemo( const minimapExtension = useMemo(
() => ( () => (
createMinimapExtension({ createMinimapExtension({
@@ -224,10 +249,12 @@ export default function CodeEditor({
isSidebar={isSidebar} isSidebar={isSidebar}
isFullscreen={isFullscreen} isFullscreen={isFullscreen}
isMarkdownFile={isMarkdownFile} isMarkdownFile={isMarkdownFile}
isHtmlPreviewFile={isHtmlPreviewFile}
markdownPreview={markdownPreview} markdownPreview={markdownPreview}
saving={saving} saving={saving}
saveSuccess={saveSuccess} saveSuccess={saveSuccess}
onToggleMarkdownPreview={() => setMarkdownPreview((previous) => !previous)} onToggleMarkdownPreview={() => setMarkdownPreview((previous) => !previous)}
onOpenHtmlPreview={openHtmlPreview}
onOpenSettings={() => paletteOps.openSettings('appearance')} onOpenSettings={() => paletteOps.openSettings('appearance')}
onDownload={handleDownload} onDownload={handleDownload}
onSave={handleSave} onSave={handleSave}
@@ -237,6 +264,7 @@ export default function CodeEditor({
showingChanges: t('header.showingChanges'), showingChanges: t('header.showingChanges'),
editMarkdown: t('actions.editMarkdown'), editMarkdown: t('actions.editMarkdown'),
previewMarkdown: t('actions.previewMarkdown'), previewMarkdown: t('actions.previewMarkdown'),
previewHtml: t('actions.previewHtml', 'Open HTML preview in new tab'),
settings: t('toolbar.settings'), settings: t('toolbar.settings'),
download: t('actions.download'), download: t('actions.download'),
save: t('actions.save'), save: t('actions.save'),

View File

@@ -1,4 +1,5 @@
import { Code2, Download, Eye, Maximize2, Minimize2, Save, Settings as SettingsIcon, X } from 'lucide-react'; import { Code2, Download, Eye, Maximize2, Minimize2, Save, Settings as SettingsIcon, X } from 'lucide-react';
import type { CodeEditorFile } from '../../types/types'; import type { CodeEditorFile } from '../../types/types';
type CodeEditorHeaderProps = { type CodeEditorHeaderProps = {
@@ -6,10 +7,12 @@ type CodeEditorHeaderProps = {
isSidebar: boolean; isSidebar: boolean;
isFullscreen: boolean; isFullscreen: boolean;
isMarkdownFile: boolean; isMarkdownFile: boolean;
isHtmlPreviewFile: boolean;
markdownPreview: boolean; markdownPreview: boolean;
saving: boolean; saving: boolean;
saveSuccess: boolean; saveSuccess: boolean;
onToggleMarkdownPreview: () => void; onToggleMarkdownPreview: () => void;
onOpenHtmlPreview: () => void;
onOpenSettings: () => void; onOpenSettings: () => void;
onDownload: () => void; onDownload: () => void;
onSave: () => void; onSave: () => void;
@@ -19,6 +22,7 @@ type CodeEditorHeaderProps = {
showingChanges: string; showingChanges: string;
editMarkdown: string; editMarkdown: string;
previewMarkdown: string; previewMarkdown: string;
previewHtml: string;
settings: string; settings: string;
download: string; download: string;
save: string; save: string;
@@ -35,10 +39,12 @@ export default function CodeEditorHeader({
isSidebar, isSidebar,
isFullscreen, isFullscreen,
isMarkdownFile, isMarkdownFile,
isHtmlPreviewFile,
markdownPreview, markdownPreview,
saving, saving,
saveSuccess, saveSuccess,
onToggleMarkdownPreview, onToggleMarkdownPreview,
onOpenHtmlPreview,
onOpenSettings, onOpenSettings,
onDownload, onDownload,
onSave, onSave,
@@ -82,6 +88,17 @@ export default function CodeEditorHeader({
</button> </button>
)} )}
{isHtmlPreviewFile && (
<button
type="button"
onClick={onOpenHtmlPreview}
className="flex items-center justify-center rounded-md p-1.5 text-gray-600 hover:bg-gray-100 hover:text-gray-900 dark:text-gray-400 dark:hover:bg-gray-800 dark:hover:text-white"
title={labels.previewHtml}
>
<Eye className="h-4 w-4" />
</button>
)}
<button <button
type="button" type="button"
onClick={onOpenSettings} onClick={onOpenSettings}