diff --git a/src/features/command-palette/constants/markdown-actions.tsx b/src/features/command-palette/constants/markdown-actions.tsx
index 03593299..85ba7194 100644
--- a/src/features/command-palette/constants/markdown-actions.tsx
+++ b/src/features/command-palette/constants/markdown-actions.tsx
@@ -15,6 +15,7 @@ interface MarkdownActionsParams {
isVirtual?: boolean,
diffData?: any,
isMarkdownPreview?: boolean,
+ isHtmlPreview?: boolean,
sourceFilePath?: string,
) => string;
onClose: () => void;
@@ -50,6 +51,7 @@ export const createMarkdownActions = (params: MarkdownActionsParams): Action[] =
true, // isVirtual
undefined, // diffData
true, // isMarkdownPreview
+ false, // isHtmlPreview
activeBuffer.path, // sourceFilePath
);
onClose();
diff --git a/src/features/editor/components/code-editor.tsx b/src/features/editor/components/code-editor.tsx
index f3766e19..d4937d05 100644
--- a/src/features/editor/components/code-editor.tsx
+++ b/src/features/editor/components/code-editor.tsx
@@ -15,6 +15,7 @@ import { HoverTooltip } from "../lsp/hover-tooltip";
import { MarkdownPreview } from "../markdown/markdown-preview";
import { ScrollDebugOverlay } from "./debug/scroll-debug-overlay";
import { Editor } from "./editor";
+import { HtmlPreview } from "./html/html-preview";
import { EditorStylesheet } from "./stylesheet";
import Breadcrumb from "./toolbar/breadcrumb";
import FindBar from "./toolbar/find-bar";
@@ -58,6 +59,7 @@ const CodeEditor = ({ className }: CodeEditorProps) => {
const onChange = activeBuffer ? handleContentChange : () => {};
const showMarkdownPreview = activeBuffer?.isMarkdownPreview || false;
+ const showHtmlPreview = activeBuffer?.isHtmlPreview || false;
// Initialize refs in store
useEffect(() => {
@@ -279,6 +281,8 @@ const CodeEditor = ({ className }: CodeEditorProps) => {
{showMarkdownPreview ? (
+ ) : showHtmlPreview ? (
+
) : (
b.id === activeBufferId);
+
+ // If this is a preview buffer, find the source buffer
+ const sourceBuffer = activeBuffer?.sourceFilePath
+ ? buffers.find((b) => b.path === activeBuffer.sourceFilePath)
+ : activeBuffer;
+
+ const [iframeContent, setIframeContent] = useState("");
+ const containerRef = useRef(null);
+
+ // Memoize the asset URL for the directory
+ const assetBaseUrl = useMemo(() => {
+ if (!sourceBuffer?.path) return "";
+
+ // Get directory path
+ const lastSlashIndex = sourceBuffer.path.lastIndexOf("/");
+ const dirPath =
+ lastSlashIndex !== -1 ? sourceBuffer.path.substring(0, lastSlashIndex) : sourceBuffer.path;
+
+ // Convert to Tauri asset URL
+ // convertFileSrc handles the protocol logic (e.g. asset:// or http://asset.localhost)
+ const url = convertFileSrc(dirPath);
+
+ // Ensure it ends with slash for correct base resolution
+ return url.endsWith("/") ? url : `${url}/`;
+ }, [sourceBuffer?.path]);
+
+ useEffect(() => {
+ if (!sourceBuffer) return;
+
+ let content = sourceBuffer.content;
+
+ // Inject tag to allow relative links (CSS/JS/Images) to work
+ if (assetBaseUrl) {
+ const baseTag = ``;
+
+ // Try to inject in head
+ if (content.includes("")) {
+ content = content.replace("", `\n${baseTag}`);
+ } else if (content.includes("")) {
+ content = content.replace("", `\n${baseTag}`);
+ } else {
+ // No head/html tags, just prepend
+ content = `${baseTag}\n${content}`;
+ }
+ }
+
+ // Add script to handle errors and console logs if needed in future
+ // For now keeping it simple with just content rendering
+ setIframeContent(content);
+ }, [sourceBuffer?.content, assetBaseUrl]);
+
+ if (!sourceBuffer) {
+ return (
+
+ No active buffer
+
+ );
+ }
+
+ return (
+
+
+
+ );
+}
diff --git a/src/features/editor/components/toolbar/breadcrumb.tsx b/src/features/editor/components/toolbar/breadcrumb.tsx
index bc2d3d23..e621f3f2 100644
--- a/src/features/editor/components/toolbar/breadcrumb.tsx
+++ b/src/features/editor/components/toolbar/breadcrumb.tsx
@@ -54,13 +54,22 @@ export default function Breadcrumb() {
return extension === "md" || extension === "markdown";
};
+ const isHtmlFile = () => {
+ if (!activeBuffer) return false;
+ const extension = activeBuffer.path.split(".").pop()?.toLowerCase();
+ return extension === "html" || extension === "htm";
+ };
+
const handlePreviewClick = () => {
- if (!activeBuffer || activeBuffer.isMarkdownPreview) return;
+ if (!activeBuffer || activeBuffer.isMarkdownPreview || activeBuffer.isHtmlPreview) return;
const { openBuffer } = useBufferStore.getState().actions;
const previewPath = `${activeBuffer.path}:preview`;
const previewName = `${activeBuffer.name} (Preview)`;
+ const isMarkdown = isMarkdownFile();
+ const isHtml = isHtmlFile();
+
openBuffer(
previewPath,
previewName,
@@ -70,7 +79,8 @@ export default function Breadcrumb() {
false, // isDiff
true, // isVirtual
undefined, // diffData
- true, // isMarkdownPreview
+ isMarkdown, // isMarkdownPreview
+ isHtml, // isHtmlPreview
activeBuffer.path, // sourceFilePath
);
};
@@ -246,7 +256,8 @@ export default function Breadcrumb() {
))}
- {isMarkdownFile() && !activeBuffer?.isMarkdownPreview && (
+ {((isMarkdownFile() && !activeBuffer?.isMarkdownPreview) ||
+ (isHtmlFile() && !activeBuffer?.isHtmlPreview)) && (