mirror of
https://github.com/wavetermdev/waveterm.git
synced 2024-12-22 16:48:23 +01:00
preview edit (#302)
This commit is contained in:
parent
aab487541b
commit
63cfe1d279
@ -1,6 +1,8 @@
|
|||||||
// Copyright 2024, Command Line Inc.
|
// Copyright 2024, Command Line Inc.
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
@import "../mixins.less";
|
||||||
|
|
||||||
.block {
|
.block {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
@ -111,12 +113,23 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.block-frame-text {
|
.block-frame-text {
|
||||||
|
.ellipsis();
|
||||||
font: var(--fixed-font);
|
font: var(--fixed-font);
|
||||||
font-size: 11px;
|
font-size: 11px;
|
||||||
opacity: 0.7;
|
opacity: 0.7;
|
||||||
overflow-x: hidden;
|
overflow-x: hidden;
|
||||||
text-wrap: nowrap;
|
flex-grow: 1;
|
||||||
text-overflow: ellipsis;
|
|
||||||
|
&.preview-filename {
|
||||||
|
span {
|
||||||
|
height: 100%;
|
||||||
|
cursor: pointer;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background: var(--highlight-bg-color);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.iconbutton {
|
.iconbutton {
|
||||||
|
@ -217,8 +217,10 @@ const HeaderTextElem = React.memo(({ elem, preview }: { elem: HeaderElem; previe
|
|||||||
return <Input decl={elem} className={clsx("block-frame-input", elem.className)} preview={preview} />;
|
return <Input decl={elem} className={clsx("block-frame-input", elem.className)} preview={preview} />;
|
||||||
} else if (elem.elemtype == "text") {
|
} else if (elem.elemtype == "text") {
|
||||||
return (
|
return (
|
||||||
<div ref={preview ? null : elem.ref} className="block-frame-text">
|
<div className={clsx("block-frame-text", elem.className)}>
|
||||||
{elem.text}
|
<span ref={preview ? null : elem.ref} onClick={() => elem?.onClick()}>
|
||||||
|
{elem.text}
|
||||||
|
</span>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
} else if (elem.elemtype == "textbutton") {
|
} else if (elem.elemtype == "textbutton") {
|
||||||
|
@ -100,4 +100,12 @@
|
|||||||
padding-left: 10px;
|
padding-left: 10px;
|
||||||
padding-right: 10px;
|
padding-right: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.font-size-11 {
|
||||||
|
font-size: 11px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.font-weight-500 {
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -64,7 +64,6 @@ interface CodeEditorProps {
|
|||||||
parentRef: React.MutableRefObject<HTMLDivElement>;
|
parentRef: React.MutableRefObject<HTMLDivElement>;
|
||||||
text: string;
|
text: string;
|
||||||
filename: string;
|
filename: string;
|
||||||
readonly: boolean;
|
|
||||||
language?: string;
|
language?: string;
|
||||||
onChange?: (text: string) => void;
|
onChange?: (text: string) => void;
|
||||||
onSave?: () => void;
|
onSave?: () => void;
|
||||||
@ -73,7 +72,6 @@ interface CodeEditorProps {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function CodeEditor({
|
export function CodeEditor({
|
||||||
readonly = false,
|
|
||||||
parentRef,
|
parentRef,
|
||||||
text,
|
text,
|
||||||
language,
|
language,
|
||||||
@ -150,7 +148,6 @@ export function CodeEditor({
|
|||||||
}
|
}
|
||||||
|
|
||||||
const editorOpts = defaultEditorOptions();
|
const editorOpts = defaultEditorOptions();
|
||||||
editorOpts.readOnly = readonly;
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="code-editor-wrapper">
|
<div className="code-editor-wrapper">
|
||||||
|
@ -4,7 +4,7 @@
|
|||||||
import { TypeAheadModal } from "@/app/modals/typeaheadmodal";
|
import { TypeAheadModal } from "@/app/modals/typeaheadmodal";
|
||||||
import { ContextMenuModel } from "@/app/store/contextmenu";
|
import { ContextMenuModel } from "@/app/store/contextmenu";
|
||||||
import { Markdown } from "@/element/markdown";
|
import { Markdown } from "@/element/markdown";
|
||||||
import { atoms, createBlock, globalStore, useBlockAtom } from "@/store/global";
|
import { createBlock, globalStore, useBlockAtom } from "@/store/global";
|
||||||
import * as services from "@/store/services";
|
import * as services from "@/store/services";
|
||||||
import * as WOS from "@/store/wos";
|
import * as WOS from "@/store/wos";
|
||||||
import { getWebServerEndpoint } from "@/util/endpoints";
|
import { getWebServerEndpoint } from "@/util/endpoints";
|
||||||
@ -35,6 +35,10 @@ function isTextFile(mimeType: string): boolean {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function canPreview(mimeType: string): boolean {
|
||||||
|
return mimeType.startsWith("text/markdown") || mimeType.startsWith("text/csv");
|
||||||
|
}
|
||||||
|
|
||||||
export class PreviewModel implements ViewModel {
|
export class PreviewModel implements ViewModel {
|
||||||
viewType: string;
|
viewType: string;
|
||||||
blockId: string;
|
blockId: string;
|
||||||
@ -45,9 +49,9 @@ export class PreviewModel implements ViewModel {
|
|||||||
preIconButton: jotai.Atom<HeaderIconButton>;
|
preIconButton: jotai.Atom<HeaderIconButton>;
|
||||||
endIconButtons: jotai.Atom<HeaderIconButton[]>;
|
endIconButtons: jotai.Atom<HeaderIconButton[]>;
|
||||||
ceReadOnly: jotai.PrimitiveAtom<boolean>;
|
ceReadOnly: jotai.PrimitiveAtom<boolean>;
|
||||||
isCeView: jotai.PrimitiveAtom<boolean>;
|
|
||||||
previewTextRef: React.RefObject<HTMLDivElement>;
|
previewTextRef: React.RefObject<HTMLDivElement>;
|
||||||
editMode: jotai.Atom<boolean>;
|
editMode: jotai.Atom<boolean>;
|
||||||
|
canPreview: jotai.PrimitiveAtom<boolean>;
|
||||||
|
|
||||||
fileName: jotai.Atom<string>;
|
fileName: jotai.Atom<string>;
|
||||||
connection: jotai.Atom<string>;
|
connection: jotai.Atom<string>;
|
||||||
@ -57,6 +61,7 @@ export class PreviewModel implements ViewModel {
|
|||||||
fileMimeTypeLoadable: jotai.Atom<Loadable<string>>;
|
fileMimeTypeLoadable: jotai.Atom<Loadable<string>>;
|
||||||
fileContent: jotai.Atom<Promise<string>>;
|
fileContent: jotai.Atom<Promise<string>>;
|
||||||
newFileContent: jotai.PrimitiveAtom<string | null>;
|
newFileContent: jotai.PrimitiveAtom<string | null>;
|
||||||
|
openFileModal: jotai.PrimitiveAtom<boolean>;
|
||||||
|
|
||||||
showHiddenFiles: jotai.PrimitiveAtom<boolean>;
|
showHiddenFiles: jotai.PrimitiveAtom<boolean>;
|
||||||
refreshVersion: jotai.PrimitiveAtom<number>;
|
refreshVersion: jotai.PrimitiveAtom<number>;
|
||||||
@ -74,7 +79,8 @@ export class PreviewModel implements ViewModel {
|
|||||||
this.refreshVersion = jotai.atom(0);
|
this.refreshVersion = jotai.atom(0);
|
||||||
this.previewTextRef = createRef();
|
this.previewTextRef = createRef();
|
||||||
this.ceReadOnly = jotai.atom(true);
|
this.ceReadOnly = jotai.atom(true);
|
||||||
this.isCeView = jotai.atom(false);
|
this.canPreview = jotai.atom(false);
|
||||||
|
this.openFileModal = jotai.atom(false);
|
||||||
this.blockAtom = WOS.getWaveObjectAtom<Block>(`block:${blockId}`);
|
this.blockAtom = WOS.getWaveObjectAtom<Block>(`block:${blockId}`);
|
||||||
this.viewIcon = jotai.atom((get) => {
|
this.viewIcon = jotai.atom((get) => {
|
||||||
let blockData = get(this.blockAtom);
|
let blockData = get(this.blockAtom);
|
||||||
@ -121,60 +127,55 @@ export class PreviewModel implements ViewModel {
|
|||||||
});
|
});
|
||||||
this.viewName = jotai.atom("Preview");
|
this.viewName = jotai.atom("Preview");
|
||||||
this.viewText = jotai.atom((get) => {
|
this.viewText = jotai.atom((get) => {
|
||||||
if (get(this.isCeView)) {
|
const blockData = get(this.blockAtom);
|
||||||
const viewTextChildren: HeaderElem[] = [
|
const editMode = blockData?.meta?.edit ?? false;
|
||||||
{
|
const viewTextChildren: HeaderElem[] = [
|
||||||
elemtype: "input",
|
{
|
||||||
value: get(this.fileName),
|
elemtype: "text",
|
||||||
isDisabled: true,
|
text: get(this.fileName),
|
||||||
},
|
ref: this.previewTextRef,
|
||||||
];
|
className: "preview-filename",
|
||||||
if (get(this.ceReadOnly) == false) {
|
onClick: () => globalStore.set(this.openFileModal, true),
|
||||||
let saveClassName = "secondary";
|
},
|
||||||
if (get(this.newFileContent) !== null) {
|
];
|
||||||
saveClassName = "primary";
|
let saveClassName = "secondary";
|
||||||
}
|
if (get(this.newFileContent) !== null) {
|
||||||
viewTextChildren.push(
|
saveClassName = "primary";
|
||||||
{
|
}
|
||||||
elemtype: "textbutton",
|
if (editMode) {
|
||||||
text: "Save",
|
viewTextChildren.push({
|
||||||
className: clsx(
|
elemtype: "textbutton",
|
||||||
`${saveClassName} warning border-radius-4 vertical-padding-2 horizontal-padding-10`
|
text: "Save",
|
||||||
),
|
className: clsx(
|
||||||
onClick: this.handleFileSave.bind(this),
|
`${saveClassName} warning border-radius-4 vertical-padding-2 horizontal-padding-10 font-size-11 font-weight-500`
|
||||||
},
|
),
|
||||||
{
|
onClick: this.handleFileSave.bind(this),
|
||||||
elemtype: "textbutton",
|
});
|
||||||
text: "Cancel",
|
if (get(this.canPreview)) {
|
||||||
className: "secondary border-radius-4 vertical-padding-2 horizontal-padding-10",
|
|
||||||
onClick: () => this.toggleCodeEditorReadOnly(true),
|
|
||||||
}
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
viewTextChildren.push({
|
viewTextChildren.push({
|
||||||
elemtype: "textbutton",
|
elemtype: "textbutton",
|
||||||
text: "Edit",
|
text: "Preview",
|
||||||
className: "secondary border-radius-4 vertical-padding-2 horizontal-padding-10",
|
className:
|
||||||
onClick: () => this.toggleCodeEditorReadOnly(false),
|
"secondary border-radius-4 vertical-padding-2 horizontal-padding-10 font-size-11 font-weight-500",
|
||||||
|
onClick: () => this.toggleEditMode(false),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
return [
|
} else if (get(this.canPreview)) {
|
||||||
{
|
viewTextChildren.push({
|
||||||
elemtype: "div",
|
elemtype: "textbutton",
|
||||||
children: viewTextChildren,
|
text: "Edit",
|
||||||
},
|
className:
|
||||||
] as HeaderElem[];
|
"secondary border-radius-4 vertical-padding-2 horizontal-padding-10 font-size-11 font-weight-500",
|
||||||
} else {
|
onClick: () => this.toggleEditMode(true),
|
||||||
return [
|
});
|
||||||
{
|
|
||||||
elemtype: "text",
|
|
||||||
text: get(this.fileName),
|
|
||||||
ref: this.previewTextRef,
|
|
||||||
},
|
|
||||||
];
|
|
||||||
}
|
}
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
elemtype: "div",
|
||||||
|
children: viewTextChildren,
|
||||||
|
},
|
||||||
|
] as HeaderElem[];
|
||||||
});
|
});
|
||||||
|
|
||||||
this.preIconButton = jotai.atom((get) => {
|
this.preIconButton = jotai.atom((get) => {
|
||||||
const mimeType = util.jotaiLoadableValue(get(this.fileMimeTypeLoadable), "");
|
const mimeType = util.jotaiLoadableValue(get(this.fileMimeTypeLoadable), "");
|
||||||
if (mimeType == "directory") {
|
if (mimeType == "directory") {
|
||||||
@ -226,27 +227,15 @@ export class PreviewModel implements ViewModel {
|
|||||||
const statFile = await services.FileService.StatFile(conn, fileName);
|
const statFile = await services.FileService.StatFile(conn, fileName);
|
||||||
return statFile;
|
return statFile;
|
||||||
});
|
});
|
||||||
this.fullFile = jotai.atom<Promise<FullFile>>(async (get) => {
|
|
||||||
const fileName = get(this.fileName);
|
|
||||||
if (fileName == null) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
const conn = get(this.connection) ?? "";
|
|
||||||
const file = await services.FileService.ReadFile(conn, fileName);
|
|
||||||
return file;
|
|
||||||
});
|
|
||||||
this.fileMimeType = jotai.atom<Promise<string>>(async (get) => {
|
this.fileMimeType = jotai.atom<Promise<string>>(async (get) => {
|
||||||
const fileInfo = await get(this.statFile);
|
const fileInfo = await get(this.statFile);
|
||||||
return fileInfo?.mimetype;
|
return fileInfo?.mimetype;
|
||||||
});
|
});
|
||||||
this.fileMimeTypeLoadable = loadable(this.fileMimeType);
|
this.fileMimeTypeLoadable = loadable(this.fileMimeType);
|
||||||
this.fileContent = jotai.atom<Promise<string>>(async (get) => {
|
|
||||||
const fullFile = await get(this.fullFile);
|
|
||||||
return util.base64ToString(fullFile?.data64);
|
|
||||||
});
|
|
||||||
this.newFileContent = jotai.atom(null) as jotai.PrimitiveAtom<string | null>;
|
this.newFileContent = jotai.atom(null) as jotai.PrimitiveAtom<string | null>;
|
||||||
|
|
||||||
this.goParentDirectory = this.goParentDirectory.bind(this);
|
this.goParentDirectory = this.goParentDirectory.bind(this);
|
||||||
|
this.toggleEditMode(false);
|
||||||
|
this.setFileContent();
|
||||||
}
|
}
|
||||||
|
|
||||||
async resolvePath(filePath, basePath) {
|
async resolvePath(filePath, basePath) {
|
||||||
@ -285,6 +274,7 @@ export class PreviewModel implements ViewModel {
|
|||||||
stack.push(part);
|
stack.push(part);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
console.log("===============================", stack.join("/"));
|
||||||
return stack.join("/");
|
return stack.join("/");
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -332,6 +322,7 @@ export class PreviewModel implements ViewModel {
|
|||||||
if (updateMeta == null) {
|
if (updateMeta == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
updateMeta.edit = false;
|
||||||
const blockOref = WOS.makeORef("block", this.blockId);
|
const blockOref = WOS.makeORef("block", this.blockId);
|
||||||
services.ObjectService.UpdateObjectMeta(blockOref, updateMeta);
|
services.ObjectService.UpdateObjectMeta(blockOref, updateMeta);
|
||||||
}
|
}
|
||||||
@ -343,6 +334,7 @@ export class PreviewModel implements ViewModel {
|
|||||||
if (updateMeta == null) {
|
if (updateMeta == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
updateMeta.edit = false;
|
||||||
const blockOref = WOS.makeORef("block", this.blockId);
|
const blockOref = WOS.makeORef("block", this.blockId);
|
||||||
services.ObjectService.UpdateObjectMeta(blockOref, updateMeta);
|
services.ObjectService.UpdateObjectMeta(blockOref, updateMeta);
|
||||||
}
|
}
|
||||||
@ -354,12 +346,39 @@ export class PreviewModel implements ViewModel {
|
|||||||
if (updateMeta == null) {
|
if (updateMeta == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
updateMeta.edit = false;
|
||||||
const blockOref = WOS.makeORef("block", this.blockId);
|
const blockOref = WOS.makeORef("block", this.blockId);
|
||||||
services.ObjectService.UpdateObjectMeta(blockOref, updateMeta);
|
services.ObjectService.UpdateObjectMeta(blockOref, updateMeta);
|
||||||
}
|
}
|
||||||
|
|
||||||
toggleCodeEditorReadOnly(readOnly: boolean) {
|
setFileContent() {
|
||||||
globalStore.set(this.ceReadOnly, readOnly);
|
const fullFileAtom = jotai.atom<Promise<FullFile>>(async (get) => {
|
||||||
|
const fileName = get(this.fileName);
|
||||||
|
if (fileName == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
const conn = get(this.connection) ?? "";
|
||||||
|
const file = await services.FileService.ReadFile(conn, fileName);
|
||||||
|
return file;
|
||||||
|
});
|
||||||
|
|
||||||
|
const fileContentAtom = jotai.atom<Promise<string>>(async (get) => {
|
||||||
|
const fullFile = await get(fullFileAtom);
|
||||||
|
return util.base64ToString(fullFile?.data64);
|
||||||
|
});
|
||||||
|
|
||||||
|
this.fullFile = fullFileAtom;
|
||||||
|
this.fileContent = fileContentAtom;
|
||||||
|
}
|
||||||
|
|
||||||
|
toggleEditMode(edit: boolean) {
|
||||||
|
if (!edit) {
|
||||||
|
this.setFileContent();
|
||||||
|
}
|
||||||
|
|
||||||
|
const blockMeta = globalStore.get(this.blockAtom)?.meta;
|
||||||
|
const blockOref = WOS.makeORef("block", this.blockId);
|
||||||
|
services.ObjectService.UpdateObjectMeta(blockOref, { ...blockMeta, edit });
|
||||||
}
|
}
|
||||||
|
|
||||||
async handleFileSave() {
|
async handleFileSave() {
|
||||||
@ -367,8 +386,10 @@ export class PreviewModel implements ViewModel {
|
|||||||
const newFileContent = globalStore.get(this.newFileContent);
|
const newFileContent = globalStore.get(this.newFileContent);
|
||||||
const conn = globalStore.get(this.connection) ?? "";
|
const conn = globalStore.get(this.connection) ?? "";
|
||||||
try {
|
try {
|
||||||
services.FileService.SaveFile(conn, fileName, util.stringToBase64(newFileContent));
|
if (newFileContent != null) {
|
||||||
globalStore.set(this.newFileContent, null);
|
services.FileService.SaveFile(conn, fileName, util.stringToBase64(newFileContent));
|
||||||
|
globalStore.set(this.newFileContent, null);
|
||||||
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Error saving file:", error);
|
console.error("Error saving file:", error);
|
||||||
}
|
}
|
||||||
@ -554,40 +575,27 @@ function CodeEditPreview({
|
|||||||
parentRef,
|
parentRef,
|
||||||
contentAtom,
|
contentAtom,
|
||||||
filename,
|
filename,
|
||||||
readonly,
|
|
||||||
isCeViewAtom,
|
|
||||||
newFileContentAtom,
|
newFileContentAtom,
|
||||||
model,
|
model,
|
||||||
}: {
|
}: {
|
||||||
parentRef: React.MutableRefObject<HTMLDivElement>;
|
parentRef: React.MutableRefObject<HTMLDivElement>;
|
||||||
contentAtom: jotai.Atom<Promise<string>>;
|
contentAtom: jotai.Atom<Promise<string>>;
|
||||||
filename: string;
|
filename: string;
|
||||||
readonly: boolean;
|
|
||||||
isCeViewAtom: jotai.PrimitiveAtom<boolean>;
|
|
||||||
newFileContentAtom: jotai.PrimitiveAtom<string>;
|
newFileContentAtom: jotai.PrimitiveAtom<string>;
|
||||||
model: PreviewModel;
|
model: PreviewModel;
|
||||||
}) {
|
}) {
|
||||||
const fileContent = jotai.useAtomValue(contentAtom);
|
const fileContent = jotai.useAtomValue(contentAtom);
|
||||||
const setIsCeView = jotai.useSetAtom(isCeViewAtom);
|
|
||||||
const setNewFileContent = jotai.useSetAtom(newFileContentAtom);
|
const setNewFileContent = jotai.useSetAtom(newFileContentAtom);
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
setIsCeView(true);
|
|
||||||
return () => {
|
|
||||||
setIsCeView(false);
|
|
||||||
};
|
|
||||||
}, [setIsCeView]);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<CodeEditor
|
<CodeEditor
|
||||||
parentRef={parentRef}
|
parentRef={parentRef}
|
||||||
readonly={readonly}
|
|
||||||
text={fileContent}
|
text={fileContent}
|
||||||
filename={filename}
|
filename={filename}
|
||||||
onChange={(text) => setNewFileContent(text)}
|
onChange={(text) => setNewFileContent(text)}
|
||||||
onSave={() => model.handleFileSave()}
|
onSave={() => model.handleFileSave()}
|
||||||
onCancel={() => model.toggleCodeEditorReadOnly(true)}
|
onCancel={() => model.toggleEditMode(true)}
|
||||||
onEdit={() => model.toggleCodeEditorReadOnly(false)}
|
onEdit={() => model.toggleEditMode(false)}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -656,20 +664,20 @@ function PreviewView({
|
|||||||
const fileMimeTypeAtom = model.fileMimeType;
|
const fileMimeTypeAtom = model.fileMimeType;
|
||||||
const fileContentAtom = model.fileContent;
|
const fileContentAtom = model.fileContent;
|
||||||
const newFileContentAtom = model.newFileContent;
|
const newFileContentAtom = model.newFileContent;
|
||||||
const ceReadOnlyAtom = model.ceReadOnly;
|
const editModeAtom = model.editMode;
|
||||||
const isCeViewAtom = model.isCeView;
|
const openFileModalAtom = model.openFileModal;
|
||||||
|
const canPreviewAtom = model.canPreview;
|
||||||
|
|
||||||
const mimeType = jotai.useAtomValue(fileMimeTypeAtom) || "";
|
const mimeType = jotai.useAtomValue(fileMimeTypeAtom) || "";
|
||||||
const fileName = jotai.useAtomValue(fileNameAtom);
|
const fileName = jotai.useAtomValue(fileNameAtom);
|
||||||
const fileInfo = jotai.useAtomValue(statFileAtom);
|
const fileInfo = jotai.useAtomValue(statFileAtom);
|
||||||
const ceReadOnly = jotai.useAtomValue(ceReadOnlyAtom);
|
|
||||||
const conn = jotai.useAtomValue(model.connection);
|
const conn = jotai.useAtomValue(model.connection);
|
||||||
const typeAhead = jotai.useAtomValue(atoms.typeAheadModalAtom);
|
const editMode = jotai.useAtomValue(editModeAtom);
|
||||||
|
const openFileModal = jotai.useAtomValue(openFileModalAtom);
|
||||||
let blockIcon = iconForFile(mimeType, fileName);
|
let blockIcon = iconForFile(mimeType, fileName);
|
||||||
|
|
||||||
const [filePath, setFilePath] = useState("");
|
const [filePath, setFilePath] = useState("");
|
||||||
const [openFileError, setOpenFileError] = useState("");
|
const [openFileError, setOpenFileError] = useState("");
|
||||||
const [openFileModal, setOpenFileModal] = useState(false);
|
|
||||||
|
|
||||||
// ensure consistent hook calls
|
// ensure consistent hook calls
|
||||||
const specializedView = (() => {
|
const specializedView = (() => {
|
||||||
@ -686,9 +694,11 @@ function PreviewView({
|
|||||||
view = <CenteredDiv>File Not Found{util.isBlank(fileName) ? null : JSON.stringify(fileName)}</CenteredDiv>;
|
view = <CenteredDiv>File Not Found{util.isBlank(fileName) ? null : JSON.stringify(fileName)}</CenteredDiv>;
|
||||||
} else if (fileInfo.size > MaxFileSize) {
|
} else if (fileInfo.size > MaxFileSize) {
|
||||||
view = <CenteredDiv>File Too Large to Preview</CenteredDiv>;
|
view = <CenteredDiv>File Too Large to Preview</CenteredDiv>;
|
||||||
} else if (mimeType === "text/markdown") {
|
} else if (mimeType === "text/markdown" && !editMode) {
|
||||||
|
globalStore.set(canPreviewAtom, true);
|
||||||
view = <MarkdownPreview contentAtom={fileContentAtom} />;
|
view = <MarkdownPreview contentAtom={fileContentAtom} />;
|
||||||
} else if (mimeType === "text/csv") {
|
} else if (mimeType === "text/csv" && !editMode) {
|
||||||
|
globalStore.set(canPreviewAtom, true);
|
||||||
if (fileInfo.size > MaxCSVSize) {
|
if (fileInfo.size > MaxCSVSize) {
|
||||||
view = <CenteredDiv>CSV File Too Large to Preview (1MB Max)</CenteredDiv>;
|
view = <CenteredDiv>CSV File Too Large to Preview (1MB Max)</CenteredDiv>;
|
||||||
} else {
|
} else {
|
||||||
@ -702,20 +712,26 @@ function PreviewView({
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
} else if (isTextFile(mimeType)) {
|
} else if (isTextFile(mimeType)) {
|
||||||
|
model.toggleEditMode(true);
|
||||||
view = (
|
view = (
|
||||||
<CodeEditPreview
|
<CodeEditPreview
|
||||||
readonly={ceReadOnly}
|
|
||||||
parentRef={contentRef}
|
parentRef={contentRef}
|
||||||
contentAtom={fileContentAtom}
|
contentAtom={fileContentAtom}
|
||||||
filename={fileName}
|
filename={fileName}
|
||||||
isCeViewAtom={isCeViewAtom}
|
|
||||||
newFileContentAtom={newFileContentAtom}
|
newFileContentAtom={newFileContentAtom}
|
||||||
model={model}
|
model={model}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
} else if (mimeType === "directory") {
|
} else if (mimeType === "directory") {
|
||||||
view = <DirectoryPreview fileNameAtom={fileNameAtom} model={model} />;
|
view = <DirectoryPreview fileNameAtom={fileNameAtom} model={model} />;
|
||||||
|
if (editMode) {
|
||||||
|
globalStore.set(openFileModalAtom, true);
|
||||||
|
} else {
|
||||||
|
globalStore.set(canPreviewAtom, false);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
|
globalStore.set(canPreviewAtom, false);
|
||||||
|
model.toggleEditMode(false);
|
||||||
view = (
|
view = (
|
||||||
<div className="view-preview">
|
<div className="view-preview">
|
||||||
<div>Preview ({mimeType})</div>
|
<div>Preview ({mimeType})</div>
|
||||||
@ -728,7 +744,7 @@ function PreviewView({
|
|||||||
const handleKeyDown = useCallback(
|
const handleKeyDown = useCallback(
|
||||||
(waveEvent: WaveKeyboardEvent): boolean => {
|
(waveEvent: WaveKeyboardEvent): boolean => {
|
||||||
const updateModalAndError = (isOpen, errorMsg = "") => {
|
const updateModalAndError = (isOpen, errorMsg = "") => {
|
||||||
setOpenFileModal(isOpen);
|
globalStore.set(openFileModalAtom, isOpen);
|
||||||
setOpenFileError(errorMsg);
|
setOpenFileError(errorMsg);
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -767,20 +783,21 @@ function PreviewView({
|
|||||||
});
|
});
|
||||||
return false;
|
return false;
|
||||||
},
|
},
|
||||||
[typeAhead, model, blockId, filePath, fileName]
|
[model, blockId, filePath, fileName]
|
||||||
);
|
);
|
||||||
|
|
||||||
const handleFileSuggestionSelect = (value) => {
|
const handleFileSuggestionSelect = (value) => {
|
||||||
globalStore.set(atoms.typeAheadModalAtom, {
|
globalStore.set(openFileModalAtom, false);
|
||||||
...(typeAhead as TypeAheadModalType),
|
|
||||||
[blockId]: false,
|
|
||||||
});
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleFileSuggestionChange = (value) => {
|
const handleFileSuggestionChange = (value) => {
|
||||||
setFilePath(value);
|
setFilePath(value);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const handleBackDropClick = () => {
|
||||||
|
globalStore.set(openFileModalAtom, false);
|
||||||
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const blockIconOverrideAtom = useBlockAtom<string>(blockId, "blockicon:override", () => {
|
const blockIconOverrideAtom = useBlockAtom<string>(blockId, "blockicon:override", () => {
|
||||||
return jotai.atom<string>(null);
|
return jotai.atom<string>(null);
|
||||||
@ -799,7 +816,7 @@ function PreviewView({
|
|||||||
onKeyDown={(e) => keyutil.keydownWrapper(handleKeyDown)(e)}
|
onKeyDown={(e) => keyutil.keydownWrapper(handleKeyDown)(e)}
|
||||||
onSelect={handleFileSuggestionSelect}
|
onSelect={handleFileSuggestionSelect}
|
||||||
onChange={handleFileSuggestionChange}
|
onChange={handleFileSuggestionChange}
|
||||||
onClickBackdrop={() => setOpenFileModal(false)}
|
onClickBackdrop={handleBackDropClick}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
<div
|
<div
|
||||||
|
2
frontend/types/custom.d.ts
vendored
2
frontend/types/custom.d.ts
vendored
@ -167,6 +167,8 @@ declare global {
|
|||||||
elemtype: "text";
|
elemtype: "text";
|
||||||
text: string;
|
text: string;
|
||||||
ref?: React.MutableRefObject<HTMLDivElement>;
|
ref?: React.MutableRefObject<HTMLDivElement>;
|
||||||
|
className?: string;
|
||||||
|
onClick?: () => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
type HeaderInput = {
|
type HeaderInput = {
|
||||||
|
Loading…
Reference in New Issue
Block a user