├── 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 |
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 |
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 |
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 |
117 | );
118 | };
119 |
120 |
121 | const WindowTitleBar = () => {
122 | const appState = useSelector((state) => state.appState);
123 | const dispatch = useDispatch();
124 |
125 | return (
126 |
127 |
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 |
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 |
45 |
46 |
47 |
48 |
49 |
50 | {backgroundWindowTabs.map((item) => (
51 | -
52 |
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 |
--------------------------------------------------------------------------------