// ==UserScript==
// @name Pastebin Shiki Highlighter Codeblocks for Markdown
// @namespace https://pastebin.com/
// @version 1.4.0
// @description Replace Pastebin code blocks in markdown mode with Shiki syntax highlighting and copy button
// @match https://pastebin.com/*
// @author BourbonCrow
// @icon https://raw.githubusercontent.com/shikijs/shiki/main/docs/public/logo.svg
// @grant GM_addStyle
// @run-at document-start
// @license MIT
// ==/UserScript==
(function () {
"use strict";
// Hide raw markdown code instantly — only original (non-shiki) <pre><code> blocks
if (typeof GM_addStyle === "function") {
GM_addStyle(`
/* Only hide the site's original code nodes; do NOT hide pre.shiki generated content */
.source.markdown pre:not(.shiki) > code,
.source.markdown code[class^="language-"]:not(.shiki) {
display: none !important;
}
`);
}
function getHeaderHeight() {
const header = document.querySelector(".header");
return header ? header.offsetHeight || 0 : 0;
}
function init() {
if (document.readyState === "loading") {
document.addEventListener("DOMContentLoaded", init);
return;
}
// Load Devicons CSS (optional icons)
const devicons = document.createElement("link");
devicons.rel = "stylesheet";
devicons.href = "https://cdn.jsdelivr.net/gh/devicons/devicon/devicon.min.css";
document.head.appendChild(devicons);
// Inject ESM module into the page so it runs in page context
const script = document.createElement("script");
script.type = "module";
script.innerHTML = `
const SHOW_DEVICONS = true;
import { codeToHtml } from 'https://esm.sh/shiki';
// --- inline language table (display, optional aliases, optional devicon) ---
const languages = {
abap: { display: "ABAP" },
actionscript: { display: "ActionScript", aliases: ["actionscript-3"] },
ada: { display: "Ada" },
"angular-html": { display: "Angular HTML" },
"angular-ts": { display:"Angular TypeScript" },
apache: { display: "Apache Conf" },
ansi: { display: "ANSI" },
apex: { display: "Apex" },
apl: { display: "APL" },
applescript: { display: "AppleScript" },
ara: { display: "Ara" },
asciidoc: { display: "AsciiDoc", aliases: ["adoc"] },
asm: { display: "Assembly" },
astro: { display: "Astro" },
awk: { display: "AWK" },
ballerina: { display: "Ballerina" },
bat: { display: "Batch File", aliases: ["batch"] },
beancount: { display: "Beancount" },
berry: { display: "Berry", aliases: ["be"] },
bibtex: { display: "BibTeX" },
bicep: { display: "Bicep" },
blade: { display: "Blade" },
bsl: { display: "1C (Enterprise)", aliases: ["1c"] },
c: { display: "C", devicon: "c-plain" },
cadence: { display: "Cadence", aliases: ["cdc"] },
cairo: { display: "Cairo" },
clarity: { display: "Clarity" },
clojure: { display: "Clojure", aliases: ["clj"], devicon: "clojure-line" },
cmake: { display: "CMake" },
cobol: { display: "COBOL" },
codeowners: { display: "CODEOWNERS" },
codeql: { display: "CodeQL", aliases: ["ql"] },
coffee: { display: "CoffeeScript", aliases: ["coffeescript"], devicon: "coffeescript-original" },
"common-lisp": { display: "Common Lisp", aliases: ["lisp"] },
coq: { display: "Coq" },
cpp: { display: "C++", aliases: ["c++"], devicon: "cplusplus-plain" },
crystal: { display: "Crystal" },
csharp: { display: "C#", aliases: ["cs","c#"], devicon: "csharp-plain" },
css: { display: "CSS", devicon: "css3-plain" },
csv: { display: "CSV" },
cue: { display: "CUE" },
cypher: { display:"Cypher", aliases: ["cql"] },
d: { display:"D" },
dart: { display: "Dart" },
dax: { display: "DAX" },
desktop: { display: "Desktop" },
diff: { display: "Diff" },
docker: { display: "Dockerfile", aliases: ["dockerfile"], devicon: "docker-plain" },
dotenv: { display: "dotEnv" },
"dream-maker": { display: "Dream Maker" },
edge: { display:"Edge" },
elixir: { display: "Elixir", devicon: "elixir-plain" },
elm: { display: "Elm", devicon: "elm-plain" },
"emacs-lisp": { display: "Emacs Lisp", aliases: ["elisp"] },
erb: { display: "ERB" },
erlang: { display: "Erlang", aliases: ["erl"], devicon: "erlang-plain" },
fennel: { display: "Fennel" },
fish: { display: "Fish" },
fluent: { display: "Fluent", aliases: ["ftl"] },
"fortran-fixed-form": { display: "Fortran (Fixed Form)", aliases: ["f77"] },
"fortran-free-form": { display: "Fortran (Free Form)", aliases: ["f90","f95","f03","f08","f18"] },
fsharp: { display: "F#", aliases: ["fs","f#"], devicon: "fsharp-plain" },
gdresource: { display: "GDResource" },
gdscript: { display: "GDScript" },
gdshader: { display: "GDShader" },
genie: { display: "Genie" },
gherkin: { display: "Gherkin" },
"git-commit": { display: "Git Commit Message" },
"git-rebase": { display: "Git Rebase Message" },
gleam: { display:"Gleam" },
"glimmer-js": { display: "Glimmer JS", aliases: ["gjs"] },
"glimmer-ts": { display: "Glimmer TS", aliases: ["gts"] },
glsl: { display: "GLSL" },
gnuplot: { display: "Gnuplot" },
go: { display: "Go", devicon: "go-plain" },
graphql: { display: "GraphQL", aliases: ["gql"] },
groovy: { display: "Groovy", devicon: "groovy-plain" },
hack: { display: "Hack" },
haml: { display: "Ruby Haml" },
handlebars: { display: "Handlebars", aliases: ["hbs"], devicon: "handlebars-plain" },
haskell: { display: "Haskell", aliases: ["hs"], devicon: "haskell-plain" },
haxe: { display: "Haxe" },
hcl: { display: "HashiCorp HCL" },
hjson: { display:"Hjson" },
hlsl: { display: "HLSL" },
html: { display: "HTML", devicon: "html5-plain" },
"html-derivative": { display: "HTML (Derivative)" },
http: { display: "HTTP" },
hxml: { display: "HXML" },
hy: { display: "Hy" },
imba: { display: "Imba" },
ini: { display: "INI", aliases: ["properties"] },
java: { display: "Java", devicon: "java-plain" },
javascript: { display: "JavaScript", aliases: ["js"], devicon: "javascript-plain" },
jinja: { display: "Jinja" },
jison: { display: "Jison" },
json: { display: "JSON", devicon: "json-plain" },
json5: { display: "JSON5" },
jsonc: { display: "JSON with Comments" },
jsonl: { display: "JSON Lines" },
jsonnet: { display: "Jsonnet" },
jssm: { display: "JSSM", aliases: ["fsl"] },
jsx: { display: "JSX", devicon: "react-original" },
julia: { display: "Julia", aliases: ["jl"] },
kotlin: { display: "Kotlin", aliases: ["kt","kts"], devicon: "kotlin-plain" },
kusto: { display: "Kusto", aliases: ["kql"] },
latex: { display: "LaTeX", devicon: "latex-original" },
lean: { display: "Lean 4", aliases: ["lean4"] },
less: { display:"Less", devicon: "less-plain-wordmark" },
liquid: { display: "Liquid" },
llvm: { display: "LLVM IR" },
log: { display :"Log file" },
logo: { display: "Logo" },
lua: { display: "Lua", devicon: "lua-plain" },
luau: { display: "Luau" },
make: { display: "Makefile", aliases: ["makefile"] },
markdown: { display: "Markdown", aliases: ["md"], devicon: "markdown-original" },
marko: { display: "Marko" },
matlab: { display: "MATLAB", devicon: "matlab-plain" },
mdc: { display: "MDC" },
mdx: { display: "MDX" },
mermaid: { display: "Mermaid", aliases: ["mmd"] },
mipsasm: { display: "MIPS Assembly", aliases: ["mips"] },
mojo: { display: "Mojo" },
move: { display: "Move" },
narrat: { display: "Narrat Language", aliases: ["nar"] },
nextflow:{display:"Nextflow",aliases:["nf"]},
nginx: { display: "Nginx" },
nim: { display: "Nim" },
nix: { display: "Nix" },
nushell: {display: "nushell", aliases: ["nu"] },
"objective-c": { display: "Objective-C", aliases: ["objc"], devicon: "objectivec-plain" },
"objective-cpp": { display: "Objective-C++" },
ocaml: { display: "OCaml", devicon: "ocaml-plain" },
pascal: { display: "Pascal" },
perl: { display: "Perl" },
php: { display: "PHP", devicon: "php-plain" },
plsql: { display: "PL/SQL" },
po: { display: "Gettext PO", aliases: ["pot","potx"] },
polar: { display: "Polar" },
postcss: { display: "PostCSS" },
powerquery: { display: "PowerQuery" },
powershell: {display: "PowerShell", aliases:["ps","ps1"], devicon: "powershell-plain" },
prisma: { display: "Prisma" },
prolog: { display: "Prolog" },
proto: { display: "Protocol Buffer 3", aliases: ["protobuf"] },
pug: { display: "Pug", aliases:["jade"] },
puppet: { display: "Puppet" },
purescript: { display: "PureScript" },
python: { display: "Python", aliases: ["py"], devicon: "python-plain" },
qml: { display: "QML" },
qmldir: { display: "QML Directory" },
qss: { display: "Qt Style Sheets" },
r: { display: "R", devicon: "r-original" },
racket: { display: "Racket" },
raku: { display: "Raku", aliases: ["perl6"] },
razor: { display: "ASP.NET Razor", aliases: ["cshtml"], devicon: "dot-net-plain" },
reg: { display: "Windows Registry Script" },
regexp: { display: "RegExp", aliases: ["regex"] },
rel: { display: "Rel" },
riscv: { display: "RISC-V" },
rst: { display: "reStructuredText" },
ruby: { display: "Ruby", aliases: ["rb"], devicon: "ruby-plain" },
rust: { display: "Rust", aliases: ["rs"], devicon: "rust-plain" },
sas: { display: "SAS", devicon: "sass-original" },
sass: { display: "Sass" },
scala: { display: "Scala", devicon: "scala-plain" },
scheme: { display: "Scheme" },
scss: { display: "SCSS", devicon: "sass-original" },
sdbl: { display: "1C (Query)", aliases: ["1c-query"] },
shaderlab: { display: "ShaderLab", aliases: ["shader"] },
shellscript: { display: "Shell", aliases: ["bash","sh","zsh"], devicon: "bash-plain" },
shellsession: { display: "Shell Session", aliases: ["console"] },
smalltalk: { display: "Smalltalk" },
solidity: { display: "Solidity" },
soy: { display: "Closure Templates", aliases: ["closure-templates"] },
sparql: { display: "SPARQL" },
splunk: { display: "Splunk Query Language", aliases: ["spl"] },
sql: { display: "SQL", devicon: "azuresqldatabase-plain" },
"ssh-config": { display: "SSH Config" },
stata: { display: "Stata" },
stylus: { display: "Stylus", aliases: ["styl"], devicon: "stylus-original" },
svelte: { display: "Svelte" },
swift: { display: "Swift", devicon: "swift-plain" },
"system-verilog": { display: "SystemVerilog" },
systemd: { display: "Systemd Units" },
talonscript: { display: "TalonScript", aliases: ["talon"] },
tasl: { display: "Tasl" },
tcl: { display: "Tcl" },
templ: { display: "Templ" },
terraform: { display: "Terraform", aliases: ["tf","tfvars"] },
tex: { display: "TeX" },
toml: { display: "TOML" },
"ts-tags": { display: "TypeScript with Tags", aliases: ["lit"], devicon: "typescript-plain" },
tsv: { display: "TSV" },
tsx: { display: "TSX", devicon: "react-original" },
turtle: { display: "Turtle" },
twig: { display: "Twig" },
typescript: { display: "TypeScript", aliases: ["ts"], devicon: "typescript-plain" },
typespec: {display: "TypeSpec", aliases: ["tsp"] },
typst: { display: "Typst", aliases: ["typ"] },
v: { display: "V" },
vala: { display: "Vala" },
vb: { display: "Visual Basic", aliases: ["cmd"], devicon: "dot-net-plain" },
verilog: { display: "Verilog" },
vhdl: { display: "VHDL" },
viml: { display: "Vim Script", aliases: ["vim","vimscript"], devicon:"vim-plain" },
vue: { display: "Vue", devicon: "vuejs-plain" },
"vue-html": { display: "Vue HTML", devicon: "vuejs-plain" },
"vue-vine": { display: "Vue Vine" },
vyper: { display: "Vyper", aliases:["vy"] },
wasm: { display: "WebAssembly" },
wenyan: { display: "Wenyan", aliases:["文言"] },
wgsl: { display: "WGSL" },
wikitext: { display: "Wikitext", aliases: ["mediawiki","wiki"] },
wit: { display: "WebAssembly Interface Types" },
wolfram: { display: "Wolfram", aliases: ["wl"] },
xml: { display: "XML" },
xsl: { display: "XSL" },
yaml: { display: "YAML", aliases: ["yml"] },
zenscript: { display: "ZenScript" },
zig: { display: "Zig" },
plaintext: { display: "Plain Text", aliases: ["text","txt"] }
};
// Build alias lookup (aliases optional)
const aliasMap = {};
for (const [id, data] of Object.entries(languages)) {
if (Array.isArray(data.aliases)) {
for (const alias of data.aliases) {
aliasMap[alias.toLowerCase()] = id;
}
}
}
function resolveLang(lang) {
if (!lang) return { name: "Plain Text", devicon: null };
const key = lang.toLowerCase();
const canonicalId = languages[key] ? key : (aliasMap[key] || "plaintext");
const data = languages[canonicalId] || { display: "Plain Text" };
return { name: data.display, devicon: data.devicon || null };
}
async function highlight() {
const markdownSource = document.querySelector(".source.markdown");
if (!markdownSource) return;
// Grab all pre > code blocks (with or without language class)
const codeBlocks = markdownSource.querySelectorAll("pre > code");
for (const block of codeBlocks) {
try {
const rawCode = block.textContent;
// detect language from class, fallback to plaintext
const match = block.className.match(/language-([^\\s]+)/);
const lang = match ? match[1] : "plaintext";
const { name, devicon } = resolveLang(lang);
// Render with shiki
const html = await codeToHtml(rawCode, {
lang,
theme: "github-dark"
});
// Wrap and replace
const wrapper = document.createElement("div");
wrapper.className = "shiki-wrapper";
wrapper.innerHTML = \`
<div class="shiki-header">
\${SHOW_DEVICONS && devicon ? \`<i class="devicon-\${devicon}"></i>\` : ""}
<span class="shiki-lang">\${name}</span>
<button class="shiki-copy">Copy code</button>
</div>
\${html}
\`;
const pre = block.closest("pre") || block.parentElement;
pre.replaceWith(wrapper);
// Floating copy button logic
const copyBtn = wrapper.querySelector(".shiki-copy");
function updatePosition() {
const headerHeight = (${getHeaderHeight.toString()})();
const wrapperRect = wrapper.getBoundingClientRect();
const btnHeight = copyBtn.offsetHeight;
const margin = 7;
const shouldFloat = wrapperRect.top < headerHeight + margin &&
wrapperRect.bottom > headerHeight + btnHeight + margin;
if (shouldFloat) {
if (!copyBtn.classList.contains("floating")) {
copyBtn.classList.add("floating");
copyBtn.style.position = "fixed";
copyBtn.style.zIndex = "999";
}
const targetTop = Math.min(
Math.max(headerHeight + margin, wrapperRect.top + margin),
wrapperRect.bottom - btnHeight - margin
);
const targetRight = window.innerWidth - wrapperRect.right + 8;
copyBtn.style.top = targetTop + "px";
copyBtn.style.right = targetRight + "px";
copyBtn.style.left = "auto";
} else {
if (copyBtn.classList.contains("floating")) {
copyBtn.classList.remove("floating");
copyBtn.style.position = "absolute";
copyBtn.style.zIndex = "1";
}
copyBtn.style.top = margin + "px";
copyBtn.style.right = "8px";
copyBtn.style.left = "auto";
}
}
// Throttle with rAF
let ticking = false;
const requestTick = () => {
if (!ticking) {
requestAnimationFrame(() => {
updatePosition();
ticking = false;
});
ticking = true;
}
};
window.addEventListener("scroll", requestTick, { passive: true });
window.addEventListener("resize", requestTick, { passive: true });
// initial
setTimeout(updatePosition, 100);
} catch (err) {
console.warn("Highlight failed:", err);
}
}
}
// Start highlighting
highlight();
// Copy button handler (module scope)
document.addEventListener("click", (e) => {
const btn = e.target.closest && e.target.closest(".shiki-copy");
if (!btn) return;
const code = btn.closest(".shiki-wrapper").querySelector("code").innerText;
navigator.clipboard.writeText(code).then(() => {
const prev = btn.textContent;
btn.textContent = "Copied!";
setTimeout(() => (btn.textContent = prev || "Copy code"), 1500);
}).catch(() => {
// fallback: still try to show UI
btn.textContent = "Copied!";
setTimeout(() => (btn.textContent = "Copy code"), 1500);
});
});
`;
document.head.appendChild(script);
// Styles for wrapper and header (generated content)
GM_addStyle(`
.shiki-wrapper {
margin: 1em 0;
border: 1px solid #171717;
border-radius: 8px;
overflow: hidden;
background: #171717;
position: relative;
}
.shiki-header {
display: flex;
align-items: center;
gap: 8px;
background: #171717;
color: #ccc;
font-size: 12px;
font-family: sans-serif;
padding: 0.3em 1.5em;
position: relative;
}
.shiki-header .shiki-lang {
user-select: none;
font-weight: bold;
color: #bbb;
flex: 1;
}
.shiki-header i[class^="devicon-"] {
font-size: 14px;
margin-right: 2px;
color: #bbb;
}
.shiki-header .shiki-copy {
background: #171717;
border-radius: 3px;
border: none;
color: #bbb;
cursor: pointer;
font-size: 12px;
font-weight: bold;
padding: 1px 10px;
position: absolute;
top: 7px;
right: 8px;
z-index: 1;
}
.shiki-header .shiki-copy:hover {
color: white;
}
pre.shiki {
margin: 0;
padding: 1em;
overflow-x: auto;
font-size: 14px;
line-height: 1.5;
border: 1px solid #171717;
background: none !important;
}
pre.shiki code {
background: none !important;
}
`);
}
init();
})();