-
Star
(170)
You must be signed in to star a gist -
Fork
(39)
You must be signed in to fork a gist
-
-
Save rebane2001/07f2d8e80df053c70a1576d27eabe97c to your computer and use it in GitHub Desktop.
| <!DOCTYPE html> | |
| <html lang="en"> | |
| <head> | |
| <link rel="icon" type="image/x-icon" href="/favicon.ico"> | |
| <title>Rebane's Discord Colored Text Generator</title> | |
| <meta charset="UTF-8"> | |
| <meta name="description" content="Rebane's Discord Colored Text Generator"> | |
| <meta name="author" content="rebane2001"> | |
| <style> | |
| /* | |
| This license applies to the entire page, not just the style block. | |
| This is free and unencumbered software released into the public domain. | |
| Anyone is free to copy, modify, publish, use, compile, sell, or | |
| distribute this software, either in source code form or as a compiled | |
| binary, for any purpose, commercial or non-commercial, and by any | |
| means. | |
| In jurisdictions that recognize copyright laws, the author or authors | |
| of this software dedicate any and all copyright interest in the | |
| software to the public domain. We make this dedication for the benefit | |
| of the public at large and to the detriment of our heirs and | |
| successors. We intend this dedication to be an overt act of | |
| relinquishment in perpetuity of all present and future rights to this | |
| software under copyright law. | |
| THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, | |
| EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF | |
| MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. | |
| IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR | |
| OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, | |
| ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR | |
| OTHER DEALINGS IN THE SOFTWARE. | |
| For more information, please refer to <https://unlicense.org> | |
| */ | |
| html { | |
| font-family: sans-serif; | |
| background-color: #36393F; | |
| text-align: center; | |
| color: #FFF; | |
| } | |
| .container { | |
| max-width: 500px; | |
| margin: auto; | |
| } | |
| .flex { | |
| height: 100%; | |
| display: flex; | |
| align-items: center; | |
| justify-content: center; | |
| } | |
| #textarea { | |
| width: 600px; | |
| height: 200px; | |
| border-radius: 5px; | |
| resize: both; | |
| overflow: auto; | |
| text-align: left; | |
| font-family: monospace; | |
| background-color: #2F3136; | |
| color: #B9BBBE; | |
| border: #202225 1px solid; | |
| padding: 5px; | |
| display: inline-block; | |
| white-space: pre-wrap; | |
| font-size: 0.875rem; | |
| line-height: 1.125rem; | |
| text-indent: 0; | |
| } | |
| .button { | |
| min-height: 32px; | |
| min-width: 32px; | |
| border: none; | |
| border-radius: 3px; | |
| color: #fff; | |
| background-color: #4f545c; | |
| font-size: 14px; | |
| padding: 2px 16px; | |
| cursor: pointer; | |
| transition: background-color 250ms linear; | |
| } | |
| a,a:visited { | |
| color: #00AFF4 | |
| } | |
| .tooltip { | |
| display: none; | |
| position: absolute; | |
| background-color: #3BA55D; | |
| border: none; | |
| border-radius: 3px; | |
| color: #fff; | |
| font-size: 14px; | |
| padding: 8px 16px; | |
| top: 0; | |
| } | |
| .ansi-1 { font-weight:700; text-decoration:none; } | |
| .ansi-4 { font-weight:500; text-decoration:underline; } | |
| .ansi-30 { color: #4f545c; } | |
| .ansi-31 { color: #dc322f; } | |
| .ansi-32 { color: #859900; } | |
| .ansi-33 { color: #b58900; } | |
| .ansi-34 { color: #268bd2; } | |
| .ansi-35 { color: #d33682; } | |
| .ansi-36 { color: #2aa198; } | |
| .ansi-37 { color: #ffffff; } | |
| .ansi-30-bg { background-color: #4f545c; } | |
| .ansi-31-bg { background-color: #dc322f; } | |
| .ansi-32-bg { background-color: #859900; } | |
| .ansi-33-bg { background-color: #b58900; } | |
| .ansi-34-bg { background-color: #268bd2; } | |
| .ansi-35-bg { background-color: #d33682; } | |
| .ansi-36-bg { background-color: #2aa198; } | |
| .ansi-37-bg { background-color: #ffffff; } | |
| .ansi-40 { background-color: #002b36; } | |
| .ansi-41 { background-color: #cb4b16; } | |
| .ansi-42 { background-color: #586e75; } | |
| .ansi-43 { background-color: #657b83; } | |
| .ansi-44 { background-color: #839496; } | |
| .ansi-45 { background-color: #6c71c4; } | |
| .ansi-46 { background-color: #93a1a1; } | |
| .ansi-47 { background-color: #fdf6e3; } | |
| </style> | |
| </head> | |
| <body> | |
| <h1>Rebane's Discord <span style="color:#5865F2">Colored</span> Text Generator</h1> | |
| <div class="container"> | |
| <h3>About</h3> | |
| <p>This is a simple app that creates colored Discord messages using the ANSI color codes available on the latest Discord desktop versions.</p> | |
| <p>To use this, write your text, select parts of it and assign colors to them, then copy it using the button below, and send in a Discord message.</p> | |
| <h3>Source Code</h3> | |
| <p>This app runs entirely in your browser and the source code is freely available on <a href="https://gist.github.com/rebane2001/07f2d8e80df053c70a1576d27eabe97c">GitHub</a>. Shout out to kkrypt0nn for <a href="https://gist.github.com/kkrypt0nn/a02506f3712ff2d1c8ca7c9e0aed7c06">this guide</a>.</p> | |
| </div> | |
| <h2>Create your text</h2> | |
| <button data-ansi="0" class="button style-button">Reset All</button> | |
| <button data-ansi="1" class="button style-button ansi-1">Bold</button> | |
| <button data-ansi="4" class="button style-button ansi-4">Line</button> | |
| <br><br> | |
| <strong>FG</strong> | |
| <button data-ansi="30" class="button style-button ansi-30-bg"> </button> | |
| <button data-ansi="31" class="button style-button ansi-31-bg"> </button> | |
| <button data-ansi="32" class="button style-button ansi-32-bg"> </button> | |
| <button data-ansi="33" class="button style-button ansi-33-bg"> </button> | |
| <button data-ansi="34" class="button style-button ansi-34-bg"> </button> | |
| <button data-ansi="35" class="button style-button ansi-35-bg"> </button> | |
| <button data-ansi="36" class="button style-button ansi-36-bg"> </button> | |
| <button data-ansi="37" class="button style-button ansi-37-bg"> </button> | |
| <br><br> | |
| <strong>BG</strong> | |
| <button data-ansi="40" class="button style-button ansi-40"> </button> | |
| <button data-ansi="41" class="button style-button ansi-41"> </button> | |
| <button data-ansi="42" class="button style-button ansi-42"> </button> | |
| <button data-ansi="43" class="button style-button ansi-43"> </button> | |
| <button data-ansi="44" class="button style-button ansi-44"> </button> | |
| <button data-ansi="45" class="button style-button ansi-45"> </button> | |
| <button data-ansi="46" class="button style-button ansi-46"> </button> | |
| <button data-ansi="47" class="button style-button ansi-47"> </button> | |
| <br><br> | |
| <div class="flex"><div id="textarea" contenteditable="true">Welcome to <span class="ansi-33">Rebane</span>'s <span class="ansi-45"><span class="ansi-37">Discord</span></span> <span class="ansi-31">C</span><span class="ansi-32">o</span><span class="ansi-33">l</span><span class="ansi-34">o</span><span class="ansi-35">r</span><span class="ansi-36">e</span><span class="ansi-37">d</span> Text Generator!</div></div> | |
| <br> | |
| <button class="button copy">Copy text as Discord formatted</button> | |
| <br> | |
| <br> | |
| <small>This is an unofficial tool, it is not made or endorsed by Discord.</small> | |
| <div class="tooltip">Tooltip</div> | |
| <script type="text/javascript"> | |
| const textarea = document.querySelector("#textarea"); | |
| const copybtn = document.querySelector(".button.copy"); | |
| const tooltip = document.querySelector(".tooltip"); | |
| const tooltipTexts = { | |
| // FG | |
| "30": "Dark Gray (33%)", | |
| "31": "Red", | |
| "32": "Yellowish Green", | |
| "33": "Gold", | |
| "34": "Light Blue", | |
| "35": "Pink", | |
| "36": "Teal", | |
| "37": "White", | |
| // BG | |
| "40": "Blueish Black", | |
| "41": "Rust Brown", | |
| "42": "Gray (40%)", | |
| "43": "Gray (45%)", | |
| "44": "Light Gray (55%)", | |
| "45": "Blurple", | |
| "46": "Light Gray (60%)", | |
| "47": "Cream White", | |
| }; | |
| // Some basic escaping of pasted HTML tags, not ideal but good enough for this situation. | |
| textarea.oninput = () => { | |
| const base = textarea.innerHTML.replace(/<(\/?(br|span|span class="ansi-[0-9]*"))>/g,"[$1]"); | |
| if (base.includes("<") || base.includes(">")) textarea.innerHTML = base.replace(/<.*?>/g,"").replace(/[<>]/g,"").replace(/\[(\/?(br|span|span class="ansi-[0-9]*"))\]/g,"<$1>"); | |
| }; | |
| // https://stackoverflow.com/a/61237402 | |
| document.addEventListener('keydown', event => { | |
| if (event.key === 'Enter') { | |
| document.execCommand('insertLineBreak') | |
| event.preventDefault() | |
| } | |
| }); | |
| document.querySelectorAll(".style-button").forEach((btn) => { | |
| btn.onclick = () => { | |
| if (!btn.dataset.ansi) { | |
| textarea.innerText = textarea.innerText; | |
| return; | |
| } | |
| const selection = window.getSelection(); | |
| const text = window.getSelection().toString(); | |
| const span = document.createElement("span"); | |
| span.innerText = text; | |
| span.classList.add(`ansi-${btn.dataset.ansi}`); | |
| const range = selection.getRangeAt(0); | |
| range.deleteContents(); | |
| range.insertNode(span); | |
| range.selectNodeContents(span); | |
| selection.removeAllRanges(); | |
| selection.addRange(range); | |
| }; | |
| btn.onmouseenter = () => { | |
| if (!(btn.dataset.ansi > 4)) return; | |
| const rect = btn.getBoundingClientRect(); | |
| tooltip.style.display = "block"; | |
| tooltip.innerText = tooltipTexts[btn.dataset.ansi]; | |
| tooltip.style.top = `${rect.top - 36}px`; | |
| tooltip.style.left = `${rect.left - tooltip.clientWidth/2 + btn.clientWidth/2}px`; | |
| }; | |
| btn.onmouseleave = () => { | |
| tooltip.style.display = "none"; | |
| }; | |
| }); | |
| function nodesToANSI(nodes, states) { | |
| let text = "" | |
| for (const node of nodes) { | |
| if (node.nodeType === 3) { | |
| text += node.textContent; | |
| continue; | |
| } | |
| if (node.nodeName === "BR") { | |
| text += "\n"; | |
| continue; | |
| } | |
| const ansiCode = +(node.className.split("-")[1]); | |
| const newState = Object.assign({}, states.at(-1)); | |
| if (ansiCode < 30) newState.st = ansiCode; | |
| if (ansiCode >= 30 && ansiCode < 40) newState.fg = ansiCode; | |
| if (ansiCode >= 40) newState.bg = ansiCode; | |
| states.push(newState) | |
| text += `\x1b[${newState.st};${(ansiCode >= 40) ? newState.bg : newState.fg}m`; | |
| text += nodesToANSI(node.childNodes, states); | |
| states.pop() | |
| text += `\x1b[0m`; | |
| if (states.at(-1).fg !== 2) text += `\x1b[${states.at(-1).st};${states.at(-1).fg}m`; | |
| if (states.at(-1).bg !== 2) text += `\x1b[${states.at(-1).st};${states.at(-1).bg}m`; | |
| } | |
| return text; | |
| } | |
| let copyCount = 0; | |
| let copyTimeout = null; | |
| copybtn.onclick = () => { | |
| const toCopy = "```ansi\n" + nodesToANSI(textarea.childNodes, [{ fg: 2, bg: 2, st:2 }]) + "\n```"; | |
| navigator.clipboard.writeText(toCopy).then(() => { | |
| if (copyTimeout) clearTimeout(copyTimeout); | |
| const funnyCopyMessages = copybtn.innerText = ["Copied!", "Double Copy!", "Triple Copy!", "Dominating!!", "Rampage!!", "Mega Copy!!", "Unstoppable!!", "Wicked Sick!!", "Monster Copy!!!", "GODLIKE!!!", "BEYOND GODLIKE!!!!", Array(16).fill(0).reduce(p => p + String.fromCharCode(Math.floor(Math.random() * 65535)),"")]; | |
| copybtn.style.backgroundColor = (copyCount <= 8) ? "#3BA55D" : "#ED4245"; | |
| copybtn.innerText = funnyCopyMessages[copyCount]; | |
| copyCount = Math.min(11, copyCount + 1); | |
| copyTimeout = setTimeout(() => { | |
| copyCount = 0; | |
| copybtn.style.backgroundColor = null; | |
| copybtn.innerText = "Copy text as Discord formatted"; | |
| }, 2000) | |
| }, (err) => { | |
| // We don't need to stop the users if they get a little too excited about the button | |
| if (copyCount > 2) return; | |
| alert("Copying failed for some reason, let's try showing an alert, maybe you can copy it instead."); | |
| alert(toCopy); | |
| }); | |
| } | |
| </script> | |
| </body> | |
| </html> |
It's true that this allows for more personalization, this site is more color oriented, and https://discordfont.org is more oriented towards the font style of the site, both of which I use often.
Really great work! Thanks for sharing you work!
For those looking to add visual flair to their Discord avatars beyond just text styling, I highly recommend checking out discord-decoration.art. While this site focuses on colored text generation, Discord Decoration Art specializes in avatar decorations and visual enhancements.
It's the perfect complement to colored text - while this site handles your message styling, Discord Decoration Art takes care of making your profile picture stand out. Together, they offer a complete Discord customization solution without the $120/year Nitro cost.
This is super useful! I'd just like to humbly request a way to turn line wrapping off in the web version to make long lines easier to edit.