Guest User

roblox rich text editor

a guest
Apr 5th, 2026
147
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 15.97 KB | None | 0 0
  1. <!DOCTYPE html>
  2. <html lang="en">
  3. <head>
  4. <meta charset="UTF-8" />
  5. <meta name="viewport" content="width=device-width, initial-scale=1.0" />
  6. <title>Roblox Rich Text Editor</title>
  7. <link href="https://fonts.googleapis.com/css2?family=DM+Mono:ital,wght@0,400;0,500;1,400&family=DM+Sans:wght@400;500;600&display=swap" rel="stylesheet" />
  8. <style>
  9. *, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
  10.  
  11. :root {
  12. --bg: #0e0f13;
  13. --surface: #16181f;
  14. --surface2: #1e2029;
  15. --border: #2a2d3a;
  16. --border2: #363a4d;
  17. --accent: #e8624a;
  18. --accent2: #f0855f;
  19. --text: #e4e6f0;
  20. --muted: #7a7f9a;
  21. --success: #4caf7d;
  22. --mono: 'DM Mono', monospace;
  23. --sans: 'DM Sans', system-ui, sans-serif;
  24. --radius: 8px;
  25. --radius-lg: 12px;
  26. }
  27.  
  28. body {
  29. font-family: var(--sans);
  30. background: var(--bg);
  31. color: var(--text);
  32. min-height: 100vh;
  33. padding: 32px 24px 48px;
  34. }
  35.  
  36. header {
  37. display: flex;
  38. align-items: baseline;
  39. gap: 12px;
  40. margin-bottom: 24px;
  41. }
  42.  
  43. header h1 {
  44. font-size: 18px;
  45. font-weight: 600;
  46. letter-spacing: -0.01em;
  47. color: var(--text);
  48. }
  49.  
  50. header span {
  51. font-size: 12px;
  52. color: var(--muted);
  53. font-family: var(--mono);
  54. background: var(--surface2);
  55. border: 1px solid var(--border);
  56. border-radius: 4px;
  57. padding: 2px 8px;
  58. }
  59.  
  60. #app {
  61. max-width: 960px;
  62. margin: 0 auto;
  63. display: flex;
  64. flex-direction: column;
  65. gap: 14px;
  66. }
  67.  
  68. /* ── Toolbar ── */
  69. .toolbar {
  70. display: flex;
  71. flex-wrap: wrap;
  72. gap: 6px;
  73. align-items: center;
  74. padding: 10px 14px;
  75. background: var(--surface);
  76. border: 1px solid var(--border);
  77. border-radius: var(--radius-lg);
  78. }
  79.  
  80. .toolbar-group {
  81. display: flex;
  82. gap: 4px;
  83. align-items: center;
  84. }
  85.  
  86. .sep {
  87. width: 1px;
  88. align-self: stretch;
  89. background: var(--border);
  90. margin: 0 6px;
  91. opacity: 0.6;
  92. }
  93.  
  94. .tb-btn {
  95. border: 1px solid var(--border2);
  96. background: var(--surface2);
  97. color: var(--text);
  98. border-radius: var(--radius);
  99. padding: 5px 11px;
  100. font-size: 13px;
  101. font-family: var(--sans);
  102. cursor: pointer;
  103. transition: background 0.12s, border-color 0.12s, color 0.12s;
  104. line-height: 1;
  105. }
  106. .tb-btn:hover {
  107. background: #272a36;
  108. border-color: var(--accent);
  109. color: var(--accent2);
  110. }
  111. .tb-btn:active { transform: scale(0.96); }
  112. .tb-btn.b { font-weight: 700; }
  113. .tb-btn.i { font-style: italic; }
  114. .tb-btn.u { text-decoration: underline; }
  115. .tb-btn.s { text-decoration: line-through; }
  116. .tb-btn.uc { text-transform: uppercase; font-size: 11px; letter-spacing: 0.05em; }
  117. .tb-btn.sc { font-variant: small-caps; }
  118.  
  119. .color-input-wrap {
  120. display: flex;
  121. align-items: center;
  122. gap: 7px;
  123. }
  124. .color-input-wrap label {
  125. font-size: 12px;
  126. color: var(--muted);
  127. font-family: var(--mono);
  128. white-space: nowrap;
  129. }
  130.  
  131. input[type=color] {
  132. width: 28px;
  133. height: 28px;
  134. border-radius: 6px;
  135. border: 1px solid var(--border2);
  136. padding: 2px;
  137. cursor: pointer;
  138. background: var(--surface2);
  139. }
  140.  
  141. select.tb-select {
  142. border: 1px solid var(--border2);
  143. background: var(--surface2);
  144. color: var(--text);
  145. border-radius: var(--radius);
  146. padding: 5px 8px;
  147. font-size: 13px;
  148. cursor: pointer;
  149. font-family: var(--sans);
  150. outline: none;
  151. transition: border-color 0.12s;
  152. }
  153. select.tb-select:hover { border-color: var(--accent); }
  154.  
  155. /* ── Panels ── */
  156. .panels {
  157. display: grid;
  158. grid-template-columns: 1fr 1fr;
  159. gap: 14px;
  160. }
  161.  
  162. .panel-label {
  163. font-size: 10px;
  164. font-weight: 600;
  165. color: var(--muted);
  166. text-transform: uppercase;
  167. letter-spacing: 0.1em;
  168. margin-bottom: 7px;
  169. font-family: var(--mono);
  170. }
  171.  
  172. textarea#editor {
  173. width: 100%;
  174. min-height: 200px;
  175. border: 1px solid var(--border);
  176. border-radius: var(--radius-lg);
  177. padding: 14px;
  178. font-family: var(--mono);
  179. font-size: 13px;
  180. color: var(--text);
  181. background: var(--surface);
  182. resize: vertical;
  183. line-height: 1.65;
  184. outline: none;
  185. transition: border-color 0.15s;
  186. caret-color: var(--accent);
  187. }
  188. textarea#editor::placeholder { color: #3d4158; }
  189. textarea#editor:focus { border-color: var(--accent); }
  190.  
  191. .preview-box {
  192. min-height: 200px;
  193. border: 1px solid var(--border);
  194. border-radius: var(--radius-lg);
  195. padding: 14px;
  196. background: var(--surface);
  197. font-size: 15px;
  198. line-height: 1.75;
  199. color: var(--text);
  200. word-break: break-word;
  201. position: relative;
  202. }
  203. .preview-box:empty::before {
  204. content: 'Preview appears here…';
  205. color: #3d4158;
  206. font-size: 14px;
  207. font-family: var(--mono);
  208. }
  209.  
  210. /* ── Actions ── */
  211. .actions {
  212. display: flex;
  213. gap: 8px;
  214. flex-wrap: wrap;
  215. align-items: center;
  216. }
  217.  
  218. .btn-copy {
  219. border: 1px solid var(--accent);
  220. background: var(--accent);
  221. color: #fff;
  222. border-radius: var(--radius);
  223. padding: 7px 16px;
  224. font-size: 13px;
  225. font-weight: 600;
  226. cursor: pointer;
  227. font-family: var(--sans);
  228. transition: background 0.12s, transform 0.1s;
  229. }
  230. .btn-copy:hover { background: var(--accent2); border-color: var(--accent2); }
  231. .btn-copy:active { transform: scale(0.97); }
  232.  
  233. .btn-clear {
  234. border: 1px solid var(--border2);
  235. background: transparent;
  236. color: var(--muted);
  237. border-radius: var(--radius);
  238. padding: 7px 16px;
  239. font-size: 13px;
  240. cursor: pointer;
  241. font-family: var(--sans);
  242. transition: color 0.12s, border-color 0.12s;
  243. }
  244. .btn-clear:hover { color: var(--text); border-color: var(--border2); }
  245.  
  246. .copy-msg {
  247. font-size: 13px;
  248. color: var(--success);
  249. font-family: var(--mono);
  250. opacity: 0;
  251. transition: opacity 0.2s;
  252. }
  253. .copy-msg.show { opacity: 1; }
  254.  
  255. .char-count {
  256. font-size: 12px;
  257. color: var(--muted);
  258. font-family: var(--mono);
  259. margin-left: auto;
  260. }
  261.  
  262. /* ── Tag reference ── */
  263. .tag-ref {
  264. background: var(--surface);
  265. border: 1px solid var(--border);
  266. border-radius: var(--radius-lg);
  267. padding: 10px 14px;
  268. display: flex;
  269. flex-wrap: wrap;
  270. gap: 6px;
  271. align-items: center;
  272. }
  273.  
  274. .tag-ref-label {
  275. font-size: 11px;
  276. color: var(--muted);
  277. font-family: var(--mono);
  278. margin-right: 4px;
  279. text-transform: uppercase;
  280. letter-spacing: 0.06em;
  281. }
  282.  
  283. .tag-pill {
  284. font-size: 12px;
  285. font-family: var(--mono);
  286. background: var(--surface2);
  287. border: 1px solid var(--border);
  288. border-radius: 999px;
  289. padding: 3px 10px;
  290. color: var(--muted);
  291. transition: color 0.12s, border-color 0.12s;
  292. cursor: default;
  293. }
  294. .tag-pill:hover { color: var(--accent2); border-color: var(--accent); }
  295.  
  296. /* ── Accent line at top ── */
  297. body::before {
  298. content: '';
  299. display: block;
  300. height: 3px;
  301. background: linear-gradient(90deg, var(--accent), #b060f8 60%, transparent);
  302. position: fixed;
  303. top: 0; left: 0; right: 0;
  304. z-index: 10;
  305. }
  306.  
  307. @media (max-width: 640px) {
  308. .panels { grid-template-columns: 1fr; }
  309. }
  310. </style>
  311. </head>
  312. <body>
  313.  
  314. <div id="app">
  315. <header>
  316. <h1>Roblox Rich Text Editor</h1>
  317. <span>RichText = true</span>
  318. </header>
  319.  
  320. <div class="toolbar">
  321. <div class="toolbar-group">
  322. <button class="tb-btn b" onclick="wrap('<b>','</b>')">B</button>
  323. <button class="tb-btn i" onclick="wrap('<i>','</i>')">I</button>
  324. <button class="tb-btn u" onclick="wrap('<u>','</u>')">U</button>
  325. <button class="tb-btn s" onclick="wrap('<s>','</s>')">S</button>
  326. </div>
  327. <div class="sep"></div>
  328. <div class="toolbar-group">
  329. <button class="tb-btn uc" onclick="wrap('<uc>','</uc>')">ABC</button>
  330. <button class="tb-btn sc" onclick="wrap('<sc>','</sc>')">Sc</button>
  331. </div>
  332. <div class="sep"></div>
  333. <div class="toolbar-group color-input-wrap">
  334. <label>color</label>
  335. <input type="color" id="color-pick" value="#FF7800" />
  336. <button class="tb-btn" onclick="wrapColor()">Apply</button>
  337. </div>
  338. <div class="sep"></div>
  339. <div class="toolbar-group color-input-wrap">
  340. <label>size</label>
  341. <select class="tb-select" id="size-pick">
  342. <option value="12">12</option>
  343. <option value="16">16</option>
  344. <option value="20" selected>20</option>
  345. <option value="24">24</option>
  346. <option value="28">28</option>
  347. <option value="32">32</option>
  348. <option value="40">40</option>
  349. <option value="48">48</option>
  350. </select>
  351. <button class="tb-btn" onclick="wrapSize()">Apply</button>
  352. </div>
  353. <div class="sep"></div>
  354. <div class="toolbar-group">
  355. <button class="tb-btn" onclick="wrap('<mark color=&quot;#009966&quot; transparency=&quot;0&quot;>','</mark>')">Mark</button>
  356. <button class="tb-btn" onclick="insertTag('<br/>')">BR</button>
  357. <button class="tb-btn" onclick="wrap('<stroke color=&quot;#00A2FF&quot; thickness=&quot;2&quot;>','</stroke>')">Stroke</button>
  358. </div>
  359. <div class="sep"></div>
  360. <div class="toolbar-group color-input-wrap">
  361. <label>face</label>
  362. <select class="tb-select" id="face-pick">
  363. <option>Michroma</option>
  364. <option>GothamSSm</option>
  365. <option>Roboto</option>
  366. <option>SourceSansPro</option>
  367. <option>Oswald</option>
  368. <option>Creepster</option>
  369. <option>Bangers</option>
  370. </select>
  371. <button class="tb-btn" onclick="wrapFace()">Apply</button>
  372. </div>
  373. </div>
  374.  
  375. <div class="panels">
  376. <div>
  377. <div class="panel-label">Rich text markup</div>
  378. <textarea id="editor" spellcheck="false" placeholder="Type or paste your rich text string here...
  379.  
  380. Example: Hello &lt;b&gt;world&lt;/b&gt;, this is &lt;font color=&quot;#FF7800&quot;&gt;orange&lt;/font&gt;!"></textarea>
  381. </div>
  382. <div>
  383. <div class="panel-label">Rendered preview</div>
  384. <div class="preview-box" id="preview"></div>
  385. </div>
  386. </div>
  387.  
  388. <div class="actions">
  389. <button class="btn-copy" onclick="copyText()">Copy markup</button>
  390. <button class="btn-clear" onclick="clearAll()">Clear</button>
  391. <span class="copy-msg" id="copy-msg">✓ Copied!</span>
  392. <span class="char-count" id="char-count">0 chars</span>
  393. </div>
  394.  
  395. <div class="tag-ref">
  396. <span class="tag-ref-label">Tags</span>
  397. <span class="tag-pill">&lt;b&gt;</span>
  398. <span class="tag-pill">&lt;i&gt;</span>
  399. <span class="tag-pill">&lt;u&gt;</span>
  400. <span class="tag-pill">&lt;s&gt;</span>
  401. <span class="tag-pill">&lt;uc&gt;</span>
  402. <span class="tag-pill">&lt;sc&gt;</span>
  403. <span class="tag-pill">&lt;br/&gt;</span>
  404. <span class="tag-pill">&lt;font color=""&gt;</span>
  405. <span class="tag-pill">&lt;font size=""&gt;</span>
  406. <span class="tag-pill">&lt;font face=""&gt;</span>
  407. <span class="tag-pill">&lt;font weight=""&gt;</span>
  408. <span class="tag-pill">&lt;font transparency=""&gt;</span>
  409. <span class="tag-pill">&lt;mark&gt;</span>
  410. <span class="tag-pill">&lt;stroke&gt;</span>
  411. </div>
  412. </div>
  413.  
  414. <script>
  415. const editor = document.getElementById('editor');
  416. const preview = document.getElementById('preview');
  417. const charCount = document.getElementById('char-count');
  418.  
  419. function richToHtml(str) {
  420. return str
  421. .replace(/&amp;/g, '&')
  422. .replace(/&lt;/g, '<TEMP_LT>')
  423. .replace(/&gt;/g, '<TEMP_GT>')
  424. .replace(/&quot;/g, '"')
  425. .replace(/&apos;/g, "'")
  426. .replace(/<b>([\s\S]*?)<\/b>/gi, '<strong>$1</strong>')
  427. .replace(/<i>([\s\S]*?)<\/i>/gi, '<em>$1</em>')
  428. .replace(/<u>([\s\S]*?)<\/u>/gi, '<span style="text-decoration:underline">$1</span>')
  429. .replace(/<s>([\s\S]*?)<\/s>/gi, '<span style="text-decoration:line-through">$1</span>')
  430. .replace(/<uppercase>([\s\S]*?)<\/uppercase>/gi, '<span style="text-transform:uppercase">$1</span>')
  431. .replace(/<uc>([\s\S]*?)<\/uc>/gi, '<span style="text-transform:uppercase">$1</span>')
  432. .replace(/<smallcaps>([\s\S]*?)<\/smallcaps>/gi, '<span style="font-variant:small-caps">$1</span>')
  433. .replace(/<sc>([\s\S]*?)<\/sc>/gi, '<span style="font-variant:small-caps">$1</span>')
  434. .replace(/<br\/>/gi, '<br>')
  435. .replace(/<font([^>]*)>([\s\S]*?)<\/font>/gi, (m, attrs, content) => {
  436. let style = '';
  437. const color = attrs.match(/color\s*=\s*["']([^"']+)["']/i);
  438. const size = attrs.match(/size\s*=\s*["']([^"']+)["']/i);
  439. const face = attrs.match(/face\s*=\s*["']([^"']+)["']/i);
  440. const family = attrs.match(/family\s*=\s*["']([^"']+)["']/i);
  441. const weight = attrs.match(/weight\s*=\s*["']([^"']+)["']/i);
  442. const trans = attrs.match(/transparency\s*=\s*["']([^"']+)["']/i);
  443. if (color) style += `color:${color[1]};`;
  444. if (size) style += `font-size:${size[1]}px;`;
  445. if (face) style += `font-family:${face[1]},sans-serif;`;
  446. if (family) style += `font-family:${family[1]},sans-serif;`;
  447. if (weight) {
  448. const wmap = {thin:'100',extralight:'200',light:'300',regular:'400',medium:'500',semibold:'600',bold:'700',extrabold:'800',heavy:'900'};
  449. style += `font-weight:${wmap[weight[1].toLowerCase()] || weight[1]};`;
  450. }
  451. if (trans) style += `opacity:${1 - parseFloat(trans[1])};`;
  452. return `<span style="${style}">${content}</span>`;
  453. })
  454. .replace(/<mark([^>]*)>([\s\S]*?)<\/mark>/gi, (m, attrs, content) => {
  455. const color = (attrs.match(/color\s*=\s*["']([^"']+)["']/i)||[])[1] || '#FFD700';
  456. const trans = (attrs.match(/transparency\s*=\s*["']([^"']+)["']/i)||[])[1] || '0';
  457. return `<mark style="background:${color};opacity:${1-parseFloat(trans)};padding:0 2px;border-radius:2px;">${content}</mark>`;
  458. })
  459. .replace(/<stroke([^>]*)>([\s\S]*?)<\/stroke>/gi, (m, attrs, content) => {
  460. const color = (attrs.match(/color\s*=\s*["']([^"']+)["']/i)||[])[1] || '#000';
  461. const thick = (attrs.match(/thickness\s*=\s*["']([^"']+)["']/i)||[])[1] || '1';
  462. return `<span style="-webkit-text-stroke:${thick}px ${color}">${content}</span>`;
  463. })
  464. .replace(/<!--[\s\S]*?-->/g, '')
  465. .replace(/<TEMP_LT>/g, '&lt;')
  466. .replace(/<TEMP_GT>/g, '&gt;');
  467. }
  468.  
  469. function updatePreview() {
  470. const val = editor.value;
  471. charCount.textContent = val.length + ' chars';
  472. try { preview.innerHTML = richToHtml(val); }
  473. catch(e) { preview.textContent = val; }
  474. }
  475.  
  476. editor.addEventListener('input', updatePreview);
  477.  
  478. function getSelection() {
  479. return { start: editor.selectionStart, end: editor.selectionEnd, text: editor.value.substring(editor.selectionStart, editor.selectionEnd) };
  480. }
  481.  
  482. function wrap(open, close) {
  483. const { start, end, text } = getSelection();
  484. const newVal = editor.value.substring(0, start) + open + text + close + editor.value.substring(end);
  485. editor.value = newVal;
  486. editor.focus();
  487. editor.setSelectionRange(start + open.length + text.length + close.length, start + open.length + text.length + close.length);
  488. updatePreview();
  489. }
  490.  
  491. function insertTag(tag) {
  492. const s = editor.selectionStart;
  493. editor.value = editor.value.substring(0, s) + tag + editor.value.substring(s);
  494. editor.focus();
  495. editor.setSelectionRange(s + tag.length, s + tag.length);
  496. updatePreview();
  497. }
  498.  
  499. function wrapColor() { wrap(`<font color="${document.getElementById('color-pick').value}">`, '</font>'); }
  500. function wrapSize() { wrap(`<font size="${document.getElementById('size-pick').value}">`, '</font>'); }
  501. function wrapFace() { wrap(`<font face="${document.getElementById('face-pick').value}">`, '</font>'); }
  502.  
  503. function copyText() {
  504. navigator.clipboard.writeText(editor.value).then(() => {
  505. const msg = document.getElementById('copy-msg');
  506. msg.classList.add('show');
  507. setTimeout(() => msg.classList.remove('show'), 1600);
  508. });
  509. }
  510.  
  511. function clearAll() { editor.value = ''; updatePreview(); }
  512.  
  513. updatePreview();
  514. </script>
  515. </body>
  516. </html>
  517.  
Advertisement
Add Comment
Please, Sign In to add comment