From d2224966eedcdad826dc70f0f402e44a65dd1c16 Mon Sep 17 00:00:00 2001 From: masaki-fukumura Date: Wed, 19 Feb 2025 01:26:48 +0900 Subject: [PATCH 1/2] Separated inerfaces --- src/App1.tsx | 318 ++++++++++++++++++++++++++++++++++++++++++++++ src/App2.tsx | 310 ++++++++++++++++++++++++++++++++++++++++++++ src/AppRoutes.tsx | 21 +++ 3 files changed, 649 insertions(+) create mode 100644 src/App1.tsx create mode 100644 src/App2.tsx create mode 100644 src/AppRoutes.tsx diff --git a/src/App1.tsx b/src/App1.tsx new file mode 100644 index 0000000..e98d5cb --- /dev/null +++ b/src/App1.tsx @@ -0,0 +1,318 @@ +//マークダウンの編集 +import { useEffect, useState, SetStateAction } from "react"; +import parse, { Element, HTMLReactParserOptions } from "html-react-parser"; +// import Markdown from "react-markdown"; +// import rehypeKatex from "rehype-katex"; +// import remarkMath from "remark-math"; +import Tippy from "@tippyjs/react"; +// import markdownLink from "/hoge.md?url"; +import { ExtractDefinitions } from "./MDToDefinitions"; +import { MDToHTML } from "./MDToHTML"; +import { replaceExternalSyntax } from "./external-syntax"; +//import ExtractPDF from "./extractPDF"; +//import pdfFile from "/chibutsu_nyumon.pdf"; +import Textarea from "@mui/joy/Textarea"; +import { Button } from "@mui/material"; +import index.css + +import "katex/dist/katex.min.css"; +import "tippy.js/dist/tippy.css"; +import UploadMarkdown from "./uploadMarkdown"; +import UploadImage from "./uploadImage"; + +//import styled from "@emotion/styled"; + +type positionInfo = null | { top: number; left: number }; + +export default function App1() { + const [markdown, setMarkdown] = useState(""); + const [html, setHTML] = useState(""); + const [dict, setDict] = useState(new Map()); + const opts = { + prefix: "!define", + suffix: "!enddef", + }; + + // ドラッグして直接参照できる機能の部分 + const [inputPosition, setInputPosition] = useState(null); // ドラッグされた位置 + const [inputValue, setInputValue] = useState(""); + const [isTextAreaFocused, setIsTextAreaFocused] = useState(false); + const [visualize, setVisualize] = useState(true); // テキストエリアを表示にするか非表示にするか + const [fileContent, setFileContent] = useState(""); + const [imageData, setImageData] = useState(""); + + // get markdown + // useEffect(() => { + // fetch(markdownLink) + // .then((res) => res.text()) + // .then((t) => setMarkdown(t)) + // .catch((err) => console.error("Error fetching Hoge.md:", err)); + // }, []); + + useEffect(() => { + setMarkdown(fileContent); + }, [fileContent]); + + useEffect(() => { + localStorage.setItem("item", markdown); + }, [markdown]); // markdownの内容が変わるたびにlocalStorageに保存。 + + // use markdown (separation is necessary because it's async) + useEffect(() => void insideUseEffect(), [markdown]); + async function insideUseEffect() { + // prepare dictionary + let d = ExtractDefinitions(markdown, opts.prefix, opts.suffix); + const newd = new Map(); + const promises: Promise>[] = []; + d.forEach((v, k) => { + let md = replaceExternalSyntax(v); + md = md.replaceAll(opts.prefix, "##").replaceAll(opts.suffix, ""); + //const p = MDToHTML(md).then((newv) => newd.set(k, newv)); + //promises.push(p); + }); + Promise.all(promises).then(() => setDict(newd)); + + // prepare HTML + var md; + try { + md = replaceExternalSyntax(markdown.replace(/!define[\s\S]*$/m, "")); // !define以下をすべて取り去る。 + } catch (e: any) { + md = e.toString(); + } + MDToHTML(md.replaceAll(opts.prefix, "##").replaceAll(opts.suffix, "")) + .then((h) => setHTML(h)) + .catch(() => console.log("MDToHTML failed")); + } + + // ドラッグして直接参照できる機能の部分 + useEffect(() => { + const handleSelectionChange = () => { + const selection = document.getSelection(); + if (selection && selection.rangeCount > 0 && !isTextAreaFocused) { + // textareaがFocusされていないときのみ、selectionを発令する。 + const range = selection.getRangeAt(0); // Range { commonAncestorContainer: #text, startContainer: #text, startOffset: 8, endContainer: #text, endOffset: 23, collapsed: true } + // 左から8文字目から23文字目であることを指している。 + const rect = range.getBoundingClientRect(); // DOMRect { x: 209.56666564941406, y: 167.25, width: 130.38333129882812, height: 29, top: 167.25, right: 339.9499969482422, bottom: 196.25, left: 209.56666564941406 } + // 位置情報の取得 + + // setSelectedText(selection.toString()); // ...unused + + if (selection.toString()) { + // console.log(selection.toString()) 選択した範囲の文字列。 + setInputPosition({ + top: rect.bottom + window.scrollY, + left: rect.left + window.scrollX, + }); + setInputValue("!define " + selection.toString()); + } else { + setInputPosition(null); + } + } + }; + document.addEventListener("selectionchange", handleSelectionChange); + return () => { + document.removeEventListener("selectionchange", handleSelectionChange); + }; + }, [isTextAreaFocused]); + + const handleInputChange = (event: { + target: { value: SetStateAction }; + }) => { + setInputValue(event.target.value); + // console.log(inputValue) 入力された内容がここに入る。 + }; + + const handleImageChange = (content: string) => { + setImageData(content); + }; + + const handleTextAreaFocus = () => { + setIsTextAreaFocused(true); + }; + + const handleTextAreaBlur = () => { + setIsTextAreaFocused(false); + }; + + // テキストファイルを保存する + const saveFile = () => { + const blob = new Blob([markdown], { + type: ".md, text/markdown", + }); + const link = document.createElement("a"); + link.href = URL.createObjectURL(blob); + link.download = localStorage.getItem("filename") ?? "hoge.md"; // localStorage上に保存したファイル名を使う。 + link.click(); + }; + + return ( + <> +
+
+ + +
+
+ +
+
+ {visualize == false && ( + <> +
+ +
+
+ +
+ + )} +
{imageData}
+ {visualize == true && ( + <> +
+ +
+
+
+ +
+