mirror of
https://github.com/cloudreve/frontend.git
synced 2025-12-25 19:52:48 +00:00
feat(file apps): add excalidraw
This commit is contained in:
parent
93d616e742
commit
44a696a2e7
|
|
@ -13,6 +13,7 @@
|
|||
"dependencies": {
|
||||
"@emotion/react": "^11.11.1",
|
||||
"@emotion/styled": "^11.11.0",
|
||||
"@excalidraw/excalidraw": "^0.18.0",
|
||||
"@fontsource/roboto": "^5.0.8",
|
||||
"@giscus/react": "^3.1.0",
|
||||
"@marsidev/react-turnstile": "^1.1.0",
|
||||
|
|
|
|||
|
|
@ -0,0 +1,7 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" width="1000" height="1000">
|
||||
<rect width="1000" height="1000" rx="200" ry="200" fill="#fff" />
|
||||
<svg viewBox="0 0 107 101" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:2">
|
||||
<path style="fill:none" d="M24 17h121v121H24z" transform="matrix(.8843 0 0 .83471 -21.223 -14.19)" />
|
||||
<path d="M119.81 105.98a.549.549 0 0 0-.53-.12c-4.19-6.19-9.52-12.06-14.68-17.73l-.85-.93c0-.11-.05-.21-.12-.3a.548.548 0 0 0-.34-.2l-.17-.18-.12-.09c-.15-.32-.53-.56-.95-.35-1.58.81-3 1.97-4.4 3.04-1.87 1.43-3.7 2.92-5.42 4.52-.7.65-1.39 1.33-1.97 2.09-.28.37-.07.72.27.87-1.22 1.2-2.45 2.45-3.68 3.74-.11.12-.17.28-.16.44.01.16.09.31.22.41l2.16 1.65s.01.03.03.04c3.09 3.05 8.51 7.28 14.25 11.76.85.67 1.71 1.34 2.57 2.01.39.47.76.94 1.12 1.4.19.25.55.3.8.11.13.1.26.21.39.31a.57.57 0 0 0 .8-.1c.07-.09.1-.2.11-.31.04 0 .07.03.1.03.15 0 .31-.06.42-.18l10.18-11.12a.56.56 0 0 0-.04-.8l.01-.01Zm-29.23-3.85c.07.09.14.17.21.25 1.16.98 2.4 2.04 3.66 3.12l-5.12-3.91s-.32-.22-.52-.36c-.11-.08-.21-.16-.31-.24l-.38-.32s.07-.07.1-.11l.35-.35c1.72-1.74 4.67-4.64 6.19-6.06-1.61 1.62-4.87 6.37-4.17 7.98h-.01Zm17.53 13.81-4.22-3.22c-1.65-1.71-3.43-3.4-5.24-5.03 2.28 1.76 4.23 3.25 4.52 3.51 2.21 1.97 2.11 1.61 3.63 2.91l1.83 1.33c-.18.16-.36.33-.53.49l.01.01Zm1.06.81-.08-.06c.16-.13.33-.25.49-.38l-.4.44h-.01ZM42.24 51.45c.14.72.27 1.43.4 2.11.69 3.7 1.33 7.03 2.55 9.56l.48 1.92c.19.73.46 1.64.71 1.83 2.85 2.52 7.22 6.28 11.89 9.82.21.16.5.15.7-.01.01.02.03.03.04.04.11.1.24.15.38.15.16 0 .31-.06.42-.19 5.98-6.65 10.43-12.12 13.6-16.7.2-.25.3-.54.29-.84.2-.24.41-.48.6-.68a.558.558 0 0 0-.1-.86.578.578 0 0 0-.17-.36c-1.39-1.34-2.42-2.31-3.46-3.28-1.84-1.72-3.74-3.5-7.77-7.51-.02-.02-.05-.04-.07-.06a.555.555 0 0 0-.22-.14c-1.11-.39-3.39-.78-6.26-1.28-4.22-.72-10-1.72-15.2-3.27h-.04v-.01s-.02 0-.03.02h-.01l.04-.02s-.31.01-.37.04c-.08.04-.14.09-.19.15-.05.06-.09.12-.47.2-.38.08.08 0 .11 0h-.11v.03c.07.34.05.58.16.97-.02.1.21 1.02.24 1.11l1.83 7.26h.03Zm30.95 6.54s-.03.04-.04.05l-.64-.71c.22.21.44.42.68.66Zm-7.09 9.39s-.07.08-.1.12l-.02-.02c.04-.03.08-.07.13-.1h-.01Zm-7.07 8.47Zm3.02-28.57c.35.35 1.74 1.65 2.06 1.97-1.45-.66-5.06-2.34-6.74-2.88 1.65.29 3.93.66 4.68.91Zm-19.18-2.77c.84 1.44 1.5 6.49 2.16 11.4-.37-1.58-.69-3.12-.99-4.6-.52-2.56-1-4.85-1.67-6.88.14.01.31.03.49.05 0 .01 0 .02.02.03h-.01Zm-.29-1.21c-.23-.02-.44-.04-.62-.05-.02-.04-.03-.08-.04-.12l.66.18v-.01Zm-2.22.45v-.02.02ZM118.9 42.57c.04-.23-1.1-1.24-.74-1.26.85-.04.86-1.35 0-1.31-1.13.06-2.27.32-3.37.53-1.98.37-3.95.78-5.92 1.21-4.39.94-8.77 1.93-13.1 3.11-1.36.37-2.86.7-4.11 1.36-.42.22-.4.67-.17.95-.09.05-.18.08-.28.09-.37.07-.74.13-1.11.19a.566.566 0 0 0-.39.86c-2.32 3.1-4.96 6.44-7.82 9.95-2.81 3.21-5.73 6.63-8.72 10.14-9.41 11.06-20.08 23.6-31.9 34.64-.23.21-.24.57-.03.8.05.06.12.1.19.13-.16.15-.32.3-.48.44-.1.09-.14.2-.16.32-.08.08-.16.17-.23.25-.21.23-.2.59.03.8.23.21.59.2.8-.03.04-.04.08-.09.12-.13a.84.84 0 0 1 1.22 0c.69.74 1.34 1.44 1.95 2.09l-1.38-1.15a.57.57 0 0 0-.8.07c-.2.24-.17.6.07.8l14.82 12.43c.11.09.24.13.37.13.15 0 .29-.06.4-.17l.36-.36a.56.56 0 0 0 .63-.12c20.09-20.18 36.27-35.43 54.8-49.06.17-.12.25-.32.23-.51a.57.57 0 0 0 .48-.39c3.42-10.46 4.08-19.72 4.28-24.27 0-.03.01-.05.02-.07.02-.05.03-.1.04-.14.03-.11.05-.19.05-.19.26-.78.17-1.53-.15-2.15v.02ZM82.98 58.94c.9-1.03 1.79-2.04 2.67-3.02-5.76 7.58-15.3 19.26-28.81 33.14 9.2-10.18 18.47-20.73 26.14-30.12Zm-32.55 52.81-.03-.03c.11.02.19.04.2.04a.47.47 0 0 0-.17 0v-.01Zm6.9 6.42-.05-.04.03-.03c.02 0 .03.02.04.02 0 .02-.02.03-.03.05h.01Zm8.36-7.21 1.38-1.44c.01.01.02.03.03.05-.47.46-.94.93-1.42 1.39h.01Zm2.24-2.21c.26-.3.56-.65.87-1.02.01-.01.02-.03.04-.04 3.29-3.39 6.68-6.82 10.18-10.25.02-.02.05-.04.07-.06.86-.66 1.82-1.39 2.72-2.08-4.52 4.32-9.11 8.78-13.88 13.46v-.01Zm21.65-55.88c-1.86 2.42-3.9 5.56-5.63 8.07-5.46 7.91-23.04 27.28-23.43 27.65-2.71 2.62-10.88 10.46-16.09 15.37-.14.13-.25.24-.34.35a.794.794 0 0 1 .03-1.13c24.82-23.4 39.88-42.89 46-51.38-.13.33-.24.69-.55 1.09l.01-.02Zm16.51 7.1-.01.02c0-.02-.02-.07.01-.02Zm-.91-5.13Zm-5.89 9.45c-2.26-1.31-3.32-3.27-2.71-5.25l.19-.66c.08-.19.17-.38.28-.57.59-.98 1.49-1.85 2.52-2.36.05-.02.1-.03.15-.04a.795.795 0 0 1-.04-.43c.05-.31.25-.58.66-.58.67 0 2.75.62 3.54 1.3.24.19.47.4.68.63.3.35.74.92.96 1.33.13.06.23.62.38.91.14.46.2.93.18 1.4 0 .02 0 .02.01.03-.03.07 0 .37-.04.4-.1.72-.36 1.43-.75 2.05-.04.05-.07.11-.11.16 0 .01-.02.02-.03.04-.3.43-.65.83-1.08 1.13-1.26.89-2.73 1.16-4.2.79a6.33 6.33 0 0 1-.57-.25l-.02-.03Zm16.27-1.63c-.49 2.05-1.09 4.19-1.8 6.38-.03.08-.03.16-.03.23-.1.01-.19.05-.27.11-4.44 3.26-8.73 6.62-12.98 10.11 3.67-3.32 7.39-6.62 11.23-9.95a6.409 6.409 0 0 0 2.11-3.74l.56-3.37.03-.1c.25-.71 1.34-.4 1.17.33h-.02Z" style="fill:#6965db;fill-rule:nonzero" transform="matrix(1 0 0 1 -26.41 -29.49)" />
|
||||
</svg>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 4.8 KiB |
|
|
@ -20,6 +20,7 @@ import VideoViewer from "../../Viewers/Video/VideoViewer.tsx";
|
|||
import PdfViewer from "../../Viewers/PdfViewer.tsx";
|
||||
import CustomViewer from "../../Viewers/CustomViewer.tsx";
|
||||
import EpubViewer from "../../Viewers/EpubViewer/EpubViewer.tsx";
|
||||
import ExcalidrawViewer from "../../Viewers/Excalidraw/ExcalidrawViewer.tsx";
|
||||
import CreateNew from "./CreateNew.tsx";
|
||||
import { useAppSelector } from "../../../redux/hooks.ts";
|
||||
import CreateArchive from "./CreateArchive.tsx";
|
||||
|
|
@ -37,6 +38,7 @@ const Dialogs = () => {
|
|||
const showAdvancedSearch = useAppSelector((state) => state.globalState.advanceSearchOpen);
|
||||
const showListViewColumnSetting = useAppSelector((state) => state.globalState.listViewColumnSettingDialogOpen);
|
||||
const directLink = useAppSelector((state) => state.globalState.directLinkDialogOpen);
|
||||
const excalidrawViewer = useAppSelector((state) => state.globalState.excalidrawViewer);
|
||||
|
||||
return (
|
||||
<>
|
||||
|
|
@ -69,6 +71,7 @@ const Dialogs = () => {
|
|||
{showAdvancedSearch != undefined && <AdvanceSearch />}
|
||||
{showListViewColumnSetting != undefined && <ColumnSetting />}
|
||||
{directLink != undefined && <DirectLinks />}
|
||||
{excalidrawViewer != undefined && <ExcalidrawViewer />}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -0,0 +1,78 @@
|
|||
import { Excalidraw as ExcalidrawComponent } from "@excalidraw/excalidraw";
|
||||
import { OrderedExcalidrawElement } from "@excalidraw/excalidraw/element/types";
|
||||
import "@excalidraw/excalidraw/index.css";
|
||||
import { AppState, BinaryFiles } from "@excalidraw/excalidraw/types";
|
||||
import { Box } from "@mui/material";
|
||||
import { useMemo } from "react";
|
||||
import "./excalidraw.css";
|
||||
|
||||
export interface ExcalidrawProps {
|
||||
value: string;
|
||||
initialValue: string;
|
||||
darkMode?: boolean;
|
||||
onChange: (value: string) => void;
|
||||
readOnly?: boolean;
|
||||
language?: string;
|
||||
onSaveShortcut?: () => void;
|
||||
}
|
||||
|
||||
interface ExcalidrawState {
|
||||
elements: OrderedExcalidrawElement[];
|
||||
appState: AppState;
|
||||
files: BinaryFiles;
|
||||
type: string;
|
||||
version: number;
|
||||
source: string;
|
||||
}
|
||||
|
||||
const serializeExcalidrawState = (elements: readonly OrderedExcalidrawElement[], appState: any, file: BinaryFiles) => {
|
||||
if (!Array.isArray(appState.collaborators)) {
|
||||
appState.collaborators = [];
|
||||
}
|
||||
return JSON.stringify({
|
||||
type: "excalidraw",
|
||||
version: 2,
|
||||
source: window.location.origin,
|
||||
elements,
|
||||
appState,
|
||||
files: file,
|
||||
});
|
||||
};
|
||||
|
||||
const Excalidraw = (props: ExcalidrawProps) => {
|
||||
const initialValue = useMemo(() => {
|
||||
try {
|
||||
return JSON.parse(props.initialValue) as ExcalidrawState;
|
||||
} catch (error) {
|
||||
return null;
|
||||
}
|
||||
}, [props.initialValue]);
|
||||
return (
|
||||
<Box
|
||||
sx={{
|
||||
width: "100%",
|
||||
height: "100%",
|
||||
minHeight: "calc(100vh - 200px)",
|
||||
}}
|
||||
onKeyDown={(e) => {
|
||||
if ((e.ctrlKey || e.metaKey) && e.key === "s") {
|
||||
e.preventDefault();
|
||||
props.onSaveShortcut?.();
|
||||
}
|
||||
}}
|
||||
>
|
||||
<ExcalidrawComponent
|
||||
isCollaborating={false}
|
||||
viewModeEnabled={props.readOnly}
|
||||
onChange={(elements, state, file) => {
|
||||
props.onChange(serializeExcalidrawState(elements, state, file));
|
||||
}}
|
||||
initialData={initialValue}
|
||||
langCode={props.language}
|
||||
theme={props.darkMode ? "dark" : "light"}
|
||||
/>
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
||||
export default Excalidraw;
|
||||
|
|
@ -0,0 +1,169 @@
|
|||
import { LoadingButton } from "@mui/lab";
|
||||
import { Box, Button, ButtonGroup, ListItemText, Menu, useTheme } from "@mui/material";
|
||||
import React, { lazy, Suspense, useCallback, useEffect, useRef, useState } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import i18next from "../../../i18n.ts";
|
||||
import { closeExcalidrawViewer } from "../../../redux/globalStateSlice.ts";
|
||||
import { useAppDispatch, useAppSelector } from "../../../redux/hooks.ts";
|
||||
import { getEntityContent } from "../../../redux/thunks/file.ts";
|
||||
import { saveExcalidraw } from "../../../redux/thunks/viewer.ts";
|
||||
import { SquareMenuItem } from "../../FileManager/ContextMenu/ContextMenu.tsx";
|
||||
import useActionDisplayOpt, { canUpdate } from "../../FileManager/ContextMenu/useActionDisplayOpt.ts";
|
||||
import CaretDown from "../../Icons/CaretDown.tsx";
|
||||
import ViewerDialog, { ViewerLoading } from "../ViewerDialog.tsx";
|
||||
|
||||
const Excalidraw = lazy(() => import("./Excalidraw.tsx"));
|
||||
|
||||
const ExcalidrawViewer = () => {
|
||||
const { t } = useTranslation();
|
||||
const dispatch = useAppDispatch();
|
||||
const theme = useTheme();
|
||||
const viewerState = useAppSelector((state) => state.globalState.excalidrawViewer);
|
||||
|
||||
const displayOpt = useActionDisplayOpt(viewerState?.file ? [viewerState?.file] : []);
|
||||
const supportUpdate = canUpdate(displayOpt);
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [value, setValue] = useState("");
|
||||
const [changedValue, setChangedValue] = useState("");
|
||||
const [loaded, setLoaded] = useState(false);
|
||||
const [saved, setSaved] = useState(true);
|
||||
const [anchorEl, setAnchorEl] = useState<null | HTMLElement>(null);
|
||||
const saveFunction = useRef(() => {});
|
||||
|
||||
const loadContent = useCallback(() => {
|
||||
if (!viewerState || !viewerState.open) {
|
||||
return;
|
||||
}
|
||||
|
||||
setLoaded(false);
|
||||
dispatch(getEntityContent(viewerState.file, viewerState.version))
|
||||
.then((res) => {
|
||||
const content = new TextDecoder().decode(res);
|
||||
setValue(content);
|
||||
setChangedValue(content);
|
||||
setLoaded(true);
|
||||
})
|
||||
.catch(() => {
|
||||
onClose();
|
||||
});
|
||||
}, [viewerState]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!viewerState || !viewerState.open) {
|
||||
return;
|
||||
}
|
||||
|
||||
setSaved(true);
|
||||
loadContent();
|
||||
}, [viewerState?.open]);
|
||||
|
||||
const onClose = useCallback(() => {
|
||||
dispatch(closeExcalidrawViewer());
|
||||
}, [dispatch]);
|
||||
|
||||
const openMore = useCallback(
|
||||
(e: React.MouseEvent<any>) => {
|
||||
setAnchorEl(e.currentTarget);
|
||||
},
|
||||
[dispatch],
|
||||
);
|
||||
|
||||
const onSave = useCallback(
|
||||
(saveAs?: boolean) => {
|
||||
if (!viewerState?.file) {
|
||||
return;
|
||||
}
|
||||
|
||||
setLoading(true);
|
||||
dispatch(saveExcalidraw(changedValue, viewerState.file, viewerState.version, saveAs))
|
||||
.then(() => {
|
||||
setSaved(true);
|
||||
})
|
||||
.finally(() => {
|
||||
setLoading(false);
|
||||
});
|
||||
},
|
||||
[changedValue, viewerState],
|
||||
);
|
||||
|
||||
const onChange = useCallback((v: string) => {
|
||||
setChangedValue(v);
|
||||
setSaved(false);
|
||||
}, []);
|
||||
|
||||
const onSaveShortcut = useCallback(() => {
|
||||
if (!saved && supportUpdate) {
|
||||
onSave(false);
|
||||
}
|
||||
}, [saved, supportUpdate, onSave]);
|
||||
|
||||
useEffect(() => {
|
||||
saveFunction.current = () => {
|
||||
if (!saved && supportUpdate) {
|
||||
onSave(false);
|
||||
}
|
||||
};
|
||||
}, [saved, supportUpdate, onSave]);
|
||||
|
||||
return (
|
||||
<ViewerDialog
|
||||
file={viewerState?.file}
|
||||
loading={loading}
|
||||
readOnly={!supportUpdate}
|
||||
actions={
|
||||
<Box sx={{ display: "flex", gap: 1 }}>
|
||||
{supportUpdate && (
|
||||
<ButtonGroup disabled={loading || !loaded || saved} disableElevation variant="contained">
|
||||
<LoadingButton loading={loading} variant={"contained"} onClick={() => onSave(false)}>
|
||||
<span>{t("fileManager.save")}</span>
|
||||
</LoadingButton>
|
||||
<Button size="small" onClick={openMore}>
|
||||
<CaretDown sx={{ fontSize: "12px!important" }} />
|
||||
</Button>
|
||||
</ButtonGroup>
|
||||
)}
|
||||
</Box>
|
||||
}
|
||||
fullScreenToggle
|
||||
dialogProps={{
|
||||
open: !!(viewerState && viewerState.open),
|
||||
onClose: onClose,
|
||||
fullWidth: true,
|
||||
maxWidth: "lg",
|
||||
}}
|
||||
>
|
||||
<Menu
|
||||
anchorEl={anchorEl}
|
||||
open={Boolean(anchorEl)}
|
||||
onClose={() => setAnchorEl(null)}
|
||||
slotProps={{
|
||||
paper: {
|
||||
sx: {
|
||||
minWidth: 150,
|
||||
},
|
||||
},
|
||||
}}
|
||||
>
|
||||
<SquareMenuItem onClick={() => onSave(true)} dense>
|
||||
<ListItemText>{t("modals.saveAs")}</ListItemText>
|
||||
</SquareMenuItem>
|
||||
</Menu>
|
||||
{!loaded && <ViewerLoading />}
|
||||
{loaded && (
|
||||
<Suspense fallback={<ViewerLoading />}>
|
||||
<Excalidraw
|
||||
language={i18next.language}
|
||||
value={changedValue}
|
||||
initialValue={value}
|
||||
readOnly={!supportUpdate}
|
||||
darkMode={theme.palette.mode === "dark"}
|
||||
onChange={(v) => onChange(v as string)}
|
||||
onSaveShortcut={onSaveShortcut}
|
||||
/>
|
||||
</Suspense>
|
||||
)}
|
||||
</ViewerDialog>
|
||||
);
|
||||
};
|
||||
|
||||
export default ExcalidrawViewer;
|
||||
|
|
@ -0,0 +1,4 @@
|
|||
.excalidraw {
|
||||
min-height: calc(100vh - 200px);
|
||||
--zIndex-modal: 1400;
|
||||
}
|
||||
|
|
@ -215,6 +215,7 @@ export interface GlobalStateSlice {
|
|||
customViewer?: CustomViewerState;
|
||||
epubViewer?: GeneralViewerState;
|
||||
musicPlayer?: MusicPlayerState;
|
||||
excalidrawViewer?: GeneralViewerState;
|
||||
|
||||
// Viewer selector
|
||||
viewerSelector?: ViewerSelectorState;
|
||||
|
|
@ -328,6 +329,7 @@ export const globalStateSlice = createSlice({
|
|||
state.pdfViewer = undefined;
|
||||
state.customViewer = undefined;
|
||||
state.epubViewer = undefined;
|
||||
state.excalidrawViewer = undefined;
|
||||
},
|
||||
setExtractArchiveDialog: (state, action: PayloadAction<{ open: boolean; file?: FileResponse }>) => {
|
||||
state.extractArchiveDialogOpen = action.payload.open;
|
||||
|
|
@ -466,6 +468,12 @@ export const globalStateSlice = createSlice({
|
|||
closeMarkdownViewer: (state) => {
|
||||
state.markdownViewer && (state.markdownViewer.open = false);
|
||||
},
|
||||
setExcalidrawViewer: (state, action: PayloadAction<GeneralViewerState>) => {
|
||||
state.excalidrawViewer = action.payload;
|
||||
},
|
||||
closeExcalidrawViewer: (state) => {
|
||||
state.excalidrawViewer && (state.excalidrawViewer.open = false);
|
||||
},
|
||||
addShareInfo: (state, action: PayloadAction<{ info: Share; id: string }>) => {
|
||||
state.shareInfo[action.payload.id] = action.payload.info;
|
||||
},
|
||||
|
|
@ -785,4 +793,6 @@ export const {
|
|||
resetDialogs,
|
||||
setPolicyOptionCache,
|
||||
setSearchPopup,
|
||||
setExcalidrawViewer,
|
||||
closeExcalidrawViewer,
|
||||
} = globalStateSlice.actions;
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@ import {
|
|||
setCustomViewer,
|
||||
setDrawIOViewer,
|
||||
setEpubViewer,
|
||||
setExcalidrawViewer,
|
||||
setImageEditor,
|
||||
setImageViewer,
|
||||
setMarkdownViewer,
|
||||
|
|
@ -48,6 +49,7 @@ export const builtInViewers = {
|
|||
pdf: "pdf",
|
||||
epub: "epub",
|
||||
music: "music",
|
||||
excalidraw: "excalidraw",
|
||||
};
|
||||
|
||||
export function openViewers(
|
||||
|
|
@ -180,6 +182,15 @@ export function openViewer(file: FileResponse, viewer: Viewer, size: number, pre
|
|||
}),
|
||||
);
|
||||
break;
|
||||
case builtInViewers.excalidraw:
|
||||
dispatch(
|
||||
setExcalidrawViewer({
|
||||
open: true,
|
||||
file,
|
||||
version: preferredVersion ?? primaryEntity,
|
||||
}),
|
||||
);
|
||||
break;
|
||||
case builtInViewers.video:
|
||||
dispatch(
|
||||
setVideoViewer({
|
||||
|
|
@ -580,6 +591,36 @@ export function saveDrawIO(
|
|||
};
|
||||
}
|
||||
|
||||
export function saveExcalidraw(
|
||||
data: string,
|
||||
file: FileResponse,
|
||||
version?: string,
|
||||
saveAsNew?: boolean,
|
||||
): AppThunk<Promise<void>> {
|
||||
return async (dispatch, getState) => {
|
||||
const isLinkedFile = file.metadata?.[Metadata.share_redirect] ?? false;
|
||||
if (!version && !isLinkedFile) {
|
||||
version = file.primary_entity;
|
||||
}
|
||||
const savedFile = await dispatch(saveFile(getFileLinkedUri(file), data, version, saveAsNew));
|
||||
|
||||
if (savedFile) {
|
||||
const {
|
||||
globalState: { excalidrawViewer },
|
||||
} = getState();
|
||||
if (excalidrawViewer) {
|
||||
dispatch(
|
||||
setExcalidrawViewer({
|
||||
...excalidrawViewer,
|
||||
file: savedFile,
|
||||
version: savedFile.primary_entity,
|
||||
}),
|
||||
);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
export function saveMarkdown(
|
||||
data: string,
|
||||
file: FileResponse,
|
||||
|
|
|
|||
|
|
@ -70,6 +70,17 @@ export default defineConfig({
|
|||
if (id.includes("@codemirror")) {
|
||||
return "codemirror";
|
||||
}
|
||||
if (
|
||||
id.toLocaleLowerCase().includes("excalidraw") ||
|
||||
id.includes("browser-fs-access") ||
|
||||
id.includes("image-blob-reduce") ||
|
||||
id.includes("pica")
|
||||
) {
|
||||
return "excalidraw";
|
||||
}
|
||||
if (id.includes("mermaid") || id.includes("katex")) {
|
||||
return "mermaid";
|
||||
}
|
||||
},
|
||||
},
|
||||
},
|
||||
|
|
|
|||
Loading…
Reference in New Issue