diff --git a/src/components/code-editor/view/CodeEditor.tsx b/src/components/code-editor/view/CodeEditor.tsx index c542ae12..588d1ec1 100644 --- a/src/components/code-editor/view/CodeEditor.tsx +++ b/src/components/code-editor/view/CodeEditor.tsx @@ -1,8 +1,9 @@ import { EditorView } from '@codemirror/view'; import { unifiedMergeView } from '@codemirror/merge'; import type { Extension } from '@codemirror/state'; -import { useMemo, useState } from 'react'; +import { useCallback, useMemo, useState } from 'react'; import { useTranslation } from 'react-i18next'; + import { usePaletteOps } from '../../../contexts/PaletteOpsContext'; import { useCodeEditorDocument } from '../hooks/useCodeEditorDocument'; import { useCodeEditorSettings } from '../hooks/useCodeEditorSettings'; @@ -11,6 +12,7 @@ import type { CodeEditorFile } from '../types/types'; import { createMinimapExtension, createScrollToFirstChunkExtension, getLanguageExtensions } from '../utils/editorExtensions'; import { getEditorStyles } from '../utils/editorStyles'; import { createEditorToolbarPanelExtension } from '../utils/editorToolbarPanel'; + import CodeEditorFooter from './subcomponents/CodeEditorFooter'; import CodeEditorHeader from './subcomponents/CodeEditorHeader'; import CodeEditorLoadingState from './subcomponents/CodeEditorLoadingState'; @@ -73,6 +75,29 @@ export default function CodeEditor({ return extension === 'md' || extension === 'markdown'; }, [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( () => ( createMinimapExtension({ @@ -224,10 +249,12 @@ export default function CodeEditor({ isSidebar={isSidebar} isFullscreen={isFullscreen} isMarkdownFile={isMarkdownFile} + isHtmlPreviewFile={isHtmlPreviewFile} markdownPreview={markdownPreview} saving={saving} saveSuccess={saveSuccess} onToggleMarkdownPreview={() => setMarkdownPreview((previous) => !previous)} + onOpenHtmlPreview={openHtmlPreview} onOpenSettings={() => paletteOps.openSettings('appearance')} onDownload={handleDownload} onSave={handleSave} @@ -237,6 +264,7 @@ export default function CodeEditor({ showingChanges: t('header.showingChanges'), editMarkdown: t('actions.editMarkdown'), previewMarkdown: t('actions.previewMarkdown'), + previewHtml: t('actions.previewHtml', 'Open HTML preview in new tab'), settings: t('toolbar.settings'), download: t('actions.download'), save: t('actions.save'), diff --git a/src/components/code-editor/view/subcomponents/CodeEditorHeader.tsx b/src/components/code-editor/view/subcomponents/CodeEditorHeader.tsx index 7c429a63..d16f21f5 100644 --- a/src/components/code-editor/view/subcomponents/CodeEditorHeader.tsx +++ b/src/components/code-editor/view/subcomponents/CodeEditorHeader.tsx @@ -1,4 +1,5 @@ import { Code2, Download, Eye, Maximize2, Minimize2, Save, Settings as SettingsIcon, X } from 'lucide-react'; + import type { CodeEditorFile } from '../../types/types'; type CodeEditorHeaderProps = { @@ -6,10 +7,12 @@ type CodeEditorHeaderProps = { isSidebar: boolean; isFullscreen: boolean; isMarkdownFile: boolean; + isHtmlPreviewFile: boolean; markdownPreview: boolean; saving: boolean; saveSuccess: boolean; onToggleMarkdownPreview: () => void; + onOpenHtmlPreview: () => void; onOpenSettings: () => void; onDownload: () => void; onSave: () => void; @@ -19,6 +22,7 @@ type CodeEditorHeaderProps = { showingChanges: string; editMarkdown: string; previewMarkdown: string; + previewHtml: string; settings: string; download: string; save: string; @@ -35,10 +39,12 @@ export default function CodeEditorHeader({ isSidebar, isFullscreen, isMarkdownFile, + isHtmlPreviewFile, markdownPreview, saving, saveSuccess, onToggleMarkdownPreview, + onOpenHtmlPreview, onOpenSettings, onDownload, onSave, @@ -82,6 +88,17 @@ export default function CodeEditorHeader({ )} + {isHtmlPreviewFile && ( + + )} +