import { useEffect, useRef, useState } from "react"; import styles from "./TextEditor.module.css"; import { HeaderMenu } from "../_utils/header-menu/HeaderMenu"; import Markdown from "markdown-to-jsx"; import { CODE_FORMATS, DEFAULT_ZOOM, EXTENSION_TO_LANGUAGE, ZOOM_FACTOR } from "../../../config/apps/textEditor.config"; import AppsManager from "../../../features/apps/appsManager"; import { TITLE_SEPARATOR } from "../../../config/windows.config"; import { MarkdownLink } from "./overrides/MarkdownLink"; import { MarkdownImage } from "./overrides/MarkdownImage"; import { useWindowsManager } from "../../../hooks/windows/windowsManagerContext"; import SyntaxHighlighter from "react-syntax-highlighter"; import { useWindowedModal } from "../../../hooks/modals/windowedModal"; import { DEFAULT_FILE_SELECTOR_SIZE } from "../../../config/modals.config"; import { FileSelector } from "../../modals/file-selector/FileSelector"; import { SELECTOR_MODE } from "../../../config/apps/fileExplorer.config"; import { VirtualFile } from "../../../features/virtual-drive/file/virtualFile"; import { WindowProps } from "../../windows/WindowView"; const OVERRIDES = { a: MarkdownLink, img: MarkdownImage, }; interface TextEditorProps extends WindowProps { file?: VirtualFile; } export function TextEditor({ file, setTitle, setIconUrl, close, mode, app, modalsManager }: TextEditorProps) { const ref = useRef(); const windowsManager = useWindowsManager(); const [currentFile, setCurrentFile] = useState(file); const [currentMode, setCurrentMode] = useState(mode); const [content, setContent] = useState(file?.content ?? ""); const [unsavedChanges, setUnsavedChanges] = useState(file == null); const [zoom, setZoom] = useState(DEFAULT_ZOOM); const { openWindowedModal } = useWindowedModal(); useEffect(() => { void (async () => { let newContent = ""; // Load file if (currentFile) { if (currentFile.content) { newContent = currentFile.content; } else if (currentFile.source) { await fetch(currentFile.source).then((response) => response.text() ).then((response) => { newContent = response; }); } const iconUrl = currentFile.getIconUrl(); if (iconUrl) setIconUrl(iconUrl); } else { setIconUrl(AppsManager.getAppIconUrl(app.id)); } setContent(newContent); if (ref.current) { (ref.current as HTMLElement).scrollTo(0, 0); } })(); }, [app.id, currentFile, setIconUrl]); useEffect(() => { // Update title let label = currentFile?.id ?? "Untitled"; if (unsavedChanges) label += "*"; if (currentMode === "view") label += " (preview)"; setTitle(`${label} ${TITLE_SEPARATOR} ${app.name}`); }, [currentFile, setTitle, unsavedChanges, currentMode, app.name]); const newText = () => { setCurrentFile(null); setCurrentMode("edit"); setUnsavedChanges(true); }; const saveTextAs = () => { onChange({ target: { value: content } }); }; const saveText = () => { if (currentFile == null) return saveTextAs(); currentFile.setContent(content); onChange({ target: { value: content } }); }; const onChange = (event: Event | { target: { value: string } }) => { const value = (event.target as HTMLInputElement).value; if (currentFile != null) { setUnsavedChanges(currentFile.content !== value); } else { setUnsavedChanges(true); } return setContent(value); }; const overrides = {}; for (const [key, value] of Object.entries(OVERRIDES)) { overrides[key] = { component: value, props: { modalsManager, setCurrentFile, currentFile, app, windowsManager } }; } return (
}{content}