Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 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
2 changes: 1 addition & 1 deletion frontend/app/block/block.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import {
SubBlockProps,
} from "@/app/block/blocktypes";
import { LauncherViewModel } from "@/app/view/launcher/launcher";
import { PreviewModel } from "@/app/view/preview/preview";
import { PreviewModel } from "@/app/view/preview/preview-model";
import { SysinfoViewModel } from "@/app/view/sysinfo/sysinfo";
import { VDomModel } from "@/app/view/vdom/vdom-model";
import { ErrorBoundary } from "@/element/errorboundary";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ import { ContextMenuModel } from "@/app/store/contextmenu";
import { atoms, getApi, globalStore } from "@/app/store/global";
import { RpcApi } from "@/app/store/wshclientapi";
import { TabRpcClient } from "@/app/store/wshrpcutil";
import { type PreviewModel } from "@/app/view/preview/preview";
import { checkKeyPressed, isCharacterKeyEvent } from "@/util/keyutil";
import { PLATFORM, PlatformMacOS } from "@/util/platformutil";
import { addOpenMenuItems } from "@/util/previewutil";
Expand All @@ -34,6 +33,7 @@ import { useDrag, useDrop } from "react-dnd";
import { quote as shellQuote } from "shell-quote";
import { debounce } from "throttle-debounce";
import "./directorypreview.scss";
import { type PreviewModel } from "./preview-model";

const PageJumpSize = 20;

Expand Down Expand Up @@ -527,20 +527,20 @@ function TableBody({
if (focusIndex === null || !bodyRef.current || !osRef) {
return;
}

const rowElement = bodyRef.current.querySelector(`[data-rowindex="${focusIndex}"]`) as HTMLDivElement;
if (!rowElement) {
return;
}

const viewport = osRef.osInstance().elements().viewport;
const viewportHeight = viewport.offsetHeight;
const rowRect = rowElement.getBoundingClientRect();
const parentRect = viewport.getBoundingClientRect();
const viewportScrollTop = viewport.scrollTop;
const rowTopRelativeToViewport = rowRect.top - parentRect.top + viewport.scrollTop;
const rowBottomRelativeToViewport = rowRect.bottom - parentRect.top + viewport.scrollTop;

if (rowTopRelativeToViewport - 30 < viewportScrollTop) {
// Row is above the visible area
let topVal = rowTopRelativeToViewport - 30;
Expand Down Expand Up @@ -732,9 +732,12 @@ const TableRow = React.forwardRef(function ({
[dragItem]
);

const dragRef = useCallback((node: HTMLDivElement | null) => {
drag(node);
}, [drag]);
const dragRef = useCallback(
(node: HTMLDivElement | null) => {
drag(node);
},
[drag]
);

return (
<div
Expand Down
79 changes: 79 additions & 0 deletions frontend/app/view/preview/preview-edit.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
// Copyright 2025, Command Line Inc.
// SPDX-License-Identifier: Apache-2.0

import { tryReinjectKey } from "@/app/store/keymodel";
import { CodeEditor } from "@/app/view/codeeditor/codeeditor";
import { globalStore } from "@/store/global";
import { adaptFromReactOrNativeKeyEvent, checkKeyPressed } from "@/util/keyutil";
import { fireAndForget } from "@/util/util";
import { Monaco } from "@monaco-editor/react";
import { useAtomValue, useSetAtom } from "jotai";
import type * as MonacoTypes from "monaco-editor/esm/vs/editor/editor.api";
import { useEffect } from "react";
import type { SpecializedViewProps } from "./preview";

function CodeEditPreview({ model }: SpecializedViewProps) {
const fileContent = useAtomValue(model.fileContent);
const setNewFileContent = useSetAtom(model.newFileContent);
const fileInfo = useAtomValue(model.statFile);
const fileName = fileInfo?.name;
const blockMeta = useAtomValue(model.blockAtom)?.meta;

function codeEditKeyDownHandler(e: WaveKeyboardEvent): boolean {
if (checkKeyPressed(e, "Cmd:e")) {
fireAndForget(() => model.setEditMode(false));
return true;
}
if (checkKeyPressed(e, "Cmd:s") || checkKeyPressed(e, "Ctrl:s")) {
fireAndForget(model.handleFileSave.bind(model));
return true;
}
if (checkKeyPressed(e, "Cmd:r")) {
fireAndForget(model.handleFileRevert.bind(model));
return true;
}
return false;
}

useEffect(() => {
model.codeEditKeyDownHandler = codeEditKeyDownHandler;
return () => {
model.codeEditKeyDownHandler = null;
model.monacoRef.current = null;
};
}, []);

function onMount(editor: MonacoTypes.editor.IStandaloneCodeEditor, monaco: Monaco): () => void {
model.monacoRef.current = editor;

editor.onKeyDown((e: MonacoTypes.IKeyboardEvent) => {
const waveEvent = adaptFromReactOrNativeKeyEvent(e.browserEvent);
const handled = tryReinjectKey(waveEvent);
if (handled) {
e.stopPropagation();
e.preventDefault();
}
});

const isFocused = globalStore.get(model.nodeModel.isFocused);
if (isFocused) {
editor.focus();
}

return null;
}

return (
<CodeEditor
blockId={model.blockId}
text={fileContent}
filename={fileName}
fileinfo={fileInfo}
meta={blockMeta}
onChange={(text) => setNewFileContent(text)}
onMount={onMount}
/>
);
}

export { CodeEditPreview };
34 changes: 34 additions & 0 deletions frontend/app/view/preview/preview-markdown.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
// Copyright 2025, Command Line Inc.
// SPDX-License-Identifier: Apache-2.0

import { Markdown } from "@/element/markdown";
import { getOverrideConfigAtom } from "@/store/global";
import { useAtomValue } from "jotai";
import { useMemo } from "react";
import type { SpecializedViewProps } from "./preview";

function MarkdownPreview({ model }: SpecializedViewProps) {
const connName = useAtomValue(model.connection);
const fileInfo = useAtomValue(model.statFile);
const fontSizeOverride = useAtomValue(getOverrideConfigAtom(model.blockId, "markdown:fontsize"));
const fixedFontSizeOverride = useAtomValue(getOverrideConfigAtom(model.blockId, "markdown:fixedfontsize"));
const resolveOpts: MarkdownResolveOpts = useMemo<MarkdownResolveOpts>(() => {
return {
connName: connName,
baseDir: fileInfo.dir,
};
}, [connName, fileInfo.dir]);
return (
<div className="view-preview view-preview-markdown">
<Markdown
textAtom={model.fileContent}
showTocAtom={model.markdownShowToc}
resolveOpts={resolveOpts}
fontSizeOverride={fontSizeOverride}
fixedFontSizeOverride={fixedFontSizeOverride}
/>
</div>
);
}

export { MarkdownPreview };
Loading
Loading