From ab6f53f6c07a734bd969eb6f68c2331b07bd75fb Mon Sep 17 00:00:00 2001 From: Thijs Zijdel Date: Sat, 10 Aug 2024 13:41:31 +0200 Subject: [PATCH 1/3] Added Feat/Readme generator --- package.json | 7 +- src/App.tsx | 2 + src/Features/markdown/Editor.tsx | 64 ++++++ src/Features/markdown/Markdown.tsx | 80 ++------ src/Features/markdown/Readme.tsx | 112 +++++++++++ src/Features/markdown/constants.ts | 41 ++++ src/Features/markdown/templates.ts | 307 +++++++++++++++++++++++++++++ src/Features/openai/OpenAI.tsx | 175 ++++++++++++++++ src/Layout/Navbar/items.tsx | 47 +++-- src/hooks/index.ts | 1 + src/hooks/useFile.tsx | 31 +++ yarn.lock | 119 ++++++++++- 12 files changed, 901 insertions(+), 85 deletions(-) create mode 100644 src/Features/markdown/Editor.tsx create mode 100644 src/Features/markdown/Readme.tsx create mode 100644 src/Features/markdown/constants.ts create mode 100644 src/Features/markdown/templates.ts create mode 100644 src/Features/openai/OpenAI.tsx create mode 100644 src/hooks/useFile.tsx diff --git a/package.json b/package.json index ea3d9c5..4ef0649 100644 --- a/package.json +++ b/package.json @@ -65,12 +65,16 @@ "react-cropper": "^2.3.3", "react-dom": "^18.2.0", "react-icons": "^5.2.1", + "react-katex": "^3.0.1", "react-live": "^4.1.5", "react-pdf": "^7.7.3", "react-router-dom": "6.22.3", "react-shepherd": "^4.3.0", "react-sizeme": "^3.0.2", "recharts": "2.12.3", + "rehype-katex": "^7.0.0", + "rehype-raw": "^7.0.0", + "remark-math": "^6.0.0", "simple-base-converter": "^1.0.19", "tauri-plugin-store-api": "https://github.com/tauri-apps/tauri-plugin-store", "terser": "^5.30.0", @@ -96,6 +100,7 @@ "@types/react": "^18.2.73", "@types/react-color": "^3.0.12", "@types/react-dom": "^18.2.23", + "@types/react-katex": "^3.0.4", "@types/tinycolor2": "^1.4.6", "@types/uuid": "^9.0.8", "@typescript-eslint/eslint-plugin": "^7.4.0", @@ -121,4 +126,4 @@ "vite": "^5.2.7", "vite-tsconfig-paths": "^5.0.1" } -} +} diff --git a/src/App.tsx b/src/App.tsx index d0cc1fe..cb74dd2 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -49,6 +49,7 @@ const ColorTesting = loadable(() => import("./Features/colors/ColorTesting")); const RegexTester = loadable(() => import("./Features/regex/RegexTester")); const TextDiff = loadable(() => import("./Features/text/TextDiff")); const Markdown = loadable(() => import("./Features/markdown/Markdown")); +const Readme = loadable(() => import("./Features/markdown/Readme")); const YamlJson = loadable(() => import("./Features/json-yaml/Yaml")); const Pastebin = loadable(() => import("./Features/pastebin/Pastebin")); const Repl = loadable(() => import("./Features/repl/Repl")); @@ -234,6 +235,7 @@ function App() { }> }> }> + }> }> }> }> diff --git a/src/Features/markdown/Editor.tsx b/src/Features/markdown/Editor.tsx new file mode 100644 index 0000000..a74ccb8 --- /dev/null +++ b/src/Features/markdown/Editor.tsx @@ -0,0 +1,64 @@ +// @ts-nocheck +import "./markdown.css"; + +import { Box, Group } from "@mantine/core"; +import MarkdownPreview from "@uiw/react-markdown-preview"; +import { Monaco } from "@/Components/MonacoWrapper"; + +import rehypeKatex from "rehype-katex"; +import rehypeRaw from "rehype-raw"; +import remarkMath from "remark-math"; + +import "katex/dist/katex.min.css"; + +const MarkdownEditor = ({ + file, + previewFile, + setFile, + showPreview = true, + showEditor = true, +}: { + file: string; + previewFile?: string; + setFile: (file: string) => void; + showPreview?: boolean; + showEditor?: boolean; +}) => { + return ( + + {showEditor ? ( + + setFile(e || "")} + value={file} + language="markdown" + onEditorMounted={(editor) => { + // eslint-disable-next-line + editor.getContribution( + "editor.linkDetector" + ).openerService._defaultExternalOpener.openExternal = () => {}; + }} + /> + + ) : null} + {showPreview ? ( + + { + if (node.tagName === "a") { + node.properties.target = "_blank"; + node.properties.rel = "noopener noreferrer"; + } + }} + /> + + ) : null} + + ); +}; + +export default MarkdownEditor; diff --git a/src/Features/markdown/Markdown.tsx b/src/Features/markdown/Markdown.tsx index dd6cf9b..e0428cd 100644 --- a/src/Features/markdown/Markdown.tsx +++ b/src/Features/markdown/Markdown.tsx @@ -1,42 +1,19 @@ -// @ts-nocheck -import "./markdown.css"; - -import { Box, Button, Group, Stack } from "@mantine/core"; -import MarkdownPreview from "@uiw/react-markdown-preview"; +import { Button, Group, Stack } from "@mantine/core"; +import { demoMdFile } from "@/Features/markdown/constants"; import { useState } from "react"; - -import { Monaco } from "@/Components/MonacoWrapper"; -import { openFileAndGetData, saveDataToFile } from "@/utils/functions"; +import { useFile } from "@/hooks"; +import MarkdownEditor from "@/Features/markdown/Editor"; const Markdown = () => { - const [source, setSource] = useState(` -# Markdown -- Supports every markdown feature + GitHub Flavored Markdown -- Refer [GFM](https://github.github.com/gfm/) -- Supports footnotes -- ⛳️ Emoji support - -\`\`\`js -const codeblock = () => { - // Code with syntax highlighting -} -\`\`\` -`); - - const openFile = async () => { - const data = await openFileAndGetData( - "Open Markdown File", - [{ name: "Markdown", extensions: ["md"] }], - "text" - ); - setSource(data); - }; - - const saveFile = async () => { - saveDataToFile(source, "Save Markdown File", [ - { name: "Markdown", extensions: ["md"] }, - ]); - }; + const [preview, setPreview] = useState(true); + const { + file: source, + setFile: setSource, + openFile, + saveFile, + } = useFile({ + initialFile: demoMdFile, + }); return ( { + - - - setSource(e || "")} - value={source} - language="markdown" - onEditorMounted={(editor) => { - // eslint-disable-next-line - editor.getContribution( - "editor.linkDetector" - ).openerService._defaultExternalOpener.openExternal = () => {}; - }} - /> - - - { - if (node.tagName === "a") { - node.properties.target = "_blank"; - node.properties.rel = "noopener noreferrer"; - } - }} - /> - - + ); }; diff --git a/src/Features/markdown/Readme.tsx b/src/Features/markdown/Readme.tsx new file mode 100644 index 0000000..bdc89a3 --- /dev/null +++ b/src/Features/markdown/Readme.tsx @@ -0,0 +1,112 @@ +import { Button, Group, Select, Stack } from "@mantine/core"; +import { useEffect, useState } from "react"; +import { useFile } from "@/hooks"; +import MarkdownEditor from "@/Features/markdown/Editor"; +import { templates } from "@/Features/markdown/templates"; + +type Part = { + title: string; + template: string; +}; + +// TODO: Add these: +// https://github.com/fosslife/devtools-x/issues/94 +// [ ] Reordering parts +// [ ] Removing parts +// [ ] Preview templates before inserting +// [ ] Build more advanced templates like Awsome Readme +const Readme = () => { + const [showPreview, setShowPreview] = useState(true); + const [showEditor, setShowEditor] = useState(true); + + const [parts, setParts] = useState([ + { + title: "Header", + template: templates.header as string, + }, + { + title: "Acknowledgements", + template: templates.acknowledgements as string, + }, + ]); + + const [activeIndex, setActiveIndex] = useState(0); + const updatePart = (template: string) => { + setParts((p) => + p.map((part, index) => + index === activeIndex ? { ...part, template } : part + ) + ); + }; + const activePart = parts[activeIndex]; + + const combineParts = () => parts.map((part) => part.template).join("\n"); + + const { file, setFile, openFile, saveFile } = useFile({ + initialFile: combineParts(), + }); + + useEffect(() => { + setFile(combineParts()); + }, [parts]); + + return ( + + + + + { + const template = templates[value as keyof typeof templates]; + const render = + typeof template === "string" ? template : template?.render; + + setParts((prev) => [ + ...prev, + { + title: value, + template: render, + } as Part, + ]); + }} + /> + + + + updatePart(value as string)} + showPreview={showPreview} + showEditor={showEditor} + /> + + ); +}; + +export default Readme; diff --git a/src/Features/markdown/constants.ts b/src/Features/markdown/constants.ts new file mode 100644 index 0000000..e145c0f --- /dev/null +++ b/src/Features/markdown/constants.ts @@ -0,0 +1,41 @@ +export const demoMdFile = ` +# Markdown +- Supports every markdown feature + GitHub Flavored Markdown +- Refer [GFM](https://github.github.com/gfm/) +- Supports footnotes +- ⛳️ Emoji support + +\`\`\`js +const codeblock = () => { + // Code with syntax highlighting +} +\`\`\` + + +A foot note[^1] + +[^1]: Actual footnote content. + +~one~ or ~~two~~ tildes. + +| a | b | c | d | +| - | :- | -: | :-: | +| 1 | 2 | 3 | 4 | + +* [ ] to do +* [x] done + +> #### [Rehype-raw](https://www.npmjs.com/package/rehype-raw) & [Rehype-katex](https://www.npmjs.com/package/rehype-katex/v/6.0.2) included + +So you can use *markdown* and HTML. + +$\\frac{-30}{8}$ + +$$ +L = \\frac{1}{2} \\rho v^2 S C_L +$$ + +
+ L = \\frac{1}{2} \\rho v^2 S C_L +
+`; diff --git a/src/Features/markdown/templates.ts b/src/Features/markdown/templates.ts new file mode 100644 index 0000000..61c2671 --- /dev/null +++ b/src/Features/markdown/templates.ts @@ -0,0 +1,307 @@ +export const templates: { + [key: string]: + | string + | { + render: string; + info?: string; + module?: string; + }; +} = { + autoGen: ` +`, + acknowledgements: ` +## Acknowledgements + +- [DevTools-X](https://github.com/fosslife/devtools-x) +- [Readme.so](https://readme.so/editor) +- [Awesome README](https://github.com/matiassingers/awesome-readme) +`, + apiReference: ` + Here’s an improved and more comprehensive version of your API reference documentation: + +--- + +## API Reference + +### Endpoints Overview + +This API allows you to retrieve and manipulate items in your collection. Below are the details for each available endpoint. + +--- + +### Get All Items + +Retrieve a list of all items available in the database. + +\`\`\`http +GET /api/items +\`\`\` + +#### Query Parameters + +| Parameter | Type | Required | Description | +| :-------- | :------- | :------- | :--------------------------------------------- | +| \`api_key\` | \`string\` | Yes | Your unique API key for authentication | +| \`limit\` | \`int\` | No | The number of items to return (default: 100) | +| \`offset\` | \`int\` | No | The number of items to skip before starting to return results | + +#### Example Request + +\`\`\`http +GET /api/items?api_key=your_api_key&limit=50&offset=0 +\`\`\` + +--- + +### Get Item by ID + +Retrieve details for a specific item by its unique identifier. + +\`\`\`http +GET /api/items/:id +\`\`\` + +#### Path Parameters + +| Parameter | Type | Required | Description | +| :-------- | :------- | :------- | :--------------------------------------- | +| \`id\` | \`string\` | Yes | The unique identifier of the item to fetch | + +#### Query Parameters + +| Parameter | Type | Required | Description | +| :-------- | :------- | :------- | :--------------------------------------------- | +| \`api_key\` | \`string\` | Yes | Your unique API key for authentication | + +#### Example Request + +\`\`\`http +GET /api/items/12345?api_key=your_api_key +\`\`\` + +--- + +### Add Function + +This utility function adds two numbers and returns the result. It can be useful in cases where arithmetic operations are needed. + +\`\`\`javascript +function add(num1, num2) { + return num1 + num2; +} +\`\`\` + +#### Parameters + +| Parameter | Type | Required | Description | +| :-------- | :------- | :------- | :------------------------------ | +| \`num1\` | \`number\` | Yes | The first number to be added | +| \`num2\` | \`number\` | Yes | The second number to be added | + +#### Example Usage + +\`\`\`javascript +const sum = add(5, 10); +console.log(sum); // Outputs: 15 +\`\`\` + +--- +`, + appendix: ` +### Appendix + +- Ensure that all requests include your API key for authentication. +- The \`limit\` and \`offset\` parameters are optional but can be used to control pagination for large datasets. +- The \`id\` parameter must be a valid string corresponding to an existing item. + +--- +`, + authors: ` +## Authors + +- [@Sparkenstein](https://www.github.com/Sparkenstein) +- [@ThijsZijdel](https://github.com/ThijsZijdel) +... +`, + badges: { + render: ` + ## Badges + +![MIT License](https://img.shields.io/github/license/fosslife/devtools-x.svg) +![GitHub issues](https://badgen.net/github/issues/fosslife/devtools-x) ![GitHub stars](https://badgen.net/github/stars/fosslife/devtools-x) +![Latest release](https://badgen.net/github/release/fosslife/devtools-x) + +`, + module: "shields", + info: "https://shields.io/badges", + }, + contributing: ` +## Contributing + +Contributions are always welcome! + +See [\`contributing.md\`](/contributing.md) for ways to get started. + +Please adhere to this project's \`code of conduct\`. +`, + demo: ` +## Demo + +Screenshot 2024-04-01 at 12 20 37 PM +`, + deployment: ` +## Deployment + +To deploy this project run + +\`\`\`bash + # Install dependencies + yarn install + + # Run dev server + yarn tauri dev +\`\`\` + +`, + documentation: ` +## Documentation + +[Documentation](https://linktodocumentation) + +`, + environment: ` +## Environment Variables + +To run this project, add the following environment variables to your \`.env\` file. You can copy \`.env.example\` as \`.env\`: + +- \`API_KEY\`: Your primary API key. +- \`ANOTHER_API_KEY\`: A secondary API key. + +Make sure to replace placeholders with your actual keys. +`, + faq: ` +## FAQ + +#### Question one + +Answer one, check out our [docs](google.com) for more info + +#### Question two + +Answer two, check out our [docs](google.com) for more info +`, + features: ` +## Features + +- **🌗 Light/Dark Mode Toggle**: Work comfortably in any lighting. +- **👀 Live Previews**: See changes instantly—no guessing! +- **🖥️ Fullscreen Mode**: Immerse yourself in your work. +- **🌍 Cross-Platform**: Use it anywhere, on any device. + +`, + feedback: ` +## Feedback + +If you have any feedback, please reach out to us at [our@repo.com](mailto:our@repo.com) +`, + installation: ` +## Installation + +Install our repo with pnpm + +\`\`\`bash + pnpm install our-repo + cd our-repo +\`\`\` + `, + lessonsLearned: ` +## Lessons Learned + +What did you learn while building this project? What challenges did you face and how did you overcome them? +`, + license: ` +## License + +[MIT](https://choosealicense.com/licenses/mit/) +`, + logo: ` +![Logo](https://github.com/fosslife/devtools-x/raw/master/assets/banner.png) + `, + optimizations: ` +## Optimizations + +What optimizations did you make in your code? E.g. refactors, performance improvements, accessibility + +`, + related: ` +## Related + +Here are some related projects + +[Awesome README](https://github.com/matiassingers/awesome-readme) +`, + roadmap: ` +## Roadmap + +- **🚀 Launch New Features** +- **🔧 Improve Performance** +- **🛡️ Enhance Security** +- **🌐 Expand Cross-Platform Support** +- **📈 Continuous User Feedback Integration** +`, + screenshots: ` +## Screenshots + +![App Screenshots](https://via.placeholder.com/468x300?text=Place+your+screenshots+here) +`, + support: ` +## Support + +If you need help, please reach out via [support@yourproject.com](mailto:support@yourproject.com). +`, + technology: ` +## Technology Used + +This project is built with: + +- **React**: Frontend library for building user interfaces. +- **Node.js**: JavaScript runtime for backend development. +- **Express**: Web framework for Node.js. +- **MongoDB**: NoSQL database for data storage. +`, + tests: ` +## Running Tests + +To run tests for this project, use the following command: + +\`\`\`bash +npm test +\`\`\` + +Ensure all tests pass before pushing changes.`, + header: ` +# **Project Name** + +A brief description of what your project does and why it’s useful. +`, + usage: ` +## Usage + +To use the main component of this project, follow the example below: + +\`\`\`javascript +import Component from 'my-project'; + +function App() { + return ; +} +\`\`\``, + usedBy: ` +## Used By + +This project is used by the following companies/projects: + +- **Company A**: Brief description of usage. +- **Project B**: Brief description of usage. +`, +}; diff --git a/src/Features/openai/OpenAI.tsx b/src/Features/openai/OpenAI.tsx new file mode 100644 index 0000000..7624a0f --- /dev/null +++ b/src/Features/openai/OpenAI.tsx @@ -0,0 +1,175 @@ +import { + Button, + Group, + NativeSelect, + Stack, + Text, + TextInput, +} from "@mantine/core"; +import { useState } from "react"; +import { useLocalStorage } from "@mantine/hooks"; +import { Monaco } from "../../Components/MonacoWrapper"; +import { notifications } from "@mantine/notifications"; + +import Client from "openai"; + +const langs = [ + "JavaScript", + "TypeScript", + "Rust", + "Python", + "C#", + "Go", + "Java", + "C", + "C++", + "Text", + "PHP", +]; + +const API_KEY_NAME = "openai-api-key"; + +// Move to /hooks + +function OpenAI() { + const [codeValue, setCodeValue] = + useState(`const call = async () => {\n\treturn "Works!"\n} +`); + + const [lang, setLang] = useState("typescript"); + const [link, setLink] = useState(""); + const [loading, setLoading] = useState(false); + + const [apiKey, setApiKey] = useLocalStorage({ + key: API_KEY_NAME, + defaultValue: "", + }); + + const [description, setDescription] = useState("Example gist"); + const [publicGist, setPublicGist] = useState(false); + const [filename, setFilename] = useState("paste.txt"); + + const createPaste = async () => { + setLoading(true); + if (!apiKey) { + notifications.show({ + message: "Please enter a valid GitHub API key", + title: "Error", + color: "red", + }); + setLoading(false); + return; + } + + const res = await fetch("https://api.github.com/gists", { + method: "POST", + headers: { + Accept: "application/vnd.github+json", + Authorization: `Bearer ${apiKey}`, + "X-GitHub-Api-Version": "2022-11-28", + }, + body: JSON.stringify({ + description: description, + public: publicGist, + files: { + [filename]: { + content: codeValue, + }, + }, + }), + }) + .then((e) => e.json()) + .catch((e) => { + console.error(e); + return {}; + }); + + setLoading(false); + setLink(res?.html_url); + }; + + const testRes = async () => { + try { + const openai = new Client({ + dangerouslyAllowBrowser: true, + apiKey, + }); + const completion = await openai.chat.completions.create({ + messages: [ + { role: "system", content: "You are a helpful assistant." }, + { role: "user", content: "What is a LLM?" }, + ], + model: "gpt-4o-mini", + }); + const res = completion.choices[0].message.content; + setCodeValue(JSON.stringify(res ?? completion)); + } catch (e: any) { + console.error(e); + notifications.show({ + message: "Error: " + e.message, + title: "Error", + color: "red", + }); + } + }; + + return ( + + + e.toUpperCase())} + onChange={(e) => { + setLang(e.target.value); + }} + > + { + setApiKey(e.currentTarget.value); + }} + > + + setCodeValue(e || "")} + value={codeValue} + language={lang.toLowerCase()} + height="60%" + extraOptions={{ + fontSize: 15, + }} + /> + + {link ? ( + <> + + {link} + + + ) : null} + + ); +} + +export default OpenAI; + +// TODO: Add a button to copy the link to clipboard +// TODO: store settings to db +// TODO: store prev generated URLS? diff --git a/src/Layout/Navbar/items.tsx b/src/Layout/Navbar/items.tsx index 03d95e2..dac73ff 100644 --- a/src/Layout/Navbar/items.tsx +++ b/src/Layout/Navbar/items.tsx @@ -1,31 +1,31 @@ -import { BsSortNumericUpAlt, BsFilePdf } from "react-icons/bs"; +import { BsFilePdf, BsSortNumericUpAlt } from "react-icons/bs"; import { - FaRandom, - FaReact, - FaPaste, FaCode, - FaMarkdown, - FaYinYang, - FaExchangeAlt, FaCompress, FaCss3, + FaExchangeAlt, + FaMarkdown, + FaPaste, + FaRandom, + FaReact, + FaYinYang, } from "react-icons/fa"; -import { FiClock, FiHash, FiFile, FiStar } from "react-icons/fi"; +import { FiClock, FiFile, FiHash, FiStar } from "react-icons/fi"; import { - MdQrCode, - MdWork, MdAnchor, MdColorize, - MdPassword, - MdQuestionMark, - MdHtml, - MdPermIdentity, MdDataExploration, - MdWeb, + MdHtml, MdImage, - MdQrCode2, MdOutlineImage, MdPalette, + MdPassword, + MdPermIdentity, + MdQrCode, + MdQrCode2, + MdQuestionMark, + MdWeb, + MdWork, } from "react-icons/md"; import { FcCamera, @@ -41,16 +41,16 @@ import { } from "react-icons/fc"; import { RiPingPongLine } from "react-icons/ri"; import { - SiPrettier, + SiHashnode, SiJsonwebtokens, SiPostgresql, - SiHashnode, + SiPrettier, } from "react-icons/si"; import { - VscSymbolString, VscDiff, - VscTypeHierarchySub, VscRegex, + VscSymbolString, + VscTypeHierarchySub, } from "react-icons/vsc"; import { NavItem } from "."; import { TbFileTypeSvg } from "react-icons/tb"; @@ -267,6 +267,13 @@ export const navitems: NavItem[] = [ text: "Markdown", group: "Previewers", }, + { + id: "readme", + to: "/readme", + icon: , + text: "Readme", + group: "Generators", + }, { id: "yamljson", to: "/yamljson", diff --git a/src/hooks/index.ts b/src/hooks/index.ts index 709f3d5..e7a0373 100644 --- a/src/hooks/index.ts +++ b/src/hooks/index.ts @@ -1 +1,2 @@ export { useDebouncedCallback } from "./useDebouceCallback"; +export { useFile } from "./useFile"; diff --git a/src/hooks/useFile.tsx b/src/hooks/useFile.tsx new file mode 100644 index 0000000..a2d540d --- /dev/null +++ b/src/hooks/useFile.tsx @@ -0,0 +1,31 @@ +import React from "react"; +import { openFileAndGetData, saveDataToFile } from "@/utils/functions"; + +type UseMarkdownFileProps = { + initialFile?: string; + extensions?: string[]; +}; + +export const useFile = ({ + initialFile = "", + extensions = ["md"], +}: UseMarkdownFileProps) => { + const [file, setFile] = React.useState(initialFile); + + const openFile = async () => { + const data = await openFileAndGetData( + `Open a ${extensions.join(", ")} File`, + [{ name: "open-file", extensions }], + "text" + ); + if (data) setFile(data); + }; + + const saveFile = async () => { + await saveDataToFile(file, `Save a ${extensions.join(", ")} File`, [ + { name: "save-file", extensions }, + ]); + }; + + return { file, setFile, openFile, saveFile }; +}; diff --git a/yarn.lock b/yarn.lock index d453a09..d57ea42 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2117,6 +2117,11 @@ dependencies: "@types/node" "*" +"@types/katex@^0.16.0": + version "0.16.7" + resolved "https://registry.yarnpkg.com/@types/katex/-/katex-0.16.7.tgz#03ab680ab4fa4fbc6cb46ecf987ecad5d8019868" + integrity sha512-HMwFiRujE5PjrgwHQ25+bsLJgowjGjm5Z8FVSf0N6PwgJrwxH0QxzHYDcKsTfV3wva0vzrpqMTJS2jXPr5BMEQ== + "@types/loadable__component@^5.13.9": version "5.13.9" resolved "https://registry.yarnpkg.com/@types/loadable__component/-/loadable__component-5.13.9.tgz#4a265ee0892ab3e52ca74ac98189f791a24f075b" @@ -2180,6 +2185,13 @@ dependencies: "@types/react" "*" +"@types/react-katex@^3.0.4": + version "3.0.4" + resolved "https://registry.yarnpkg.com/@types/react-katex/-/react-katex-3.0.4.tgz#2b60eebf76938bb385337fd850d99cc53ad6ef67" + integrity sha512-aLkykKzSKLpXI6REJ3uClao6P47HAFfR1gcHOZwDeTuALsyjgMhz+oynLV4gX0kiJVnvHrBKF/TLXqyNTpHDUg== + dependencies: + "@types/react" "*" + "@types/react@*", "@types/react@^18.2.73": version "18.2.73" resolved "https://registry.yarnpkg.com/@types/react/-/react-18.2.73.tgz#0579548ad122660d99e00499d22e33b81e73ed94" @@ -3043,6 +3055,11 @@ commander@^4.0.0: resolved "https://registry.yarnpkg.com/commander/-/commander-4.1.1.tgz#9fd602bd936294e9e9ef46a3f4d6964044b18068" integrity sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA== +commander@^8.3.0: + version "8.3.0" + resolved "https://registry.yarnpkg.com/commander/-/commander-8.3.0.tgz#4837ea1b2da67b9c616a67afbb0fafee567bca66" + integrity sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww== + commander@^9.3.0: version "9.5.0" resolved "https://registry.yarnpkg.com/commander/-/commander-9.5.0.tgz#bc08d1eb5cedf7ccb797a96199d41c7bc3e60d30" @@ -4300,6 +4317,25 @@ hasown@^2.0.0, hasown@^2.0.1, hasown@^2.0.2: dependencies: function-bind "^1.1.2" +hast-util-from-dom@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/hast-util-from-dom/-/hast-util-from-dom-5.0.0.tgz#d32edd25bf28f4b178b5ae318f8d05762e67bd16" + integrity sha512-d6235voAp/XR3Hh5uy7aGLbM3S4KamdW0WEgOaU1YoewnuYw4HXb5eRtv9g65m/RFGEfUY1Mw4UqCc5Y8L4Stg== + dependencies: + "@types/hast" "^3.0.0" + hastscript "^8.0.0" + web-namespaces "^2.0.0" + +hast-util-from-html-isomorphic@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/hast-util-from-html-isomorphic/-/hast-util-from-html-isomorphic-2.0.0.tgz#b31baee386a899a2472326a3c5692f29f86d1d3c" + integrity sha512-zJfpXq44yff2hmE0XmwEOzdWin5xwH+QIhMLOScpX91e/NSGPsAzNCvLQDIEPyO2TXi+lBmU6hjLIhV8MwP2kw== + dependencies: + "@types/hast" "^3.0.0" + hast-util-from-dom "^5.0.0" + hast-util-from-html "^2.0.0" + unist-util-remove-position "^5.0.0" + hast-util-from-html@^2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/hast-util-from-html/-/hast-util-from-html-2.0.1.tgz#9cd38ee81bf40b2607368b92a04b0905fa987488" @@ -4443,6 +4479,16 @@ hast-util-to-string@^3.0.0: dependencies: "@types/hast" "^3.0.0" +hast-util-to-text@^4.0.0: + version "4.0.2" + resolved "https://registry.yarnpkg.com/hast-util-to-text/-/hast-util-to-text-4.0.2.tgz#57b676931e71bf9cb852453678495b3080bfae3e" + integrity sha512-KK6y/BN8lbaq654j7JgBydev7wuNMcID54lkRav1P0CaE1e47P72AWWPiGKXTJU271ooYzcvTAn/Zt0REnvc7A== + dependencies: + "@types/hast" "^3.0.0" + "@types/unist" "^3.0.0" + hast-util-is-element "^3.0.0" + unist-util-find-after "^5.0.0" + hast-util-whitespace@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/hast-util-whitespace/-/hast-util-whitespace-3.0.0.tgz#7778ed9d3c92dd9e8c5c8f648a49c21fc51cb621" @@ -4965,6 +5011,13 @@ jsonfile@^6.0.1: object.assign "^4.1.4" object.values "^1.1.6" +katex@^0.16.0: + version "0.16.11" + resolved "https://registry.yarnpkg.com/katex/-/katex-0.16.11.tgz#4bc84d5584f996abece5f01c6ad11304276a33f5" + integrity sha512-RQrI8rlHY92OLf3rho/Ts8i/XvjgguEjOkO1BEXcU3N8BqPpSzBNwV/G0Ukr+P/l3ivvJUE/Fa/CwbS6HesGNQ== + dependencies: + commander "^8.3.0" + keyv@^4.5.3: version "4.5.4" resolved "https://registry.yarnpkg.com/keyv/-/keyv-4.5.4.tgz#a879a99e29452f942439f2a405e3af8b31d4de93" @@ -5385,6 +5438,19 @@ mdast-util-gfm@^3.0.0: mdast-util-gfm-task-list-item "^2.0.0" mdast-util-to-markdown "^2.0.0" +mdast-util-math@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/mdast-util-math/-/mdast-util-math-3.0.0.tgz#8d79dd3baf8ab8ac781f62b8853768190b9a00b0" + integrity sha512-Tl9GBNeG/AhJnQM221bJR2HPvLOSnLE/T9cJI9tlc6zwQk2nPk/4f0cHkOdEixQPC/j8UtKDdITswvLAy1OZ1w== + dependencies: + "@types/hast" "^3.0.0" + "@types/mdast" "^4.0.0" + devlop "^1.0.0" + longest-streak "^3.0.0" + mdast-util-from-markdown "^2.0.0" + mdast-util-to-markdown "^2.1.0" + unist-util-remove-position "^5.0.0" + mdast-util-mdx-expression@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/mdast-util-mdx-expression/-/mdast-util-mdx-expression-2.0.0.tgz#4968b73724d320a379110d853e943a501bfd9d87" @@ -5451,7 +5517,7 @@ mdast-util-to-hast@^13.0.0: unist-util-visit "^5.0.0" vfile "^6.0.0" -mdast-util-to-markdown@^2.0.0: +mdast-util-to-markdown@^2.0.0, mdast-util-to-markdown@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/mdast-util-to-markdown/-/mdast-util-to-markdown-2.1.0.tgz#9813f1d6e0cdaac7c244ec8c6dabfdb2102ea2b4" integrity sha512-SR2VnIEdVNCJbP6y7kVTJgPLifdr8WEU440fQec7qHoHOUz/oJ2jmNRqdDQ3rbiStOXb2mCDGTuwsK5OPUgYlQ== @@ -5593,6 +5659,19 @@ micromark-extension-gfm@^3.0.0: micromark-util-combine-extensions "^2.0.0" micromark-util-types "^2.0.0" +micromark-extension-math@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/micromark-extension-math/-/micromark-extension-math-3.1.0.tgz#c42ee3b1dd5a9a03584e83dd8f08e3de510212c1" + integrity sha512-lvEqd+fHjATVs+2v/8kg9i5Q0AP2k85H0WUOwpIVvUML8BapsMvh1XAogmQjOCsLpoKRCVQqEkQBB3NhVBcsOg== + dependencies: + "@types/katex" "^0.16.0" + devlop "^1.0.0" + katex "^0.16.0" + micromark-factory-space "^2.0.0" + micromark-util-character "^2.0.0" + micromark-util-symbol "^2.0.0" + micromark-util-types "^2.0.0" + micromark-factory-destination@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/micromark-factory-destination/-/micromark-factory-destination-2.0.0.tgz#857c94debd2c873cba34e0445ab26b74f6a6ec07" @@ -6486,6 +6565,13 @@ react-is@^18.0.0: resolved "https://registry.yarnpkg.com/react-is/-/react-is-18.2.0.tgz#199431eeaaa2e09f86427efbb4f1473edb47609b" integrity sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w== +react-katex@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/react-katex/-/react-katex-3.0.1.tgz#262b44f49c5fa727f1d13cbab595f791318e5083" + integrity sha512-wIUW1fU5dHlkKvq4POfDkHruQsYp3fM8xNb/jnc8dnQ+nNCnaj0sx5pw7E6UyuEdLRyFKK0HZjmXBo+AtXXy0A== + dependencies: + katex "^0.16.0" + react-live@^4.1.5: version "4.1.6" resolved "https://registry.yarnpkg.com/react-live/-/react-live-4.1.6.tgz#6d9b7d381bd2b359ca859767501135112b6bab33" @@ -6811,6 +6897,19 @@ rehype-ignore@^2.0.0: unified "^11.0.0" unist-util-visit "^5.0.0" +rehype-katex@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/rehype-katex/-/rehype-katex-7.0.0.tgz#f5e9e2825981175a7b0a4d58ed9816c33576dfed" + integrity sha512-h8FPkGE00r2XKU+/acgqwWUlyzve1IiOKwsEkg4pDL3k48PiE0Pt+/uLtVHDVkN1yA4iurZN6UES8ivHVEQV6Q== + dependencies: + "@types/hast" "^3.0.0" + "@types/katex" "^0.16.0" + hast-util-from-html-isomorphic "^2.0.0" + hast-util-to-text "^4.0.0" + katex "^0.16.0" + unist-util-visit-parents "^6.0.0" + vfile "^6.0.0" + rehype-parse@^9.0.0: version "9.0.0" resolved "https://registry.yarnpkg.com/rehype-parse/-/rehype-parse-9.0.0.tgz#3949faeec6f466ec57774215661e0d75469195d9" @@ -6885,6 +6984,16 @@ remark-github-blockquote-alert@^1.0.0: dependencies: unist-util-visit "^5.0.0" +remark-math@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/remark-math/-/remark-math-6.0.0.tgz#0acdf74675f1c195fea6efffa78582f7ed7fc0d7" + integrity sha512-MMqgnP74Igy+S3WwnhQ7kqGlEerTETXMvJhrUzDikVZ2/uogJCb+WHUg97hK9/jcfc0dkD73s3LN8zU49cTEtA== + dependencies: + "@types/mdast" "^4.0.0" + mdast-util-math "^3.0.0" + micromark-extension-math "^3.0.0" + unified "^11.0.0" + remark-parse@^11.0.0: version "11.0.0" resolved "https://registry.yarnpkg.com/remark-parse/-/remark-parse-11.0.0.tgz#aa60743fcb37ebf6b069204eb4da304e40db45a1" @@ -7756,6 +7865,14 @@ unist-util-filter@^5.0.0: unist-util-is "^6.0.0" unist-util-visit-parents "^6.0.0" +unist-util-find-after@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/unist-util-find-after/-/unist-util-find-after-5.0.0.tgz#3fccc1b086b56f34c8b798e1ff90b5c54468e896" + integrity sha512-amQa0Ep2m6hE2g72AugUItjbuM8X8cGQnFoHk0pGfrFeT9GZhzN5SW8nRsiGKK7Aif4CrACPENkA6P/Lw6fHGQ== + dependencies: + "@types/unist" "^3.0.0" + unist-util-is "^6.0.0" + unist-util-is@^6.0.0: version "6.0.0" resolved "https://registry.yarnpkg.com/unist-util-is/-/unist-util-is-6.0.0.tgz#b775956486aff107a9ded971d996c173374be424" From f086aca6bb0018292306789dd80ac25198b56a47 Mon Sep 17 00:00:00 2001 From: Thijs Zijdel Date: Tue, 20 Aug 2024 23:06:46 +0200 Subject: [PATCH 2/3] add: readme reordering & deletion --- src/Features/markdown/Readme.tsx | 196 +++++++++++++++++++++++------ src/Features/markdown/templates.ts | 2 +- 2 files changed, 157 insertions(+), 41 deletions(-) diff --git a/src/Features/markdown/Readme.tsx b/src/Features/markdown/Readme.tsx index bdc89a3..b4ba609 100644 --- a/src/Features/markdown/Readme.tsx +++ b/src/Features/markdown/Readme.tsx @@ -1,23 +1,32 @@ -import { Button, Group, Select, Stack } from "@mantine/core"; +import { Box, Button, Group, Popover, Stack, Text } from "@mantine/core"; import { useEffect, useState } from "react"; import { useFile } from "@/hooks"; import MarkdownEditor from "@/Features/markdown/Editor"; import { templates } from "@/Features/markdown/templates"; +import { + DragDropContext, + Draggable, + Droppable, + OnDragEndResponder, +} from "@hello-pangea/dnd"; +import cx from "clsx"; +import classes from "@/Layout/Navbar/components/ungrouped.module.css"; +import MarkdownPreview from "@uiw/react-markdown-preview"; +import remarkMath from "remark-math"; +import rehypeKatex from "rehype-katex"; +import rehypeRaw from "rehype-raw"; type Part = { title: string; template: string; }; -// TODO: Add these: -// https://github.com/fosslife/devtools-x/issues/94 -// [ ] Reordering parts -// [ ] Removing parts -// [ ] Preview templates before inserting -// [ ] Build more advanced templates like Awsome Readme +// TODO: Build more advanced templates like Awsome Readme & some cleanup / reusability + const Readme = () => { const [showPreview, setShowPreview] = useState(true); const [showEditor, setShowEditor] = useState(true); + const [opened, setOpened] = useState(false); const [parts, setParts] = useState([ { @@ -50,6 +59,14 @@ const Readme = () => { setFile(combineParts()); }, [parts]); + const onDragEnd: OnDragEndResponder = (res) => { + if (res.destination?.index === res.source.index) return; + const items = [...parts]; + const [reorderedItem] = items.splice(res.source.index, 1); + items.splice(res.destination!.index, 0, reorderedItem); + setParts(items); + }; + return ( { - { - const template = templates[value as keyof typeof templates]; - const render = - typeof template === "string" ? template : template?.render; - - setParts((prev) => [ - ...prev, - { - title: value, - template: render, - } as Part, - ]); - }} - /> + {activeIndex ? ( + + ) : null} - updatePart(value as string)} - showPreview={showPreview} - showEditor={showEditor} - /> +
+ + + + {(provided) => ( +
+ {parts.map((part, index) => ( + + {(provided) => ( +
setActiveIndex(index)} + > + + + {titleCase(part.title)} + + +
+ )} +
+ ))} + {provided.placeholder} +
+ )} +
+
+ + + + + + + + {Object.keys(templates).map((key) => { + const template = templates[key as keyof typeof templates]; + return ( +
{ + setParts((prev) => [ + ...prev, + { + title: key, + template: + typeof template === "string" + ? template + : template?.render, + } as Part, + ]); + }} + > + + {titleCase(key)} + + +
+ ); + })} +
+
+
+
+ updatePart(value as string)} + showPreview={showPreview} + showEditor={showEditor} + /> +
); }; +const titleCase = (pascalCase: string) => { + return pascalCase + .replace(/([A-Z])/g, " $1") + .replace(/^./, (str) => str.toUpperCase()); +}; + export default Readme; diff --git a/src/Features/markdown/templates.ts b/src/Features/markdown/templates.ts index 61c2671..528ecc5 100644 --- a/src/Features/markdown/templates.ts +++ b/src/Features/markdown/templates.ts @@ -125,7 +125,7 @@ console.log(sum); // Outputs: 15 `, badges: { render: ` - ## Badges +## Badges ![MIT License](https://img.shields.io/github/license/fosslife/devtools-x.svg) ![GitHub issues](https://badgen.net/github/issues/fosslife/devtools-x) ![GitHub stars](https://badgen.net/github/stars/fosslife/devtools-x) From d95f8dde39f8be7dddad6caa06fb9c2edef43030 Mon Sep 17 00:00:00 2001 From: Thijs Zijdel Date: Tue, 20 Aug 2024 23:10:15 +0200 Subject: [PATCH 3/3] Add: readme in sidebar --- src/Layout/Navbar/items.tsx | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/Layout/Navbar/items.tsx b/src/Layout/Navbar/items.tsx index 5ead96a..4f65045 100644 --- a/src/Layout/Navbar/items.tsx +++ b/src/Layout/Navbar/items.tsx @@ -275,6 +275,13 @@ export const navitems: NavItem[] = [ text: "Markdown", group: "Previewers", }, + { + id: "readme", + to: "/readme", + icon: , + text: "Readme", + group: "Generators", + }, { id: "yamljson", to: "/yamljson",