Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions src/features/command-palette/components/command-palette.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ import KeybindingBadge from "@/ui/keybinding-badge";
import { createAdvancedActions } from "../constants/advanced-actions";
import { createFileActions } from "../constants/file-actions";
import { createGitActions } from "../constants/git-actions";
import { createMarkdownActions } from "../constants/markdown-actions";
import { createNavigationActions } from "../constants/navigation-actions";
import { createSettingsActions } from "../constants/settings-actions";
import { createViewActions } from "../constants/view-actions";
Expand Down Expand Up @@ -78,6 +79,7 @@ const CommandPalette = () => {
const { showToast } = useToast();
const buffers = useBufferStore.use.buffers();
const activeBufferId = useBufferStore.use.activeBufferId();
const activeBuffer = buffers.find((b) => b.id === activeBufferId) || null;
const {
closeBuffer,
setActiveBuffer,
Expand All @@ -86,9 +88,23 @@ const CommandPalette = () => {
reopenClosedTab,
} = useBufferStore.use.actions();
const { zoomIn, zoomOut, resetZoom } = useZoomStore.use.actions();
const { openBuffer } = useBufferStore.use.actions();

// Helper function to check if the active buffer is a markdown file
const isMarkdownFile = () => {
if (!activeBuffer) return false;
const extension = activeBuffer.path.split(".").pop()?.toLowerCase();
return extension === "md" || extension === "markdown";
};

// Create all actions using factory functions
const allActions: Action[] = [
...createMarkdownActions({
isMarkdownFile: isMarkdownFile(),
activeBuffer,
openBuffer,
onClose,
}),
...createViewActions({
isSidebarVisible,
setIsSidebarVisible,
Expand Down
59 changes: 59 additions & 0 deletions src/features/command-palette/constants/markdown-actions.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import { Eye } from "lucide-react";
import type { Buffer } from "@/features/editor/stores/buffer-store";
import type { Action } from "../models/action.types";

interface MarkdownActionsParams {
isMarkdownFile: boolean;
activeBuffer: Buffer | null;
openBuffer: (
path: string,
name: string,
content: string,
isImage?: boolean,
isSQLite?: boolean,
isDiff?: boolean,
isVirtual?: boolean,
diffData?: any,
isMarkdownPreview?: boolean,
sourceFilePath?: string,
) => string;
onClose: () => void;
}

export const createMarkdownActions = (params: MarkdownActionsParams): Action[] => {
const { isMarkdownFile, activeBuffer, openBuffer, onClose } = params;

if (!isMarkdownFile || !activeBuffer) {
return [];
}

return [
{
id: "markdown-preview",
label: "Markdown: Preview Markdown",
description: "Open markdown preview in a new tab",
icon: <Eye size={14} />,
category: "Markdown",
action: () => {
// Create a virtual path for the preview
const previewPath = `${activeBuffer.path}:preview`;
const previewName = `${activeBuffer.name} (Preview)`;

// Open a new buffer for the preview
openBuffer(
previewPath,
previewName,
activeBuffer.content,
false, // isImage
false, // isSQLite
false, // isDiff
true, // isVirtual
undefined, // diffData
true, // isMarkdownPreview
activeBuffer.path, // sourceFilePath
);
onClose();
},
},
];
};
2 changes: 1 addition & 1 deletion src/features/command-palette/models/action.types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,4 @@ export interface Action {
action: () => void;
}

export type ActionCategory = "View" | "Settings" | "File" | "Window" | "Navigation";
export type ActionCategory = "View" | "Settings" | "File" | "Window" | "Navigation" | "Markdown";
5 changes: 1 addition & 4 deletions src/features/editor/components/code-editor.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ import { useAppStore } from "@/stores/app-store";
import { useZoomStore } from "@/stores/zoom-store";
import { HoverTooltip } from "../lsp/hover-tooltip";
import { MarkdownPreview } from "../markdown/markdown-preview";
import { isMarkdownFile } from "../utils/lines";
import { Editor } from "./editor";
import { EditorStylesheet } from "./stylesheet";
import Breadcrumb from "./toolbar/breadcrumb";
Expand Down Expand Up @@ -41,7 +40,6 @@ const CodeEditor = ({ className }: CodeEditorProps) => {
const { setRefs, setContent, setFileInfo } = useEditorStateStore.use.actions();
// No longer need to sync content - editor-view-store computes from buffer
const { setDisabled } = useEditorSettingsStore.use.actions();
const isMarkdownPreview = useEditorSettingsStore.use.isMarkdownPreview();

const buffers = useBufferStore.use.buffers();
const activeBufferId = useBufferStore.use.activeBufferId();
Expand All @@ -61,8 +59,7 @@ const CodeEditor = ({ className }: CodeEditorProps) => {
const filePath = activeBuffer?.path || "";
const onChange = activeBuffer ? handleContentChange : () => {};

const showMarkdownPreview =
activeBuffer && isMarkdownFile(activeBuffer.path) && isMarkdownPreview;
const showMarkdownPreview = activeBuffer?.isMarkdownPreview || false;

// Initialize refs in store
useEffect(() => {
Expand Down
26 changes: 1 addition & 25 deletions src/features/editor/components/toolbar/breadcrumb.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
import { ArrowLeft, ChevronRight, Eye, Search, Sparkles } from "lucide-react";
import { ArrowLeft, ChevronRight, Search, Sparkles } from "lucide-react";
import { type RefObject, useRef, useState } from "react";
import { createPortal } from "react-dom";
import { useEventListener, useOnClickOutside } from "usehooks-ts";
import { EDITOR_CONSTANTS } from "@/features/editor/config/constants";
import { useBufferStore } from "@/features/editor/stores/buffer-store";
import { useEditorSettingsStore } from "@/features/editor/stores/settings-store";
import { useEditorStateStore } from "@/features/editor/stores/state-store";
import { logger } from "@/features/editor/utils/logger";
import FileIcon from "@/features/file-explorer/views/file.icon";
Expand All @@ -28,8 +27,6 @@ export default function Breadcrumb() {
const activeBuffer = buffers.find((b) => b.id === activeBufferId) || null;
const { rootFolderPath, handleFileSelect } = useFileSystemStore();
const { isFindVisible, setIsFindVisible } = useUIState();
const isMarkdownPreview = useEditorSettingsStore.use.isMarkdownPreview();
const { setIsMarkdownPreview } = useEditorSettingsStore.use.actions();
const { toggle: toggleInlineEditToolbar } = useInlineEditToolbarStore.use.actions();
const selection = useEditorStateStore.use.selection?.();

Expand All @@ -45,22 +42,12 @@ export default function Breadcrumb() {
setIsFindVisible(!isFindVisible);
};

const handlePreviewClick = () => {
setIsMarkdownPreview(!isMarkdownPreview);
};

const handleInlineEditClick = () => {
toggleInlineEditToolbar();
};

const hasSelection = selection && selection.start.offset !== selection.end.offset;

const isMarkdownFile = () => {
if (!activeBuffer) return false;
const extension = activeBuffer.path.split(".").pop()?.toLowerCase();
return extension === "md" || extension === "markdown";
};

const filePath = activeBuffer?.path || "";
const rootPath = rootFolderPath;
const onNavigate = handleNavigate;
Expand Down Expand Up @@ -232,17 +219,6 @@ export default function Breadcrumb() {
))}
</div>
<div className="flex items-center gap-1">
{isMarkdownFile() && (
<button
onClick={handlePreviewClick}
className={`flex h-5 w-5 items-center justify-center rounded transition-colors hover:bg-hover hover:text-text ${
isMarkdownPreview ? "bg-hover text-accent" : "text-text-lighter"
}`}
title="Toggle markdown preview"
>
<Eye size={12} />
</button>
)}
<button
onClick={handleInlineEditClick}
disabled={!hasSelection}
Expand Down
17 changes: 11 additions & 6 deletions src/features/editor/markdown/markdown-preview.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,16 @@ export function MarkdownPreview() {
const [html, setHtml] = useState("");
const containerRef = useRef<HTMLDivElement>(null);

// Get the source buffer if this is a preview buffer
const sourceBuffer = activeBuffer?.sourceFilePath
? buffers.find((b) => b.path === activeBuffer.sourceFilePath)
: activeBuffer;

useEffect(() => {
if (!activeBuffer) return;
const parsedHtml = parseMarkdown(activeBuffer.content);
if (!sourceBuffer) return;
const parsedHtml = parseMarkdown(sourceBuffer.content);
setHtml(parsedHtml);
}, [activeBuffer?.content, activeBuffer]);
}, [sourceBuffer?.content, sourceBuffer]);

const resolvePath = useCallback(
(href: string, currentFilePath: string): string => {
Expand Down Expand Up @@ -98,9 +103,9 @@ export function MarkdownPreview() {
return;
}

if (!activeBuffer) return;
if (!sourceBuffer) return;

const targetPath = resolvePath(href, activeBuffer.path);
const targetPath = resolvePath(href, sourceBuffer.path);

try {
const fileExists = await exists(targetPath);
Expand All @@ -121,7 +126,7 @@ export function MarkdownPreview() {
logger.error("MarkdownPreview", "Failed to handle link:", error);
}
},
[activeBuffer, handleFileSelect, resolvePath],
[sourceBuffer, handleFileSelect, resolvePath],
);

return (
Expand Down
36 changes: 31 additions & 5 deletions src/features/editor/stores/buffer-store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import type { GitDiff } from "@/features/version-control/git/types/git";
import { useSessionStore } from "@/stores/session-store";
import { createSelectors } from "@/utils/zustand-selectors";

interface Buffer {
export interface Buffer {
id: string;
path: string;
name: string;
Expand All @@ -24,11 +24,14 @@ interface Buffer {
isImage: boolean;
isSQLite: boolean;
isDiff: boolean;
isMarkdownPreview: boolean;
isExternalEditor: boolean;
isActive: boolean;
language?: string; // File language for syntax highlighting and formatting
// For diff buffers, store the parsed diff data (single or multi-file)
diffData?: GitDiff | MultiFileDiff;
// For markdown preview buffers, store the source file path
sourceFilePath?: string;
// For external editor buffers, store the terminal connection ID
terminalConnectionId?: string;
// Cached syntax highlighting tokens
Expand Down Expand Up @@ -71,6 +74,8 @@ interface BufferActions {
isDiff?: boolean,
isVirtual?: boolean,
diffData?: GitDiff | MultiFileDiff,
isMarkdownPreview?: boolean,
sourceFilePath?: string,
) => string;
openExternalEditorBuffer: (path: string, name: string, terminalConnectionId: string) => string;
closeBuffer: (bufferId: string) => void;
Expand Down Expand Up @@ -126,7 +131,15 @@ const saveSessionToStore = (buffers: Buffer[], activeBufferId: string | null) =>

// Only save real files, not virtual/diff/image/sqlite/external editor buffers
const persistableBuffers = buffers
.filter((b) => !b.isVirtual && !b.isDiff && !b.isImage && !b.isSQLite && !b.isExternalEditor)
.filter(
(b) =>
!b.isVirtual &&
!b.isDiff &&
!b.isImage &&
!b.isSQLite &&
!b.isMarkdownPreview &&
!b.isExternalEditor,
)
.map((b) => ({
path: b.path,
name: b.name,
Expand All @@ -141,6 +154,7 @@ const saveSessionToStore = (buffers: Buffer[], activeBufferId: string | null) =>
!activeBuffer.isDiff &&
!activeBuffer.isImage &&
!activeBuffer.isSQLite &&
!activeBuffer.isMarkdownPreview &&
!activeBuffer.isExternalEditor
? activeBuffer.path
: null;
Expand All @@ -167,6 +181,8 @@ export const useBufferStore = createSelectors(
isDiff = false,
isVirtual = false,
diffData?: GitDiff | MultiFileDiff,
isMarkdownPreview = false,
sourceFilePath?: string,
) => {
const { buffers, maxOpenTabs } = get();

Expand Down Expand Up @@ -202,10 +218,12 @@ export const useBufferStore = createSelectors(
isImage,
isSQLite,
isDiff,
isMarkdownPreview,
isExternalEditor: false,
isActive: true,
language: detectLanguageFromFileName(name),
diffData,
sourceFilePath,
tokens: [],
};

Expand All @@ -214,8 +232,8 @@ export const useBufferStore = createSelectors(
state.activeBufferId = newBuffer.id;
});

// Track in recent files (only for real files, not virtual/diff buffers)
if (!isVirtual && !isDiff && !isImage && !isSQLite) {
// Track in recent files (only for real files, not virtual/diff/markdown preview buffers)
if (!isVirtual && !isDiff && !isImage && !isSQLite && !isMarkdownPreview) {
useRecentFilesStore.getState().addOrUpdateRecentFile(path, name);

// Check if extension is available and start LSP or prompt installation
Expand Down Expand Up @@ -333,6 +351,7 @@ export const useBufferStore = createSelectors(
isImage: false,
isSQLite: false,
isDiff: false,
isMarkdownPreview: false,
isExternalEditor: true,
isActive: true,
language: detectLanguageFromFileName(name),
Expand Down Expand Up @@ -392,6 +411,7 @@ export const useBufferStore = createSelectors(
!closedBuffer.isDiff &&
!closedBuffer.isImage &&
!closedBuffer.isSQLite &&
!closedBuffer.isMarkdownPreview &&
!closedBuffer.isExternalEditor
) {
// Stop LSP for this file in background (don't block buffer closing)
Expand Down Expand Up @@ -690,7 +710,13 @@ export const useBufferStore = createSelectors(

reloadBufferFromDisk: async (bufferId: string): Promise<void> => {
const buffer = get().buffers.find((b) => b.id === bufferId);
if (!buffer || buffer.isVirtual || buffer.isImage || buffer.isSQLite) {
if (
!buffer ||
buffer.isVirtual ||
buffer.isImage ||
buffer.isSQLite ||
buffer.isMarkdownPreview
) {
return;
}

Expand Down
4 changes: 0 additions & 4 deletions src/features/editor/stores/settings-store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ interface EditorSettingsState {
lineNumbers: boolean;
disabled: boolean;
theme: string;
isMarkdownPreview: boolean;
actions: EditorSettingsActions;
}

Expand All @@ -23,7 +22,6 @@ interface EditorSettingsActions {
setLineNumbers: (show: boolean) => void;
setDisabled: (disabled: boolean) => void;
setTheme: (theme: string) => void;
setIsMarkdownPreview: (preview: boolean) => void;
}

export const useEditorSettingsStore = createSelectors(
Expand All @@ -36,7 +34,6 @@ export const useEditorSettingsStore = createSelectors(
lineNumbers: true,
disabled: false,
theme: "auto",
isMarkdownPreview: false,
actions: {
setFontSize: (size) => set({ fontSize: size }),
setFontFamily: (family) => set({ fontFamily: family }),
Expand All @@ -45,7 +42,6 @@ export const useEditorSettingsStore = createSelectors(
setLineNumbers: (show) => set({ lineNumbers: show }),
setDisabled: (disabled) => set({ disabled }),
setTheme: (theme) => set({ theme }),
setIsMarkdownPreview: (preview) => set({ isMarkdownPreview: preview }),
},
})),
),
Expand Down