├── .babelrc ├── .eslintrc ├── .github └── workflows │ └── static.yml ├── .gitignore ├── LICENSE ├── README.md ├── deploy.ps1 ├── eslint.config.mjs ├── jsconfig.json ├── languages ├── lang.yml └── samples │ ├── bash.txt │ ├── batch.txt │ ├── c.txt │ ├── cmake.txt │ ├── cpp.txt │ ├── csharp.txt │ ├── css.txt │ ├── diff.txt │ ├── docker.txt │ ├── glsl.txt │ ├── go.txt │ ├── html.txt │ ├── java.txt │ ├── javascript.txt │ ├── json.txt │ ├── kotlin.txt │ ├── latex.txt │ ├── llvm.txt │ ├── lua.txt │ ├── makefile.txt │ ├── markdown.txt │ ├── matlab.txt │ ├── nasm.txt │ ├── perl.txt │ ├── php.txt │ ├── powershell.txt │ ├── python.txt │ ├── r.txt │ ├── ruby.txt │ ├── rust.txt │ ├── scala.txt │ ├── sql.txt │ ├── swift.txt │ ├── toml.txt │ ├── typescript.txt │ ├── verilog.txt │ ├── xml.txt │ └── yaml.txt ├── package-lock.json ├── package.json ├── postcss.config.js ├── posthtml.json ├── scripts ├── minify_json.py ├── post_build.py ├── pre_deploy.py └── update_samples.py ├── src ├── css │ ├── dark.css │ ├── doc.css │ ├── effect.css │ ├── fonts.css │ ├── index.css │ ├── main.css │ └── vendor │ │ ├── barber-shop.css │ │ └── prism.css ├── favicon.png ├── js │ ├── .gitignore │ ├── code.js │ ├── events │ │ ├── actions.js │ │ ├── help.js │ │ ├── listeners.js │ │ └── options.js │ ├── main.js │ ├── paste.js │ ├── state.js │ ├── utils.js │ ├── vendor │ │ ├── alertify-extension.js │ │ ├── cookie-extension.js │ │ ├── jquery-extensions.js │ │ └── prism-extension.js │ └── version.js ├── res │ ├── fonts │ │ ├── Consolas.eot │ │ ├── Consolas.svg │ │ ├── Consolas.woff │ │ ├── Consolas.woff2 │ │ ├── LucidaHandwriting-Italic.eot │ │ ├── LucidaHandwriting-Italic.svg │ │ ├── LucidaHandwriting-Italic.woff │ │ └── LucidaHandwriting-Italic.woff2 │ └── logo.png ├── version.json └── views │ ├── components │ ├── .gitignore │ ├── head.html │ └── meta.html │ ├── index.html │ └── parts │ ├── banner.html │ ├── docs.html │ ├── footer.html │ ├── header.html │ ├── panel.html │ └── toolbar.html └── webpack.config.js /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "plugins": [ 3 | [ 4 | "prismjs", 5 | { 6 | "languages": [ 7 | // Programming Languages 8 | "c", 9 | "cpp", 10 | "csharp", 11 | "go", 12 | "java", 13 | "kotlin", 14 | "perl", 15 | "python", 16 | "r", 17 | "ruby", 18 | "rust", 19 | "scala", 20 | "swift", 21 | // Web Development 22 | "css", 23 | "html", 24 | "javascript", 25 | "php", 26 | "typescript", 27 | // Scripting Language 28 | "bash", 29 | "batch", 30 | "lua", 31 | "powershell", 32 | // Markup Language 33 | "json", 34 | "latex", 35 | "markdown", 36 | "toml", 37 | "xml", 38 | "yaml", 39 | // Configuration Language 40 | "cmake", 41 | "docker", 42 | "makefile", 43 | // Database 44 | "sql", 45 | // Assembly Language 46 | "llvm", 47 | "nasm", 48 | // Others 49 | "diff", 50 | "glsl", 51 | "matlab", 52 | "verilog" 53 | ], 54 | "plugins": [ 55 | "line-numbers", 56 | "show-language", 57 | "normalize-whitespace", 58 | "toolbar" 59 | ] 60 | } 61 | ], 62 | [ 63 | "module-resolver", 64 | { 65 | "root": [ 66 | "." 67 | ], 68 | "alias": { 69 | "~": "./src/js" 70 | } 71 | } 72 | ] 73 | ] 74 | } -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "browser": true, 4 | "es6": true 5 | }, 6 | "extends": "eslint:recommended", 7 | "parserOptions": { 8 | "ecmaVersion": 6, 9 | "sourceType": "module" 10 | } 11 | } -------------------------------------------------------------------------------- /.github/workflows/static.yml: -------------------------------------------------------------------------------- 1 | name: Deploy CodePaste 2 | 3 | on: 4 | push: 5 | branches: ["main"] 6 | 7 | # Sets permissions of the GITHUB_TOKEN to allow deployment to GitHub Pages 8 | permissions: 9 | contents: read 10 | pages: write 11 | id-token: write 12 | 13 | # Allow only one concurrent deployment, skipping runs queued between the run in-progress and latest queued. 14 | # However, do NOT cancel in-progress runs as we want to allow these production deployments to complete. 15 | concurrency: 16 | group: "pages" 17 | cancel-in-progress: false 18 | 19 | jobs: 20 | # Build job to minify the project 21 | build: 22 | runs-on: windows-latest 23 | steps: 24 | - name: Checkout 25 | uses: actions/checkout@v4 26 | 27 | - name: Install NodeJS 28 | uses: actions/setup-node@v4 29 | with: 30 | node-version: "20.x" 31 | 32 | - name: Install Python 33 | uses: actions/setup-python@v5 34 | with: 35 | python-version: "3.x" 36 | 37 | - name: Install Dependencies 38 | run: | 39 | npm install 40 | pip install PyYAML 41 | 42 | - name: Add Secret Files 43 | env: 44 | NOTIFICATION: ${{ secrets.NOTIFICATION }} 45 | STATISTICS: ${{ secrets.STATISTICS }} 46 | SUPPORT: ${{ secrets.SUPPORT }} 47 | run: | 48 | python scripts/pre_deploy.py 49 | 50 | - name: Build Project 51 | run: | 52 | npm run build 53 | 54 | - name: Upload artifact 55 | uses: actions/upload-pages-artifact@v3 56 | with: 57 | path: 'dist' 58 | 59 | # Deploy job to GitHub Pages 60 | deploy: 61 | environment: 62 | name: github-pages 63 | url: ${{ steps.deployment.outputs.page_url }} 64 | runs-on: windows-latest 65 | needs: build 66 | steps: 67 | - name: Deploy to GitHub Pages 68 | id: deployment 69 | uses: actions/deploy-pages@v4 70 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .vscode/ 2 | node_modules/ 3 | dist/ 4 | config.json 5 | deploy.bat 6 | .env 7 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 Lord Turmoil 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Code Paste 2 | 3 | Copyright © Tony's Studio 2023 - 2025 4 | 5 | --- 6 | 7 | [![Build](https://github.com/Lord-Turmoil/CodePaste/actions/workflows/static.yml/badge.svg?branch=main)](https://github.com/Lord-Turmoil/CodePaste/actions/workflows/static.yml) 8 | 9 | ## Description 10 | 11 | This tool provides you the ability to create highlighted code block for Microsoft Office, mainly for Word and PowerPoint. You can use it to create a beautiful code block in your document or presentation. 12 | 13 | ### Try it now! 14 | 15 | - [Code Paste on Tony's Studio](https://codepaste.top/) 16 | - [Code Paste on GitHub Page](https://lord-turmoil.github.io/CodePaste/) 17 | 18 | --- 19 | 20 | ## Development 21 | 22 | If you find Code Paste useful and want to host it on your own, this section will be useful. By the way, feel free to contribute to this project. You can report bugs, suggest new features, or even submit a pull request. 😊 23 | 24 | ### Quick Start 25 | 26 | Code Paste is written in native HTML, CSS and JavaScript. To start development, clone the repo first, then install required packages. 27 | 28 | ```bash 29 | npm install 30 | npm run init # initialize placeholder files 31 | ``` 32 | 33 | Then, you can run the project. There are three options for this. 34 | 35 | ```bash 36 | npm run build # build for production 37 | npm run dev # build for development and watch for changes 38 | ``` 39 | 40 | > To preview the project locally, I recommend using [Live Server](https://marketplace.visualstudio.com/items?itemName=ritwickdey.LiveServer) plugin for Visual Studio Code. Just open `dist/index.html` after your local server is on. 41 | 42 | ### Code Samples 43 | 44 | Code Paste provides sample code for each supported language. The supported languages are listed in `languages/lang.yml`, grouped by their categories. All the code samples are placed under `languages/samples/` directory. 45 | 46 | > [!NOTE] 47 | > 48 | > The display order of the language options in the final webpage is exactly the same as that in `lang.yml`. 49 | 50 | Managing language options in HTML and JavaScript is tedious, so there is a script to automatically generate related code. You should run this command after modifying the language list or adding new samples. This will generate `src/js/samples.js` and `src/views/components/languages.html`. 51 | 52 | ```bash 53 | npm run sample 54 | ``` 55 | 56 | To add a new language, first add it to `languages/lang.yml`. In the suitable category, add a new list item in the format of `key value`, where `key` is the language id of [Prism.js](https://prismjs.com/#supported-languages), and the `value` is the display name of the language in the select box. Then, create a new file `{key}.txt` under `languages/samples/` and write your sample code in it. Finally, add this language to `.babelrc` so that it can be correctly loaded. 57 | 58 | ### Customization 59 | 60 | Since I removed sensitive information from the project, you need to run `npm run init` to create placeholder files even if you don't need them. These files are placed under `src/views/components/`. 61 | 62 | **Analytics** 63 | 64 | In file `statistics.html`, and place all your scripts into it. If you don't need them, just leave this file empty. Here is an example for this file. 65 | 66 | ```html 67 | 68 | 75 | 76 | 85 | ``` 86 | 87 | **Notification** 88 | 89 | For users who visit your website for the first time, you may want to prompt a notice or agreement. In `notification.html`, write your custom `
` for it. The default class for it is `notification`, you can change it and add your own CSS style. 90 | 91 | ```html 92 |
93 |

Greetings from all members of Tony's Studio!

94 |
95 | ``` 96 | 97 | **Support** 98 | 99 | If you want to add a support page, write it in `support.html`. Like notification, the default class for it is `coffee` and you can customize it. If you add this, there will be an extra support button in the center of the page. 100 | 101 | ```html 102 |
103 | WeChat Pay 104 |

We appreciate your sponsorship!🌹

105 |
106 | ``` 107 | 108 | ### Self-hosting 109 | 110 | There is still one step before you can host Code Paste on your website. I use [Font Awesome](https://fontawesome.com/) for lovely icons, and the linked JavaScript only works for my domain. Therefore, you have to get your kit [here](https://fontawesome.com/kits) and replace the link in `src/views/components/head.html`. 111 | 112 | After all these are done, just copy `dist` folder to your server and enjoy!🎉 113 | 114 | --- 115 | 116 | ## Sponsors 💖 117 | 118 | Here, I would like to express my sincere gratitude for all who sponsor Code Paste. THANK YOU! 🥰 119 | 120 | > I may not be able to know your GitHub account from the payment. If you bought me a coffee but are not present in the following table, feel free to contact me via E-mail.🙏 121 | 122 | 123 | 124 | 125 | 130 | 135 | 140 | 145 | 150 | 151 | 152 |
126 | Pan-Pan-Pan 127 |
128 | Pan-Pan-Pan 129 |
131 | Liu Yizhou 132 |
133 | Liu Yizhou 134 |
136 | hongshuobuaa 137 |
138 | hongshuobuaa 139 |
141 | 秋子夜 142 |
143 | 秋子夜 144 |
146 | GuoLan-Fruket 147 |
148 | GuoLan-Fruket 149 |
153 | -------------------------------------------------------------------------------- /deploy.ps1: -------------------------------------------------------------------------------- 1 | # Mimic CI/CD pipeline to deploy project to remote host 2 | # 3 | # To run this script, you should have PowerShell 7.0 or later installed. 4 | # You can download it at https://github.com/PowerShell/PowerShell/releases 5 | # 6 | # To auto-deploy Hexo blog to remote host, follow these steps: 7 | # 8 | # 1. Create a .env file with the following content: 9 | # ``` 10 | # username@hostname 11 | # /path/to/deploy 12 | # app-name 13 | # build folder 14 | # ``` 15 | # - `username@hostname` can also be the connection name in your SSH config file. 16 | # - `app-name` is the name of the app folder on the remote host, thus the final path 17 | # will be `/path/to/deploy/app-name`. 18 | # - `build folder` is the folder to be deployed, e.g. `dist` for npm projects. 19 | # 20 | # 2. Run this script under project root directory. 21 | # 22 | 23 | # Read first and second line from .env file and raise error if not found 24 | $envFile = Get-Content .env 25 | $hostConnection = $envFile[0] 26 | if (-not $hostConnection) { 27 | Write-Host "Error: hostConnection not found in .env file" -ForegroundColor Red 28 | exit 1 29 | } 30 | $deployPath = $envFile[1] 31 | if (-not $deployPath) { 32 | Write-Host "Error: deployPath not found in .env file" -ForegroundColor Red 33 | exit 1 34 | } 35 | $tempPath = "$deployPath/tmp" 36 | $app = $envFile[2] 37 | if (-not $app) { 38 | Write-Host "Error: app not found in .env file" -ForegroundColor Red 39 | exit 1 40 | } 41 | $build = $envFile[3] 42 | if (-not $build) { 43 | Write-Host "Error: build not found in .env file" -ForegroundColor Red 44 | exit 1 45 | } 46 | 47 | # Pack build folder into tar.gz 48 | Write-Host "Packing build folder into tar.gz" 49 | $randomString = -join ((65..90) + (97..122) | Get-Random -Count 10 | % {[char]$_}) 50 | $archiveName = "$randomString.tar.gz" 51 | tar -czf $archiveName $build 52 | 53 | # Copy archive to remote host 54 | Write-Host "Copying archive to remote host" 55 | scp $archiveName ${hostConnection}:/tmp/$archiveName 56 | 57 | # Execute remote commands: 58 | Write-Host "Executing deploy commands" 59 | $commands= @( 60 | " rm -rf $tempPath/$build $deployPath/$app", 61 | " && tar -xzf /tmp/$archiveName -C $tempPath --overwrite", 62 | " && mv $tempPath/$build $deployPath/$app", 63 | " && rm /tmp/$archiveName" 64 | ) 65 | ssh $hostConnection $commands 66 | 67 | # Delete archive 68 | Write-Host "Finishing up" 69 | Remove-Item $archiveName 70 | -------------------------------------------------------------------------------- /eslint.config.mjs: -------------------------------------------------------------------------------- 1 | import globals from "globals"; 2 | import pluginJs from "@eslint/js"; 3 | 4 | 5 | export default [ 6 | { 7 | languageOptions: { globals: globals.browser } 8 | }, 9 | pluginJs.configs.recommended, 10 | { 11 | rules: { 12 | semi: "error", 13 | quotes: ["error", "double", { "avoidEscape": true }], 14 | } 15 | } 16 | ]; -------------------------------------------------------------------------------- /jsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "paths": { 4 | "~/*": [ 5 | "./src/js/*" 6 | ] 7 | } 8 | } 9 | } -------------------------------------------------------------------------------- /languages/lang.yml: -------------------------------------------------------------------------------- 1 | Programming Languages: 2 | - c C 3 | - cpp C++ 4 | - csharp C# 5 | - go Go 6 | - java Java 7 | - kotlin Kotlin 8 | - perl Perl 9 | - python Python 10 | - r R 11 | - ruby Ruby 12 | - rust Rust 13 | - scala Scala 14 | - swift Swift 15 | Web Development: 16 | - css CSS 17 | - html HTML 18 | - javascript JavaScript 19 | - php PHP 20 | - typescript TypeScript 21 | Scripting Language: 22 | - bash Bash 23 | - batch Batch 24 | - lua Lua 25 | - powershell PowerShell 26 | Markup Language: 27 | - json JSON 28 | - latex LaTeX 29 | - markdown Markdown 30 | - toml TOML 31 | - xml XML 32 | - yaml YAML 33 | Configuration Language: 34 | - cmake CMake 35 | - docker Docker 36 | - makefile Makefile 37 | Database: 38 | - sql SQL 39 | Assembly Language: 40 | - llvm LLVM 41 | - nasm NASM 42 | Others: 43 | - diff Diff 44 | - glsl GLSL 45 | - matlab MATLAB 46 | - verilog Verilog 47 | -------------------------------------------------------------------------------- /languages/samples/bash.txt: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | name="John" 4 | age=30 5 | 6 | if [ $age -lt 18 ]; then 7 | echo "$name is a minor." 8 | else 9 | echo "$name is an adult." 10 | fi 11 | -------------------------------------------------------------------------------- /languages/samples/batch.txt: -------------------------------------------------------------------------------- 1 | @echo off 2 | 3 | set name=John 4 | set age=30 5 | 6 | if %age% lss 18 ( 7 | echo %name% is a minor. 8 | ) else ( 9 | echo %name% is an adult. 10 | ) 11 | -------------------------------------------------------------------------------- /languages/samples/c.txt: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | int main() { 4 | int num = 10; 5 | int* ptr = # 6 | 7 | printf("The value of num is %d\n", num); 8 | printf("The value of ptr is %p\n", ptr); 9 | printf("The value that ptr points to is %d\n", *ptr); 10 | 11 | return 0; 12 | } 13 | -------------------------------------------------------------------------------- /languages/samples/cmake.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.26) 2 | project(MIoC) 3 | 4 | set(CMAKE_CXX_STANDARD 11) 5 | 6 | # Add all source files 7 | file(GLOB_RECURSE SRC_LIST CONFIGURE_DEPENDS src/*.cpp) 8 | 9 | # Add final target. 10 | add_executable(${CMAKE_PROJECT_NAME} ${SRC_LIST}) 11 | 12 | # dependency 13 | add_subdirectory(mioc) 14 | target_link_libraries(${CMAKE_PROJECT_NAME} PRIVATE mioc) 15 | -------------------------------------------------------------------------------- /languages/samples/cpp.txt: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | int main() { 4 | std::cout << "Hello, there!" << std::endl; 5 | std::cout << "General Grievous!" << std::endl; 6 | 7 | return 0; 8 | } 9 | -------------------------------------------------------------------------------- /languages/samples/csharp.txt: -------------------------------------------------------------------------------- 1 | public class Startup 2 | { 3 | public void Configure(IApplicationBuilder app, IWebHostEnvironment env) 4 | { 5 | app.UseRouting(); 6 | app.UseCors(CorsOptions.CorsPolicyName); 7 | app.UseEndpoints(endpoints => { 8 | endpoints.MapControllers(); 9 | endpoints.MapSwagger(); 10 | }); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /languages/samples/css.txt: -------------------------------------------------------------------------------- 1 | .action-wrapper .action { 2 | width: 50px; 3 | height: 50px; 4 | display: flex; 5 | justify-content: center; 6 | border-radius: 50%; 7 | align-self: center; 8 | cursor: pointer; 9 | transition: 0.3s; 10 | } 11 | -------------------------------------------------------------------------------- /languages/samples/diff.txt: -------------------------------------------------------------------------------- 1 | - Jedi Order 2 | + Sith Empire 3 | - Kamino 4 | - Alderaan 5 | + Mandalore 6 | -------------------------------------------------------------------------------- /languages/samples/docker.txt: -------------------------------------------------------------------------------- 1 | # PatBoot Dockerfile 2 | 3 | FROM openjdk:17-jdk-slim 4 | 5 | ARG VERSION 6 | 7 | COPY target/PatBoot-${VERSION}.jar /application.jar 8 | 9 | EXPOSE 8080 10 | 11 | CMD ["java", "-jar", "/application.jar"] 12 | -------------------------------------------------------------------------------- /languages/samples/glsl.txt: -------------------------------------------------------------------------------- 1 | #version 450 core 2 | 3 | layout(location = 0) out vec4 o_Color; 4 | 5 | struct VertexOutput 6 | { 7 | vec4 Color; 8 | }; 9 | 10 | layout (location = 0) in VertexOutput Input; 11 | 12 | void main() 13 | { 14 | o_Color = Input.Color; 15 | } 16 | -------------------------------------------------------------------------------- /languages/samples/go.txt: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | func add(a int, b int) int { 6 | return a + b 7 | } 8 | 9 | func main() { 10 | sum := add(3, 4) 11 | fmt.Println("Sum:", sum) 12 | } 13 | -------------------------------------------------------------------------------- /languages/samples/html.txt: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Hello, world! 6 | 7 | 8 |

Hello, world!

9 | 10 | 11 | -------------------------------------------------------------------------------- /languages/samples/java.txt: -------------------------------------------------------------------------------- 1 | public class Main { 2 | public static void main(String[] args) { 3 | int a = 5; 4 | int b = 10; 5 | int sum = add(a, b); 6 | System.out.println("Sum: " + sum); 7 | } 8 | 9 | public static int add(int a, int b) { 10 | return a + b; 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /languages/samples/javascript.txt: -------------------------------------------------------------------------------- 1 | function fibonacci(n) { 2 | let a = 0, b = 1; 3 | while (a < n) { 4 | console.log(a); 5 | [a, b] = [b, a + b]; 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /languages/samples/json.txt: -------------------------------------------------------------------------------- 1 | { 2 | "name": "John Doe", 3 | "age": 30, 4 | "email": "johndoe@example.com", 5 | "address": { 6 | "street": "123 Main St", 7 | "city": "Anytown", 8 | "state": "CA", 9 | "zip": "12345" 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /languages/samples/kotlin.txt: -------------------------------------------------------------------------------- 1 | fun main() { 2 | val numbers = listOf(1, 2, 3, 4, 5) 3 | val evenNumbers = numbers.map { it * 2 }.filter { it % 2 == 0 } 4 | val sumOfEvenNumbers = evenNumbers.sum() 5 | 6 | println("Original numbers: $numbers") 7 | println("Even numbers: $evenNumbers") 8 | println("Sum of even numbers: $sumOfEvenNumbers") 9 | } 10 | 11 | fun factorial(n: Int): Int { 12 | return if (n == 0) 1 else n * factorial(n - 1) 13 | } 14 | -------------------------------------------------------------------------------- /languages/samples/latex.txt: -------------------------------------------------------------------------------- 1 | The Pythagorean theorem states that 2 | for a right triangle with legs of 3 | length $a$ and $b$ and hypotenuse of 4 | length $c$, the following equation holds: 5 | 6 | $$a^2 + b^2 = c^2$$ 7 | -------------------------------------------------------------------------------- /languages/samples/llvm.txt: -------------------------------------------------------------------------------- 1 | ; Function Attrs: noinline nounwind optnone uwtable 2 | define dso_local i32 @add(i32 %0, i32 %1) #0 { 3 | %3 = alloca i32, align 4 4 | %4 = alloca i32, align 4 5 | store i32 %0, i32* %3, align 4 6 | store i32 %1, i32* %4, align 4 7 | %5 = load i32, i32* %3, align 4 8 | %6 = load i32, i32* %4, align 4 9 | %7 = add i32 %5, %6 10 | ret i32 %7 11 | } 12 | -------------------------------------------------------------------------------- /languages/samples/lua.txt: -------------------------------------------------------------------------------- 1 | function printNumbers(n) 2 | for i = 1, n do 3 | print(i) 4 | end 5 | end 6 | 7 | printNumbers(10) 8 | -------------------------------------------------------------------------------- /languages/samples/makefile.txt: -------------------------------------------------------------------------------- 1 | CC = gcc 2 | CFLAGS = -Wall -Wextra -pedantic 3 | 4 | all: program 5 | 6 | program: main.o utils.o 7 | $(CC) $(CFLAGS) -o program main.o utils.o 8 | 9 | main.o: main.c utils.h 10 | $(CC) $(CFLAGS) -c main.c 11 | 12 | utils.o: utils.c utils.h 13 | $(CC) $(CFLAGS) -c utils.c 14 | 15 | clean: 16 | rm -f program *.o 17 | -------------------------------------------------------------------------------- /languages/samples/markdown.txt: -------------------------------------------------------------------------------- 1 | # My Shopping List 2 | 3 | > This is a good list. 4 | 5 | - Apples 6 | - Bananas 7 | - Oranges 8 | - Strawberries 9 | -------------------------------------------------------------------------------- /languages/samples/matlab.txt: -------------------------------------------------------------------------------- 1 | % Define the range of x values 2 | x = linspace(0, 2*pi, 100); 3 | 4 | % Calculate the sine of each x value 5 | y = sin(x); 6 | 7 | % Create a plot of the sine wave 8 | figure; 9 | plot(x, y); 10 | 11 | % Add title and labels 12 | title('Sine Wave'); 13 | xlabel('x'); 14 | ylabel('sin(x)'); 15 | 16 | % Display grid 17 | grid on; 18 | -------------------------------------------------------------------------------- /languages/samples/nasm.txt: -------------------------------------------------------------------------------- 1 | section .data 2 | msg db 'Ready, assemble!', 0xA 3 | len equ $ - msg 4 | 5 | section .text 6 | global _start 7 | 8 | _start: 9 | mov eax, 4 ; syscall number for sys_write 10 | mov ebx, 1 ; file descriptor 1 (stdout) 11 | mov ecx, msg ; pointer to the message 12 | mov edx, len ; length of the message 13 | int 0x80 ; make the syscall 14 | 15 | mov eax, 1 ; syscall number for sys_exit 16 | xor ebx, ebx ; exit status 0 17 | int 0x80 ; make the syscall 18 | -------------------------------------------------------------------------------- /languages/samples/perl.txt: -------------------------------------------------------------------------------- 1 | sub printNumbers { 2 | my $n = shift; 3 | for (my $i = 1; $i <= $n; $i++) { 4 | print "$i\n"; 5 | } 6 | } 7 | 8 | printNumbers(10); 9 | -------------------------------------------------------------------------------- /languages/samples/php.txt: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /languages/samples/powershell.txt: -------------------------------------------------------------------------------- 1 | $name = "John" 2 | 3 | for ($i = 1; $i -le 5; $i++) { 4 | Write-Host "Hello, $name! This is message number $i." 5 | } 6 | -------------------------------------------------------------------------------- /languages/samples/python.txt: -------------------------------------------------------------------------------- 1 | def fibonacci(n): 2 | a, b = 0, 1 3 | while a < n: 4 | print(a) 5 | a, b = b, a + b 6 | 7 | fibonacci(100) 8 | -------------------------------------------------------------------------------- /languages/samples/r.txt: -------------------------------------------------------------------------------- 1 | # Generate a sequence of numbers from 0 to 2*pi 2 | x <- seq(0, 2*pi, length.out = 100) 3 | 4 | # Compute the sine of each number 5 | y <- sin(x) 6 | 7 | # Plot the sine wave 8 | plot(x, y, type = "l", col = "blue", lwd = 2, 9 | main = "Sine Wave", xlab = "x", ylab = "sin(x)") 10 | 11 | # Add grid lines for better visualization 12 | grid() 13 | -------------------------------------------------------------------------------- /languages/samples/ruby.txt: -------------------------------------------------------------------------------- 1 | name = "John" 2 | age = 30 3 | 4 | if age < 18 5 | puts "#{name} is a minor." 6 | else 7 | puts "#{name} is an adult." 8 | end 9 | 10 | 5.times do |i| 11 | puts "This is message number #{i + 1}." 12 | end 13 | -------------------------------------------------------------------------------- /languages/samples/rust.txt: -------------------------------------------------------------------------------- 1 | fn main() { 2 | let name = "Rust"; 3 | let version = 1.56; 4 | 5 | println!("Hello, {}!", name); 6 | println!("Current version: {}", version); 7 | 8 | let sum = add(5, 10); 9 | println!("Sum of 5 and 10 is: {}", sum); 10 | } 11 | 12 | fn add(a: i32, b: i32) -> i32 { 13 | a + b 14 | } 15 | -------------------------------------------------------------------------------- /languages/samples/scala.txt: -------------------------------------------------------------------------------- 1 | val counter = Var(0) 2 | 3 | // create a counter button that increments on-click 4 | def counterButton() = button( 5 | tpe := "button", 6 | "count is ", 7 | child.text <-- counter, 8 | onClick --> { event => counter.update(c => c + 1) }, 9 | ) 10 | val app = dom.document.getElementById("app") 11 | render(app, counterButton()) 12 | -------------------------------------------------------------------------------- /languages/samples/sql.txt: -------------------------------------------------------------------------------- 1 | CREATE TABLE users ( 2 | id INT PRIMARY KEY, 3 | name VARCHAR(50), 4 | email VARCHAR(50) 5 | ); 6 | 7 | INSERT INTO users (id, name, email) 8 | VALUES (1, 'John Doe', 'johndoe@example.com'), 9 | (2, 'Jane Smith', 'janesmith@example.com'), 10 | (3, 'Bob Johnson', 'bobjohnson@example.com'); 11 | 12 | SELECT * FROM users; 13 | -------------------------------------------------------------------------------- /languages/samples/swift.txt: -------------------------------------------------------------------------------- 1 | let numbers = [1, 2, 3, 4, 5] 2 | 3 | let doubledNumbers = numbers.map { $0 * 2 } 4 | 5 | print(doubledNumbers) 6 | -------------------------------------------------------------------------------- /languages/samples/toml.txt: -------------------------------------------------------------------------------- 1 | # This is a TOML document 2 | 3 | title = "TOML Example" 4 | 5 | [owner] 6 | name = "Tom Preston-Werner" 7 | dob = 1979-05-27T07:32:00-08:00 8 | 9 | [database] 10 | enabled = true 11 | ports = [ 8000, 8001, 8002 ] 12 | data = [ ["delta", "phi"], [3.14] ] 13 | temp_targets = { cpu = 79.5, case = 72.0 } 14 | 15 | [servers] 16 | 17 | [servers.alpha] 18 | ip = "10.0.0.1" 19 | role = "frontend" 20 | 21 | [servers.beta] 22 | ip = "10.0.0.2" 23 | role = "backend" -------------------------------------------------------------------------------- /languages/samples/typescript.txt: -------------------------------------------------------------------------------- 1 | function fibonacci(n: number): void { 2 | let a: number = 0, b: number = 1; 3 | while (a < n) { 4 | console.log(a); 5 | [a, b] = [b, a + b]; 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /languages/samples/verilog.txt: -------------------------------------------------------------------------------- 1 | module counter( 2 | input clk, 3 | input rst, 4 | output reg [7:0] count 5 | ); 6 | 7 | always @(posedge clk, posedge rst) begin 8 | if (rst) begin 9 | count <= 0; 10 | end else begin 11 | count <= count + 1; 12 | end 13 | end 14 | 15 | endmodule 16 | -------------------------------------------------------------------------------- /languages/samples/xml.txt: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Star Wars 5 | George Lucas 6 | 10.99 7 | 8 | 9 | The Silent Spring 10 | Rachel Carson 11 | 7.99 12 | 13 | 14 | -------------------------------------------------------------------------------- /languages/samples/yaml.txt: -------------------------------------------------------------------------------- 1 | - title: The Great Gatsby 2 | author: F. Scott Fitzgerald 3 | year: 1925 4 | publisher: Scribner 5 | - title: To Kill a Mockingbird 6 | author: Harper Lee 7 | year: 1960 8 | publisher: J. B. Lippincott & Co. 9 | - title: 1984 10 | author: George Orwell 11 | year: 1949 12 | publisher: Secker & Warburg 13 | - title: The Catcher in the Rye 14 | author: J. D. Salinger 15 | year: 1951 16 | publisher: Little, Brown and Company 17 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "code-paste", 3 | "version": "3.1.1", 4 | "description": "Online code highlighter for Microsoft Word and PowerPoint", 5 | "main": "index.js", 6 | "scripts": { 7 | "build:html": "posthtml -c posthtml.json", 8 | "build:js": "webpack --mode=production", 9 | "build:css": "postcss src/css -d dist/css", 10 | "build:res": "shx cp -r src/res src/favicon.png dist", 11 | "build:version": "python scripts/minify_json.py src/version.json dist/version.json", 12 | "build": "rimraf dist && npm run sample && npm-run-all build:* && python scripts/post_build.py", 13 | "build-dev:html": "posthtml -c posthtml.json", 14 | "build-dev:js": "webpack --mode=development", 15 | "build-dev:css": "postcss src/css -d dist/css", 16 | "build-dev:res": "shx cp -r src/res src/favicon.png dist", 17 | "build-dev:version": "shx cp src/version.json dist/version.json", 18 | "build-dev": "npm run sample && npm-run-all build-dev:*", 19 | "watch:html": "onchange \"src/views\" -- npm run build-dev:html", 20 | "watch:js": "onchange \"src/js\" -- npm run build-dev:js", 21 | "watch:css": "onchange \"src/css\" -- npm run build-dev:css", 22 | "watch:res": "onchange \"src/res\" -- npm run build-dev:res", 23 | "watch:samples": "onchange \"languages\" -- npm run sample", 24 | "watch:version": "onchange \"src/version.json\" -- npm run build-dev:version", 25 | "dev": "npm run build-dev && run-p watch:*", 26 | "sample": "python scripts/update_samples.py", 27 | "init": "shx touch src/views/components/notification.html src/views/components/support.html src/views/components/statistics.html", 28 | "lint": ".\\node_modules\\.bin\\eslint src\\** --fix", 29 | "deploy": "npm run build && pwsh deploy.ps1" 30 | }, 31 | "keywords": [ 32 | "highlighter", 33 | "code", 34 | "office", 35 | "word", 36 | "powerpoint" 37 | ], 38 | "author": "Tony Skywalker", 39 | "license": "MIT", 40 | "devDependencies": { 41 | "@babel/preset-env": "^7.25.4", 42 | "@eslint/js": "^9.11.1", 43 | "babel-loader": "^9.2.1", 44 | "babel-plugin-module-resolver": "^5.0.2", 45 | "babel-plugin-prismjs": "^2.1.0", 46 | "css-loader": "^7.1.2", 47 | "cssnano": "^7.0.6", 48 | "eslint": "^9.11.1", 49 | "eslint-config-webpack": "^1.2.5", 50 | "eslint-webpack-plugin": "^4.2.0", 51 | "globals": "^15.9.0", 52 | "htmlnano": "^2.1.1", 53 | "npm-run-all": "^4.1.5", 54 | "onchange": "^7.1.0", 55 | "postcss": "^8.4.47", 56 | "postcss-cli": "^11.0.0", 57 | "posthtml": "^0.16.6", 58 | "posthtml-cli": "^0.7.7", 59 | "posthtml-modules": "^0.9.1", 60 | "rimraf": "^6.0.1", 61 | "shx": "^0.3.4", 62 | "webpack": "^5.95.0", 63 | "webpack-cli": "^5.1.4" 64 | }, 65 | "dependencies": { 66 | "alertifyjs": "^1.14.0", 67 | "jquery": "^3.7.1", 68 | "prismjs": "^1.29.0" 69 | } 70 | } -------------------------------------------------------------------------------- /postcss.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: [ 3 | require('cssnano')({ 4 | preset: 'default', 5 | }), 6 | ], 7 | }; -------------------------------------------------------------------------------- /posthtml.json: -------------------------------------------------------------------------------- 1 | { 2 | "input": "src/views/*.html", 3 | "output": "dist", 4 | "plugins": { 5 | "posthtml-modules": { 6 | "root": "./src/views", 7 | "initial": true 8 | }, 9 | "htmlnano": {} 10 | } 11 | } -------------------------------------------------------------------------------- /scripts/minify_json.py: -------------------------------------------------------------------------------- 1 | # Minify JSON 2 | # Usage: python minify_json.py 3 | 4 | import json 5 | import sys 6 | 7 | 8 | def minify_json(input_file, output_file): 9 | with open(input_file, "r") as f: 10 | data = json.load(f) 11 | with open(output_file, "w") as f: 12 | json.dump(data, f, separators=(",", ":")) 13 | 14 | 15 | if __name__ == "__main__": 16 | if len(sys.argv) != 3: 17 | print("Usage: python minify_json.py ") 18 | sys.exit(1) 19 | minify_json(sys.argv[1], sys.argv[2]) 20 | -------------------------------------------------------------------------------- /scripts/post_build.py: -------------------------------------------------------------------------------- 1 | """ 2 | This script is used to auto rename bundle.js to bundle.[hash].js 3 | """ 4 | 5 | import os 6 | import sys 7 | 8 | html_file = os.path.abspath("dist/index.html") 9 | bundle_file = os.path.abspath("dist/bundle.js") 10 | 11 | if not os.path.exists(html_file): 12 | print(f"Error: {html_file} not found.") 13 | sys.exit(1) 14 | 15 | if not os.path.exists(bundle_file): 16 | print(f"Error: {bundle_file} not found.") 17 | sys.exit(1) 18 | 19 | # get a random hash 20 | hash = os.urandom(4).hex() 21 | 22 | # rename bundle.js to bundle.[hash].js 23 | os.rename(bundle_file, os.path.join(os.path.dirname(bundle_file), f"bundle.{hash}.js")) 24 | 25 | # replace bundle.js with bundle.[hash].js in the HTML file 26 | with open(html_file, "r", encoding="utf-8") as f: 27 | content = f.read() 28 | content = content.replace("bundle.js", f"bundle.{hash}.js") 29 | with open(html_file, "w", encoding="utf-8") as f: 30 | f.write(content) 31 | -------------------------------------------------------------------------------- /scripts/pre_deploy.py: -------------------------------------------------------------------------------- 1 | """ 2 | This script is used to generate the private HTML files in 3 | GitHub actions. The HTML files are stored in the environment. 4 | """ 5 | 6 | import os 7 | 8 | notification = os.environ["NOTIFICATION"] 9 | statistics = os.environ["STATISTICS"] 10 | support = os.environ["SUPPORT"] 11 | 12 | assert notification is not None 13 | assert statistics is not None 14 | assert support is not None 15 | 16 | print("Generating HTML files...") 17 | with open("src/views/components/notification.html", "w", encoding="utf-8") as f: 18 | f.write(notification) 19 | with open("src/views/components/statistics.html", "w", encoding="utf-8") as f: 20 | f.write(statistics) 21 | with open("src/views/components/support.html", "w", encoding="utf-8") as f: 22 | f.write(support) 23 | print("HTML files generated.") 24 | -------------------------------------------------------------------------------- /scripts/update_samples.py: -------------------------------------------------------------------------------- 1 | """ 2 | This script load all languages and generate necessary files for the app. 3 | """ 4 | 5 | import json 6 | import os 7 | import yaml 8 | 9 | CONFIG_FILE = "languages/lang.yml" 10 | SAMPLES_DIR = "languages/samples" 11 | SAMPLES_JS = "src/js/samples.js" 12 | OPTIONS_HTML = "src/views/components/languages.html" 13 | 14 | with open(CONFIG_FILE, "r") as f: 15 | CONFIG = yaml.safe_load(f) 16 | 17 | LANGUAGES = [] 18 | SAMPLES = {} 19 | for section in CONFIG: 20 | optgroup = {"label": section, "options": []} 21 | for language in CONFIG[section]: 22 | key, value = language.split(" ") 23 | optgroup["options"].append({"value": key, "text": value}) 24 | with open(os.path.join(SAMPLES_DIR, f"{key}.txt"), "r") as f: 25 | SAMPLES[key] = f.read() 26 | LANGUAGES.append(optgroup) 27 | 28 | # Generate samples.js 29 | with open(SAMPLES_JS, "w") as f: 30 | # write the list of languages 31 | f.write("const CODE_SET = {\n") 32 | for sample in SAMPLES: 33 | f.write(f" {sample}: {json.dumps(SAMPLES[sample])},\n") 34 | f.write("};\n") 35 | f.write("export default CODE_SET;\n") 36 | 37 | # Generate languages.html 38 | with open(OPTIONS_HTML, "w") as f: 39 | for optgroup in LANGUAGES: 40 | f.write(f"\n") 41 | for option in optgroup["options"]: 42 | f.write( 43 | f" \n" 44 | ) 45 | f.write("\n") 46 | -------------------------------------------------------------------------------- /src/css/dark.css: -------------------------------------------------------------------------------- 1 | /* 2 | _______ 3 | / \ 4 | .==. .==. 5 | (( ))==(( )) 6 | / "==" "=="\ 7 | /____|| || ||___\ 8 | ________ ____ ________ ___ ___ 9 | | ___ \ / \ | ___ \ | | / / 10 | | | \ \ / /\ \ | | \ \| |_/ / 11 | | | ) / /__\ \ | |__/ /| ___ \ 12 | | |__/ / ______ \| ____ \| | \ \ 13 | _______|_______/__/ ____ \__\__|___\__\__|___\__\____ 14 | | ___ \ | ____/ / \ | ___ \ | ____| ___ \ 15 | | | \ \| |___ / /\ \ | | \ \| |___| | \ \ 16 | | |__/ /| ____/ /__\ \ | | ) | ____| |__/ / 17 | | ____ \| |__/ ______ \| |__/ /| |___| ____ \ 18 | |__| \__\____/__/ \__\_______/ |______|__| \__\ 19 | https://darkreader.org 20 | */ 21 | 22 | /*! Dark reader generated CSS | Licensed under MIT https://github.com/darkreader/darkreader/blob/main/LICENSE */ 23 | 24 | /* User-Agent Style */ 25 | @layer { 26 | html { 27 | background-color: #181a1b !important; 28 | filter: brightness(0.85) !important; 29 | } 30 | 31 | html { 32 | color-scheme: dark !important; 33 | } 34 | 35 | iframe { 36 | color-scheme: initial; 37 | } 38 | 39 | html, 40 | body { 41 | background-color: #181a1b; 42 | } 43 | 44 | html, 45 | body { 46 | border-color: #736b5e; 47 | color: #e8e6e3; 48 | } 49 | 50 | a { 51 | color: #3391ff; 52 | } 53 | 54 | table { 55 | border-color: #545b5e; 56 | } 57 | 58 | mark { 59 | color: #e8e6e3; 60 | } 61 | 62 | ::placeholder { 63 | color: #b2aba1; 64 | } 65 | 66 | input:-webkit-autofill, 67 | textarea:-webkit-autofill, 68 | select:-webkit-autofill { 69 | background-color: #404400 !important; 70 | color: #e8e6e3 !important; 71 | } 72 | 73 | ::-webkit-scrollbar { 74 | background-color: #202324 !important; 75 | color: #aba499 !important; 76 | } 77 | 78 | ::-webkit-scrollbar-thumb { 79 | background-color: #454a4d !important; 80 | } 81 | 82 | ::-webkit-scrollbar-thumb:hover { 83 | background-color: #575e62 !important; 84 | } 85 | 86 | ::-webkit-scrollbar-thumb:active { 87 | background-color: #484e51 !important; 88 | } 89 | 90 | ::-webkit-scrollbar-corner { 91 | background-color: #181a1b !important; 92 | } 93 | 94 | ::selection { 95 | background-color: #004daa !important; 96 | color: #e8e6e3 !important; 97 | } 98 | 99 | ::-moz-selection { 100 | background-color: #004daa !important; 101 | color: #e8e6e3 !important; 102 | } 103 | } 104 | 105 | /* Invert Style */ 106 | .jfk-bubble.gtx-bubble, 107 | .captcheck_answer_label>input+img, 108 | span#closed_text>img[src^="https://www.gstatic.com/images/branding/googlelogo"], 109 | span[data-href^="https://www.hcaptcha.com/"]>#icon, 110 | ::-webkit-calendar-picker-indicator, 111 | img.Wirisformula { 112 | filter: invert(100%) hue-rotate(180deg) contrast(90%) !important; 113 | } 114 | 115 | /* Variables Style */ 116 | :root { 117 | --darkreader-neutral-background: #131516; 118 | --darkreader-neutral-text: #d8d4cf; 119 | --darkreader-selection-background: #004daa; 120 | --darkreader-selection-text: #e8e6e3; 121 | } 122 | 123 | /* Modified CSS */ 124 | 125 | .pace { 126 | background-color: rgb(24, 26, 27); 127 | background-image: initial; 128 | } 129 | 130 | .pace .pace-progress { 131 | background-color: rgb(154, 0, 0); 132 | } 133 | 134 | .pace .pace-activity { 135 | background-image: linear-gradient(45deg, rgba(24, 26, 27, 0.2) 25%, rgba(0, 0, 0, 0) 25%, rgba(0, 0, 0, 0) 50%, rgba(24, 26, 27, 0.2) 50%, rgba(24, 26, 27, 0.2) 75%, rgba(0, 0, 0, 0) 75%, rgba(0, 0, 0, 0)); 136 | } 137 | 138 | :root { 139 | --animate-delay: 1s; 140 | --animate-duration: 1s; 141 | --animate-repeat: 1; 142 | } 143 | 144 | :focus { 145 | outline-color: initial; 146 | } 147 | 148 | li { 149 | list-style-image: initial; 150 | } 151 | 152 | a { 153 | text-decoration-color: initial; 154 | } 155 | 156 | hr { 157 | background-image: linear-gradient(90deg, rgba(0, 0, 0, 0) 0%, rgb(47, 51, 53) 50%, rgba(0, 0, 0, 0) 100%); 158 | border-color: initial; 159 | border-style: none; 160 | border-width: initial; 161 | } 162 | 163 | .flip-wrapper .flip-inner .flip-front, 164 | .flip-wrapper .flip-inner .flip-back { 165 | box-shadow: rgba(0, 0, 0, 0.1) 0px 0px 2px 1px; 166 | } 167 | 168 | .flip-wrapper .flip-inner .flip-front { 169 | background-color: rgb(62, 68, 70); 170 | color: rgb(232, 230, 227); 171 | } 172 | 173 | .flip-wrapper .flip-inner .flip-back { 174 | background-color: rgb(141, 0, 71); 175 | color: rgb(232, 230, 227); 176 | } 177 | 178 | .checkbox-wrapper-8 .tgl, 179 | .checkbox-wrapper-8 .tgl::after, 180 | .checkbox-wrapper-8 .tgl::before, 181 | .checkbox-wrapper-8 .tgl *, 182 | .checkbox-wrapper-8 .tgl ::after, 183 | .checkbox-wrapper-8 .tgl ::before, 184 | .checkbox-wrapper-8 .tgl+.tgl-btn { 185 | box-shadow: rgba(0, 0, 0, 0.1) 0px 0px 2px 1px; 186 | } 187 | 188 | .checkbox-wrapper-8 .tgl+.tgl-btn { 189 | outline-color: initial; 190 | } 191 | 192 | .checkbox-wrapper-8 .tgl-skewed+.tgl-btn { 193 | background-color: rgb(91, 99, 103); 194 | background-image: initial; 195 | } 196 | 197 | .checkbox-wrapper-8 .tgl-skewed+.tgl-btn::after, 198 | .checkbox-wrapper-8 .tgl-skewed+.tgl-btn::before { 199 | color: rgb(232, 230, 227); 200 | text-shadow: rgba(0, 0, 0, 0.4) 0px 1px 0px; 201 | } 202 | 203 | .checkbox-wrapper-8 .tgl-skewed+.tgl-btn:active { 204 | background-color: rgb(91, 99, 103); 205 | background-image: initial; 206 | } 207 | 208 | .checkbox-wrapper-8 .tgl-skewed:checked+.tgl-btn { 209 | background-color: rgba(0, 164, 0, 0.8); 210 | background-image: initial; 211 | } 212 | 213 | .select-wrapper { 214 | --select-height: 40px; 215 | } 216 | 217 | .select { 218 | box-shadow: rgba(0, 0, 0, 0.1) 0px 0px 2px 1px; 219 | } 220 | 221 | .select select { 222 | background-color: rgb(27, 54, 35); 223 | background-image: linear-gradient(-45deg, rgb(37, 71, 48) 0%, rgb(37, 53, 30) 100%); 224 | border-color: initial; 225 | border-style: none; 226 | border-width: initial; 227 | box-shadow: none; 228 | color: rgb(192, 187, 179); 229 | outline-color: initial; 230 | } 231 | 232 | .select::before { 233 | background-color: rgba(24, 26, 27, 0.1); 234 | color: rgba(107, 255, 107, 0.5); 235 | } 236 | 237 | .select:hover::before { 238 | background-color: rgba(24, 26, 27, 0.2); 239 | color: rgba(107, 255, 107, 0.8); 240 | } 241 | 242 | .glow-on-hover::before { 243 | background-color: initial; 244 | background-image: linear-gradient(45deg, rgb(204, 0, 0), rgb(204, 92, 0), rgb(153, 151, 0), rgb(94, 204, 0), rgb(0, 204, 179), rgb(0, 34, 204), rgb(98, 0, 204), rgb(204, 0, 160), rgb(204, 0, 0)); 245 | } 246 | 247 | .glow-on-hover::after { 248 | background-color: rgb(62, 68, 70); 249 | } 250 | 251 | .glow-on-hover:hover::after { 252 | background-color: transparent; 253 | } 254 | 255 | .doc-wrapper { 256 | --h1-font-size: 1.6em; 257 | --h2-font-size: 1.3em; 258 | --h3-font-size: 1.1em; 259 | --p-font-size: 1em; 260 | } 261 | 262 | /* .doc-hr { 263 | background-image: linear-gradient(90deg, rgba(0, 0, 0, 0) 0%, rgb(47, 51, 53) 50%, rgba(0, 0, 0, 0) 100%); 264 | border-color: initial; 265 | border-style: none; 266 | border-width: initial; 267 | } */ 268 | 269 | .alertify .ajs-dimmer { 270 | background-color: rgb(28, 30, 31); 271 | } 272 | 273 | .alertify .ajs-dialog { 274 | background-color: rgb(24, 26, 27); 275 | outline-color: initial; 276 | } 277 | 278 | .alertify .ajs-commands button { 279 | background-color: transparent; 280 | border-color: initial; 281 | border-style: initial; 282 | border-width: 0px; 283 | } 284 | 285 | .alertify .ajs-commands button.ajs-close { 286 | background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKCAYAAACNMs+9AAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAABZ0RVh0Q3JlYXRpb24gVGltZQAwNy8xMy8xNOrZqugAAAAcdEVYdFNvZnR3YXJlAEFkb2JlIEZpcmV3b3JrcyBDUzbovLKMAAAAh0lEQVQYlY2QsQ0EIQwEB9cBAR1CJUaI/gigDnwR6NBL/7/xWLNrZ2b8EwGotVpr7eOitWa1VjugiNB7R1UPrKrWe0dEAHBbXUqxMQbeewDmnHjvyTm7C3zDwAUd9c63YQdUVdu6EAJzzquz7HXvTiklt+H9DQFYaxFjvDqllFyMkbXWvfpXHjJrWFgdBq/hAAAAAElFTkSuQmCC"); 287 | } 288 | 289 | .alertify .ajs-commands button.ajs-maximize { 290 | background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKCAYAAACNMs+9AAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAABZ0RVh0Q3JlYXRpb24gVGltZQAwNy8xMy8xNOrZqugAAAAcdEVYdFNvZnR3YXJlAEFkb2JlIEZpcmV3b3JrcyBDUzbovLKMAAAAOUlEQVQYlWP8//8/AzGAhYGBgaG4uBiv6t7eXkYmooxjYGAgWiELsvHYFMCcRX2rSXcjoSBiJDbAAeD+EGu+8BZcAAAAAElFTkSuQmCC"); 291 | } 292 | 293 | .alertify .ajs-header { 294 | background-color: rgb(24, 26, 27); 295 | } 296 | 297 | .alertify .ajs-footer { 298 | background-color: rgb(24, 26, 27); 299 | } 300 | 301 | .alertify .ajs-handle { 302 | background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKCAYAAACNMs+9AAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAABZ0RVh0Q3JlYXRpb24gVGltZQAwNy8xMS8xNEDQYmMAAAAcdEVYdFNvZnR3YXJlAEFkb2JlIEZpcmV3b3JrcyBDUzbovLKMAAAAQ0lEQVQYlaXNMQoAIAxD0dT7H657l0KX3iJuUlBUNOsPPCGJm7VDp6ryeMxMuDsAQH7owW3pyn3RS26iKxERMLN3ugOaAkaL3sWVigAAAABJRU5ErkJggg=="); 303 | } 304 | 305 | .alertify.ajs-maximized .ajs-commands button.ajs-maximize { 306 | background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKCAYAAACNMs+9AAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAABZ0RVh0Q3JlYXRpb24gVGltZQAwNy8xMy8xNOrZqugAAAAcdEVYdFNvZnR3YXJlAEFkb2JlIEZpcmV3b3JrcyBDUzbovLKMAAAASklEQVQYlZWQ0QkAMQhDtXRincOZX78KVtrDCwgqJNEoIB3MPLj7lRUROlpyVXGzby6zWuY+kz6tj5sBMTMAyVV3/595RbOh3cAXsww1raeiOcoAAAAASUVORK5CYII="); 307 | } 308 | 309 | .alertify.ajs-modeless.ajs-pinnable .ajs-commands button.ajs-pin { 310 | background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKCAYAAACNMs+9AAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAABZ0RVh0Q3JlYXRpb24gVGltZQAwNy8xMy8xNOrZqugAAAAcdEVYdFNvZnR3YXJlAEFkb2JlIEZpcmV3b3JrcyBDUzbovLKMAAAAQklEQVQYlcWPMQ4AIAwCqU9u38GbcbHRWN1MvKQDhQFMEpKImGJA0gCgnYw0V0rwxseg5erT4oSkQVI5d9f+e9+xA0NbLpWfitPXAAAAAElFTkSuQmCC"); 311 | } 312 | 313 | .alertify.ajs-modeless.ajs-unpinned .ajs-commands button.ajs-pin { 314 | background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKCAYAAACNMs+9AAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAABZ0RVh0Q3JlYXRpb24gVGltZQAwNy8xMy8xNOrZqugAAAAcdEVYdFNvZnR3YXJlAEFkb2JlIEZpcmV3b3JrcyBDUzbovLKMAAAAO0lEQVQYlWP8//8/AzGAiShV6AqLi4txGs+CLoBLMYbC3t5eRmyaWfBZhwwYkX2NTxPRvibKjRhW4wMAhxkYGbLu3pEAAAAASUVORK5CYII="); 315 | } 316 | 317 | .ajs-no-overflow { 318 | outline-color: initial; 319 | } 320 | 321 | .alertify-notifier .ajs-message.ajs-success { 322 | background-color: rgba(54, 135, 89, 0.95); 323 | background-image: initial; 324 | } 325 | 326 | .alertify-notifier .ajs-message.ajs-error { 327 | background-color: rgba(139, 32, 32, 0.95); 328 | background-image: initial; 329 | } 330 | 331 | .alertify-notifier .ajs-message.ajs-warning { 332 | background-color: rgba(146, 104, 0, 0.9); 333 | background-image: initial; 334 | } 335 | 336 | .alertify-notifier .ajs-message .ajs-close { 337 | background-color: rgba(0, 0, 0, 0.5); 338 | background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAgAAAAICAYAAADED76LAAAABGdBTUEAALGPC/xhBQAAAFBJREFUGBl1j0EKADEIA+ve/P9f9bh1hEihNBfjVCO1v7RKVqJK4h8gM5cAPR42AkQEpSXPwMTyoi13n5N9YqJehm3Fnr7nL1D0ZEbD5OubGyC7a9gx+9eNAAAAAElFTkSuQmCC"); 339 | } 340 | 341 | .border { 342 | background-image: linear-gradient(135deg, rgb(28, 31, 32) 0%, rgb(49, 53, 55) 100%); 343 | } 344 | 345 | .wrapper { 346 | --code-font-size: 14px; 347 | --panel-border-radius: 10px; 348 | --panel-max-height: 600px; 349 | --panel-min-height: 400px; 350 | } 351 | 352 | .wrapper .action-wrapper { 353 | --animate-duration: 1s; 354 | } 355 | 356 | #source { 357 | border-color: initial; 358 | border-style: none; 359 | border-width: initial; 360 | } 361 | 362 | #source:focus { 363 | box-shadow: rgba(8, 30, 51, 0.35) 0px 0px 6px 0px inset; 364 | } 365 | 366 | .panel .cover { 367 | --animate-duration: 0.3s; 368 | background-image: linear-gradient(45deg, rgb(20, 94, 92) 0%, rgb(74, 2, 25) 100%); 369 | box-shadow: rgba(0, 0, 0, 0.1) 0px 0px 10px 2px inset; 370 | } 371 | 372 | .footer .copyright:hover { 373 | text-decoration-color: initial; 374 | } 375 | 376 | .doc-wrapper .bubble:hover { 377 | color: rgb(222, 219, 215); 378 | text-shadow: rgba(0, 0, 0, 0.5) 0px 0px 5px; 379 | } 380 | 381 | .alertify-notifier .ajs-message.ajs-success { 382 | background-color: rgba(74, 147, 14, 0.9); 383 | background-image: initial; 384 | } 385 | 386 | .alertify-notifier .ajs-message.ajs-warning { 387 | background-color: rgba(146, 104, 0, 0.9); 388 | background-image: initial; 389 | } 390 | 391 | .alertify-notifier .ajs-message.ajs-error { 392 | background-color: rgba(203, 21, 0, 0.9); 393 | background-image: initial; 394 | } 395 | 396 | .alertify .ajs-dialog .ajs-button { 397 | background-color: rgba(6, 154, 77); 398 | border-color: initial; 399 | border-style: none; 400 | border-width: initial; 401 | box-shadow: rgba(0, 0, 0, 0.1) 0px 0px 3px 2px; 402 | color: rgb(232, 230, 227); 403 | } 404 | 405 | .alertify .ajs-dialog .ajs-button:hover { 406 | box-shadow: rgba(0, 0, 0, 0.3) 0px 0px 3px 2px; 407 | } 408 | 409 | .action i { 410 | color: #c5c3c1; 411 | } 412 | 413 | .expand { 414 | background-color: rgb(62, 68, 70); 415 | } 416 | .expand:hover { 417 | background-color: rgb(72, 78, 81); 418 | box-shadow: rgba(0, 0, 0, 0.1) 0px 0px 5px 2px; 419 | } 420 | .expand:active { 421 | background-color: rgb(82, 88, 92); 422 | } 423 | 424 | .code { 425 | filter: brightness(0.85); 426 | } -------------------------------------------------------------------------------- /src/css/doc.css: -------------------------------------------------------------------------------- 1 | .doc-wrapper { 2 | --h1-font-size: 1.6em; 3 | --h2-font-size: 1.3em; 4 | --h3-font-size: 1.1em; 5 | --p-font-size: 1em; 6 | width: 80%; 7 | max-width: 1000px; 8 | margin: 10px auto; 9 | } 10 | 11 | .doc-wrapper .bubble { 12 | padding: 2px 8px; 13 | white-space: nowrap; 14 | /* it's a trick to make pill shape */ 15 | border-radius: 100px; 16 | } 17 | 18 | .doc-wrapper .bubble:hover { 19 | color: #efefef; 20 | text-shadow: 0px 0px 5px rgba(0, 0, 0, 0.5); 21 | } 22 | 23 | .doc-align-center { 24 | text-align: center; 25 | } 26 | 27 | .doc-align-left { 28 | text-align: left; 29 | } 30 | 31 | .doc-align-right { 32 | text-align: right; 33 | } 34 | 35 | .doc-align-justify { 36 | text-align: justify; 37 | } 38 | 39 | .doc-wrapper .doc-chapter { 40 | margin: 14px auto 10px auto; 41 | } 42 | 43 | .doc-wrapper .doc-chapter h1 { 44 | font-size: var(--h1-font-size); 45 | font-family: 'Lucida Handwriting'; 46 | } 47 | 48 | .doc-wrapper .doc-section { 49 | margin: 10px auto 6px auto; 50 | } 51 | 52 | .doc-wrapper .doc-section h2 { 53 | font-size: var(--h2-font-size); 54 | } 55 | 56 | .doc-wrapper .doc-section .doc-indent { 57 | text-indent: calc(var(--h2-font-size) * 2); 58 | } 59 | 60 | .doc-wrapper .doc-entry { 61 | margin: 8px auto 16px auto; 62 | } 63 | 64 | .doc-wrapper .doc-entry h3 { 65 | font-size: var(--h3-font-size); 66 | } 67 | 68 | .doc-wrapper .doc-entry.doc-indent { 69 | text-indent: calc(var(--h3-font-size) * 2); 70 | } 71 | 72 | .doc-wrapper .doc-text { 73 | margin: 4px auto; 74 | } 75 | 76 | .doc-wrapper .doc-text p { 77 | margin: 8px auto; 78 | } 79 | 80 | .doc-wrapper .doc-text p { 81 | font-size: var(--p-font-size); 82 | } 83 | 84 | .doc-wrapper .doc-text .doc-indent { 85 | text-indent: calc(var(--p-font-size) * 2); 86 | } 87 | 88 | .doc-hr { 89 | width: 80%; 90 | margin: 5px auto; 91 | height: 1px; 92 | border: none; 93 | background-image: linear-gradient(90deg, transparent 0%, #3f3f3f 50%, transparent 100%); 94 | } 95 | 96 | .doc-badge-base { 97 | position: relative; 98 | display: inline-block; 99 | } 100 | 101 | .doc-badge { 102 | position: absolute; 103 | right: 0; 104 | top: 0; 105 | transform: translate(100%, -30%); 106 | } -------------------------------------------------------------------------------- /src/css/effect.css: -------------------------------------------------------------------------------- 1 | :root { 2 | --mode-transition: 0.2s; 3 | } 4 | 5 | html, 6 | body, 7 | .border, 8 | .border textarea, 9 | .tool *, 10 | .code * { 11 | transition: var(--mode-transition); 12 | } 13 | 14 | /* flip badge */ 15 | .flip-wrapper { 16 | width: 2em; 17 | height: 2em; 18 | line-height: 2em; 19 | text-align: center; 20 | transition: opacity 0.2s; 21 | padding: 5px; 22 | cursor: pointer; 23 | } 24 | 25 | .flip-wrapper:hover { 26 | opacity: 80%; 27 | } 28 | 29 | .flip-inner { 30 | position: relative; 31 | width: 100%; 32 | height: 100%; 33 | text-align: center; 34 | transition: transform 0.3s; 35 | transform-style: preserve-3d; 36 | } 37 | 38 | .flip-wrapper .active { 39 | transform: rotateY(180deg); 40 | } 41 | 42 | .flip-wrapper .flip-inner .flip-front, 43 | .flip-wrapper .flip-inner .flip-back { 44 | position: absolute; 45 | width: 100%; 46 | height: 100%; 47 | -webkit-backface-visibility: hidden; 48 | backface-visibility: hidden; 49 | border-radius: 50%; 50 | overflow: hidden; 51 | box-shadow: 0 0 2px 1px rgba(0, 0, 0, 0.1); 52 | } 53 | 54 | .flip-wrapper .flip-inner .flip-front { 55 | background-color: #bbb; 56 | color: black; 57 | } 58 | 59 | .flip-wrapper .flip-inner .flip-back { 60 | background-color: #FF69B4; 61 | color: white; 62 | transform: rotateY(180deg); 63 | } 64 | 65 | /* checkbox */ 66 | .checkbox-wrapper-8 .tgl { 67 | display: none; 68 | } 69 | 70 | .checkbox-wrapper-8 .tgl, 71 | .checkbox-wrapper-8 .tgl:after, 72 | .checkbox-wrapper-8 .tgl:before, 73 | .checkbox-wrapper-8 .tgl *, 74 | .checkbox-wrapper-8 .tgl *:after, 75 | .checkbox-wrapper-8 .tgl *:before, 76 | .checkbox-wrapper-8 .tgl+.tgl-btn { 77 | box-sizing: border-box; 78 | box-shadow: 0 0 2px 1px rgba(0, 0, 0, 0.1); 79 | } 80 | 81 | .checkbox-wrapper-8 .tgl::-moz-selection, 82 | .checkbox-wrapper-8 .tgl:after::-moz-selection, 83 | .checkbox-wrapper-8 .tgl:before::-moz-selection, 84 | .checkbox-wrapper-8 .tgl *::-moz-selection, 85 | .checkbox-wrapper-8 .tgl *:after::-moz-selection, 86 | .checkbox-wrapper-8 .tgl *:before::-moz-selection, 87 | .checkbox-wrapper-8 .tgl+.tgl-btn::-moz-selection, 88 | .checkbox-wrapper-8 .tgl::selection, 89 | .checkbox-wrapper-8 .tgl:after::selection, 90 | .checkbox-wrapper-8 .tgl:before::selection, 91 | .checkbox-wrapper-8 .tgl *::selection, 92 | .checkbox-wrapper-8 .tgl *:after::selection, 93 | .checkbox-wrapper-8 .tgl *:before::selection, 94 | .checkbox-wrapper-8 .tgl+.tgl-btn::selection { 95 | background: none; 96 | } 97 | 98 | .checkbox-wrapper-8 .tgl+.tgl-btn { 99 | outline: 0; 100 | display: block; 101 | width: 4em; 102 | height: 2em; 103 | position: relative; 104 | cursor: pointer; 105 | -webkit-user-select: none; 106 | -moz-user-select: none; 107 | -ms-user-select: none; 108 | user-select: none; 109 | } 110 | 111 | .checkbox-wrapper-8 .tgl+.tgl-btn:after, 112 | .checkbox-wrapper-8 .tgl+.tgl-btn:before { 113 | position: relative; 114 | display: block; 115 | content: ""; 116 | width: 50%; 117 | height: 100%; 118 | } 119 | 120 | .checkbox-wrapper-8 .tgl+.tgl-btn:after { 121 | left: 0; 122 | } 123 | 124 | .checkbox-wrapper-8 .tgl+.tgl-btn:before { 125 | display: none; 126 | } 127 | 128 | .checkbox-wrapper-8 .tgl:checked+.tgl-btn:after { 129 | left: 50%; 130 | } 131 | 132 | .checkbox-wrapper-8 .tgl-skewed+.tgl-btn { 133 | overflow: hidden; 134 | /* transform: skew(-10deg); */ 135 | -webkit-backface-visibility: hidden; 136 | backface-visibility: hidden; 137 | transition: all 0.2s ease; 138 | font-family: sans-serif; 139 | background: #888; 140 | border-radius: 10px; 141 | } 142 | 143 | .checkbox-wrapper-8 .tgl-skewed+.tgl-btn:after, 144 | .checkbox-wrapper-8 .tgl-skewed+.tgl-btn:before { 145 | /* transform: skew(10deg); */ 146 | display: inline-block; 147 | transition: all 0.2s ease; 148 | width: 100%; 149 | text-align: center; 150 | position: absolute; 151 | line-height: 2em; 152 | font-weight: bold; 153 | color: #fff; 154 | text-shadow: 0 1px 0 rgba(0, 0, 0, 0.4); 155 | } 156 | 157 | .checkbox-wrapper-8 .tgl-skewed+.tgl-btn:after { 158 | left: 100%; 159 | content: attr(data-tg-on); 160 | } 161 | 162 | .checkbox-wrapper-8 .tgl-skewed+.tgl-btn:before { 163 | left: 0; 164 | content: attr(data-tg-off); 165 | } 166 | 167 | .checkbox-wrapper-8 .tgl-skewed+.tgl-btn:active { 168 | background: #888; 169 | } 170 | 171 | .checkbox-wrapper-8 .tgl-skewed+.tgl-btn:active:before { 172 | left: -10%; 173 | } 174 | 175 | .checkbox-wrapper-8 .tgl-skewed:checked+.tgl-btn { 176 | background: #00CD00; 177 | } 178 | 179 | .checkbox-wrapper-8 .tgl-skewed:checked+.tgl-btn:before { 180 | left: -100%; 181 | } 182 | 183 | .checkbox-wrapper-8 .tgl-skewed:checked+.tgl-btn:after { 184 | left: 0; 185 | } 186 | 187 | .checkbox-wrapper-8 .tgl-skewed:checked+.tgl-btn:active:after { 188 | left: 10%; 189 | } 190 | 191 | /* select */ 192 | 193 | .select-wrapper { 194 | --select-height: 40px; 195 | } 196 | 197 | .select { 198 | position: relative; 199 | border-radius: 10px; 200 | overflow: hidden; 201 | height: var(--select-height); 202 | box-shadow: 0 0 2px 1px rgba(0, 0, 0, 0.1); 203 | } 204 | 205 | .select select { 206 | background-image: linear-gradient(-45deg, #c1dfc4 0%, #deecdd 100%); 207 | background-color: #ddeede; 208 | color: #3f3f3f; 209 | padding: 12px; 210 | width: 250px; 211 | border: none; 212 | font-size: 16px; 213 | box-shadow: none; 214 | -webkit-appearance: none; 215 | appearance: none; 216 | outline: none; 217 | cursor: pointer; 218 | } 219 | 220 | .select::before { 221 | content: "\f13a"; 222 | font-family: FontAwesome; 223 | position: absolute; 224 | top: 0; 225 | right: 0; 226 | height: 100%; 227 | aspect-ratio: 1; 228 | text-align: center; 229 | font-size: 28px; 230 | line-height: var(--select-height); 231 | color: rgba(0, 139, 0, 1.0); 232 | background-color: rgba(255, 255, 255, 0.1); 233 | pointer-events: none; 234 | transition: 0.3s; 235 | } 236 | 237 | .select:hover::before { 238 | color: rgba(0, 139, 0, 0.8); 239 | background-color: rgba(255, 255, 255, 0.2); 240 | } 241 | 242 | .select select option { 243 | padding: 30px; 244 | } 245 | 246 | /* glow button */ 247 | .glow-on-hover { 248 | cursor: default; 249 | position: absolute; 250 | z-index: 0; 251 | border-radius: 10px; 252 | } 253 | 254 | .glow-on-hover::before { 255 | content: ''; 256 | background: linear-gradient(45deg, #ff0000, #ff7300, #fffb00, #48ff00, #00ffd5, #002bff, #7a00ff, #ff00c8, #ff0000); 257 | position: absolute; 258 | top: -1px; 259 | left: -1px; 260 | background-size: 400%; 261 | z-index: -1; 262 | filter: blur(5px); 263 | width: calc(100% + 2px); 264 | height: calc(100% + 2px); 265 | animation: glowing 30s linear infinite; 266 | opacity: 0; 267 | transition: opacity .3s ease-in-out; 268 | border-radius: 10px; 269 | } 270 | 271 | .glow-on-hover:hover::before { 272 | opacity: 1; 273 | } 274 | 275 | .glow-on-hover::after { 276 | z-index: -1; 277 | content: ''; 278 | position: absolute; 279 | width: 100%; 280 | height: 100%; 281 | background-color: #bbb; 282 | border-radius: 100px; 283 | left: 0; 284 | top: 0; 285 | transition: background-color 0.3s; 286 | } 287 | 288 | .glow-on-hover:hover::after { 289 | background-color: transparent; 290 | } 291 | 292 | 293 | @keyframes glowing { 294 | 0% { 295 | background-position: 0 0; 296 | } 297 | 298 | 50% { 299 | background-position: 400% 0; 300 | } 301 | 302 | 100% { 303 | background-position: 0 0; 304 | } 305 | } 306 | 307 | /* light theme scroll bar */ 308 | ::-webkit-scrollbar { 309 | background-color: #fcfcfc; 310 | } 311 | 312 | ::-webkit-scrollbar-thumb { 313 | background-color: #8b8b8b; 314 | } 315 | 316 | ::-webkit-scrollbar-thumb:hover { 317 | background-color: #636363; 318 | } 319 | 320 | ::-webkit-scrollbar-thumb:active { 321 | background-color: #484e51 322 | } 323 | 324 | ::-webkit-scrollbar-corner { 325 | background-color: #181a1b; 326 | } 327 | 328 | .action i { 329 | color: #181a1b; 330 | } -------------------------------------------------------------------------------- /src/css/fonts.css: -------------------------------------------------------------------------------- 1 | @font-face { 2 | font-family: 'Lucida Handwriting'; 3 | src: url('../res/fonts/LucidaHandwriting-Italic.eot'); 4 | src: url('../res/fonts/LucidaHandwriting-Italic.eot?#iefix') format('embedded-opentype'), 5 | url('../res/fonts/LucidaHandwriting-Italic.woff2') format('woff2'), 6 | url('../res/fonts/LucidaHandwriting-Italic.woff') format('woff'), 7 | url('../res/fonts/LucidaHandwriting-Italic.svg#LucidaHandwriting-Italic') format('svg'); 8 | font-weight: normal; 9 | font-style: italic; 10 | font-display: swap; 11 | } 12 | 13 | @font-face { 14 | font-family: 'Consolas'; 15 | src: url('../res/fonts/Consolas.eot'); 16 | src: url('../res/fonts/Consolas.eot?#iefix') format('embedded-opentype'), 17 | url('../res/fonts/Consolas.woff2') format('woff2'), 18 | url('../res/fonts/Consolas.woff') format('woff'), 19 | url('../res/fonts/Consolas.svg#Consolas') format('svg'); 20 | font-weight: normal; 21 | font-style: normal; 22 | font-display: swap; 23 | } 24 | -------------------------------------------------------------------------------- /src/css/index.css: -------------------------------------------------------------------------------- 1 | * { 2 | margin: 0; 3 | padding: 0; 4 | } 5 | 6 | *:focus { 7 | outline: none; 8 | } 9 | 10 | li { 11 | list-style: none; 12 | } 13 | 14 | .clearfix:before, 15 | .clearfix::after { 16 | content: ""; 17 | display: table; 18 | } 19 | 20 | .clearfix::after { 21 | clear: both; 22 | } 23 | 24 | .clearfix { 25 | zoom: 1; 26 | } 27 | 28 | a { 29 | text-decoration: none; 30 | } 31 | 32 | /* common styles */ 33 | body { 34 | font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; 35 | } 36 | 37 | hr { 38 | width: 90%; 39 | margin: 0px auto; 40 | height: 2px; 41 | border: none; 42 | background-image: linear-gradient(90deg, transparent 0%, #3f3f3f 50%, transparent 100%); 43 | } 44 | 45 | html, 46 | body { 47 | height: 100%; 48 | width: 100%; 49 | overflow-x: auto; 50 | } 51 | 52 | .border { 53 | width: 100%; 54 | min-width: 600px; 55 | min-height: 100%; 56 | overflow-x: hidden; 57 | background-color: rgb(228, 233, 240); 58 | } 59 | 60 | /* header */ 61 | .header { 62 | padding: 10px; 63 | text-align: center; 64 | } 65 | 66 | .header span { 67 | font-family: 'Lucida Handwriting'; 68 | font-size: 3em; 69 | font-weight: bold; 70 | } 71 | 72 | .header span img { 73 | height: 1.7em; 74 | vertical-align: middle; 75 | margin: 0 5px; 76 | } 77 | 78 | #logo { 79 | cursor: pointer; 80 | } 81 | 82 | /* marquee */ 83 | .banner { 84 | margin: 5px 0; 85 | } 86 | 87 | /* tool bar */ 88 | .tools-wrapper { 89 | display: flex; 90 | flex-direction: row; 91 | justify-content: center; 92 | margin: 10px; 93 | } 94 | 95 | .tools-wrapper .tools { 96 | display: flex; 97 | flex-direction: row; 98 | justify-content: center; 99 | justify-self: start; 100 | } 101 | 102 | .tools-wrapper .tools .tool { 103 | align-self: center; 104 | display: flex; 105 | flex-direction: row; 106 | margin: 5px; 107 | } 108 | 109 | .tools-wrapper .tools .tool>div { 110 | margin: 0 5px; 111 | align-self: center; 112 | } 113 | 114 | .tools-wrapper .flip-wrapper { 115 | align-self: center; 116 | margin-left: 10px; 117 | } 118 | 119 | /* code panel */ 120 | .wrapper { 121 | --panel-min-height: 400px; 122 | --panel-max-height: 600px; 123 | --panel-border-radius: 10px; 124 | --code-font-size: 14px; 125 | box-sizing: border-box; 126 | display: flex; 127 | flex-direction: row; 128 | width: 95%; 129 | margin: 10px auto; 130 | } 131 | 132 | .wrapper .panel { 133 | position: relative; 134 | width: 50%; 135 | align-self: flex-start; 136 | min-height: var(--panel-min-height); 137 | max-height: var(--panel-max-height); 138 | flex-grow: 0; 139 | overflow: hidden; 140 | overflow-y: overlay; 141 | } 142 | 143 | .wrapper .action-wrapper { 144 | --animate-duration: 1s; 145 | display: flex; 146 | flex-direction: column; 147 | align-self: center; 148 | } 149 | 150 | .action-wrapper .action { 151 | width: 50px; 152 | height: 50px; 153 | display: flex; 154 | justify-content: center; 155 | border-radius: 50%; 156 | align-self: center; 157 | cursor: pointer; 158 | transition: 0.3s; 159 | } 160 | 161 | .action-wrapper .action:hover { 162 | opacity: 80%; 163 | transform: scale(1.1); 164 | } 165 | 166 | .action-wrapper .action i { 167 | align-self: center; 168 | font-size: 30px; 169 | } 170 | 171 | .wrapper .action-wrapper .animate__spin i { 172 | animation: spin 0.5s ease 0s forwards; 173 | } 174 | 175 | @keyframes spin { 176 | 0% { 177 | -webkit-transform: rotate(0); 178 | transform: rotate(0); 179 | } 180 | 181 | 100% { 182 | -webkit-transform: rotate(360deg); 183 | transform: rotate(360deg); 184 | } 185 | } 186 | 187 | .wrapper .action-wrapper .animate__grow i { 188 | animation: grow 0.5s ease 0s forwards; 189 | } 190 | 191 | @keyframes grow { 192 | 0% { 193 | -webkit-transform: scale(1.0); 194 | transform: scale(1.0); 195 | } 196 | 197 | 20% { 198 | -webkit-transform: scale(0.8); 199 | transform: scale(0.8); 200 | } 201 | 202 | 60% { 203 | -webkit-transform: scale(1.2); 204 | transform: scale(1.2); 205 | } 206 | 207 | 100% { 208 | -webkit-transform: scale(1.0); 209 | transform: scale(1.0); 210 | } 211 | } 212 | 213 | .wrapper .action-wrapper .animate__shake i { 214 | animation: shake 0.5s ease 0s forwards; 215 | } 216 | 217 | @keyframes shake { 218 | 219 | 0%, 220 | to { 221 | -webkit-transform: translateZ(0); 222 | transform: translateZ(0) 223 | } 224 | 225 | 20%, 226 | 60% { 227 | -webkit-transform: translate3d(-5px, 0, 0); 228 | transform: translate3d(-5px, 0, 0) 229 | } 230 | 231 | 232 | 40%, 233 | 80% { 234 | -webkit-transform: translate3d(5px, 0, 0); 235 | transform: translate3d(5px, 0, 0) 236 | } 237 | } 238 | 239 | .wrapper .action-wrapper .animate__rubber i { 240 | animation: rubber 0.5s ease 0s forwards; 241 | } 242 | 243 | @keyframes rubber { 244 | 0% { 245 | -webkit-transform: scaleX(1); 246 | transform: scaleX(1) 247 | } 248 | 249 | 30% { 250 | -webkit-transform: scale3d(1.25, .75, 1); 251 | transform: scale3d(1.25, .75, 1) 252 | } 253 | 254 | 40% { 255 | -webkit-transform: scale3d(.75, 1.25, 1); 256 | transform: scale3d(.75, 1.25, 1) 257 | } 258 | 259 | 50% { 260 | -webkit-transform: scale3d(1.15, .85, 1); 261 | transform: scale3d(1.15, .85, 1) 262 | } 263 | 264 | 65% { 265 | -webkit-transform: scale3d(.95, 1.05, 1); 266 | transform: scale3d(.95, 1.05, 1) 267 | } 268 | 269 | 75% { 270 | -webkit-transform: scale3d(1.05, .95, 1); 271 | transform: scale3d(1.05, .95, 1) 272 | } 273 | 274 | to { 275 | -webkit-transform: scale(1); 276 | transform: scale(1) 277 | } 278 | } 279 | 280 | .wrapper .action-wrapper .animate__blink i { 281 | animation: tada 0.5s ease 0s forwards; 282 | } 283 | 284 | @keyframes tada { 285 | 0% { 286 | -webkit-transform: scaleX(1); 287 | transform: scaleX(1.1) 288 | } 289 | 290 | 10%, 291 | 20% { 292 | -webkit-transform: scale3d(1.0, 1.0, 1.0) rotate(-3deg); 293 | transform: scale3d(1.0, 1.0, 1.0) rotate(-3deg) 294 | } 295 | 296 | 30%, 297 | 50%, 298 | 70%, 299 | 90% { 300 | -webkit-transform: scale3d(1.2, 1.2, 1.2) rotate(3deg); 301 | transform: scale3d(1.2, 1.2, 1.2) rotate(3deg) 302 | } 303 | 304 | 40%, 305 | 60%, 306 | 80% { 307 | -webkit-transform: scale3d(1.2, 1.2, 1.2) rotate(-3deg); 308 | transform: scale3d(1.2, 1.2, 1.2) rotate(-3deg) 309 | } 310 | 311 | to { 312 | -webkit-transform: scaleX(1.1); 313 | transform: scaleX(1.1) 314 | } 315 | } 316 | 317 | /* input text area */ 318 | .wrapper .panel { 319 | min-height: var(--panel-min-height); 320 | } 321 | 322 | .wrapper .panel .code { 323 | box-sizing: border-box; 324 | width: 100%; 325 | min-height: var(--panel-min-height); 326 | max-height: var(--panel-max-height); 327 | border-radius: var(--panel-border-radius); 328 | overflow: hidden; 329 | } 330 | 331 | #source { 332 | box-sizing: border-box; 333 | width: 100%; 334 | resize: none; 335 | border: none; 336 | padding: 8px 10px; 337 | font-size: var(--code-font-size); 338 | border-radius: var(--panel-border-radius); 339 | z-index: 5; 340 | } 341 | 342 | #source:focus { 343 | box-shadow: 0px 0px 6px 0px inset rgba(10, 37, 64, 0.35); 344 | } 345 | 346 | .panel .cover { 347 | --animate-duration: 0.3s; 348 | position: absolute; 349 | left: 0; 350 | top: 0; 351 | width: 100%; 352 | height: 100%; 353 | display: flex; 354 | flex-direction: column; 355 | min-height: var(--panel-min-height); 356 | border-radius: var(--panel-border-radius); 357 | background-image: linear-gradient(45deg, #a8edea 0%, #fed6e3 100%); 358 | z-index: 5; 359 | text-align: center; 360 | overflow: auto; 361 | box-shadow: 0 0 10px 2px rgba(0, 0, 0, 0.1) inset; 362 | } 363 | 364 | .panel .cover p { 365 | align-self: center; 366 | width: 90%; 367 | margin: auto; 368 | } 369 | 370 | /* footer */ 371 | .footer { 372 | width: 100%; 373 | text-align: center; 374 | } 375 | 376 | .footer .badge-wrapper { 377 | width: 100%; 378 | display: flex; 379 | flex-direction: row; 380 | justify-content: center; 381 | align-items: center; 382 | margin: 10px auto; 383 | } 384 | 385 | .footer .badge-wrapper .badge { 386 | margin: 0 5px; 387 | } 388 | 389 | .footer .copyright { 390 | display: inline-block; 391 | margin: auto; 392 | margin-bottom: 20px; 393 | text-align: center; 394 | font-family: 'Lucida Handwriting'; 395 | } 396 | 397 | .footer .copyright:hover { 398 | text-decoration: underline; 399 | } 400 | 401 | @media screen and (max-width: 950px) { 402 | .tools-wrapper .tools { 403 | flex-direction: column; 404 | justify-content: flex-start; 405 | } 406 | 407 | .tools-wrapper .tools .tool { 408 | align-self: flex-start; 409 | } 410 | 411 | .tools-wrapper .tools .tool .prompt { 412 | width: 8em; 413 | text-align: right; 414 | } 415 | } 416 | 417 | @media screen and (max-width: 768px) { 418 | .wrapper { 419 | flex-direction: column; 420 | } 421 | 422 | .wrapper .panel { 423 | width: 100%; 424 | } 425 | 426 | .wrapper .action-wrapper { 427 | flex-direction: row; 428 | } 429 | 430 | .footer .badge-wrapper { 431 | flex-direction: column; 432 | } 433 | } 434 | 435 | /* alertify */ 436 | .alertify-notifier .ajs-message { 437 | border-radius: 100px; 438 | text-align: center; 439 | text-shadow: 0 0 10px rgba(255, 255, 255, 1.0); 440 | box-shadow: 0 0 10px 1px rgba(255, 255, 255, 0.3); 441 | } 442 | 443 | .alertify-notifier .ajs-message.ajs-success { 444 | background: rgba(92, 184, 17, 0.9); 445 | } 446 | 447 | .alertify-notifier .ajs-message.ajs-warning { 448 | background: rgba(251, 192, 45, 0.9); 449 | } 450 | 451 | .alertify-notifier .ajs-message.ajs-error { 452 | background: rgba(254, 26, 0, 0.9); 453 | } 454 | 455 | .alertify .ajs-dialog { 456 | border-radius: 20px; 457 | overflow: hidden; 458 | } 459 | 460 | .alertify .ajs-dialog .ajs-header { 461 | font-family: 'Lucida Handwriting'; 462 | font-size: large; 463 | } 464 | 465 | .alertify .ajs-dialog .ajs-button { 466 | border-radius: 10px; 467 | background-color: #07C160; 468 | border: none; 469 | cursor: pointer; 470 | box-shadow: 0 0 3px 2px rgba(0, 0, 0, 0.1); 471 | color: white; 472 | transition: 0.3s; 473 | } 474 | 475 | .alertify .ajs-dialog .ajs-button:hover { 476 | opacity: 0.9; 477 | box-shadow: 0 0 3px 2px rgba(0, 0, 0, 0.3); 478 | } 479 | 480 | .coffee { 481 | width: 80%; 482 | margin: auto; 483 | font-family: 'Lucida Handwriting'; 484 | } 485 | 486 | .coffee img { 487 | width: 100%; 488 | } 489 | 490 | .notification { 491 | width: 80%; 492 | margin: auto; 493 | font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; 494 | } 495 | 496 | /* Prism */ 497 | /* Override default Prism theme */ 498 | pre { 499 | box-sizing: border-box !important; 500 | margin: 0 !important; 501 | max-height: var(--panel-max-height); 502 | overflow-x: hidden; 503 | overflow-y: auto; 504 | padding: 8px 10px !important; 505 | min-height: 400px !important; 506 | } 507 | 508 | pre.line-numbers { 509 | padding-left: 50px !important; 510 | } 511 | 512 | pre code { 513 | display: block !important; 514 | box-sizing: border-box !important; 515 | border-radius: var(--panel-border-radius); 516 | font-size: var(--code-font-size) !important; 517 | width: max-content; 518 | min-width: 100% !important; 519 | } 520 | 521 | pre code .token { 522 | font-size: var(--code-font-size); 523 | } 524 | 525 | pre code table { 526 | font-family: inherit; 527 | border-collapse: collapse; 528 | } 529 | 530 | pre code table tr .lineno { 531 | text-align: right; 532 | padding-right: 5px; 533 | --color: #ccc; 534 | border-right: 0.5px solid var(--color); 535 | } 536 | 537 | pre code table tr .line { 538 | padding-left: 5px; 539 | } 540 | 541 | /* on copy overload */ 542 | .code.pre-copy>.code-toolbar>pre[class*=language-] { 543 | transition: background 1s; 544 | } 545 | 546 | .code.copy>.code-toolbar>pre[class*=language-] { 547 | background: transparent !important; 548 | } 549 | 550 | .code>.code-toolbar>pre[class*=language-]>code * { 551 | background: transparent !important; 552 | } 553 | 554 | .expand { 555 | display: inline-block; 556 | padding: 5px 15px; 557 | background-color: #bbb; 558 | border-radius: 1000px; 559 | cursor: pointer; 560 | } 561 | 562 | .expand.hidden { 563 | display: none; 564 | } 565 | 566 | .expand i { 567 | margin-left: 5px; 568 | } 569 | 570 | .expand:hover { 571 | background-color: #aaa; 572 | box-shadow: 0 0 5px 2px rgba(0, 0, 0, 0.1); 573 | transform: scale(1.05); 574 | } 575 | 576 | .expand:active { 577 | background-color: #999; 578 | } 579 | 580 | #previous.hidden { 581 | height: 0; 582 | overflow: hidden; 583 | transition: height 0.5s; 584 | } -------------------------------------------------------------------------------- /src/css/main.css: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Lord-Turmoil/CodePaste/7ea55da35e3c73b3bd1b348aba5ca95379d564db/src/css/main.css -------------------------------------------------------------------------------- /src/css/vendor/barber-shop.css: -------------------------------------------------------------------------------- 1 | .pace { 2 | -webkit-pointer-events: none; 3 | pointer-events: none; 4 | 5 | -webkit-user-select: none; 6 | -moz-user-select: none; 7 | user-select: none; 8 | 9 | overflow: hidden; 10 | position: fixed; 11 | top: 0; 12 | left: 0; 13 | z-index: 2000; 14 | width: 100%; 15 | height: 12px; 16 | background: #fff; 17 | } 18 | 19 | .pace-inactive { 20 | display: none; 21 | } 22 | 23 | .pace .pace-progress { 24 | background-color: #c00000; 25 | position: fixed; 26 | top: 0; 27 | bottom: 0; 28 | right: 100%; 29 | width: 100%; 30 | overflow: hidden; 31 | height: 12px; 32 | } 33 | 34 | .pace .pace-activity { 35 | position: fixed; 36 | top: 0; 37 | right: -32px; 38 | bottom: 0; 39 | left: 0; 40 | height: 12px; 41 | 42 | -webkit-transform: translate3d(0, 0, 0); 43 | -moz-transform: translate3d(0, 0, 0); 44 | -ms-transform: translate3d(0, 0, 0); 45 | -o-transform: translate3d(0, 0, 0); 46 | transform: translate3d(0, 0, 0); 47 | 48 | background-image: -webkit-gradient(linear, 0 100%, 100% 0, color-stop(0.25, rgba(255, 255, 255, 0.2)), color-stop(0.25, transparent), color-stop(0.5, transparent), color-stop(0.5, rgba(255, 255, 255, 0.2)), color-stop(0.75, rgba(255, 255, 255, 0.2)), color-stop(0.75, transparent), to(transparent)); 49 | background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, 0.2) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.2) 50%, rgba(255, 255, 255, 0.2) 75%, transparent 75%, transparent); 50 | background-image: -moz-linear-gradient(45deg, rgba(255, 255, 255, 0.2) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.2) 50%, rgba(255, 255, 255, 0.2) 75%, transparent 75%, transparent); 51 | background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, 0.2) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.2) 50%, rgba(255, 255, 255, 0.2) 75%, transparent 75%, transparent); 52 | background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.2) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.2) 50%, rgba(255, 255, 255, 0.2) 75%, transparent 75%, transparent); 53 | -webkit-background-size: 32px 32px; 54 | -moz-background-size: 32px 32px; 55 | -o-background-size: 32px 32px; 56 | background-size: 32px 32px; 57 | 58 | -webkit-animation: pace-theme-barber-shop-motion 500ms linear infinite; 59 | -moz-animation: pace-theme-barber-shop-motion 500ms linear infinite; 60 | -ms-animation: pace-theme-barber-shop-motion 500ms linear infinite; 61 | -o-animation: pace-theme-barber-shop-motion 500ms linear infinite; 62 | animation: pace-theme-barber-shop-motion 500ms linear infinite; 63 | } 64 | 65 | @-webkit-keyframes pace-theme-barber-shop-motion { 66 | 0% { -webkit-transform: none; transform: none; } 67 | 100% { -webkit-transform: translate(-32px, 0); transform: translate(-32px, 0); } 68 | } 69 | @-moz-keyframes pace-theme-barber-shop-motion { 70 | 0% { -moz-transform: none; transform: none; } 71 | 100% { -moz-transform: translate(-32px, 0); transform: translate(-32px, 0); } 72 | } 73 | @-o-keyframes pace-theme-barber-shop-motion { 74 | 0% { -o-transform: none; transform: none; } 75 | 100% { -o-transform: translate(-32px, 0); transform: translate(-32px, 0); } 76 | } 77 | @-ms-keyframes pace-theme-barber-shop-motion { 78 | 0% { -ms-transform: none; transform: none; } 79 | 100% { -ms-transform: translate(-32px, 0); transform: translate(-32px, 0); } 80 | } 81 | @keyframes pace-theme-barber-shop-motion { 82 | 0% { transform: none; transform: none; } 83 | 100% { transform: translate(-32px, 0); transform: translate(-32px, 0); } 84 | } -------------------------------------------------------------------------------- /src/css/vendor/prism.css: -------------------------------------------------------------------------------- 1 | pre[class*=language-].line-numbers{position:relative;padding-left:3.8em;counter-reset:linenumber}pre[class*=language-].line-numbers>code{position:relative;white-space:inherit}.line-numbers .line-numbers-rows{position:absolute;pointer-events:none;top:0;font-size:100%;left:-3.8em;width:3em;letter-spacing:-1px;border-right:1px solid #999;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.line-numbers-rows>span{display:block;counter-increment:linenumber}.line-numbers-rows>span:before{content:counter(linenumber);color:#999;display:block;padding-right:.8em;text-align:right} 2 | div.code-toolbar{position:relative}div.code-toolbar>.toolbar{position:absolute;z-index:10;top:.3em;right:.2em;transition:opacity .3s ease-in-out;opacity:0}div.code-toolbar:hover>.toolbar{opacity:1}div.code-toolbar:focus-within>.toolbar{opacity:1}div.code-toolbar>.toolbar>.toolbar-item{display:inline-block}div.code-toolbar>.toolbar>.toolbar-item>a{cursor:pointer}div.code-toolbar>.toolbar>.toolbar-item>button{background:0 0;border:0;color:inherit;font:inherit;line-height:normal;overflow:visible;padding:0;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none}div.code-toolbar>.toolbar>.toolbar-item>a,div.code-toolbar>.toolbar>.toolbar-item>button,div.code-toolbar>.toolbar>.toolbar-item>span{color:#bbb;font-size:.8em;padding:0 .5em;background:#f5f2f0;background:rgba(224,224,224,.2);box-shadow:0 2px 0 0 rgba(0,0,0,.2);border-radius:.5em}div.code-toolbar>.toolbar>.toolbar-item>a:focus,div.code-toolbar>.toolbar>.toolbar-item>a:hover,div.code-toolbar>.toolbar>.toolbar-item>button:focus,div.code-toolbar>.toolbar>.toolbar-item>button:hover,div.code-toolbar>.toolbar>.toolbar-item>span:focus,div.code-toolbar>.toolbar>.toolbar-item>span:hover{color:inherit;text-decoration:none} -------------------------------------------------------------------------------- /src/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Lord-Turmoil/CodePaste/7ea55da35e3c73b3bd1b348aba5ca95379d564db/src/favicon.png -------------------------------------------------------------------------------- /src/js/.gitignore: -------------------------------------------------------------------------------- 1 | samples.js 2 | -------------------------------------------------------------------------------- /src/js/code.js: -------------------------------------------------------------------------------- 1 | import CODE_SET from "~/samples.js"; 2 | 3 | class CodeSet { 4 | constructor(code) { 5 | this.code = code; 6 | this.count = Object.keys(code).length; 7 | } 8 | 9 | /** 10 | * Get a random language code. 11 | * @param {string} avoid the language to avoid 12 | */ 13 | getRandomLanguage(avoid) { 14 | if (avoid === null) { 15 | avoid = ""; 16 | } 17 | var lang = this.nextRandomLanguage(); 18 | while (lang === avoid) { 19 | lang = this.nextRandomLanguage(); 20 | } 21 | return lang; 22 | } 23 | 24 | /** 25 | * Get the code for the given language. 26 | * @param {string} lang the language of the code 27 | * @returns the code, or dummy if language not available 28 | */ 29 | getCode(lang) { 30 | if (this.hasLanguage(lang)) { 31 | return this.code[lang]; 32 | } 33 | return "Oops, write your own code!"; 34 | } 35 | 36 | hasLanguage(lang) { 37 | return Object.prototype.hasOwnProperty.call(this.code, lang); 38 | } 39 | 40 | nextRandomLanguage() { 41 | const target = Math.floor(Math.random() * this.count); 42 | var i = 0; 43 | for (const key of Object.keys(this.code)) { 44 | if (i === target) { 45 | return key; 46 | } 47 | i++; 48 | } 49 | } 50 | }; 51 | 52 | const codeset = new CodeSet(CODE_SET); 53 | 54 | export default codeset; 55 | -------------------------------------------------------------------------------- /src/js/events/actions.js: -------------------------------------------------------------------------------- 1 | import $ from "~/vendor/jquery-extensions"; 2 | import alertify from "~/vendor/alertify-extension"; 3 | import { toggleHelp, closeHelp } from "~/events/help"; 4 | import paste from "~/paste"; 5 | import codeset from "~/code"; 6 | 7 | var issueLock = false; 8 | 9 | function registerActions() { 10 | $("div.action").each(function () { 11 | $(this).on("animationend", () => { 12 | $(this).removeClassRegex("animate__*"); 13 | }); 14 | }); 15 | 16 | var convertButton = $("#convert"); 17 | convertButton.on("click", () => { 18 | convertButton.addClass("animate__animated animate__spin"); 19 | 20 | updateLanguage(true); 21 | paste.dispatch("make"); 22 | closeHelp(); 23 | toggleHelp(); 24 | }); 25 | 26 | var eraseButton = $("#erase"); 27 | eraseButton.on("click", () => { 28 | eraseButton.addClass("animate__animated animate__shake"); 29 | paste.dispatch("erase"); 30 | toggleHelp(); 31 | }); 32 | 33 | var copyButton = $("#copy"); 34 | copyButton.on("click", () => { 35 | copyButton.addClass("animate__animated animate__grow"); 36 | paste.dispatch("copy"); 37 | }); 38 | 39 | var randomButton = $("#random"); 40 | randomButton.on("click", () => { 41 | randomButton.addClass("animate__animated animate__rubber"); 42 | makeRandomPaste(null); 43 | }); 44 | 45 | var issueButton = $("#issue"); 46 | issueButton.on("click", () => { 47 | issueButton.addClass("animate__animated animate__tada"); 48 | 49 | if (issueLock) { 50 | alertify.error("Issue page is already opening... ⌛"); 51 | return; 52 | } 53 | issueLock = true; 54 | 55 | var duration = 3; 56 | var notification = alertify.success(`Opening issue page in ${duration}... 📝`, duration, function () { clearInterval(interval); }); 57 | var interval = setInterval(function () { 58 | notification.setContent(`Opening issue page in ${--duration}... 📝`); 59 | }, 1000); 60 | 61 | setTimeout(() => { 62 | window.open("https://github.com/Lord-Turmoil/CodePaste/issues/new", "_blank"); 63 | issueLock = false; 64 | }, duration * 1000); 65 | }); 66 | 67 | $("#logo").on("click", () => { 68 | promptNotification(); 69 | }); 70 | 71 | registerSupport(); 72 | } 73 | 74 | function updateLanguage(persist) { 75 | const optionSelected = $("#lang").find("option:selected"); 76 | updateActiveLanguage(optionSelected.val(), persist); 77 | updateLanguageTip(optionSelected.text()); 78 | } 79 | 80 | function updateActiveLanguage(lang, persist) { 81 | $("#code").removeClass().addClass("language-" + lang); 82 | if (persist) { 83 | localStorage.setItem("language", lang); 84 | } 85 | } 86 | 87 | function updateLanguageTip(lang) { 88 | var tip = $("div.toolbar-item span"); 89 | if (tip.length) { 90 | tip.text(lang); 91 | } 92 | } 93 | 94 | function registerSupport() { 95 | var supportButton = $("#coffee"); 96 | var support = $("#support"); 97 | if (support.children().length !== 1) { 98 | supportButton.remove(); 99 | return; 100 | } 101 | const html = support.html(); 102 | supportButton.on("click", () => { 103 | supportButton.addClass("animate__animated animate__grow"); 104 | alertify.alert("Buy me a coffee 🍵", html); 105 | }); 106 | } 107 | 108 | function makeRandomPaste(lang) { 109 | if (lang === null) { 110 | const optionSelected = $("#lang").find("option:selected"); 111 | const current = optionSelected.val(); 112 | lang = codeset.getRandomLanguage(current); 113 | } 114 | paste.setInput(codeset.getCode(lang)); 115 | 116 | $("option[value=\"" + lang + "\"]").prop("selected", true); 117 | updateLanguage(false); 118 | 119 | paste.dispatch("random"); 120 | closeHelp(); 121 | toggleHelp(); 122 | } 123 | 124 | function promptNotification() { 125 | const agreement = $("#notification"); 126 | if (agreement.children().length === 1) { 127 | const html = agreement.html(); 128 | alertify.alert("Notification 🔔", html); 129 | } 130 | } 131 | 132 | export { registerActions, makeRandomPaste, promptNotification }; 133 | -------------------------------------------------------------------------------- /src/js/events/help.js: -------------------------------------------------------------------------------- 1 | import $ from "~/vendor/jquery-extensions"; 2 | import paste from "~/paste"; 3 | 4 | var help = $("#help"); 5 | var isHelpOn = false; // used in help 6 | help.click(function () { 7 | help.toggleClass("active"); 8 | isHelpOn = !isHelpOn; 9 | toggleHelp(); 10 | }); 11 | 12 | var cover = $("div.cover"); 13 | var showHelp = true; 14 | var interval = null; 15 | function toggleHelp() { 16 | var show = (paste.isEmpty() || isHelpOn); 17 | if (show === showHelp) { 18 | return; 19 | } 20 | 21 | showHelp = show; 22 | if (interval !== null) { 23 | clearInterval(interval); 24 | interval = null; 25 | } 26 | cover.removeClassRegex("animate__*"); 27 | if (showHelp) { 28 | cover.addClass("animate__animated animate__fadeIn"); 29 | cover.show(); 30 | } else { 31 | cover.addClass("animate__animated animate__fadeOut"); 32 | interval = setTimeout(function () { 33 | cover.hide(); 34 | }, 500); 35 | } 36 | } 37 | 38 | // Close help if it is on when paste is made. 39 | function closeHelp() { 40 | if (isHelpOn) { 41 | help.click(); 42 | } 43 | } 44 | 45 | export { toggleHelp, closeHelp }; -------------------------------------------------------------------------------- /src/js/events/listeners.js: -------------------------------------------------------------------------------- 1 | import $ from "~/vendor/jquery-extensions"; 2 | 3 | // auto-fit text area 4 | // Reference: https://stackoverflow.com/questions/454202/creating-a-textarea-with-auto-resize 5 | const MIN_HEIGHT = 400; 6 | const MAX_HEIGHT = 600; 7 | 8 | function registerOnTextAreaChange() { 9 | $("textarea").each(function () { 10 | this.setAttribute("style", "height:" + (MIN_HEIGHT) + "px;"); 11 | }).on("input", function () { 12 | this.style.height = "auto"; 13 | var targetHeight = this.scrollHeight; 14 | if (targetHeight > MAX_HEIGHT) { 15 | $(this).addClass("full"); 16 | targetHeight = MAX_HEIGHT; 17 | } else { 18 | $(this).removeClass("full"); 19 | if (targetHeight < MIN_HEIGHT) { 20 | targetHeight = MIN_HEIGHT; 21 | } 22 | } 23 | this.style.height = (targetHeight) + "px"; 24 | }); 25 | } 26 | 27 | function prepareIndentation(textarea) { 28 | const start = textarea.selectionStart; 29 | const end = textarea.selectionEnd; 30 | const lines = textarea.value.split("\n"); 31 | 32 | let affectedLines = []; 33 | let count = 0; 34 | 35 | for (let i = 0; i < lines.length; i++) { 36 | const lstart = count; 37 | const lend = count + lines[i].length + 1; 38 | if (lstart <= end && lend > start) { 39 | affectedLines.unshift({ line: i, start: lstart, end: lend }); 40 | } 41 | count = lend; 42 | } 43 | 44 | return { start: start, end: end, affectedLines: affectedLines }; 45 | } 46 | 47 | function registerEditListeners() { 48 | $("textarea").on("keydown", function (e) { 49 | if (e.key == "Tab") { 50 | e.preventDefault(); 51 | $(this).trigger(e.shiftKey ? "unindent" : "indent", [prepareIndentation(this)]); 52 | } 53 | }).on("indent", function (event, data) { 54 | if (data.start == data.end) { 55 | document.execCommand("insertText", false, " "); 56 | this.setSelectionRange(data.start + 4, data.start + 4); 57 | return; 58 | } 59 | 60 | for (let i = 0; i < data.affectedLines.length; i++) { 61 | const line = data.affectedLines[i]; 62 | this.setSelectionRange(line.start, line.start); 63 | document.execCommand("insertText", false, " "); 64 | } 65 | this.setSelectionRange(data.start + 4, data.end + 4 * data.affectedLines.length); 66 | }).on("unindent", function (event, data) { 67 | let newStart = data.start; 68 | let newEnd = data.end; 69 | 70 | for (let i = 0; i < data.affectedLines.length; i++) { 71 | const line = data.affectedLines[i]; 72 | const start = line.start; 73 | let end = start; 74 | while (((end < line.end) && (end - start < 4)) && (this.value[end] == " ")) { 75 | end++; 76 | } 77 | 78 | if (i == data.affectedLines.length - 1) { 79 | newStart -= end - start; 80 | newStart = Math.max(newStart, line.start); 81 | } 82 | newEnd -= end - start; 83 | console.log(newStart, newEnd, line.start, end - start); 84 | 85 | if (end > start) { 86 | this.setSelectionRange(start, end); 87 | document.execCommand("delete"); 88 | } 89 | } 90 | this.setSelectionRange(newStart, Math.max(newStart, newEnd)); 91 | }).on("paste", function (e) { 92 | e.preventDefault(); 93 | // This is a bug when copying from PowerPoint, where '\n' is 94 | // replaced with '\v' (vertical tab) and ' ' is replaced with '\xa0' (non-breaking space) 95 | const text = e.originalEvent.clipboardData.getData("text") 96 | .replaceAll(String.fromCharCode(11), "\n") 97 | .replaceAll(String.fromCharCode(160), " "); 98 | document.execCommand("insertText", false, text); 99 | }); 100 | } 101 | 102 | function registerListeners() { 103 | registerOnTextAreaChange(); 104 | registerEditListeners(); 105 | } 106 | 107 | export { registerListeners }; -------------------------------------------------------------------------------- /src/js/events/options.js: -------------------------------------------------------------------------------- 1 | import $ from "~/vendor/jquery-extensions"; 2 | import paste from "~/paste"; 3 | import { closeHelp, toggleHelp } from "~/events/help"; 4 | import { setTheme } from "~/vendor/prism-extension"; 5 | 6 | var isDark = false; 7 | 8 | function registerOptions() { 9 | $("#cb3-8").on("change", function () { 10 | updateLineNumber(this.checked); 11 | paste.dispatch("refresh"); 12 | closeHelp(); 13 | toggleHelp(); 14 | }); 15 | 16 | $("#theme-selector").on("change", function () { 17 | setTheme(this.value); 18 | }); 19 | 20 | var mode = $("#mode"); 21 | mode.on("click", function () { 22 | setMode(!isDark); 23 | }); 24 | } 25 | 26 | function updateLineNumber(enable) { 27 | if (enable) { 28 | $("#pre").addClass("line-numbers"); 29 | $("#cb3-8").get(0).checked = true; 30 | } else { 31 | $("#pre").removeClass("line-numbers"); 32 | } 33 | paste.enableLineNumber(enable); 34 | localStorage.setItem("line-number", enable); 35 | } 36 | 37 | function setMode(dark) { 38 | isDark = dark; 39 | if (isDark) { 40 | $("#mode").addClass("active"); 41 | } else { 42 | $("#mode").removeClass("active"); 43 | } 44 | applyMode(); 45 | } 46 | 47 | function applyMode() { 48 | if (isDark) { 49 | $("#mode-css").attr("href", "css/dark.css"); 50 | localStorage.setItem("mode", "dark"); 51 | } else { 52 | $("#mode-css").attr("href", ""); 53 | localStorage.setItem("mode", "light"); 54 | } 55 | } 56 | 57 | export { registerOptions, updateLineNumber, setMode }; 58 | -------------------------------------------------------------------------------- /src/js/main.js: -------------------------------------------------------------------------------- 1 | import { registerListeners } from "~/events/listeners"; 2 | import { registerActions } from "~/events/actions"; 3 | import { registerOptions } from "~/events/options"; 4 | import { loadVersion } from "~/version"; 5 | import { restoreUserPreferences } from "~/state"; 6 | import $ from "~/vendor/jquery-extensions"; 7 | 8 | registerListeners(); 9 | registerActions(); 10 | registerOptions(); 11 | loadVersion(); 12 | 13 | restoreUserPreferences(); 14 | 15 | const currentYear = new Date().getFullYear(); 16 | $("#year").text((currentYear > 2023) ? "2023 - " + currentYear : 2023); 17 | -------------------------------------------------------------------------------- /src/js/paste.js: -------------------------------------------------------------------------------- 1 | import Prism from "prismjs"; 2 | import $ from "~/vendor/jquery-extensions"; 3 | import alertify from "~/vendor/alertify-extension"; 4 | import { normalizeString, copyHTMLElement } from "~/utils"; 5 | 6 | Prism.manual = true; 7 | 8 | function copyPasteImpl(element, enableLineNumber) { 9 | const backupElem = element.innerHTML; 10 | 11 | var target = element; 12 | if (enableLineNumber) { 13 | target = transformPaste(element); 14 | } 15 | if (!copyHTMLElement(target)) { 16 | element.innerHTML = backupElem; 17 | return false; 18 | } 19 | element.innerHTML = backupElem; 20 | 21 | return true; 22 | } 23 | 24 | // Transform paste into table. 25 | function transformPaste(element) { 26 | var content = element.innerHTML; 27 | // Remove last element. 28 | var pos = content.lastIndexOf(""); 29 | content = content.substring(0, pos); 30 | 31 | // Split content by
32 | var lines = content.split("
"); 33 | var table = ""; 34 | for (var i = 0; i < lines.length; i++) { 35 | table += ""; 36 | } 37 | table += "
" + (i + 1) + "" + lines[i] + "
"; 38 | 39 | // Get dynamic styles. 40 | var height = window.getComputedStyle($("code[class*=language-] .line-numbers-rows span").get(0), null).getPropertyValue("height"); 41 | var fontStyle = window.getComputedStyle($("code[class*=language-], pre[class*=language-]").get(0), null).getPropertyValue("font-family"); 42 | var tableElement = element.cloneNode(false); 43 | tableElement.innerHTML = table; 44 | element.innerHTML = table; 45 | 46 | $("pre code table tr td").css("font-family", fontStyle); 47 | $("pre code table tr").css("height", height); 48 | 49 | return element; 50 | } 51 | 52 | async function overloadStyle() { 53 | $(".code").addClass("pre-copy"); 54 | $(".code").addClass("copy"); 55 | await new Promise(r => setTimeout(r, 256)); 56 | } 57 | 58 | function restoreStyle() { 59 | $(".code").removeClass("copy"); 60 | $(".code").removeClass("pre-copy"); 61 | } 62 | 63 | class Paste { 64 | constructor(input, output) { 65 | this.map = { 66 | "make": this.make.bind(this), 67 | "copy": this.copy.bind(this), 68 | "erase": this.erase.bind(this), 69 | "random": this.random.bind(this), 70 | "refresh": this.refresh.bind(this), 71 | }; 72 | this.inputSelector = input; 73 | this.outputSelector = output; 74 | this.withLineNumber = false; 75 | this.update(); 76 | } 77 | 78 | update() { 79 | this.input = $(this.inputSelector); 80 | this.output = $(this.outputSelector); 81 | } 82 | 83 | hasInput() { 84 | return this.input.val() !== ""; 85 | } 86 | 87 | /** 88 | * We have to expose this to let outside world set the random language. 89 | * @returns the input object 90 | */ 91 | setInput(text) { 92 | this.input.val(text); 93 | this.input.get(0).dispatchEvent(new Event("input")); 94 | } 95 | 96 | isEmpty() { 97 | return (this.output.get(0).textContent.trim() === "") && (this.output.get(0).childElementCount === 0); 98 | } 99 | 100 | hasOutput() { 101 | return !this.isEmpty(); 102 | } 103 | 104 | enableLineNumber(enabled) { 105 | this.withLineNumber = enabled; 106 | } 107 | 108 | /** 109 | * Dispatch command to the paste engine. 110 | * @param {string} type action type for the reducer 111 | * make 112 | * copy 113 | * erase 114 | * random 115 | */ 116 | async dispatch(type) { 117 | this.map[type](); 118 | } 119 | 120 | async make() { 121 | if (this.hasInput()) { 122 | this.highlight(); 123 | alertify.success("Code paste ready to go 😋"); 124 | } else { 125 | alertify.warning("The ingredient, please 🤧"); 126 | } 127 | return Promise.resolve(); 128 | } 129 | 130 | async copy() { 131 | if (this.isEmpty()) { 132 | alertify.warning("Make your code paste first 😨"); 133 | } else { 134 | await overloadStyle(); 135 | if (copyPasteImpl(this.output.get(0), this.withLineNumber)) { 136 | alertify.success("Code paste copied to clipboard 🤩"); 137 | } else { 138 | alertify.error("Oops! Something went wrong 🤯"); 139 | } 140 | restoreStyle(); 141 | } 142 | return Promise.resolve(); 143 | } 144 | 145 | async erase() { 146 | this.input.val(""); 147 | this.input.get(0).dispatchEvent(new Event("input")); 148 | 149 | if (this.isEmpty()) { 150 | alertify.warning("You haven't made any paste yet 😅"); 151 | } 152 | else { 153 | this.highlight(); 154 | alertify.success("Code paste cleared 🥹"); 155 | if (this.hasOutput()) { 156 | alertify.error("The paste is too good to be erased 😅"); 157 | } 158 | } 159 | 160 | return Promise.resolve(); 161 | } 162 | 163 | /** 164 | * Same as make, but different success message. 165 | */ 166 | async random() { 167 | if (this.hasInput()) { 168 | this.highlight(); 169 | alertify.success("You like the random paste? 🙃"); 170 | } else { 171 | alertify.error("The ingredient, please 🫨"); 172 | } 173 | return Promise.resolve(); 174 | } 175 | 176 | /** 177 | * Just refresh the output with no message. 178 | */ 179 | async refresh() { 180 | this.highlight(); 181 | return Promise.resolve(); 182 | } 183 | 184 | highlight() { 185 | // FIXME: Currently will ignore the whitespace at the beginning. 186 | this.output.html(""); 187 | this.output.text(this.input.val().trim()); 188 | Prism.highlightAll(); 189 | var corrected = this.output.html().toString().replaceAll("\n", "
"); 190 | this.output.html(normalizeString(corrected)); 191 | this.update(); 192 | } 193 | } 194 | 195 | // Because Prism will insert parent element for the code block, we have to 196 | // re-select the input and output element. 197 | const paste = new Paste("#source", "pre>code"); 198 | 199 | export default paste; 200 | -------------------------------------------------------------------------------- /src/js/state.js: -------------------------------------------------------------------------------- 1 | import { makeRandomPaste, promptNotification } from "~/events/actions"; 2 | import { setMode, updateLineNumber } from "~/events/options"; 3 | import { getCookie, setCookie } from "./vendor/cookie-extension"; 4 | import { setTheme } from "~/vendor/prism-extension"; 5 | 6 | function restoreUserPreferences() { 7 | restoreColorMode(); 8 | restoreLanguage(); 9 | restoreLineNumber(); 10 | restoreTheme(); 11 | restoreNotification(); 12 | } 13 | 14 | function restoreLanguage() { 15 | const lang = localStorage.getItem("language"); 16 | if (lang) { 17 | setTimeout(function () { 18 | makeRandomPaste(lang); 19 | }, 1000); 20 | } else { 21 | setTimeout(function () { 22 | makeRandomPaste(null); 23 | }, 1000); 24 | } 25 | } 26 | 27 | function restoreLineNumber() { 28 | const enableLineNumber = localStorage.getItem("line-number"); 29 | if (enableLineNumber !== null) { 30 | updateLineNumber(enableLineNumber === "true"); 31 | } 32 | } 33 | 34 | function restoreColorMode() { 35 | const mode = localStorage.getItem("mode"); 36 | if (mode !== null) { 37 | const isDark = localStorage.getItem("mode") === "dark"; 38 | setMode(isDark); 39 | } 40 | } 41 | 42 | function restoreTheme() { 43 | const theme = localStorage.getItem("theme"); 44 | if (theme !== null) { 45 | setTheme(theme); 46 | } 47 | } 48 | 49 | function restoreNotification() { 50 | const cookie = getCookie("notification"); 51 | if (cookie !== "") { 52 | return; 53 | } 54 | setCookie("notification", "notified", 24 * 7); // one week 55 | 56 | promptNotification(); 57 | } 58 | 59 | export { restoreUserPreferences }; 60 | -------------------------------------------------------------------------------- /src/js/utils.js: -------------------------------------------------------------------------------- 1 | // replace spaces with   in string form html while skipping tags 2 | // here we assume that the string is legal 3 | function normalizeString(str) { 4 | var res = ""; 5 | var length = str.length; 6 | var cnt = 0; 7 | 8 | for (var i = 0; i < length; i++) { 9 | if (str[i] === " ") { 10 | res += (cnt === 0) ? " " : " "; 11 | } else { 12 | res += str[i]; 13 | if (str[i] === "<") { 14 | cnt++; 15 | } else if (str[i] === ">") { 16 | cnt--; 17 | } 18 | } 19 | } 20 | 21 | return res; 22 | } 23 | 24 | // copy html element while preserving the format and style 25 | // Reference: https://htmldom.dev/copy-highlighted-code-to-the-clipboard/ 26 | function copyHTMLElement(elem) { 27 | // Create new selection 28 | const selection = window.getSelection(); 29 | 30 | // Save the current selection 31 | const currentRange = (selection.rangeCount === 0) ? null : selection.getRangeAt(0); 32 | 33 | // Select the text content of code element 34 | const range = document.createRange(); 35 | range.selectNodeContents(elem); 36 | selection.removeAllRanges(); 37 | selection.addRange(range); 38 | 39 | // Copy to the clipboard 40 | var hasError = false; 41 | try { 42 | document.execCommand("copy"); 43 | } catch (err) { 44 | // Unable to copy 45 | console.log(err); 46 | hasError = true; 47 | } finally { 48 | // Restore the previous selection 49 | selection.removeAllRanges(); 50 | currentRange && selection.addRange(currentRange); 51 | } 52 | 53 | return !hasError; 54 | }; 55 | 56 | export { normalizeString, copyHTMLElement }; 57 | -------------------------------------------------------------------------------- /src/js/vendor/alertify-extension.js: -------------------------------------------------------------------------------- 1 | import alertify from "alertifyjs"; 2 | 3 | alertify.set("notifier", "delay", 1.5); 4 | alertify.set("notifier", "position", "bottom-left"); 5 | 6 | export default alertify; -------------------------------------------------------------------------------- /src/js/vendor/cookie-extension.js: -------------------------------------------------------------------------------- 1 | // Reference: https://www.w3schools.com/js/js_cookies.asp 2 | 3 | /** 4 | * Set a cookie. 5 | * @param {string} name name of the cookie 6 | * @param {string} value value of the cookie 7 | * @param {number} hours hours to expire 8 | */ 9 | function setCookie(name, value, hours) { 10 | const expireDate = new Date(); 11 | expireDate.setTime(expireDate.getTime() + (hours * 60 * 60 * 1000)); 12 | let expires = "expires=" + expireDate.toUTCString(); 13 | document.cookie = name + "=" + value + ";" + expires + ";path=/"; 14 | } 15 | 16 | function getCookie(name) { 17 | name = name + "="; 18 | let decodedCookie = decodeURIComponent(document.cookie); 19 | let ca = decodedCookie.split(";"); 20 | for (let i = 0; i < ca.length; i++) { 21 | let c = ca[i]; 22 | while (c.charAt(0) == " ") { 23 | c = c.substring(1); 24 | } 25 | if (c.indexOf(name) == 0) { 26 | return c.substring(name.length, c.length); 27 | } 28 | } 29 | return ""; 30 | } 31 | 32 | export { setCookie, getCookie }; 33 | -------------------------------------------------------------------------------- /src/js/vendor/jquery-extensions.js: -------------------------------------------------------------------------------- 1 | import $ from "jquery"; 2 | 3 | $.fn.removeClassRegex = function (pattern) { 4 | var element = $(this); 5 | var classNames = element.attr("class").split(" "); 6 | $(classNames).each(function (key, value) { 7 | if (value.match(pattern)) { 8 | element.removeClass(value); 9 | } 10 | }); 11 | }; 12 | 13 | export default $; -------------------------------------------------------------------------------- /src/js/vendor/prism-extension.js: -------------------------------------------------------------------------------- 1 | import $ from "~/vendor/jquery-extensions"; 2 | 3 | const themes = { 4 | "https://cdnjs.cloudflare.com/ajax/libs/prism/1.28.0/themes/{id}.min.css": [ 5 | "prism", 6 | "prism-dark", 7 | "prism-funky", 8 | "prism-okaidia", 9 | "prism-twilight", 10 | "prism-solarizedlight", 11 | "prism-tomorrow" 12 | ], 13 | "https://cdnjs.cloudflare.com/ajax/libs/prism-themes/1.9.0/prism-{id}.min.css": [ 14 | "a11y-dark", 15 | "atom-dark", 16 | "base16-ateliersulphurpool.light", 17 | "cb", 18 | "coldark-cold", 19 | "coldark-dark", 20 | "coy-without-shadows", 21 | "darcula", 22 | "dracula", 23 | "duotone-dark", 24 | "duotone-earth", 25 | "duotone-forest", 26 | "duotone-light", 27 | "duotone-sea", 28 | "duotone-space", 29 | "ghcolors", 30 | "gruvbox-dark", 31 | "gruvbox-light", 32 | "holi-theme", 33 | "hopscotch", 34 | "lucario", 35 | "material-dark", 36 | "material-light", 37 | "material-oceanic", 38 | "night-owl", 39 | "nord", 40 | "one-dark", 41 | "one-light", 42 | "pojoaque", 43 | "shades-of-purple", 44 | "solarized-dark-atom", 45 | "synthwave84", 46 | "vs", 47 | "vsc-dark-plus", 48 | "xonokai", 49 | "z-touch" 50 | ] 51 | }; 52 | 53 | 54 | //url for id from dictionary 55 | function getThemeUrl(id) { 56 | for (const [key, value] of Object.entries(themes)) { 57 | if (value.includes(id)) return key; 58 | } 59 | return null; 60 | } 61 | 62 | function setTheme(id) { 63 | let url = getThemeUrl(id); 64 | if (url == null) { 65 | console.error("Theme not found: " + id); 66 | return null; 67 | } 68 | if ($("#theme-selector").val() !== id) { 69 | $("#theme-selector").val(id); 70 | } 71 | 72 | let theme_url = url.replace(/\{id}/g, id); 73 | $("#theme").attr("href", theme_url); 74 | 75 | localStorage.setItem("theme", id); 76 | }; 77 | 78 | export { setTheme }; 79 | -------------------------------------------------------------------------------- /src/js/version.js: -------------------------------------------------------------------------------- 1 | import alertify from "~/vendor/alertify-extension"; 2 | import $ from "~/vendor/jquery-extensions"; 3 | 4 | function render(versions) { 5 | var options = { year: "numeric", month: "long", day: "numeric" }; 6 | var latest = ""; 7 | var previous = ""; 8 | versions.forEach((version, index) => { 9 | var html = ""; 10 | html += '
'; 11 | html += "

Version " + version.version + "

"; 12 | html += '
'; 13 | html += "

" + new Date(version.date).toLocaleDateString("en-US", options) + "

"; 14 | html += "
"; 15 | html += "
"; 16 | html += '
'; 17 | version.entries.forEach((entry) => { 18 | html += "

" + entry.title + "

"; 19 | html += '
'; 20 | entry.items.forEach((item) => { 21 | html += "

" + item + "

"; 22 | }); 23 | html += "
"; 24 | }); 25 | html += "
"; 26 | if (index == 0) { 27 | latest = html; 28 | } else { 29 | previous += html; 30 | } 31 | }); 32 | $("#latest").html(latest); 33 | $("#previous").html(previous); 34 | } 35 | 36 | const expandMore = $("#expand-more"); 37 | const expandLess = $("#expand-less"); 38 | const previous = $("#previous"); 39 | 40 | function expand() { 41 | previous.removeClass("hidden"); 42 | previous.height(previous[0].scrollHeight); 43 | expandMore.addClass("hidden"); 44 | expandLess.removeClass("hidden"); 45 | setTimeout(() => { 46 | $("#more-anchor").get(0).scrollIntoView({ behavior: "smooth" }); 47 | }, 150); 48 | } 49 | 50 | function collapse() { 51 | previous.addClass("hidden"); 52 | previous.height(0); 53 | expandMore.removeClass("hidden"); 54 | expandLess.addClass("hidden"); 55 | setTimeout(() => { 56 | $("#less-anchor").get(0).scrollIntoView({ behavior: "smooth" }); 57 | }, 150); 58 | } 59 | 60 | function loadVersion() { 61 | fetch("./version.json") 62 | .then((res) => { 63 | if (!res.ok) { 64 | throw new Error("Cannot find \"data.json\"."); 65 | } 66 | return res.json(); 67 | }) 68 | .then((data) => render(data)) 69 | .catch((error) => { 70 | console.log(error); 71 | alertify.error("Failed to load version data 😰"); 72 | }); 73 | 74 | expandMore.on("click", expand); 75 | expandLess.on("click", collapse); 76 | } 77 | 78 | export { loadVersion }; 79 | -------------------------------------------------------------------------------- /src/res/fonts/Consolas.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Lord-Turmoil/CodePaste/7ea55da35e3c73b3bd1b348aba5ca95379d564db/src/res/fonts/Consolas.eot -------------------------------------------------------------------------------- /src/res/fonts/Consolas.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Lord-Turmoil/CodePaste/7ea55da35e3c73b3bd1b348aba5ca95379d564db/src/res/fonts/Consolas.woff -------------------------------------------------------------------------------- /src/res/fonts/Consolas.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Lord-Turmoil/CodePaste/7ea55da35e3c73b3bd1b348aba5ca95379d564db/src/res/fonts/Consolas.woff2 -------------------------------------------------------------------------------- /src/res/fonts/LucidaHandwriting-Italic.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Lord-Turmoil/CodePaste/7ea55da35e3c73b3bd1b348aba5ca95379d564db/src/res/fonts/LucidaHandwriting-Italic.eot -------------------------------------------------------------------------------- /src/res/fonts/LucidaHandwriting-Italic.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Lord-Turmoil/CodePaste/7ea55da35e3c73b3bd1b348aba5ca95379d564db/src/res/fonts/LucidaHandwriting-Italic.woff -------------------------------------------------------------------------------- /src/res/fonts/LucidaHandwriting-Italic.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Lord-Turmoil/CodePaste/7ea55da35e3c73b3bd1b348aba5ca95379d564db/src/res/fonts/LucidaHandwriting-Italic.woff2 -------------------------------------------------------------------------------- /src/res/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Lord-Turmoil/CodePaste/7ea55da35e3c73b3bd1b348aba5ca95379d564db/src/res/logo.png -------------------------------------------------------------------------------- /src/version.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "version": "3.1.1", 4 | "date": "2025.1.18", 5 | "entries": [ 6 | { 7 | "title": "Changes", 8 | "items": [ 9 | "Add support for more languages", 10 | "Use Tab or Shift + Tab to indent or unindent", 11 | "Minor adjustments to the layout" 12 | ] 13 | }, 14 | { 15 | "title": "Fixes", 16 | "items": [ 17 | "Tab key will no longer clear input history", 18 | "Compatibility issue with Microsoft PowerPoint" 19 | ] 20 | } 21 | ] 22 | }, 23 | { 24 | "version": "3.1.0", 25 | "date": "2024.10.3", 26 | "entries": [ 27 | { 28 | "title": "Refactor", 29 | "items": [ 30 | "Refactored project with npm package" 31 | ] 32 | }, 33 | { 34 | "title": "Changes", 35 | "items": [ 36 | "Minor updates to the style" 37 | ] 38 | }, 39 | { 40 | "title": "Fixes", 41 | "items": [ 42 | "Fixed the bug that input box cannot auto-shrink" 43 | ] 44 | } 45 | ] 46 | }, 47 | { 48 | "version": "3.0.1", 49 | "date": "2024.7.25", 50 | "entries": [ 51 | { 52 | "title": "Changes", 53 | "items": [ 54 | "Introduced native dark mode", 55 | "Improved animation effects", 56 | "Disabled spell check and completion for input box", 57 | "Copy code with line numbers (Experimental)" 58 | ] 59 | }, 60 | { 61 | "title": "Fixes", 62 | "items": [ 63 | "Removed the annoying text shadow" 64 | ] 65 | }, 66 | { 67 | "title": "Notice", 68 | "items": [ 69 | "You may need to manually adjust font and column width after pasting" 70 | ] 71 | } 72 | ] 73 | }, 74 | { 75 | "version": "2.2.3", 76 | "date": "2023.12.31", 77 | "entries": [ 78 | { 79 | "title": "Changes", 80 | "items": [ 81 | "Can use Tab key to indent 4 spaces", 82 | "Can remember history language and theme", 83 | "Add prompt for the use of Microsoft Clarity" 84 | ] 85 | }, 86 | { 87 | "title": "Fixes", 88 | "items": [ 89 | "Bad URL links" 90 | ] 91 | } 92 | ] 93 | }, 94 | { 95 | "version": "2.0.3", 96 | "date": "2023.10.6", 97 | "entries": [ 98 | { 99 | "title": "Changes", 100 | "items": [ 101 | "More highlight color schemes!", 102 | "Provide random code sample", 103 | "Add language hint in the code block", 104 | "Better animation effects", 105 | "Remove line number option" 106 | ] 107 | }, 108 | { 109 | "title": "Fixes", 110 | "items": [ 111 | "Some visual glitches" 112 | ] 113 | }, 114 | { 115 | "title": "Bug", 116 | "items": [ 117 | "Layout may still have bug for certain browsers" 118 | ] 119 | } 120 | ] 121 | }, 122 | { 123 | "version": "1.2.x", 124 | "date": "2023.10.4", 125 | "entries": [ 126 | { 127 | "title": "Changes", 128 | "items": [ 129 | "Support for one-click copy", 130 | "Add beautiful alertify dialogs", 131 | "Adapt to new Edge UI", 132 | "Fix typo", 133 | "Reorder action buttons" 134 | ] 135 | } 136 | ] 137 | }, 138 | { 139 | "version": "1.1.x", 140 | "date": "2023.6.26", 141 | "entries": [ 142 | { 143 | "title": "General", 144 | "items": [ 145 | "First stable version", 146 | "Primary features realized" 147 | ] 148 | }, 149 | { 150 | "title": "Fixes", 151 | "items": [ 152 | "Fix potential format error", 153 | "Fix compatibility with Microsoft PowerPoint" 154 | ] 155 | } 156 | ] 157 | } 158 | ] -------------------------------------------------------------------------------- /src/views/components/.gitignore: -------------------------------------------------------------------------------- 1 | notification.html 2 | statistics.html 3 | support.html 4 | languages.html 5 | -------------------------------------------------------------------------------- /src/views/components/head.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /src/views/components/meta.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /src/views/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Code Paste | Syntax Highlight for Office 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 |
19 | 20 |
21 | 22 |
23 | 24 |
25 | 26 |
27 | 28 | 29 | 30 | 33 | 36 |
37 | 38 | 39 | 40 | -------------------------------------------------------------------------------- /src/views/parts/banner.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/views/parts/docs.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |

Why Code Paste?

4 |
5 |
6 |
7 |

8 | Adding code snippets to Office documents like Word or PowerPoint could be a challenge, 9 | especially when you want to preserve the syntax highlighting. 😢 Furthermore, the typical dark 10 | themes of most IDEs doesn't work well for presentations. While converting code into images is an 11 | option, it often suffers from non-transparent background, and apparently not as flexible or 12 | crystal clear as text. 😖 13 |

14 |

15 | So, is there a better way to embed highlighted code snippets in these documents? 🤔 16 | Absolutely, and that's where Code Paste comes in! 😆 Simply paste your code into the left 17 | panel, choose your preferred language and color scheme. With a few clicks, you'll have a 18 | beautifully highlighted code snippet ready to go. 😋 19 |

20 |

21 | Can't find the language you need? 🫨 Well, don't be shy! Open an 22 | issue 23 | and I will add it for you! 😘 24 |

25 |
26 |
27 |
28 |
29 |
30 |
31 | 32 |
Show more
33 | 34 |
-------------------------------------------------------------------------------- /src/views/parts/footer.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/views/parts/header.html: -------------------------------------------------------------------------------- 1 |
2 | Code 3 | 4 | Paste 5 |
-------------------------------------------------------------------------------- /src/views/parts/panel.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 | 6 |
7 |
8 |
9 |
10 | 11 |
12 |
13 | 14 |
15 |
16 | 17 |
18 |
19 | 20 |
21 |
22 | 23 |
24 |
25 | 26 |
27 |
28 |
29 |
30 |

Step 1
Select the language of your code 🧐

31 |

Step 2
Paste your code to the input panel 🤨

32 |

Step 3
Click the circle with arrow to make yummy code paste 😋

33 |

Step 4
Change the color scheme to meet your taste 😆

34 |

Step 5
Click the clipboard-like button to copy the code 😁

35 |

Step 6
Paste the code to your document and be professional! 😍

36 |
37 |
38 |
39 |
40 |
41 |
-------------------------------------------------------------------------------- /src/views/parts/toolbar.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 |
Language
5 |
6 |
7 | 10 |
11 |
12 |
13 |
14 |
Color Scheme
15 |
16 |
17 | 62 |
63 |
64 |
65 |
66 |
Line Number
67 |
68 | 69 | 70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
84 |
85 |
86 |
-------------------------------------------------------------------------------- /webpack.config.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable no-undef */ 2 | module.exports = { 3 | entry: './src/js/main.js', 4 | output: { 5 | path: __dirname + '/dist', 6 | filename: 'bundle.js' 7 | }, 8 | module: { 9 | rules: [ 10 | { 11 | test: /\.m?js$/, 12 | exclude: /node_modules/, 13 | use: { 14 | loader: 'babel-loader', 15 | options: { 16 | presets: ['@babel/preset-env'] 17 | } 18 | } 19 | }, 20 | { 21 | test: /\.css$/, 22 | use: ['css-loader'] 23 | } 24 | ] 25 | } 26 | }; --------------------------------------------------------------------------------