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
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,14 @@ import {
SUPERSET_THEME,
useMonacoReady,
} from "renderer/contexts/MonacoProvider";
import type { Tab } from "renderer/stores/tabs/types";
import type { DiffViewMode, FileContents } from "shared/changes-types";
import { registerCopyPathLineAction } from "./editor-actions";
import {
EditorContextMenu,
type PaneActions,
registerCopyPathLineAction,
useEditorActions,
} from "../../../components/EditorContextMenu";

function scrollToFirstDiff(
editor: Monaco.editor.IStandaloneDiffEditor,
Expand All @@ -29,13 +35,25 @@ function scrollToFirstDiff(
}
}

export interface DiffViewerContextMenuProps {
onSplitHorizontal: () => void;
onSplitVertical: () => void;
onClosePane: () => void;
currentTabId: string;
availableTabs: Tab[];
onMoveToTab: (tabId: string) => void;
onMoveToNewTab: () => void;
}

interface DiffViewerProps {
contents: FileContents;
viewMode: DiffViewMode;
filePath: string;
editable?: boolean;
onSave?: (content: string) => void;
onChange?: (content: string) => void;
// Optional context menu props - when provided, wraps editor with context menu
contextMenuProps?: DiffViewerContextMenuProps;
}

export function DiffViewer({
Expand All @@ -45,6 +63,7 @@ export function DiffViewer({
editable = false,
onSave,
onChange,
contextMenuProps,
}: DiffViewerProps) {
const isMonacoReady = useMonacoReady();
const diffEditorRef = useRef<Monaco.editor.IStandaloneDiffEditor | null>(
Expand Down Expand Up @@ -131,6 +150,20 @@ export function DiffViewer({
};
}, [isEditorMounted, onChange]);

// Get the active editor (modified or original)
const getEditor = useCallback(() => {
return (
modifiedEditorRef.current || diffEditorRef.current?.getOriginalEditor()
);
}, []);

// Use shared editor actions hook - diff viewer is read-only (no cut/paste)
const editorActions = useEditorActions({
getEditor,
filePath,
editable: false,
});

if (!isMonacoReady) {
return (
<div className="flex items-center justify-center h-full text-muted-foreground">
Expand All @@ -140,30 +173,51 @@ export function DiffViewer({
);
}

const diffEditor = (
<DiffEditor
height="100%"
original={contents.original}
modified={contents.modified}
language={contents.language}
theme={SUPERSET_THEME}
onMount={handleMount}
loading={
<div className="flex items-center justify-center h-full text-muted-foreground">
<LuLoader className="w-4 h-4 animate-spin mr-2" />
<span>Loading editor...</span>
</div>
}
options={{
...MONACO_EDITOR_OPTIONS,
renderSideBySide: viewMode === "side-by-side",
readOnly: !editable,
originalEditable: false,
renderOverviewRuler: false,
diffWordWrap: "on",
contextmenu: !contextMenuProps, // Disable Monaco's context menu if we have custom props
}}
/>
);

// If no context menu props, return plain editor
if (!contextMenuProps) {
return <div className="h-full w-full">{diffEditor}</div>;
}

// Wrap with custom context menu
const paneActions: PaneActions = {
onSplitHorizontal: contextMenuProps.onSplitHorizontal,
onSplitVertical: contextMenuProps.onSplitVertical,
onClosePane: contextMenuProps.onClosePane,
currentTabId: contextMenuProps.currentTabId,
availableTabs: contextMenuProps.availableTabs,
onMoveToTab: contextMenuProps.onMoveToTab,
onMoveToNewTab: contextMenuProps.onMoveToNewTab,
};

return (
<div className="h-full w-full">
<DiffEditor
height="100%"
original={contents.original}
modified={contents.modified}
language={contents.language}
theme={SUPERSET_THEME}
onMount={handleMount}
loading={
<div className="flex items-center justify-center h-full text-muted-foreground">
<LuLoader className="w-4 h-4 animate-spin mr-2" />
<span>Loading editor...</span>
</div>
}
options={{
...MONACO_EDITOR_OPTIONS,
renderSideBySide: viewMode === "side-by-side",
readOnly: !editable,
originalEditable: false,
renderOverviewRuler: false,
diffWordWrap: "on",
}}
/>
</div>
<EditorContextMenu editorActions={editorActions} paneActions={paneActions}>
<div className="h-full w-full">{diffEditor}</div>
</EditorContextMenu>
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import type * as Monaco from "monaco-editor";
import { useCallback, useEffect, useRef, useState } from "react";
import type { MosaicBranch } from "react-mosaic-component";
import { useTabsStore } from "renderer/stores/tabs/store";
import type { Pane } from "renderer/stores/tabs/types";
import type { Pane, Tab } from "renderer/stores/tabs/types";
import type { FileViewerMode } from "shared/tabs-types";
import { BasePaneWindow } from "../components";
import { FileViewerContent } from "./components/FileViewerContent";
Expand All @@ -24,8 +24,21 @@ interface FileViewerPaneProps {
dimensions: { width: number; height: number },
path?: MosaicBranch[],
) => void;
splitPaneHorizontal: (
tabId: string,
sourcePaneId: string,
path?: MosaicBranch[],
) => void;
splitPaneVertical: (
tabId: string,
sourcePaneId: string,
path?: MosaicBranch[],
) => void;
Comment thread
Kitenite marked this conversation as resolved.
removePane: (paneId: string) => void;
setFocusedPane: (tabId: string, paneId: string) => void;
availableTabs: Tab[];
onMoveToTab: (targetTabId: string) => void;
onMoveToNewTab: () => void;
}

export function FileViewerPane({
Expand All @@ -36,8 +49,13 @@ export function FileViewerPane({
tabId,
worktreePath,
splitPaneAuto,
splitPaneHorizontal,
splitPaneVertical,
removePane,
setFocusedPane,
availableTabs,
onMoveToTab,
onMoveToNewTab,
}: FileViewerPaneProps) {
const editorRef = useRef<Monaco.editor.IStandaloneCodeEditor | null>(null);
const [isDirty, setIsDirty] = useState(false);
Expand Down Expand Up @@ -290,6 +308,14 @@ export function FileViewerPane({
onEditorChange={handleEditorChange}
onDiffChange={isDiffEditable ? handleDiffChange : undefined}
setIsDirty={setIsDirty}
// Context menu props
onSplitHorizontal={() => splitPaneHorizontal(tabId, paneId, path)}
onSplitVertical={() => splitPaneVertical(tabId, paneId, path)}
onClosePane={() => removePane(paneId)}
currentTabId={tabId}
availableTabs={availableTabs}
onMoveToTab={onMoveToTab}
onMoveToNewTab={onMoveToNewTab}
/>
</BasePaneWindow>
<UnsavedChangesDialog
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import type * as Monaco from "monaco-editor";
import { type MutableRefObject, type ReactNode, useCallback } from "react";
import type { Tab } from "renderer/stores/tabs/types";
import { EditorContextMenu, useEditorActions } from "../../../../../components";

interface FileEditorContextMenuProps {
children: ReactNode;
editorRef: MutableRefObject<Monaco.editor.IStandaloneCodeEditor | null>;
filePath: string;
onSplitHorizontal: () => void;
onSplitVertical: () => void;
onClosePane: () => void;
currentTabId: string;
availableTabs: Tab[];
onMoveToTab: (tabId: string) => void;
onMoveToNewTab: () => void;
}

export function FileEditorContextMenu({
children,
editorRef,
filePath,
onSplitHorizontal,
onSplitVertical,
onClosePane,
currentTabId,
availableTabs,
onMoveToTab,
onMoveToNewTab,
}: FileEditorContextMenuProps) {
const getEditor = useCallback(() => editorRef.current, [editorRef]);

const editorActions = useEditorActions({
getEditor,
filePath,
editable: true,
});

return (
<EditorContextMenu
editorActions={editorActions}
paneActions={{
onSplitHorizontal,
onSplitVertical,
onClosePane,
currentTabId,
availableTabs,
onMoveToTab,
onMoveToNewTab,
}}
>
{children}
</EditorContextMenu>
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { FileEditorContextMenu } from "./FileEditorContextMenu";
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,12 @@ import {
SUPERSET_THEME,
useMonacoReady,
} from "renderer/contexts/MonacoProvider";
import type { Tab } from "renderer/stores/tabs/types";
import { detectLanguage } from "shared/detect-language";
import type { FileViewerMode } from "shared/tabs-types";
import { DiffViewer } from "../../../../../ChangesContent/components/DiffViewer";
import { registerCopyPathLineAction } from "../../../../../components/EditorContextMenu";
import { FileEditorContextMenu } from "../FileEditorContextMenu";

interface RawFileData {
ok: true;
Expand Down Expand Up @@ -54,6 +57,14 @@ interface FileViewerContentProps {
onEditorChange: (value: string | undefined) => void;
onDiffChange?: (content: string) => void;
setIsDirty: (dirty: boolean) => void;
// Context menu props
onSplitHorizontal: () => void;
onSplitVertical: () => void;
onClosePane: () => void;
currentTabId: string;
availableTabs: Tab[];
onMoveToTab: (tabId: string) => void;
onMoveToNewTab: () => void;
}

export function FileViewerContent({
Expand All @@ -74,6 +85,14 @@ export function FileViewerContent({
onEditorChange,
onDiffChange,
setIsDirty,
// Context menu props
onSplitHorizontal,
onSplitVertical,
onClosePane,
currentTabId,
availableTabs,
onMoveToTab,
onMoveToNewTab,
}: FileViewerContentProps) {
const isMonacoReady = useMonacoReady();
const hasAppliedInitialLocationRef = useRef(false);
Expand All @@ -96,8 +115,16 @@ export function FileViewerContent({
}
setIsDirty(editor.getValue() !== originalContentRef.current);
registerSaveAction(editor, onSaveRaw);
registerCopyPathLineAction(editor, filePath);
},
[onSaveRaw, editorRef, originalContentRef, draftContentRef, setIsDirty],
[
onSaveRaw,
editorRef,
originalContentRef,
draftContentRef,
setIsDirty,
filePath,
],
);

useEffect(() => {
Expand Down Expand Up @@ -164,6 +191,15 @@ export function FileViewerContent({
editable={isDiffEditable}
onSave={isDiffEditable ? onSaveDiff : undefined}
onChange={isDiffEditable ? onDiffChange : undefined}
contextMenuProps={{
onSplitHorizontal,
onSplitVertical,
onClosePane,
currentTabId,
availableTabs,
onMoveToTab,
onMoveToNewTab,
}}
/>
);
}
Expand Down Expand Up @@ -212,21 +248,38 @@ export function FileViewerContent({
}

return (
<Editor
key={filePath}
height="100%"
language={detectLanguage(filePath)}
value={draftContentRef.current ?? rawFileData.content}
theme={SUPERSET_THEME}
onMount={handleEditorMount}
onChange={onEditorChange}
loading={
<div className="flex items-center justify-center h-full text-muted-foreground">
<LuLoader className="w-4 h-4 animate-spin mr-2" />
<span>Loading editor...</span>
</div>
}
options={MONACO_EDITOR_OPTIONS}
/>
<FileEditorContextMenu
editorRef={editorRef}
filePath={filePath}
onSplitHorizontal={onSplitHorizontal}
onSplitVertical={onSplitVertical}
onClosePane={onClosePane}
currentTabId={currentTabId}
availableTabs={availableTabs}
onMoveToTab={onMoveToTab}
onMoveToNewTab={onMoveToNewTab}
>
<div className="w-full h-full">
<Editor
key={filePath}
height="100%"
language={detectLanguage(filePath)}
value={draftContentRef.current ?? rawFileData.content}
theme={SUPERSET_THEME}
onMount={handleEditorMount}
onChange={onEditorChange}
loading={
<div className="flex items-center justify-center h-full text-muted-foreground">
<LuLoader className="w-4 h-4 animate-spin mr-2" />
<span>Loading editor...</span>
</div>
}
options={{
...MONACO_EDITOR_OPTIONS,
contextmenu: false, // Disable Monaco's native context menu to use our custom one
}}
/>
</div>
</FileEditorContextMenu>
);
}
Loading
Loading