├── .eslintrc.cjs ├── .gitignore ├── .husky └── pre-commit ├── .prettierrc ├── README.md ├── index.html ├── package.json ├── pnpm-lock.yaml ├── postcss.config.js ├── public ├── favicon.svg ├── images │ └── slush.webp └── opengraph.png ├── src ├── App.tsx ├── components │ ├── DraggableSection.tsx │ ├── Header.tsx │ ├── MonacoEditor.tsx │ ├── Preview.tsx │ ├── RawMD.tsx │ ├── Section.tsx │ ├── SectionCreation.tsx │ ├── SortableItem.tsx │ ├── common │ │ └── Button.tsx │ ├── constants.ts │ └── theme-provider.tsx ├── i18n.js ├── index.css ├── lib │ ├── fuse.ts │ └── handleMDFormat.ts ├── main.tsx ├── placeholders.json ├── store │ └── useSections.ts └── theme │ └── onedarkpro.json ├── tailwind.config.ts ├── tsconfig.json ├── tsconfig.node.json └── vite.config.ts /.eslintrc.cjs: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | root: true, 3 | env: { browser: true, es2020: true }, 4 | extends: [ 5 | 'eslint:recommended', 6 | 'plugin:@typescript-eslint/recommended', 7 | 'plugin:react-hooks/recommended' 8 | ], 9 | ignorePatterns: ['dist', '.eslintrc.cjs'], 10 | parser: '@typescript-eslint/parser', 11 | plugins: ['react-refresh'], 12 | rules: { 13 | 'react-refresh/only-export-components': [ 14 | 'warn', 15 | { allowConstantExport: true } 16 | ] 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | pnpm-debug.log* 8 | lerna-debug.log* 9 | 10 | node_modules 11 | dist 12 | dist-ssr 13 | *.local 14 | 15 | # Editor directories and files 16 | .vscode/* 17 | !.vscode/extensions.json 18 | .idea 19 | .DS_Store 20 | *.suo 21 | *.ntvs* 22 | *.njsproj 23 | *.sln 24 | *.sw? 25 | -------------------------------------------------------------------------------- /.husky/pre-commit: -------------------------------------------------------------------------------- 1 | # .husky/pre-commit 2 | npx lint-staged 3 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "tabWidth": 2, 3 | "useTabs": false, 4 | "semi": false, 5 | "singleQuote": true, 6 | "jsxSingleQuote": false, 7 | "trailingComma": "none", 8 | "bracketSpacing": true, 9 | "bracketSameLine": false, 10 | "arrowParens": "always" 11 | } 12 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # 🌐 README Generator 2 | 3 | Create beautiful documentation 4 | 5 | # Figma 6 | 7 | ![figma]()(https://www.figma.com/) 8 | 9 | ## 🚀 Getting Started 10 | 11 | To get a local copy up and running, follow these simple steps: 12 | 13 | 1. Clone the repository 14 | 15 | ```shellscript 16 | git clone https://github.com/yourusername/resources-web.git 17 | cd resources-web 18 | ``` 19 | 20 | 2. Install dependencies 21 | 22 | Choose your preferred package manager: 23 | 24 | ```shellscript 25 | # Using npm 26 | npm install 27 | 28 | # Using yarn 29 | yarn install 30 | 31 | # Using pnpm 32 | pnpm install 33 | 34 | # Using bun 35 | bun install 36 | ``` 37 | 38 | 3. Start the development server 39 | 40 | ```shellscript 41 | # Using npm 42 | npm run dev 43 | 44 | # Using yarn 45 | yarn dev 46 | 47 | # Using pnpm 48 | pnpm dev 49 | 50 | # Using bun 51 | bun run dev 52 | ``` 53 | 54 | 4. Open your browser and visit `http://localhost:4321` 55 | 56 | ### 📦 Building for Production 57 | 58 | To create a production-ready build: 59 | 60 | ```shellscript 61 | # Using npm 62 | npm run build 63 | 64 | # Using yarn 65 | yarn build 66 | 67 | # Using pnpm 68 | pnpm build 69 | 70 | # Using bun 71 | bun run build 72 | ``` 73 | 74 | ### 🔍 Preview Production Build 75 | 76 | To preview the production build locally: 77 | 78 | ```shellscript 79 | # Using npm 80 | npm run preview 81 | 82 | # Using yarn 83 | yarn preview 84 | 85 | # Using pnpm 86 | pnpm preview 87 | 88 | # Using bun 89 | bun run preview 90 | ``` 91 | 92 | This updated "Getting Started" section now provides instructions for multiple package managers (npm, yarn, pnpm, and bun), making it more versatile for different user preferences. It also includes additional sections for building and previewing the production version of the project, which can be helpful for users who want to deploy the site. 93 | 94 | Remember to adjust the port number if your Astro configuration uses a different port than the default **4321**. 95 | 96 | ## 🤝 Contributing 97 | 98 | Contributions are what make the open-source community such an amazing place to learn, inspire, and create. Any contributions you make are **greatly appreciated**. 99 | 100 | 1. Fork the Project 101 | 2. Create your Feature Branch (`git checkout -b feature/AmazingFeature`) 102 | 3. Commit your Changes (`git commit -m 'Add some AmazingFeature'`) 103 | 4. Push to the Branch (`git push origin feature/AmazingFeature`) 104 | 5. Open a Pull Request 105 | 106 | ## Contributors 107 | 108 | Thanks to all the contributors who have made this project possible! 109 | 110 | [![Contributors](https://contrib.rocks/image?repo=afordigital/palette-generator)](https://github.com/afordigital/palette-generator/graphs/contributors) 111 | 112 | --- 113 | 114 | Made with ❤️ by [comuafor 🐀](https://discord.com/invite/comuafor) 115 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | README Generator 8 | 9 | README Generator 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 32 | 33 | 37 | 38 | 39 |
40 | 41 | 42 | 43 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "make-a-readme", 3 | "version": "0.0.0", 4 | "type": "module", 5 | "scripts": { 6 | "dev": "vite", 7 | "build": "tsc && vite build", 8 | "lint": "eslint . --ext js,jsx --report-unused-disable-directives --max-warnings 0", 9 | "preview": "vite preview", 10 | "prepare": "husky" 11 | }, 12 | "dependencies": { 13 | "@dnd-kit/core": "^6.1.0", 14 | "@dnd-kit/sortable": "^8.0.0", 15 | "@dnd-kit/utilities": "^3.2.2", 16 | "@formkit/auto-animate": "^0.8.2", 17 | "@monaco-editor/react": "^4.6.0", 18 | "@pheralb/toast": "^1.0.0", 19 | "@radix-ui/react-icons": "^1.3.0", 20 | "@radix-ui/themes": "^3.1.1", 21 | "@types/react-syntax-highlighter": "^15.5.13", 22 | "fuse.js": "^7.0.0", 23 | "github-markdown-css": "^5.8.1", 24 | "i18next": "^24.2.2", 25 | "i18next-browser-languagedetector": "^8.0.3", 26 | "lodash-es": "^4.17.21", 27 | "lucide-react": "^0.379.0", 28 | "prismjs": "^1.29.0", 29 | "react": "^18.2.0", 30 | "react-dom": "^18.2.0", 31 | "react-i18next": "^15.4.1", 32 | "react-markdown": "^9.0.1", 33 | "react-split": "^2.0.14", 34 | "react-syntax-highlighter": "^15.5.0", 35 | "rehype-raw": "^7.0.0", 36 | "remark-gfm": "^4.0.0", 37 | "zustand": "^4.5.2" 38 | }, 39 | "devDependencies": { 40 | "@types/lodash-es": "^4.17.12", 41 | "@types/react": "^18.2.25", 42 | "@types/react-dom": "^18.2.11", 43 | "@typescript-eslint/eslint-plugin": "^6.14.0", 44 | "@typescript-eslint/parser": "^6.14.0", 45 | "@vitejs/plugin-react": "^4.0.3", 46 | "autoprefixer": "^10.4.16", 47 | "eslint": "^8.45.0", 48 | "eslint-plugin-react-hooks": "^4.6.0", 49 | "eslint-plugin-react-refresh": "^0.4.3", 50 | "husky": "^9.0.7", 51 | "lint-staged": "^15.2.0", 52 | "postcss": "^8.4.31", 53 | "tailwindcss": "^3.3.3", 54 | "typescript": "^5.3.2", 55 | "vite": "^4.4.5" 56 | }, 57 | "lint-staged": { 58 | "**/*.{js,jsx,ts,tsx}": [ 59 | "npx eslint --fix" 60 | ] 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /postcss.config.js: -------------------------------------------------------------------------------- 1 | export default { 2 | plugins: { 3 | tailwindcss: {}, 4 | autoprefixer: {} 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /public/favicon.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /public/images/slush.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/afordigital/make-a-readme/9797edc766c9336fb5753ce3de11a5591c2fa15c/public/images/slush.webp -------------------------------------------------------------------------------- /public/opengraph.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/afordigital/make-a-readme/9797edc766c9336fb5753ce3de11a5591c2fa15c/public/opengraph.png -------------------------------------------------------------------------------- /src/App.tsx: -------------------------------------------------------------------------------- 1 | import { Toaster } from '@pheralb/toast' 2 | import { DraggableSection } from './components/DraggableSection' 3 | import { MonacoEditor } from './components/MonacoEditor' 4 | import { Preview } from './components/Preview' 5 | import { SectionCreation } from './components/SectionCreation' 6 | import Split, { SplitProps } from 'react-split' 7 | import { Box } from '@radix-ui/themes' 8 | import { Header } from './components/Header' 9 | import { ThemeProvider } from './components/theme-provider' 10 | import { useState } from 'react' 11 | import { RawMD } from './components/RawMD' 12 | 13 | type SnapCenterProps = { 14 | sizes: number[] 15 | SNAP_TRESHOLD: number 16 | setSizes: (sizes: number[]) => void 17 | } 18 | 19 | const SNAP_TRESHOLD = 5 20 | 21 | function App() { 22 | const [markdownView, setMarkdownView] = useState(true) 23 | const [sizes, setSizes] = useState([50, 50]) 24 | 25 | const createGutterElement = (direction: 'horizontal' | 'vertical') => { 26 | const gutterElement = document.createElement('div') 27 | gutterElement.className = `gutter gutter-${direction}` 28 | return gutterElement 29 | } 30 | 31 | const handleSnapCenter = ({ 32 | sizes, 33 | SNAP_TRESHOLD, 34 | setSizes 35 | }: SnapCenterProps) => { 36 | const [leftPanel, rightPanel] = sizes 37 | 38 | if ( 39 | Math.abs(leftPanel - 50) <= SNAP_TRESHOLD && 40 | Math.abs(rightPanel - 50) <= SNAP_TRESHOLD 41 | ) { 42 | setSizes([50, 50]) 43 | } else { 44 | setSizes(sizes) 45 | } 46 | } 47 | 48 | const splitProps: SplitProps = { 49 | cursor: 'col-resize', 50 | direction: 'horizontal', 51 | expandToMin: true, 52 | gutter: (_, direction) => createGutterElement(direction), 53 | gutterAlign: 'center', 54 | gutterSize: 1, 55 | minSize: 100, 56 | sizes: sizes, 57 | onDrag: (sizes) => setSizes(sizes), 58 | onDragEnd: (sizes) => handleSnapCenter({ sizes, SNAP_TRESHOLD, setSizes }) 59 | } 60 | 61 | return ( 62 | 63 | 64 | 65 |
66 |
67 | 71 | 72 |
73 | 74 | 75 | {markdownView ? ( 76 | 77 | ) : ( 78 | 79 | )} 80 | 81 |
82 |
83 | 84 | 85 | ) 86 | } 87 | 88 | export default App 89 | -------------------------------------------------------------------------------- /src/components/DraggableSection.tsx: -------------------------------------------------------------------------------- 1 | import { 2 | DndContext, 3 | closestCenter, 4 | KeyboardSensor, 5 | PointerSensor, 6 | useSensor, 7 | useSensors, 8 | MeasuringStrategy, 9 | TouchSensor, 10 | DragEndEvent 11 | } from '@dnd-kit/core' 12 | import { 13 | arrayMove, 14 | SortableContext, 15 | sortableKeyboardCoordinates, 16 | verticalListSortingStrategy 17 | } from '@dnd-kit/sortable' 18 | 19 | import { SortableItem } from './SortableItem' 20 | import { useSectionsStore } from '../store/useSections' 21 | import { toast } from '@pheralb/toast' 22 | import placeholders from '../placeholders.json' 23 | 24 | const measuringConfig = { 25 | droppable: { 26 | strategy: MeasuringStrategy.Always 27 | } 28 | } 29 | 30 | export const DraggableSection = () => { 31 | const { 32 | sections, 33 | activeSection, 34 | setActiveSection, 35 | setSections, 36 | updateSection 37 | } = useSectionsStore() 38 | 39 | const sensors = useSensors( 40 | useSensor(PointerSensor), 41 | useSensor(KeyboardSensor, { 42 | coordinateGetter: sortableKeyboardCoordinates 43 | }), 44 | useSensor(TouchSensor) 45 | ) 46 | 47 | const handleActiveSection = (id: string) => { 48 | const sectionToUpdate = sections.find((item) => item.id === id) 49 | if (!sectionToUpdate) return 50 | setActiveSection(sectionToUpdate) 51 | } 52 | 53 | const handleResetSection = (id: string, placeholderId: string) => { 54 | console.log('reset section', id, placeholderId) 55 | const placeholder = placeholders.find((item) => item.id === placeholderId) 56 | if (!placeholder) return 57 | 58 | const selectedSection = sections.find((item) => item.id === id) 59 | if (!selectedSection) return 60 | 61 | updateSection({ 62 | ...selectedSection, 63 | title: placeholder.title, 64 | content: placeholder.content 65 | }) 66 | 67 | toast.success({ 68 | text: '✨ Section was reset successfully!' 69 | }) 70 | } 71 | 72 | const handleRemove = (id: string) => { 73 | setSections(sections.filter((section) => section.id !== id)) 74 | 75 | if (activeSection?.id === id) { 76 | setActiveSection(null) 77 | } 78 | 79 | toast.success({ 80 | text: '✨ Section was removed successfully!' 81 | }) 82 | } 83 | 84 | const handleDragEnd = (event: DragEndEvent) => { 85 | const { active, over } = event 86 | if (!over) return 87 | 88 | const activeSection = sections.find((section) => section.id === active.id) 89 | if (activeSection) setActiveSection(activeSection) 90 | 91 | if (active.id !== over.id) { 92 | const oldIndex = sections.findIndex((section) => section.id === active.id) 93 | const newIndex = sections.findIndex((section) => section.id === over.id) 94 | 95 | setSections(arrayMove(sections, oldIndex, newIndex)) 96 | } 97 | } 98 | 99 | return ( 100 | 106 | 107 | {sections.length !== 0 && ( 108 |
109 |

Current Sections

110 |
    111 | {sections.map((section) => ( 112 | 120 | ))} 121 |
122 |
123 | )} 124 |
125 |
126 | ) 127 | } 128 | -------------------------------------------------------------------------------- /src/components/Header.tsx: -------------------------------------------------------------------------------- 1 | import { Star } from "lucide-react"; 2 | import { useTranslation } from "react-i18next"; 3 | 4 | export const Header = () => { 5 | const { t, i18n } = useTranslation(); 6 | 7 | return ( 8 |
9 |
10 | file-application-logo 15 |
16 |

{t("title")}

17 |

18 | {t("description")} 19 |

20 |
21 |
22 | 28 | 29 | Star on Github 30 | 31 |
32 | ); 33 | }; 34 | -------------------------------------------------------------------------------- /src/components/MonacoEditor.tsx: -------------------------------------------------------------------------------- 1 | import { Editor, type Monaco } from '@monaco-editor/react' 2 | import { useSectionsStore } from '../store/useSections' 3 | import OneDarkPro from '../theme/onedarkpro.json' 4 | 5 | export const MonacoEditor = () => { 6 | const { activeSection, updateSection, setActiveSection } = useSectionsStore() 7 | 8 | const handleUpdateSection = (value: string) => { 9 | if (!activeSection) return 10 | 11 | const newActiveSection = { 12 | ...activeSection, 13 | content: value 14 | } 15 | 16 | setActiveSection(newActiveSection) 17 | updateSection(newActiveSection) 18 | } 19 | 20 | const handleEditorDidMount = (monaco: Monaco) => { 21 | monaco.editor.defineTheme('OneDarkPro', { 22 | base: 'vs-dark', 23 | inherit: true, 24 | ...OneDarkPro 25 | }) 26 | } 27 | 28 | return ( 29 | { 46 | if (!value) return 47 | handleUpdateSection(value) 48 | }} 49 | /> 50 | ) 51 | } 52 | -------------------------------------------------------------------------------- /src/components/Preview.tsx: -------------------------------------------------------------------------------- 1 | import ReactMarkdown from "react-markdown"; 2 | import type { ExtraProps } from "react-markdown"; 3 | import { useSectionsStore } from "../store/useSections"; 4 | import remarkGfm from "remark-gfm"; 5 | import rehypeRaw from "rehype-raw"; 6 | import SyntaxHighlighter from "react-syntax-highlighter"; 7 | import { dracula } from "react-syntax-highlighter/dist/esm/styles/prism"; 8 | 9 | export const Preview = () => { 10 | const { sections } = useSectionsStore(); 11 | 12 | const markdown = sections.map((section) => section.content).join("\n"); 13 | 14 | // TODO: Change this markdown as an output to copy code 15 | // Investigate onClick={() => {navigator.clipboard.writeText(this.state.textToCopy)}} 16 | 17 | return ( 18 | 32 | {String(children).replace(/\n$/, "")} 33 | 34 | ) : ( 35 | 36 | {children} 37 | 38 | ); 39 | }, 40 | // eslint-disable-next-line @typescript-eslint/no-unused-vars 41 | a({ node, ...props }) { 42 | return ; 43 | }, 44 | }} 45 | > 46 | {markdown} 47 | 48 | ); 49 | }; 50 | -------------------------------------------------------------------------------- /src/components/RawMD.tsx: -------------------------------------------------------------------------------- 1 | import { handleMDFormart } from "../lib/handleMDFormat"; 2 | import { useSectionsStore } from "../store/useSections"; 3 | 4 | export const RawMD = () => { 5 | const { sections } = useSectionsStore(); 6 | 7 | return ( 8 |