├── src ├── utils │ ├── index.js │ └── color.js ├── index.css ├── components │ ├── Editor │ │ ├── Editor.jsx │ │ ├── get-extentions.js │ │ └── themes.jsx │ ├── Preview.jsx │ ├── BackgroundPicker.jsx │ └── Toolbar.jsx ├── store │ ├── store.js │ └── slices │ │ └── appStateSlice.js ├── main.jsx ├── App.css ├── contexts │ └── EditorContext.jsx ├── App.jsx └── data │ ├── gradient.js │ └── color.js ├── postcss.config.js ├── vite.config.js ├── .gitignore ├── tailwind.config.js ├── index.html ├── README (1).md ├── README.md ├── .eslintrc.cjs ├── public └── vite.svg └── package.json /src/utils/index.js: -------------------------------------------------------------------------------- 1 | export * from "./colors"; 2 | -------------------------------------------------------------------------------- /src/index.css: -------------------------------------------------------------------------------- 1 | @tailwind base; 2 | @tailwind components; 3 | @tailwind utilities; -------------------------------------------------------------------------------- /postcss.config.js: -------------------------------------------------------------------------------- 1 | export default { 2 | plugins: { 3 | tailwindcss: {}, 4 | autoprefixer: {}, 5 | }, 6 | } 7 | -------------------------------------------------------------------------------- /src/utils/color.js: -------------------------------------------------------------------------------- 1 | import Color from "color"; 2 | 3 | export const isDark = (color) => { 4 | const c = Color(color); 5 | return c.isDark(); 6 | }; 7 | -------------------------------------------------------------------------------- /src/components/Editor/Editor.jsx: -------------------------------------------------------------------------------- 1 | import CodeMirror from "@uiw/react-codemirror"; 2 | 3 | const Editor = ({ ...rest }) => { 4 | return ; 5 | }; 6 | 7 | export default Editor; 8 | -------------------------------------------------------------------------------- /vite.config.js: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'vite' 2 | import react from '@vitejs/plugin-react' 3 | 4 | // https://vitejs.dev/config/ 5 | export default defineConfig({ 6 | plugins: [react()], 7 | }) 8 | -------------------------------------------------------------------------------- /src/store/store.js: -------------------------------------------------------------------------------- 1 | import { configureStore } from "@reduxjs/toolkit"; 2 | import appStateReducer from "./slices/appStateSlice"; 3 | 4 | export const store = configureStore({ 5 | reducer: { 6 | appState: appStateReducer, 7 | } 8 | }) -------------------------------------------------------------------------------- /.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 | 26 | .vercel 27 | -------------------------------------------------------------------------------- /tailwind.config.js: -------------------------------------------------------------------------------- 1 | const colors = require("tailwindcss/colors"); 2 | 3 | 4 | /** @type {import('tailwindcss').Config} */ 5 | export default { 6 | content: [ 7 | "./index.html", 8 | "./src/**/*.{js,ts,jsx,tsx}", 9 | ], 10 | darkMode: "class", 11 | theme: { 12 | extend: { 13 | colors: { 14 | primary: colors.indigo, 15 | }, 16 | }, 17 | }, 18 | plugins: [], 19 | } 20 | 21 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Vite + React 8 | 9 | 10 |
11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /README (1).md: -------------------------------------------------------------------------------- 1 | 2 | # CodeFrame 3 | CodeFrame is a powerful and intuitive code snippet creation tool. 4 | 5 | ## Tech Stack 6 | 7 | - React 8 | - Redux-Toolkit 9 | - Codemirror 10 | ## Features 11 | 12 | - Gradient Selection 13 | - Color Selection 14 | - Dark Mode 15 | - Line Number 16 | - Font Size 17 | - Padding 18 | - Reset 19 | - Export you snippet 20 | 21 | 22 | 23 | 24 | ## Authors 25 | 26 | - [@Robo707](https://github.com/Robo707) (Himanshu Sharma) 27 | 28 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | # CodeFrame 3 | CodeFrame is a powerful and intuitive code snippet creation tool. 4 | 5 | 6 | 7 | ## Tech Stack 8 | 9 | - React 10 | - Redux-Toolkit 11 | - Codemirror 12 | ## Features 13 | 14 | - Gradient Selection 15 | - Color Selection 16 | - Dark Mode 17 | - Line Number 18 | - Font Size 19 | - Padding 20 | - Reset 21 | - Export you snippet 22 | 23 | 24 | 25 | 26 | ## Authors 27 | 28 | - [@Robo707](https://github.com/Robo707) (Himanshu Sharma) 29 | 30 | -------------------------------------------------------------------------------- /.eslintrc.cjs: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | env: { browser: true, es2020: true }, 3 | extends: [ 4 | 'eslint:recommended', 5 | 'plugin:react/recommended', 6 | 'plugin:react/jsx-runtime', 7 | 'plugin:react-hooks/recommended', 8 | ], 9 | parserOptions: { ecmaVersion: 'latest', sourceType: 'module' }, 10 | settings: { react: { version: '18.2' } }, 11 | plugins: ['react-refresh'], 12 | rules: { 13 | 'react-refresh/only-export-components': 'warn', 14 | }, 15 | } 16 | -------------------------------------------------------------------------------- /src/main.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import ReactDOM from 'react-dom/client' 3 | import App from './App.jsx' 4 | import './index.css' 5 | import { EditorProvider } from './contexts/EditorContext.jsx' 6 | import { store } from './store/store.js' 7 | import { Provider } from 'react-redux' 8 | 9 | ReactDOM.createRoot(document.getElementById('root')).render( 10 | 11 | 12 | 13 | 14 | , 15 | 16 | 17 | 18 | ) 19 | -------------------------------------------------------------------------------- /src/App.css: -------------------------------------------------------------------------------- 1 | @import 'tailwindcss/base'; 2 | @import 'tailwindcss/components'; 3 | @import 'tailwindcss/utilities'; 4 | 5 | 6 | 7 | 8 | @import url("https://fonts.googleapis.com/css2?family=Roboto+Mono:ital,wght@0,400;0,500;1,500&display=swap"); 9 | 10 | html { 11 | color-scheme: dark; 12 | } 13 | 14 | @layer base { 15 | body { 16 | @apply bg-[#000000] text-gray-300; 17 | } 18 | } 19 | 20 | .cm-editor.cm-focused { 21 | outline: none !important; 22 | } 23 | 24 | .cm-editor { 25 | padding: 0.5em; 26 | } 27 | .cm-editor .cm-scroller { 28 | font-family: "Roboto Mono", monospace; 29 | } 30 | -------------------------------------------------------------------------------- /src/contexts/EditorContext.jsx: -------------------------------------------------------------------------------- 1 | import { 2 | createContext, 3 | useContext, 4 | useRef, 5 | } from "react"; 6 | 7 | 8 | export const EditorContext = createContext(null); 9 | export const EditorProvider = ({ children }) => { 10 | 11 | const canvasRef = useRef(null); 12 | return ( 13 | 18 | {children} 19 | 20 | ) 21 | } 22 | export const useEditor = () => { 23 | const context = useContext(EditorContext); 24 | if (!context) { 25 | throw "Editor context is not initialized!"; 26 | } 27 | return context; 28 | }; -------------------------------------------------------------------------------- /src/App.jsx: -------------------------------------------------------------------------------- 1 | import './App.css' 2 | import Preview from './components/Preview' 3 | import Toolbar from "./components/Toolbar"; 4 | import { AiOutlineGithub } from "react-icons/ai" 5 | 6 | function App() { 7 | 8 | return ( 9 | <> 10 | 11 |
12 | 13 |
14 | 15 | 16 |
17 | 18 |
19 | 20 | 21 |
22 | Made with ❤️ by Himanshu Sharma 23 |
24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | ) 32 | } 33 | 34 | export default App 35 | const GitHubStarButton = () => { 36 | return ( 37 |
38 | Star on Github 39 | 40 |
41 | ); 42 | }; 43 | -------------------------------------------------------------------------------- /src/store/slices/appStateSlice.js: -------------------------------------------------------------------------------- 1 | import { createSlice } from "@reduxjs/toolkit"; 2 | import { gradients } from "../../data/gradient"; 3 | 4 | const DEFAULT_JS_VALUE = `function hello() { 5 | console.log("Hello, World!"); 6 | } 7 | 8 | hello();`; 9 | 10 | export const initialState = { 11 | filename: "Untitled", 12 | darkMode: true, 13 | dropShadow: true, 14 | showTitle: false, 15 | showLineNumber: true, 16 | bgBlur: true, 17 | fontSize: "16px", 18 | language: "javascript", 19 | padding: "36px", 20 | title: "Title Text", 21 | code: DEFAULT_JS_VALUE, 22 | backgroundImage: gradients[0].gradient, 23 | backgroundThumb: gradients[0].gradient, 24 | backgroundColor: gradients[0].color, 25 | renderFormat: "png", 26 | }; 27 | 28 | const appStateSlice = createSlice({ 29 | name: "appState", 30 | initialState, 31 | reducers: { 32 | updateAppState: (state, action) => { 33 | return { ...state, ...action.payload }; 34 | }, 35 | }, 36 | }) 37 | 38 | export const { updateAppState } = appStateSlice.actions; 39 | export default appStateSlice.reducer; -------------------------------------------------------------------------------- /public/vite.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "codeframe", 3 | "private": true, 4 | "version": "0.0.0", 5 | "type": "module", 6 | "scripts": { 7 | "dev": "vite", 8 | "build": "vite build", 9 | "lint": "eslint src --ext js,jsx --report-unused-disable-directives --max-warnings 0", 10 | "preview": "vite preview" 11 | }, 12 | "dependencies": { 13 | "@codemirror/lang-cpp": "^6.0.2", 14 | "@codemirror/lang-css": "^6.0.2", 15 | "@codemirror/lang-html": "^6.4.2", 16 | "@codemirror/lang-java": "^6.0.1", 17 | "@codemirror/lang-javascript": "^6.1.3", 18 | "@codemirror/lang-json": "^6.0.1", 19 | "@codemirror/lang-lezer": "^6.0.1", 20 | "@codemirror/lang-markdown": "^6.0.5", 21 | "@codemirror/lang-php": "^6.0.1", 22 | "@codemirror/lang-python": "^6.1.1", 23 | "@codemirror/lang-rust": "^6.0.1", 24 | "@codemirror/lang-sql": "^6.4.0", 25 | "@codemirror/lang-wast": "^6.0.1", 26 | "@codemirror/lang-xml": "^6.0.2", 27 | "@codemirror/state": "^6.2.0", 28 | "@codemirror/theme-one-dark": "^6.1.0", 29 | "@lezer/highlight": "^1.1.3", 30 | "@radix-ui/react-popover": "^1.0.6", 31 | "@radix-ui/react-select": "^0.1.1", 32 | "@radix-ui/react-switch": "^1.0.3", 33 | "@reduxjs/toolkit": "^1.9.5", 34 | "@uiw/codemirror-themes": "^4.19.9", 35 | "@uiw/react-codemirror": "^4.19.9", 36 | "classnames": "^2.3.2", 37 | "color": "^4.2.3", 38 | "html-to-image": "^1.11.11", 39 | "react": "^18.2.0", 40 | "react-dom": "^18.2.0", 41 | "react-icons": "^4.7.1", 42 | "react-redux": "^8.1.1" 43 | }, 44 | "devDependencies": { 45 | "@types/react": "^18.0.37", 46 | "@types/react-dom": "^18.0.11", 47 | "@vitejs/plugin-react": "^4.0.0", 48 | "autoprefixer": "^10.4.14", 49 | "eslint": "^8.38.0", 50 | "eslint-plugin-react": "^7.32.2", 51 | "eslint-plugin-react-hooks": "^4.6.0", 52 | "eslint-plugin-react-refresh": "^0.3.4", 53 | "postcss": "^8.4.24", 54 | "tailwindcss": "^3.3.2", 55 | "vite": "^4.3.9" 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /src/components/Editor/get-extentions.js: -------------------------------------------------------------------------------- 1 | 2 | import { javascript } from "@codemirror/lang-javascript"; 3 | import { python } from "@codemirror/lang-python"; 4 | import { html } from "@codemirror/lang-html"; 5 | import { rust } from "@codemirror/lang-rust"; 6 | import { css } from "@codemirror/lang-css"; 7 | import { markdown } from "@codemirror/lang-markdown"; 8 | import { cpp } from "@codemirror/lang-cpp"; 9 | import { java } from "@codemirror/lang-java"; 10 | import { json } from "@codemirror/lang-json"; 11 | import { xml } from "@codemirror/lang-xml"; 12 | import { wast } from "@codemirror/lang-wast"; 13 | import { lezer } from "@codemirror/lang-lezer"; 14 | import { sql } from "@codemirror/lang-sql"; 15 | import { php } from "@codemirror/lang-php"; 16 | 17 | export const getExtentions = (language) => { 18 | switch (language) { 19 | case "json": 20 | return [json()]; 21 | case "javascript": 22 | return [javascript()]; 23 | case "coffeescript": 24 | return [javascript()]; 25 | case "jsx": 26 | return [javascript({ jsx: true, typescript: true })]; 27 | case "typescript": 28 | return [javascript({ typescript: true })]; 29 | case "python": 30 | return [python()]; 31 | case "html": 32 | return [html()]; 33 | case "markdown": 34 | return [markdown()]; 35 | case "css": 36 | return [css()]; 37 | case "scss": 38 | return [css()]; 39 | case "rust": 40 | return [rust()]; 41 | case "c++": 42 | return [cpp()]; 43 | case "c": 44 | return [cpp()]; 45 | case "c#": 46 | return [cpp()]; 47 | case "swift": 48 | return [cpp()]; 49 | case "java": 50 | return [java()]; 51 | case "xml": 52 | return [xml()]; 53 | case "wast": 54 | return [wast()]; 55 | case "lezer": 56 | return [lezer()]; 57 | case "sql": 58 | return [sql()]; 59 | case "php": 60 | return [php()]; 61 | default: 62 | return undefined; 63 | } 64 | }; 65 | -------------------------------------------------------------------------------- /src/components/Editor/themes.jsx: -------------------------------------------------------------------------------- 1 | import { createTheme } from "@uiw/codemirror-themes"; 2 | import { tags } from "@lezer/highlight"; 3 | 4 | const defaultSettings = { 5 | background: "transparent", 6 | foreground: "#4D4D4C", 7 | caret: "#facc15", 8 | selection: "#1e293b80", 9 | selectionMatch: "transparent", 10 | gutterBackground: "transparent", 11 | gutterForeground: "#4D4D4C", 12 | gutterBorder: "transparent", 13 | lineHighlight: "transparent", 14 | }; 15 | 16 | export const lightTheme = createTheme({ 17 | theme: "light", 18 | settings: { 19 | ...defaultSettings, 20 | foreground: "#1e293b", 21 | gutterForeground: "#1e293b", 22 | selection: "rgba(0,0,0,0.1)", 23 | }, 24 | styles: [ 25 | { tag: tags.keyword, color: "#1a6eff" }, 26 | { tag: tags.comment, color: "#475569" }, 27 | { tag: tags.bracket, color: "#FB923C" }, 28 | { tag: tags.angleBracket, color: "#334155" }, 29 | { tag: tags.variableName, color: "#334155" }, 30 | { tag: tags.string, color: "#379d6c" }, 31 | { tag: tags.number, color: "#ff4656" }, 32 | { tag: tags.punctuation, color: "#1a6eff" }, 33 | { tag: tags.squareBracket, color: "#ff9f46" }, 34 | { tag: tags.tagName, color: "#ff4656" }, 35 | { tag: tags.attributeName, color: "#a327e2" }, 36 | { tag: tags.propertyName, color: "#a327e2" }, 37 | { tag: tags.typeName, color: "#5cabff" }, 38 | { tag: tags.bool, color: "#ff4656" }, 39 | ], 40 | }); 41 | 42 | export const darkTheme = createTheme({ 43 | theme: "dark", 44 | settings: { 45 | ...defaultSettings, 46 | foreground: "#fff", 47 | gutterForeground: "#fff", 48 | selection: "rgba(255,255,255,0.1)", 49 | }, 50 | styles: [ 51 | { tag: tags.keyword, color: "#4ff0ff" }, 52 | { tag: tags.comment, color: "#94a3b8" }, 53 | { tag: tags.bracket, color: "#ffc248" }, 54 | { tag: tags.attributeName, color: "#d47dff" }, 55 | { tag: tags.angleBracket, color: "#ffffff" }, 56 | { tag: tags.variableName, color: "#ffffff" }, 57 | { tag: tags.string, color: "#c0ff5b" }, 58 | { tag: tags.number, color: "#ff808a" }, 59 | { tag: tags.bool, color: "#ff808a" }, 60 | { tag: tags.punctuation, color: "#4ff0ff" }, 61 | { tag: tags.tagName, color: "#ff808a" }, 62 | { tag: tags.squareBracket, color: "#ff808a" }, 63 | { tag: tags.propertyName, color: "#d47dff" }, 64 | { tag: tags.typeName, color: "#5cabff" }, 65 | ], 66 | }); 67 | -------------------------------------------------------------------------------- /src/data/gradient.js: -------------------------------------------------------------------------------- 1 | export const gradients = [ 2 | { 3 | color: "#FBAB7E", 4 | gradient: "linear-gradient(62deg, #FBAB7E 0%, #F7CE68 100%)", 5 | }, 6 | 7 | { 8 | color: "#8BC6EC", 9 | gradient: "linear-gradient(135deg, #8BC6EC 0%, #9599E2 100%)", 10 | }, 11 | { 12 | color: "#4158D0", 13 | gradient: "linear-gradient(43deg, #4158D0 0%, #C850C0 46%, #FFCC70 100%)", 14 | }, 15 | { 16 | color: "#0093E9", 17 | gradient: "linear-gradient(160deg, #0093E9 0%, #80D0C7 100%)", 18 | }, 19 | { 20 | color: "#8EC5FC", 21 | gradient: "linear-gradient(62deg, #8EC5FC 0%, #E0C3FC 100%)", 22 | }, 23 | { 24 | color: "#D9AFD9", 25 | gradient: "linear-gradient(0deg, #D9AFD9 0%, #97D9E1 100%)", 26 | }, 27 | { 28 | color: "#00DBDE", 29 | gradient: "linear-gradient(90deg, #00DBDE 0%, #FC00FF 100%)", 30 | }, 31 | 32 | { 33 | color: "#85FFBD", 34 | gradient: "linear-gradient(45deg, #85FFBD 0%, #FFFB7D 100%)", 35 | }, 36 | 37 | { 38 | color: "#FFDEE9", 39 | gradient: "linear-gradient(0deg, #FFDEE9 0%, #B5FFFC 100%)", 40 | }, 41 | { 42 | color: "#08AEEA", 43 | gradient: "linear-gradient(0deg, #08AEEA 0%, #2AF598 100%)", 44 | }, 45 | { 46 | color: "#52ACFF", 47 | gradient: "linear-gradient(180deg, #52ACFF 25%, #FFE32C 100%)", 48 | }, 49 | { 50 | color: "#FFE53B", 51 | gradient: "linear-gradient(147deg, #FFE53B 0%, #FF2525 74%)", 52 | }, 53 | { 54 | color: "#21D4FD", 55 | gradient: "linear-gradient(19deg, #21D4FD 0%, #B721FF 100%)", 56 | }, 57 | { 58 | color: "#3EECAC", 59 | gradient: "linear-gradient(19deg, #3EECAC 0%, #EE74E1 100%)", 60 | }, 61 | { 62 | color: "#FA8BFF", 63 | gradient: "linear-gradient(45deg, #FA8BFF 0%, #2BD2FF 52%, #2BFF88 90%)", 64 | }, 65 | { 66 | color: "#FF9A8B", 67 | gradient: "linear-gradient(90deg, #FF9A8B 0%, #FF6A88 55%, #FF99AC 100%)", 68 | }, 69 | { 70 | color: "#FBDA61", 71 | gradient: "linear-gradient(45deg, #FBDA61 0%, #FF5ACD 100%)", 72 | }, 73 | { 74 | color: "#F4D03F", 75 | gradient: "linear-gradient(132deg, #F4D03F 0%, #16A085 100%)", 76 | }, 77 | { 78 | color: "#A9C9FF", 79 | gradient: "linear-gradient(180deg, #A9C9FF 0%, #FFBBEC 100%)", 80 | }, 81 | { 82 | color: "#74EBD5", 83 | gradient: "linear-gradient(90deg, #74EBD5 0%, #9FACE6 100%)", 84 | }, 85 | { 86 | color: "#FAACA8", 87 | gradient: "linear-gradient(19deg, #FAACA8 0%, #DDD6F3 100%)", 88 | }, 89 | { 90 | color: "#FAD961", 91 | gradient: "linear-gradient(90deg, #FAD961 0%, #F76B1C 100%)", 92 | }, 93 | { 94 | color: "#FEE140", 95 | gradient: "linear-gradient(90deg, #FEE140 0%, #FA709A 100%)", 96 | }, 97 | { 98 | color: "#FF3CAC", 99 | gradient: "linear-gradient(225deg, #FF3CAC 0%, #784BA0 50%, #2B86C5 100%)", 100 | }, 101 | ]; 102 | -------------------------------------------------------------------------------- /src/data/color.js: -------------------------------------------------------------------------------- 1 | export const colors = [ 2 | "transparent", 3 | "#ffffff", 4 | "#000000", 5 | "#f8fafc", 6 | "#f1f5f9", 7 | "#e2e8f0", 8 | "#cbd5e1", 9 | "#94a3b8", 10 | "#64748b", 11 | "#475569", 12 | "#334155", 13 | "#1e293b", 14 | "#0f172a", 15 | "#f9fafb", 16 | "#f3f4f6", 17 | "#e5e7eb", 18 | "#d1d5db", 19 | "#9ca3af", 20 | "#6b7280", 21 | "#4b5563", 22 | "#374151", 23 | "#1f2937", 24 | "#111827", 25 | "#fafafa", 26 | "#f4f4f5", 27 | "#e4e4e7", 28 | "#d4d4d8", 29 | "#a1a1aa", 30 | "#71717a", 31 | "#52525b", 32 | "#3f3f46", 33 | "#27272a", 34 | "#18181b", 35 | "#fafafa", 36 | "#f5f5f5", 37 | "#e5e5e5", 38 | "#d4d4d4", 39 | "#a3a3a3", 40 | "#737373", 41 | "#525252", 42 | "#404040", 43 | "#262626", 44 | "#171717", 45 | "#fafaf9", 46 | "#f5f5f4", 47 | "#e7e5e4", 48 | "#d6d3d1", 49 | "#a8a29e", 50 | "#78716c", 51 | "#57534e", 52 | "#44403c", 53 | "#292524", 54 | "#1c1917", 55 | "#fef2f2", 56 | "#fee2e2", 57 | "#fecaca", 58 | "#fca5a5", 59 | "#f87171", 60 | "#ef4444", 61 | "#dc2626", 62 | "#b91c1c", 63 | "#991b1b", 64 | "#7f1d1d", 65 | "#fff7ed", 66 | "#ffedd5", 67 | "#fed7aa", 68 | "#fdba74", 69 | "#fb923c", 70 | "#f97316", 71 | "#ea580c", 72 | "#c2410c", 73 | "#9a3412", 74 | "#7c2d12", 75 | "#fffbeb", 76 | "#fef3c7", 77 | "#fde68a", 78 | "#fcd34d", 79 | "#fbbf24", 80 | "#f59e0b", 81 | "#d97706", 82 | "#b45309", 83 | "#92400e", 84 | "#78350f", 85 | "#fefce8", 86 | "#fef9c3", 87 | "#fef08a", 88 | "#fde047", 89 | "#facc15", 90 | "#eab308", 91 | "#ca8a04", 92 | "#a16207", 93 | "#854d0e", 94 | "#713f12", 95 | "#f7fee7", 96 | "#ecfccb", 97 | "#d9f99d", 98 | "#bef264", 99 | "#a3e635", 100 | "#84cc16", 101 | "#65a30d", 102 | "#4d7c0f", 103 | "#3f6212", 104 | "#365314", 105 | "#f0fdf4", 106 | "#dcfce7", 107 | "#bbf7d0", 108 | "#86efac", 109 | "#4ade80", 110 | "#22c55e", 111 | "#16a34a", 112 | "#15803d", 113 | "#166534", 114 | "#14532d", 115 | "#ecfdf5", 116 | "#d1fae5", 117 | "#a7f3d0", 118 | "#6ee7b7", 119 | "#34d399", 120 | "#10b981", 121 | "#059669", 122 | "#047857", 123 | "#065f46", 124 | "#064e3b", 125 | "#f0fdfa", 126 | "#ccfbf1", 127 | "#99f6e4", 128 | "#5eead4", 129 | "#2dd4bf", 130 | "#14b8a6", 131 | "#0d9488", 132 | "#0f766e", 133 | "#115e59", 134 | "#134e4a", 135 | "#ecfeff", 136 | "#cffafe", 137 | "#a5f3fc", 138 | "#67e8f9", 139 | "#22d3ee", 140 | "#06b6d4", 141 | "#0891b2", 142 | "#0e7490", 143 | "#155e75", 144 | "#164e63", 145 | "#f0f9ff", 146 | "#e0f2fe", 147 | "#bae6fd", 148 | "#7dd3fc", 149 | "#38bdf8", 150 | "#0ea5e9", 151 | "#0284c7", 152 | "#0369a1", 153 | "#075985", 154 | "#0c4a6e", 155 | "#eff6ff", 156 | "#dbeafe", 157 | "#bfdbfe", 158 | "#93c5fd", 159 | "#60a5fa", 160 | "#3b82f6", 161 | "#2563eb", 162 | "#1d4ed8", 163 | "#1e40af", 164 | "#1e3a8a", 165 | "#eef2ff", 166 | "#e0e7ff", 167 | "#c7d2fe", 168 | "#a5b4fc", 169 | "#818cf8", 170 | "#6366f1", 171 | "#4f46e5", 172 | "#4338ca", 173 | "#3730a3", 174 | "#312e81", 175 | "#f5f3ff", 176 | "#ede9fe", 177 | "#ddd6fe", 178 | "#c4b5fd", 179 | "#a78bfa", 180 | "#8b5cf6", 181 | "#7c3aed", 182 | "#6d28d9", 183 | "#5b21b6", 184 | "#4c1d95", 185 | "#faf5ff", 186 | "#f3e8ff", 187 | "#e9d5ff", 188 | "#d8b4fe", 189 | "#c084fc", 190 | "#a855f7", 191 | "#9333ea", 192 | "#7e22ce", 193 | "#6b21a8", 194 | "#581c87", 195 | "#fdf4ff", 196 | "#fae8ff", 197 | "#f5d0fe", 198 | "#f0abfc", 199 | "#e879f9", 200 | "#d946ef", 201 | "#c026d3", 202 | "#a21caf", 203 | "#86198f", 204 | "#701a75", 205 | "#fdf2f8", 206 | "#fce7f3", 207 | "#fbcfe8", 208 | "#f9a8d4", 209 | "#f472b6", 210 | "#ec4899", 211 | "#db2777", 212 | "#be185d", 213 | "#9d174d", 214 | "#831843", 215 | "#fff1f2", 216 | "#ffe4e6", 217 | "#fecdd3", 218 | "#fda4af", 219 | "#fb7185", 220 | "#f43f5e", 221 | "#e11d48", 222 | "#be123c", 223 | "#9f1239", 224 | "#881337", 225 | ]; 226 | -------------------------------------------------------------------------------- /src/components/Preview.jsx: -------------------------------------------------------------------------------- 1 | import { useCallback, useEffect, useMemo, useState } from "react"; 2 | import { useEditor } from "../contexts/EditorContext"; 3 | import Editor from "./Editor/Editor"; 4 | import { getExtentions } from "./Editor/get-extentions"; 5 | import { darkTheme, lightTheme } from "./Editor/themes"; 6 | 7 | import { useDispatch, useSelector } from "react-redux"; 8 | import { updateAppState } from "../store/slices/appStateSlice"; 9 | 10 | const Preview = () => { 11 | const appState = useSelector((state) => state.appState); 12 | const { canvasRef } = useEditor(); 13 | return ( 14 |
18 |
22 |
31 | 32 |
33 |
34 |
35 | ) 36 | } 37 | 38 | export default Preview 39 | 40 | 41 | const Window = () => { 42 | const [bgWidth, setBgWidth] = useState(0); 43 | const [bgHeight, setBgHeight] = useState(0); 44 | const [extentions, setExtentions] = useState([]); 45 | const { canvasRef } = useEditor(); 46 | console.log(canvasRef) 47 | const appState = useSelector((state) => state.appState); 48 | const dispatch = useDispatch(); 49 | 50 | const theme = useMemo( 51 | () => (appState.darkMode ? darkTheme : lightTheme), 52 | [appState.darkMode] 53 | ); 54 | 55 | const onCodeChange = useCallback( 56 | (value) => { 57 | dispatch(updateAppState({ 58 | code: value 59 | })) 60 | }, 61 | [appState, dispatch] 62 | ); 63 | 64 | useEffect(() => { 65 | setExtentions(getExtentions(appState.language)); 66 | }, [appState.language]); 67 | 68 | useEffect(() => { 69 | if (canvasRef && canvasRef.current) { 70 | setBgWidth(canvasRef.current.clientWidth); 71 | setBgHeight(canvasRef.current.clientHeight); 72 | } 73 | }, [appState.padding, appState.code, appState.title, canvasRef]); 74 | 75 | return ( 76 |
85 |
99 | 100 | 116 |
117 | ); 118 | }; 119 | 120 | 121 | const WindowTitleBar = () => { 122 | const appState = useSelector((state) => state.appState); 123 | const dispatch = useDispatch(); 124 | 125 | return ( 126 |
127 |
128 |
129 |
130 |
131 |
132 | { 137 | dispatch(updateAppState({ 138 | filename: e.target.value 139 | })) 140 | }} 141 | className="w-auto min-w-0 bg-transparent outline-none p-0 border-0 text-center text-gray-600 dark:text-gray-300 flex-1" 142 | /> 143 |
144 |
145 |
146 |
147 |
148 |
149 | ); 150 | }; 151 | -------------------------------------------------------------------------------- /src/components/BackgroundPicker.jsx: -------------------------------------------------------------------------------- 1 | import * as Popover from "@radix-ui/react-popover"; 2 | import { useCallback, useState } from "react"; 3 | import { colors } from "../data/color"; 4 | import { gradients } from "../data/gradient"; 5 | import { useDispatch, useSelector } from "react-redux"; 6 | import { updateAppState } from "../store/slices/appStateSlice"; 7 | 8 | 9 | export const backgroundWindowTabs = [ 10 | { 11 | id: "colors", 12 | label: "Colors", 13 | }, 14 | { 15 | id: "gradients", 16 | label: "Gradients", 17 | }, 18 | 19 | ]; 20 | 21 | const BackgroundPicker = () => { 22 | 23 | const [tab, setTab] = useState(backgroundWindowTabs[0].id); 24 | const dispatch = useDispatch(); 25 | const appState = useSelector((state) => state.appState); 26 | 27 | return ( 28 |
29 | 32 | 33 | 34 | 58 | 59 | ))} 60 | 61 |
62 | { 63 | tab === "colors" ? ( 64 | 65 | ) : 66 | } 67 |
68 | 69 | 70 |
71 | ); 72 | }; 73 | export default BackgroundPicker 74 | 75 | 76 | 77 | const ColorsPanel = () => { 78 | const dispatch = useDispatch(); 79 | const appState = useSelector((state) => state.appState); 80 | const setColor = useCallback( 81 | (color) => { 82 | dispatch(updateAppState({ 83 | ...appState, 84 | backgroundImage: undefined, 85 | backgroundThumb: undefined, 86 | backgroundColor: color, 87 | }) 88 | ); 89 | }, 90 | [appState, dispatch] 91 | ); 92 | return ( 93 |
    94 | {colors.map((color, i) => ( 95 |
  • 96 | 107 |
  • 108 | ))} 109 |
110 | ); 111 | }; 112 | 113 | const GradientsPanel = () => { 114 | const dispatch = useDispatch(); 115 | const appState = useSelector((state) => state.appState); 116 | 117 | const setGradient = useCallback( 118 | (gradient) => { 119 | dispatch( 120 | updateAppState({ 121 | ...appState, 122 | backgroundColor: gradient.color, 123 | backgroundImage: gradient.gradient, 124 | backgroundThumb: gradient.gradient, 125 | }) 126 | ) 127 | }, 128 | [appState, dispatch] 129 | ); 130 | 131 | return ( 132 |
    133 | {gradients.map((gradient, i) => ( 134 |
  • 135 | 145 |
  • 146 | ))} 147 |
148 | ); 149 | }; 150 | -------------------------------------------------------------------------------- /src/components/Toolbar.jsx: -------------------------------------------------------------------------------- 1 | import classNames from "classnames"; 2 | import { MdCheck } from "react-icons/md"; 3 | import BackgroundPicker from "./BackgroundPicker"; 4 | import * as Select from "@radix-ui/react-select"; 5 | import { toPng, toJpeg, toSvg } from "html-to-image"; 6 | import { updateAppState, initialState } from "../store/slices/appStateSlice"; 7 | import { useDispatch, useSelector } from "react-redux"; 8 | import { useEditor } from "../contexts/EditorContext"; 9 | import { useCallback } from "react"; 10 | 11 | 12 | 13 | 14 | const ToolBar = () => { 15 | 16 | const appState = useSelector((state) => state.appState) 17 | const dispatch = useDispatch(); 18 | const { canvasRef } = useEditor(); 19 | console.log(canvasRef) 20 | 21 | const onReset = () => { 22 | dispatch(updateAppState(initialState)); 23 | 24 | } 25 | 26 | const onExport = useCallback(async () => { 27 | if (!canvasRef.current) { 28 | return; 29 | } 30 | 31 | var imageUrl = null; 32 | const fileExtension = `.${appState.renderFormat.toLowerCase()}`; 33 | 34 | switch (fileExtension) { 35 | case ".jpeg": 36 | imageUrl = await toJpeg(canvasRef.current); 37 | break; 38 | case ".svg": 39 | imageUrl = await toSvg(canvasRef.current); 40 | break; 41 | default: 42 | imageUrl = await toPng(canvasRef.current); 43 | break; 44 | } 45 | 46 | if (!imageUrl) return; 47 | const filename = `${appState.filename || "Untitled"}${fileExtension}`; 48 | const link = document.createElement("a"); 49 | link.download = filename; 50 | link.href = imageUrl; 51 | link.click(); 52 | }, [appState.renderFormat, appState.filename, canvasRef]); 53 | 54 | 55 | return ( 56 |
57 |
58 |
59 |
60 | 61 | 65 | dispatch(updateAppState({ 66 | ...appState, darkMode: !appState.darkMode 67 | })) 68 | } 69 | /> 70 | 74 | dispatch(updateAppState({ 75 | showLineNumber: !appState.showLineNumber, 76 | })) 77 | } 78 | /> 79 | { 94 | dispatch(updateAppState({ 95 | ...appState, 96 | fontSize: value, 97 | })) 98 | 99 | }} 100 | /> 101 | { 106 | dispatch(updateAppState({ 107 | ...appState, 108 | padding: value, 109 | })) 110 | 111 | }} 112 | /> 113 | (a < b ? -1 : 1))} 138 | value={appState.language} 139 | onChange={(value) => { 140 | dispatch(updateAppState({ 141 | ...appState, 142 | language: value, 143 | })); 144 | }} 145 | /> 146 |
147 |
148 | 154 | 155 |
156 | 162 |
163 |
164 |
165 |
166 | ) 167 | } 168 | 169 | export default ToolBar; 170 | 171 | 172 | 173 | 174 | const SwitchItem = ({ 175 | label, 176 | value, 177 | onChange, 178 | }) => { 179 | return ( 180 |
181 | 187 | 202 |
203 | ); 204 | }; 205 | 206 | 207 | 208 | const SelectItem = ({ 209 | label, 210 | value, 211 | onChange, 212 | options, 213 | 214 | }) => { 215 | return ( 216 |
217 | 220 | onChange(e)} 224 | > 225 | 226 | 227 | 228 | 229 | 230 | 231 | 232 | {options.map((option, i) => ( 233 | 238 | {option} 239 | 240 | 241 | 242 | 243 | ))} 244 | 245 | 246 | 247 | 248 |
249 | ); 250 | }; 251 | 252 | --------------------------------------------------------------------------------