at://nekomimi.pet/sh.tangled.repo.pull/3lqasymgqb422
Back to Collection
Record JSON
{
"$type": "sh.tangled.repo.pull",
"createdAt": "",
"patch": "From 695bf8098b54d84d75f338238c035fa774caf268 Mon Sep 17 00:00:00 2001\nFrom: waveringana \u003cana@nekomimi.pet\u003e\nDate: Wed, 28 May 2025 14:06:59 -0400\nSubject: [PATCH] add server sided code highlighting from shiki\n\n---\n package-lock.json | 523 ++++++++++++++++++++++++++++++++++++++++++\n package.json | 1 +\n src/lib/highlight.ts | 48 ++++\n src/public/styles.css | 33 +++\n src/routes.ts | 4 +\n src/views/index.pug | 2 +\n src/views/paste.pug | 16 +-\n 7 files changed, 614 insertions(+), 13 deletions(-)\n create mode 100644 src/lib/highlight.ts\n\ndiff --git a/package-lock.json b/package-lock.json\nindex cc16ee6..f67071e 100644\n--- a/package-lock.json\n+++ b/package-lock.json\n@@ -26,6 +26,7 @@\n \"multiformats\": \"^9.9.0\",\n \"pino\": \"^9.3.2\",\n \"pug\": \"^3.0.3\",\n+ \"shiki\": \"^3.4.2\",\n \"uhtml\": \"^4.5.9\"\n },\n \"devDependencies\": {\n@@ -1484,6 +1485,73 @@\n \"win32\"\n ]\n },\n+ \"node_modules/@shikijs/core\": {\n+ \"version\": \"3.4.2\",\n+ \"resolved\": \"https://registry.npmjs.org/@shikijs/core/-/core-3.4.2.tgz\",\n+ \"integrity\": \"sha512-AG8vnSi1W2pbgR2B911EfGqtLE9c4hQBYkv/x7Z+Kt0VxhgQKcW7UNDVYsu9YxwV6u+OJrvdJrMq6DNWoBjihQ==\",\n+ \"license\": \"MIT\",\n+ \"dependencies\": {\n+ \"@shikijs/types\": \"3.4.2\",\n+ \"@shikijs/vscode-textmate\": \"^10.0.2\",\n+ \"@types/hast\": \"^3.0.4\",\n+ \"hast-util-to-html\": \"^9.0.5\"\n+ }\n+ },\n+ \"node_modules/@shikijs/engine-javascript\": {\n+ \"version\": \"3.4.2\",\n+ \"resolved\": \"https://registry.npmjs.org/@shikijs/engine-javascript/-/engine-javascript-3.4.2.tgz\",\n+ \"integrity\": \"sha512-1/adJbSMBOkpScCE/SB6XkjJU17ANln3Wky7lOmrnpl+zBdQ1qXUJg2GXTYVHRq+2j3hd1DesmElTXYDgtfSOQ==\",\n+ \"license\": \"MIT\",\n+ \"dependencies\": {\n+ \"@shikijs/types\": \"3.4.2\",\n+ \"@shikijs/vscode-textmate\": \"^10.0.2\",\n+ \"oniguruma-to-es\": \"^4.3.3\"\n+ }\n+ },\n+ \"node_modules/@shikijs/engine-oniguruma\": {\n+ \"version\": \"3.4.2\",\n+ \"resolved\": \"https://registry.npmjs.org/@shikijs/engine-oniguruma/-/engine-oniguruma-3.4.2.tgz\",\n+ \"integrity\": \"sha512-zcZKMnNndgRa3ORja6Iemsr3DrLtkX3cAF7lTJkdMB6v9alhlBsX9uNiCpqofNrXOvpA3h6lHcLJxgCIhVOU5Q==\",\n+ \"license\": \"MIT\",\n+ \"dependencies\": {\n+ \"@shikijs/types\": \"3.4.2\",\n+ \"@shikijs/vscode-textmate\": \"^10.0.2\"\n+ }\n+ },\n+ \"node_modules/@shikijs/langs\": {\n+ \"version\": \"3.4.2\",\n+ \"resolved\": \"https://registry.npmjs.org/@shikijs/langs/-/langs-3.4.2.tgz\",\n+ \"integrity\": \"sha512-H6azIAM+OXD98yztIfs/KH5H4PU39t+SREhmM8LaNXyUrqj2mx+zVkr8MWYqjceSjDw9I1jawm1WdFqU806rMA==\",\n+ \"license\": \"MIT\",\n+ \"dependencies\": {\n+ \"@shikijs/types\": \"3.4.2\"\n+ }\n+ },\n+ \"node_modules/@shikijs/themes\": {\n+ \"version\": \"3.4.2\",\n+ \"resolved\": \"https://registry.npmjs.org/@shikijs/themes/-/themes-3.4.2.tgz\",\n+ \"integrity\": \"sha512-qAEuAQh+brd8Jyej2UDDf+b4V2g1Rm8aBIdvt32XhDPrHvDkEnpb7Kzc9hSuHUxz0Iuflmq7elaDuQAP9bHIhg==\",\n+ \"license\": \"MIT\",\n+ \"dependencies\": {\n+ \"@shikijs/types\": \"3.4.2\"\n+ }\n+ },\n+ \"node_modules/@shikijs/types\": {\n+ \"version\": \"3.4.2\",\n+ \"resolved\": \"https://registry.npmjs.org/@shikijs/types/-/types-3.4.2.tgz\",\n+ \"integrity\": \"sha512-zHC1l7L+eQlDXLnxvM9R91Efh2V4+rN3oMVS2swCBssbj2U/FBwybD1eeLaq8yl/iwT+zih8iUbTBCgGZOYlVg==\",\n+ \"license\": \"MIT\",\n+ \"dependencies\": {\n+ \"@shikijs/vscode-textmate\": \"^10.0.2\",\n+ \"@types/hast\": \"^3.0.4\"\n+ }\n+ },\n+ \"node_modules/@shikijs/vscode-textmate\": {\n+ \"version\": \"10.0.2\",\n+ \"resolved\": \"https://registry.npmjs.org/@shikijs/vscode-textmate/-/vscode-textmate-10.0.2.tgz\",\n+ \"integrity\": \"sha512-83yeghZ2xxin3Nj8z1NMd/NCuca+gsYXswywDy5bHvwlWL8tpTQmzGeUuHd9FC3E/SBEMvzJRwWEOz5gGes9Qg==\",\n+ \"license\": \"MIT\"\n+ },\n \"node_modules/@ts-morph/common\": {\n \"version\": \"0.17.0\",\n \"resolved\": \"https://registry.npmjs.org/@ts-morph/common/-/common-0.17.0.tgz\",\n@@ -1602,6 +1670,15 @@\n \"@types/send\": \"*\"\n }\n },\n+ \"node_modules/@types/hast\": {\n+ \"version\": \"3.0.4\",\n+ \"resolved\": \"https://registry.npmjs.org/@types/hast/-/hast-3.0.4.tgz\",\n+ \"integrity\": \"sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==\",\n+ \"license\": \"MIT\",\n+ \"dependencies\": {\n+ \"@types/unist\": \"*\"\n+ }\n+ },\n \"node_modules/@types/http-errors\": {\n \"version\": \"2.0.4\",\n \"resolved\": \"https://registry.npmjs.org/@types/http-errors/-/http-errors-2.0.4.tgz\",\n@@ -1609,6 +1686,15 @@\n \"dev\": true,\n \"license\": \"MIT\"\n },\n+ \"node_modules/@types/mdast\": {\n+ \"version\": \"4.0.4\",\n+ \"resolved\": \"https://registry.npmjs.org/@types/mdast/-/mdast-4.0.4.tgz\",\n+ \"integrity\": \"sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA==\",\n+ \"license\": \"MIT\",\n+ \"dependencies\": {\n+ \"@types/unist\": \"*\"\n+ }\n+ },\n \"node_modules/@types/mime\": {\n \"version\": \"1.3.5\",\n \"resolved\": \"https://registry.npmjs.org/@types/mime/-/mime-1.3.5.tgz\",\n@@ -1663,6 +1749,18 @@\n \"@types/send\": \"*\"\n }\n },\n+ \"node_modules/@types/unist\": {\n+ \"version\": \"3.0.3\",\n+ \"resolved\": \"https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz\",\n+ \"integrity\": \"sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==\",\n+ \"license\": \"MIT\"\n+ },\n+ \"node_modules/@ungap/structured-clone\": {\n+ \"version\": \"1.3.0\",\n+ \"resolved\": \"https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.3.0.tgz\",\n+ \"integrity\": \"sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==\",\n+ \"license\": \"ISC\"\n+ },\n \"node_modules/@webreflection/signal\": {\n \"version\": \"2.1.2\",\n \"resolved\": \"https://registry.npmjs.org/@webreflection/signal/-/signal-2.1.2.tgz\",\n@@ -2095,6 +2193,16 @@\n \"cborg\": \"cli.js\"\n }\n },\n+ \"node_modules/ccount\": {\n+ \"version\": \"2.0.1\",\n+ \"resolved\": \"https://registry.npmjs.org/ccount/-/ccount-2.0.1.tgz\",\n+ \"integrity\": \"sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg==\",\n+ \"license\": \"MIT\",\n+ \"funding\": {\n+ \"type\": \"github\",\n+ \"url\": \"https://github.com/sponsors/wooorm\"\n+ }\n+ },\n \"node_modules/chalk\": {\n \"version\": \"4.1.2\",\n \"resolved\": \"https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz\",\n@@ -2112,6 +2220,26 @@\n \"url\": \"https://github.com/chalk/chalk?sponsor=1\"\n }\n },\n+ \"node_modules/character-entities-html4\": {\n+ \"version\": \"2.1.0\",\n+ \"resolved\": \"https://registry.npmjs.org/character-entities-html4/-/character-entities-html4-2.1.0.tgz\",\n+ \"integrity\": \"sha512-1v7fgQRj6hnSwFpq1Eu0ynr/CDEw0rXo2B61qXrLNdHZmPKgb7fqS1a2JwF0rISo9q77jDI8VMEHoApn8qDoZA==\",\n+ \"license\": \"MIT\",\n+ \"funding\": {\n+ \"type\": \"github\",\n+ \"url\": \"https://github.com/sponsors/wooorm\"\n+ }\n+ },\n+ \"node_modules/character-entities-legacy\": {\n+ \"version\": \"3.0.0\",\n+ \"resolved\": \"https://registry.npmjs.org/character-entities-legacy/-/character-entities-legacy-3.0.0.tgz\",\n+ \"integrity\": \"sha512-RpPp0asT/6ufRm//AJVwpViZbGM/MkjQFxJccQRHmISF/22NBtsHqAWmL+/pmkPWoIUJdWyeVleTl1wydHATVQ==\",\n+ \"license\": \"MIT\",\n+ \"funding\": {\n+ \"type\": \"github\",\n+ \"url\": \"https://github.com/sponsors/wooorm\"\n+ }\n+ },\n \"node_modules/character-parser\": {\n \"version\": \"2.2.0\",\n \"resolved\": \"https://registry.npmjs.org/character-parser/-/character-parser-2.2.0.tgz\",\n@@ -2189,6 +2317,16 @@\n \"node\": \"\u003e= 0.8\"\n }\n },\n+ \"node_modules/comma-separated-tokens\": {\n+ \"version\": \"2.0.3\",\n+ \"resolved\": \"https://registry.npmjs.org/comma-separated-tokens/-/comma-separated-tokens-2.0.3.tgz\",\n+ \"integrity\": \"sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg==\",\n+ \"license\": \"MIT\",\n+ \"funding\": {\n+ \"type\": \"github\",\n+ \"url\": \"https://github.com/sponsors/wooorm\"\n+ }\n+ },\n \"node_modules/commander\": {\n \"version\": \"9.5.0\",\n \"resolved\": \"https://registry.npmjs.org/commander/-/commander-9.5.0.tgz\",\n@@ -2361,6 +2499,15 @@\n \"node\": \"\u003e= 0.8\"\n }\n },\n+ \"node_modules/dequal\": {\n+ \"version\": \"2.0.3\",\n+ \"resolved\": \"https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz\",\n+ \"integrity\": \"sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==\",\n+ \"license\": \"MIT\",\n+ \"engines\": {\n+ \"node\": \"\u003e=6\"\n+ }\n+ },\n \"node_modules/destroy\": {\n \"version\": \"1.2.0\",\n \"resolved\": \"https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz\",\n@@ -2380,6 +2527,19 @@\n \"node\": \"\u003e=8\"\n }\n },\n+ \"node_modules/devlop\": {\n+ \"version\": \"1.1.0\",\n+ \"resolved\": \"https://registry.npmjs.org/devlop/-/devlop-1.1.0.tgz\",\n+ \"integrity\": \"sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA==\",\n+ \"license\": \"MIT\",\n+ \"dependencies\": {\n+ \"dequal\": \"^2.0.0\"\n+ },\n+ \"funding\": {\n+ \"type\": \"github\",\n+ \"url\": \"https://github.com/sponsors/wooorm\"\n+ }\n+ },\n \"node_modules/diff\": {\n \"version\": \"4.0.2\",\n \"resolved\": \"https://registry.npmjs.org/diff/-/diff-4.0.2.tgz\",\n@@ -3043,6 +3203,42 @@\n \"node\": \"\u003e= 0.4\"\n }\n },\n+ \"node_modules/hast-util-to-html\": {\n+ \"version\": \"9.0.5\",\n+ \"resolved\": \"https://registry.npmjs.org/hast-util-to-html/-/hast-util-to-html-9.0.5.tgz\",\n+ \"integrity\": \"sha512-OguPdidb+fbHQSU4Q4ZiLKnzWo8Wwsf5bZfbvu7//a9oTYoqD/fWpe96NuHkoS9h0ccGOTe0C4NGXdtS0iObOw==\",\n+ \"license\": \"MIT\",\n+ \"dependencies\": {\n+ \"@types/hast\": \"^3.0.0\",\n+ \"@types/unist\": \"^3.0.0\",\n+ \"ccount\": \"^2.0.0\",\n+ \"comma-separated-tokens\": \"^2.0.0\",\n+ \"hast-util-whitespace\": \"^3.0.0\",\n+ \"html-void-elements\": \"^3.0.0\",\n+ \"mdast-util-to-hast\": \"^13.0.0\",\n+ \"property-information\": \"^7.0.0\",\n+ \"space-separated-tokens\": \"^2.0.0\",\n+ \"stringify-entities\": \"^4.0.0\",\n+ \"zwitch\": \"^2.0.4\"\n+ },\n+ \"funding\": {\n+ \"type\": \"opencollective\",\n+ \"url\": \"https://opencollective.com/unified\"\n+ }\n+ },\n+ \"node_modules/hast-util-whitespace\": {\n+ \"version\": \"3.0.0\",\n+ \"resolved\": \"https://registry.npmjs.org/hast-util-whitespace/-/hast-util-whitespace-3.0.0.tgz\",\n+ \"integrity\": \"sha512-88JUN06ipLwsnv+dVn+OIYOvAuvBMy/Qoi6O7mQHxdPXpjy+Cd6xRkWwux7DKO+4sYILtLBRIKgsdpS2gQc7qw==\",\n+ \"license\": \"MIT\",\n+ \"dependencies\": {\n+ \"@types/hast\": \"^3.0.0\"\n+ },\n+ \"funding\": {\n+ \"type\": \"opencollective\",\n+ \"url\": \"https://opencollective.com/unified\"\n+ }\n+ },\n \"node_modules/help-me\": {\n \"version\": \"5.0.0\",\n \"resolved\": \"https://registry.npmjs.org/help-me/-/help-me-5.0.0.tgz\",\n@@ -3056,6 +3252,16 @@\n \"integrity\": \"sha512-RuMffC89BOWQoY0WKGpIhn5gX3iI54O6nRA0yC124NYVtzjmFWBIiFd8M0x+ZdX0P9R4lADg1mgP8C7PxGOWuQ==\",\n \"license\": \"MIT\"\n },\n+ \"node_modules/html-void-elements\": {\n+ \"version\": \"3.0.0\",\n+ \"resolved\": \"https://registry.npmjs.org/html-void-elements/-/html-void-elements-3.0.0.tgz\",\n+ \"integrity\": \"sha512-bEqo66MRXsUGxWHV5IP0PUiAWwoEjba4VCzg0LjFJBpchPaTfyfCKTG6bc5F8ucKec3q5y6qOdGyYTSBEvhCrg==\",\n+ \"license\": \"MIT\",\n+ \"funding\": {\n+ \"type\": \"github\",\n+ \"url\": \"https://github.com/sponsors/wooorm\"\n+ }\n+ },\n \"node_modules/htmlparser2\": {\n \"version\": \"9.1.0\",\n \"resolved\": \"https://registry.npmjs.org/htmlparser2/-/htmlparser2-9.1.0.tgz\",\n@@ -3404,6 +3610,27 @@\n \"dev\": true,\n \"license\": \"ISC\"\n },\n+ \"node_modules/mdast-util-to-hast\": {\n+ \"version\": \"13.2.0\",\n+ \"resolved\": \"https://registry.npmjs.org/mdast-util-to-hast/-/mdast-util-to-hast-13.2.0.tgz\",\n+ \"integrity\": \"sha512-QGYKEuUsYT9ykKBCMOEDLsU5JRObWQusAolFMeko/tYPufNkRffBAQjIE+99jbA87xv6FgmjLtwjh9wBWajwAA==\",\n+ \"license\": \"MIT\",\n+ \"dependencies\": {\n+ \"@types/hast\": \"^3.0.0\",\n+ \"@types/mdast\": \"^4.0.0\",\n+ \"@ungap/structured-clone\": \"^1.0.0\",\n+ \"devlop\": \"^1.0.0\",\n+ \"micromark-util-sanitize-uri\": \"^2.0.0\",\n+ \"trim-lines\": \"^3.0.0\",\n+ \"unist-util-position\": \"^5.0.0\",\n+ \"unist-util-visit\": \"^5.0.0\",\n+ \"vfile\": \"^6.0.0\"\n+ },\n+ \"funding\": {\n+ \"type\": \"opencollective\",\n+ \"url\": \"https://opencollective.com/unified\"\n+ }\n+ },\n \"node_modules/media-typer\": {\n \"version\": \"0.3.0\",\n \"resolved\": \"https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz\",\n@@ -3441,6 +3668,95 @@\n \"node\": \"\u003e= 0.6\"\n }\n },\n+ \"node_modules/micromark-util-character\": {\n+ \"version\": \"2.1.1\",\n+ \"resolved\": \"https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.1.tgz\",\n+ \"integrity\": \"sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q==\",\n+ \"funding\": [\n+ {\n+ \"type\": \"GitHub Sponsors\",\n+ \"url\": \"https://github.com/sponsors/unifiedjs\"\n+ },\n+ {\n+ \"type\": \"OpenCollective\",\n+ \"url\": \"https://opencollective.com/unified\"\n+ }\n+ ],\n+ \"license\": \"MIT\",\n+ \"dependencies\": {\n+ \"micromark-util-symbol\": \"^2.0.0\",\n+ \"micromark-util-types\": \"^2.0.0\"\n+ }\n+ },\n+ \"node_modules/micromark-util-encode\": {\n+ \"version\": \"2.0.1\",\n+ \"resolved\": \"https://registry.npmjs.org/micromark-util-encode/-/micromark-util-encode-2.0.1.tgz\",\n+ \"integrity\": \"sha512-c3cVx2y4KqUnwopcO9b/SCdo2O67LwJJ/UyqGfbigahfegL9myoEFoDYZgkT7f36T0bLrM9hZTAaAyH+PCAXjw==\",\n+ \"funding\": [\n+ {\n+ \"type\": \"GitHub Sponsors\",\n+ \"url\": \"https://github.com/sponsors/unifiedjs\"\n+ },\n+ {\n+ \"type\": \"OpenCollective\",\n+ \"url\": \"https://opencollective.com/unified\"\n+ }\n+ ],\n+ \"license\": \"MIT\"\n+ },\n+ \"node_modules/micromark-util-sanitize-uri\": {\n+ \"version\": \"2.0.1\",\n+ \"resolved\": \"https://registry.npmjs.org/micromark-util-sanitize-uri/-/micromark-util-sanitize-uri-2.0.1.tgz\",\n+ \"integrity\": \"sha512-9N9IomZ/YuGGZZmQec1MbgxtlgougxTodVwDzzEouPKo3qFWvymFHWcnDi2vzV1ff6kas9ucW+o3yzJK9YB1AQ==\",\n+ \"funding\": [\n+ {\n+ \"type\": \"GitHub Sponsors\",\n+ \"url\": \"https://github.com/sponsors/unifiedjs\"\n+ },\n+ {\n+ \"type\": \"OpenCollective\",\n+ \"url\": \"https://opencollective.com/unified\"\n+ }\n+ ],\n+ \"license\": \"MIT\",\n+ \"dependencies\": {\n+ \"micromark-util-character\": \"^2.0.0\",\n+ \"micromark-util-encode\": \"^2.0.0\",\n+ \"micromark-util-symbol\": \"^2.0.0\"\n+ }\n+ },\n+ \"node_modules/micromark-util-symbol\": {\n+ \"version\": \"2.0.1\",\n+ \"resolved\": \"https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.1.tgz\",\n+ \"integrity\": \"sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q==\",\n+ \"funding\": [\n+ {\n+ \"type\": \"GitHub Sponsors\",\n+ \"url\": \"https://github.com/sponsors/unifiedjs\"\n+ },\n+ {\n+ \"type\": \"OpenCollective\",\n+ \"url\": \"https://opencollective.com/unified\"\n+ }\n+ ],\n+ \"license\": \"MIT\"\n+ },\n+ \"node_modules/micromark-util-types\": {\n+ \"version\": \"2.0.2\",\n+ \"resolved\": \"https://registry.npmjs.org/micromark-util-types/-/micromark-util-types-2.0.2.tgz\",\n+ \"integrity\": \"sha512-Yw0ECSpJoViF1qTU4DC6NwtC4aWGt1EkzaQB8KPPyCRR8z9TWeV0HbEFGTO+ZY1wB22zmxnJqhPyTpOVCpeHTA==\",\n+ \"funding\": [\n+ {\n+ \"type\": \"GitHub Sponsors\",\n+ \"url\": \"https://github.com/sponsors/unifiedjs\"\n+ },\n+ {\n+ \"type\": \"OpenCollective\",\n+ \"url\": \"https://opencollective.com/unified\"\n+ }\n+ ],\n+ \"license\": \"MIT\"\n+ },\n \"node_modules/micromatch\": {\n \"version\": \"4.0.8\",\n \"resolved\": \"https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz\",\n@@ -3671,6 +3987,23 @@\n \"wrappy\": \"1\"\n }\n },\n+ \"node_modules/oniguruma-parser\": {\n+ \"version\": \"0.12.1\",\n+ \"resolved\": \"https://registry.npmjs.org/oniguruma-parser/-/oniguruma-parser-0.12.1.tgz\",\n+ \"integrity\": \"sha512-8Unqkvk1RYc6yq2WBYRj4hdnsAxVze8i7iPfQr8e4uSP3tRv0rpZcbGUDvxfQQcdwHt/e9PrMvGCsa8OqG9X3w==\",\n+ \"license\": \"MIT\"\n+ },\n+ \"node_modules/oniguruma-to-es\": {\n+ \"version\": \"4.3.3\",\n+ \"resolved\": \"https://registry.npmjs.org/oniguruma-to-es/-/oniguruma-to-es-4.3.3.tgz\",\n+ \"integrity\": \"sha512-rPiZhzC3wXwE59YQMRDodUwwT9FZ9nNBwQQfsd1wfdtlKEyCdRV0avrTcSZ5xlIvGRVPd/cx6ZN45ECmS39xvg==\",\n+ \"license\": \"MIT\",\n+ \"dependencies\": {\n+ \"oniguruma-parser\": \"^0.12.1\",\n+ \"regex\": \"^6.0.1\",\n+ \"regex-recursion\": \"^6.0.2\"\n+ }\n+ },\n \"node_modules/p-finally\": {\n \"version\": \"1.0.0\",\n \"resolved\": \"https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz\",\n@@ -3972,6 +4305,16 @@\n \"asap\": \"~2.0.3\"\n }\n },\n+ \"node_modules/property-information\": {\n+ \"version\": \"7.1.0\",\n+ \"resolved\": \"https://registry.npmjs.org/property-information/-/property-information-7.1.0.tgz\",\n+ \"integrity\": \"sha512-TwEZ+X+yCJmYfL7TPUOcvBZ4QfoT5YenQiJuX//0th53DE6w0xxLEtfK3iyryQFddXuvkIk51EEgrJQ0WJkOmQ==\",\n+ \"license\": \"MIT\",\n+ \"funding\": {\n+ \"type\": \"github\",\n+ \"url\": \"https://github.com/sponsors/wooorm\"\n+ }\n+ },\n \"node_modules/proxy-addr\": {\n \"version\": \"2.0.7\",\n \"resolved\": \"https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz\",\n@@ -4284,6 +4627,30 @@\n \"node\": \"\u003e= 12.13.0\"\n }\n },\n+ \"node_modules/regex\": {\n+ \"version\": \"6.0.1\",\n+ \"resolved\": \"https://registry.npmjs.org/regex/-/regex-6.0.1.tgz\",\n+ \"integrity\": \"sha512-uorlqlzAKjKQZ5P+kTJr3eeJGSVroLKoHmquUj4zHWuR+hEyNqlXsSKlYYF5F4NI6nl7tWCs0apKJ0lmfsXAPA==\",\n+ \"license\": \"MIT\",\n+ \"dependencies\": {\n+ \"regex-utilities\": \"^2.3.0\"\n+ }\n+ },\n+ \"node_modules/regex-recursion\": {\n+ \"version\": \"6.0.2\",\n+ \"resolved\": \"https://registry.npmjs.org/regex-recursion/-/regex-recursion-6.0.2.tgz\",\n+ \"integrity\": \"sha512-0YCaSCq2VRIebiaUviZNs0cBz1kg5kVS2UKUfNIx8YVs1cN3AV7NTctO5FOKBA+UT2BPJIWZauYHPqJODG50cg==\",\n+ \"license\": \"MIT\",\n+ \"dependencies\": {\n+ \"regex-utilities\": \"^2.3.0\"\n+ }\n+ },\n+ \"node_modules/regex-utilities\": {\n+ \"version\": \"2.3.0\",\n+ \"resolved\": \"https://registry.npmjs.org/regex-utilities/-/regex-utilities-2.3.0.tgz\",\n+ \"integrity\": \"sha512-8VhliFJAWRaUiVvREIiW2NXXTmHs4vMNnSzuJVhscgmGav3g9VDxLrQndI3dZZVVdp0ZO/5v0xmX516/7M9cng==\",\n+ \"license\": \"MIT\"\n+ },\n \"node_modules/resolve\": {\n \"version\": \"1.22.8\",\n \"resolved\": \"https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz\",\n@@ -4564,6 +4931,22 @@\n \"node\": \"\u003e=8\"\n }\n },\n+ \"node_modules/shiki\": {\n+ \"version\": \"3.4.2\",\n+ \"resolved\": \"https://registry.npmjs.org/shiki/-/shiki-3.4.2.tgz\",\n+ \"integrity\": \"sha512-wuxzZzQG8kvZndD7nustrNFIKYJ1jJoWIPaBpVe2+KHSvtzMi4SBjOxrigs8qeqce/l3U0cwiC+VAkLKSunHQQ==\",\n+ \"license\": \"MIT\",\n+ \"dependencies\": {\n+ \"@shikijs/core\": \"3.4.2\",\n+ \"@shikijs/engine-javascript\": \"3.4.2\",\n+ \"@shikijs/engine-oniguruma\": \"3.4.2\",\n+ \"@shikijs/langs\": \"3.4.2\",\n+ \"@shikijs/themes\": \"3.4.2\",\n+ \"@shikijs/types\": \"3.4.2\",\n+ \"@shikijs/vscode-textmate\": \"^10.0.2\",\n+ \"@types/hast\": \"^3.0.4\"\n+ }\n+ },\n \"node_modules/side-channel\": {\n \"version\": \"1.0.6\",\n \"resolved\": \"https://registry.npmjs.org/side-channel/-/side-channel-1.0.6.tgz\",\n@@ -4662,6 +5045,16 @@\n \"node\": \"\u003e= 8\"\n }\n },\n+ \"node_modules/space-separated-tokens\": {\n+ \"version\": \"2.0.2\",\n+ \"resolved\": \"https://registry.npmjs.org/space-separated-tokens/-/space-separated-tokens-2.0.2.tgz\",\n+ \"integrity\": \"sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q==\",\n+ \"license\": \"MIT\",\n+ \"funding\": {\n+ \"type\": \"github\",\n+ \"url\": \"https://github.com/sponsors/wooorm\"\n+ }\n+ },\n \"node_modules/split2\": {\n \"version\": \"4.2.0\",\n \"resolved\": \"https://registry.npmjs.org/split2/-/split2-4.2.0.tgz\",\n@@ -4753,6 +5146,20 @@\n \"node\": \"\u003e=8\"\n }\n },\n+ \"node_modules/stringify-entities\": {\n+ \"version\": \"4.0.4\",\n+ \"resolved\": \"https://registry.npmjs.org/stringify-entities/-/stringify-entities-4.0.4.tgz\",\n+ \"integrity\": \"sha512-IwfBptatlO+QCJUo19AqvrPNqlVMpW9YEL2LIVY+Rpv2qsjCGxaDLNRgeGsQWJhfItebuJhsGSLjaBbNSQ+ieg==\",\n+ \"license\": \"MIT\",\n+ \"dependencies\": {\n+ \"character-entities-html4\": \"^2.0.0\",\n+ \"character-entities-legacy\": \"^3.0.0\"\n+ },\n+ \"funding\": {\n+ \"type\": \"github\",\n+ \"url\": \"https://github.com/sponsors/wooorm\"\n+ }\n+ },\n \"node_modules/strip-ansi\": {\n \"version\": \"7.1.0\",\n \"resolved\": \"https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz\",\n@@ -5044,6 +5451,16 @@\n \"tree-kill\": \"cli.js\"\n }\n },\n+ \"node_modules/trim-lines\": {\n+ \"version\": \"3.0.1\",\n+ \"resolved\": \"https://registry.npmjs.org/trim-lines/-/trim-lines-3.0.1.tgz\",\n+ \"integrity\": \"sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg==\",\n+ \"license\": \"MIT\",\n+ \"funding\": {\n+ \"type\": \"github\",\n+ \"url\": \"https://github.com/sponsors/wooorm\"\n+ }\n+ },\n \"node_modules/ts-interface-checker\": {\n \"version\": \"0.1.13\",\n \"resolved\": \"https://registry.npmjs.org/ts-interface-checker/-/ts-interface-checker-0.1.13.tgz\",\n@@ -5752,6 +6169,74 @@\n \"dev\": true,\n \"license\": \"MIT\"\n },\n+ \"node_modules/unist-util-is\": {\n+ \"version\": \"6.0.0\",\n+ \"resolved\": \"https://registry.npmjs.org/unist-util-is/-/unist-util-is-6.0.0.tgz\",\n+ \"integrity\": \"sha512-2qCTHimwdxLfz+YzdGfkqNlH0tLi9xjTnHddPmJwtIG9MGsdbutfTc4P+haPD7l7Cjxf/WZj+we5qfVPvvxfYw==\",\n+ \"license\": \"MIT\",\n+ \"dependencies\": {\n+ \"@types/unist\": \"^3.0.0\"\n+ },\n+ \"funding\": {\n+ \"type\": \"opencollective\",\n+ \"url\": \"https://opencollective.com/unified\"\n+ }\n+ },\n+ \"node_modules/unist-util-position\": {\n+ \"version\": \"5.0.0\",\n+ \"resolved\": \"https://registry.npmjs.org/unist-util-position/-/unist-util-position-5.0.0.tgz\",\n+ \"integrity\": \"sha512-fucsC7HjXvkB5R3kTCO7kUjRdrS0BJt3M/FPxmHMBOm8JQi2BsHAHFsy27E0EolP8rp0NzXsJ+jNPyDWvOJZPA==\",\n+ \"license\": \"MIT\",\n+ \"dependencies\": {\n+ \"@types/unist\": \"^3.0.0\"\n+ },\n+ \"funding\": {\n+ \"type\": \"opencollective\",\n+ \"url\": \"https://opencollective.com/unified\"\n+ }\n+ },\n+ \"node_modules/unist-util-stringify-position\": {\n+ \"version\": \"4.0.0\",\n+ \"resolved\": \"https://registry.npmjs.org/unist-util-stringify-position/-/unist-util-stringify-position-4.0.0.tgz\",\n+ \"integrity\": \"sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ==\",\n+ \"license\": \"MIT\",\n+ \"dependencies\": {\n+ \"@types/unist\": \"^3.0.0\"\n+ },\n+ \"funding\": {\n+ \"type\": \"opencollective\",\n+ \"url\": \"https://opencollective.com/unified\"\n+ }\n+ },\n+ \"node_modules/unist-util-visit\": {\n+ \"version\": \"5.0.0\",\n+ \"resolved\": \"https://registry.npmjs.org/unist-util-visit/-/unist-util-visit-5.0.0.tgz\",\n+ \"integrity\": \"sha512-MR04uvD+07cwl/yhVuVWAtw+3GOR/knlL55Nd/wAdblk27GCVt3lqpTivy/tkJcZoNPzTwS1Y+KMojlLDhoTzg==\",\n+ \"license\": \"MIT\",\n+ \"dependencies\": {\n+ \"@types/unist\": \"^3.0.0\",\n+ \"unist-util-is\": \"^6.0.0\",\n+ \"unist-util-visit-parents\": \"^6.0.0\"\n+ },\n+ \"funding\": {\n+ \"type\": \"opencollective\",\n+ \"url\": \"https://opencollective.com/unified\"\n+ }\n+ },\n+ \"node_modules/unist-util-visit-parents\": {\n+ \"version\": \"6.0.1\",\n+ \"resolved\": \"https://registry.npmjs.org/unist-util-visit-parents/-/unist-util-visit-parents-6.0.1.tgz\",\n+ \"integrity\": \"sha512-L/PqWzfTP9lzzEa6CKs0k2nARxTdZduw3zyh8d2NVBnsyvHjSX4TWse388YrrQKbvI8w20fGjGlhgT96WwKykw==\",\n+ \"license\": \"MIT\",\n+ \"dependencies\": {\n+ \"@types/unist\": \"^3.0.0\",\n+ \"unist-util-is\": \"^6.0.0\"\n+ },\n+ \"funding\": {\n+ \"type\": \"opencollective\",\n+ \"url\": \"https://opencollective.com/unified\"\n+ }\n+ },\n \"node_modules/unpipe\": {\n \"version\": \"1.0.0\",\n \"resolved\": \"https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz\",\n@@ -5798,6 +6283,34 @@\n \"node\": \"\u003e= 0.8\"\n }\n },\n+ \"node_modules/vfile\": {\n+ \"version\": \"6.0.3\",\n+ \"resolved\": \"https://registry.npmjs.org/vfile/-/vfile-6.0.3.tgz\",\n+ \"integrity\": \"sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q==\",\n+ \"license\": \"MIT\",\n+ \"dependencies\": {\n+ \"@types/unist\": \"^3.0.0\",\n+ \"vfile-message\": \"^4.0.0\"\n+ },\n+ \"funding\": {\n+ \"type\": \"opencollective\",\n+ \"url\": \"https://opencollective.com/unified\"\n+ }\n+ },\n+ \"node_modules/vfile-message\": {\n+ \"version\": \"4.0.2\",\n+ \"resolved\": \"https://registry.npmjs.org/vfile-message/-/vfile-message-4.0.2.tgz\",\n+ \"integrity\": \"sha512-jRDZ1IMLttGj41KcZvlrYAaI3CfqpLpfpf+Mfig13viT6NKvRzWZ+lXz0Y5D60w6uJIBAOGq9mSHf0gktF0duw==\",\n+ \"license\": \"MIT\",\n+ \"dependencies\": {\n+ \"@types/unist\": \"^3.0.0\",\n+ \"unist-util-stringify-position\": \"^4.0.0\"\n+ },\n+ \"funding\": {\n+ \"type\": \"opencollective\",\n+ \"url\": \"https://opencollective.com/unified\"\n+ }\n+ },\n \"node_modules/void-elements\": {\n \"version\": \"3.1.0\",\n \"resolved\": \"https://registry.npmjs.org/void-elements/-/void-elements-3.1.0.tgz\",\n@@ -6004,6 +6517,16 @@\n \"funding\": {\n \"url\": \"https://github.com/sponsors/colinhacks\"\n }\n+ },\n+ \"node_modules/zwitch\": {\n+ \"version\": \"2.0.4\",\n+ \"resolved\": \"https://registry.npmjs.org/zwitch/-/zwitch-2.0.4.tgz\",\n+ \"integrity\": \"sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A==\",\n+ \"license\": \"MIT\",\n+ \"funding\": {\n+ \"type\": \"github\",\n+ \"url\": \"https://github.com/sponsors/wooorm\"\n+ }\n }\n }\n }\ndiff --git a/package.json b/package.json\nindex 23262d2..005e46b 100644\n--- a/package.json\n+++ b/package.json\n@@ -30,6 +30,7 @@\n \"multiformats\": \"^9.9.0\",\n \"pino\": \"^9.3.2\",\n \"pug\": \"^3.0.3\",\n+ \"shiki\": \"^3.4.2\",\n \"uhtml\": \"^4.5.9\"\n },\n \"devDependencies\": {\ndiff --git a/src/lib/highlight.ts b/src/lib/highlight.ts\nnew file mode 100644\nindex 0000000..4aa7d90\n--- /dev/null\n+++ b/src/lib/highlight.ts\n@@ -0,0 +1,48 @@\n+import { createHighlighter, HighlighterGeneric, BundledLanguage, BundledTheme } from 'shiki'\n+import type { Element } from 'hast'\n+\n+let highlighter: HighlighterGeneric\u003cBundledLanguage, BundledTheme\u003e | null = null\n+\n+export async function getHighlighterInstance() {\n+ if (!highlighter) {\n+ highlighter = await createHighlighter({\n+ themes: ['catppuccin-latte', 'catppuccin-mocha'],\n+ langs: [\n+ 'javascript', 'typescript', 'python', 'java', 'rust', \n+ 'go', 'c', 'cpp', 'csharp', 'php', 'ruby', 'cobol',\n+ 'plaintext', 'txt', 'bash', 'nix'\n+ ]\n+ })\n+ }\n+ return highlighter\n+}\n+\n+export async function highlightCode(code: string, lang: string) {\n+ const highlighter = await getHighlighterInstance()\n+ \n+ try {\n+ return highlighter.codeToHtml(code, {\n+ lang: lang,\n+ themes: {\n+ light: 'catppuccin-latte',\n+ dark: 'catppuccin-mocha'\n+ },\n+ transformers: [\n+ {\n+ line(node: Element, line: number) {\n+ node.properties['code-line'] = line\n+ }\n+ }\n+ ]\n+ })\n+ } catch (error) {\n+ console.warn(`Language \"${lang}\" not supported by Shiki, falling back to plaintext`)\n+ return highlighter.codeToHtml(code, {\n+ lang: 'txt',\n+ themes: {\n+ light: 'catppuccin-latte',\n+ dark: 'catppuccin-mocha'\n+ }\n+ })\n+ }\n+} \n\\ No newline at end of file\ndiff --git a/src/public/styles.css b/src/public/styles.css\nindex b153f92..61d3019 100644\n--- a/src/public/styles.css\n+++ b/src/public/styles.css\n@@ -231,3 +231,36 @@ select {\n .footer a:hover {\n text-decoration: underline;\n }\n+\n+.highlighted-code {\n+ margin: 1rem 0;\n+ counter-reset: step;\n+ counter-increment: step calc(var(--start, 1) - 1);\n+}\n+\n+.highlighted-code pre {\n+ padding: 1rem;\n+ overflow-x: auto;\n+ border-radius: 4px;\n+}\n+\n+code .line::before {\n+ content: counter(step);\n+ counter-increment: step;\n+ width: 1rem;\n+ margin-right: 1.5rem;\n+ display: inline-block;\n+ text-align: right;\n+ color: rgba(115,138,148,.4)\n+}\n+\n+@media (prefers-color-scheme: dark) {\n+ .shiki,\n+ .shiki span {\n+ color: var(--shiki-dark) !important;\n+ background-color: var(--shiki-dark-bg) !important;\n+ font-style: var(--shiki-dark-font-style) !important;\n+ font-weight: var(--shiki-dark-font-weight) !important;\n+ text-decoration: var(--shiki-dark-text-decoration) !important;\n+ }\n+}\n\\ No newline at end of file\ndiff --git a/src/routes.ts b/src/routes.ts\nindex 17fa00e..e4782ca 100644\n--- a/src/routes.ts\n+++ b/src/routes.ts\n@@ -11,6 +11,7 @@ import { newShortUrl } from \"#/db\";\n \n import * as Paste from \"#/lexicons/types/li/plonk/paste\";\n import * as Comment from \"#/lexicons/types/li/plonk/comment\";\n+import { highlightCode } from \"#/lib/highlight\";\n \n type Session = {\n \tdid: string;\n@@ -209,9 +210,12 @@ export const createRouter = (ctx: Ctx) =\u003e {\n \t\t\t),\n \t\t);\n \n+\t\tconst highlightedCode = await highlightCode(pasteCode, pasteLang);\n+\n \t\tconst paste = {\n \t\t\turi: pasteUri,\n \t\t\tcode: pasteCode,\n+\t\t\thighlightedCode,\n \t\t\ttitle: pasteTitle,\n \t\t\tlang: pasteLang,\n \t\t\tshortUrl,\ndiff --git a/src/views/index.pug b/src/views/index.pug\nindex bc9085a..17ddcbc 100644\n--- a/src/views/index.pug\n+++ b/src/views/index.pug\n@@ -9,6 +9,7 @@ include ../mixins/post\n - \n var langs = [\"plaintext\"]\n .concat([\n+ \"bash\",\n \"javascript\",\n \"typescript\",\n \"java\",\n@@ -21,6 +22,7 @@ include ../mixins/post\n \"c#\",\n \"c++\",\n \"cobol\",\n+ \"nix\",\n ].toSorted())\n doctype html\n html\ndiff --git a/src/views/paste.pug b/src/views/paste.pug\nindex 3014107..aca4c07 100644\n--- a/src/views/paste.pug\n+++ b/src/views/paste.pug\n@@ -15,18 +15,8 @@ html\n | #{timeDifference(now, Date.parse(paste.createdAt))} ago ·\n | #{paste.lang} · \n | #{paste.code.split('\\n').length} loc · \n- a(href=`/r/${paste.shortUrl}`) raw\n- | \u0026nbsp;·\n- | #{comments.length} #{pluralize(comments.length, 'comment')}\n- pre\n- code\n- - var lines = paste.code.split(/\\r?\\n|\\r|\\n/g)\n- - var tot_chars = lines.length.toString().length\n- each line, idx in lines\n- span.code-line\n- span.code-line-num(id=`L${idx + 1}` style=`min-width: ${tot_chars}ch;`)\n- | #{idx + 1}\n- span.code-line-content #{line}\n+ a(href=`/r/${paste.shortUrl}`) raw \n+ div.highlighted-code !{paste.highlightedCode}\n hr\n \n if comments.length != 0\n@@ -53,4 +43,4 @@ html\n a(href=\"/login\") login\n |\u0026nbsp;to post a comment\n \n- +footer()\n+ +footer()\n\\ No newline at end of file\n-- \n2.43.0\n\n\nFrom f5924e27abdaf9aa098081623eef21fd88a2e94d Mon Sep 17 00:00:00 2001\nFrom: waveringana \u003cana@nekomimi.pet\u003e\nDate: Wed, 28 May 2025 14:09:06 -0400\nSubject: [PATCH] add copy button\n\n---\n src/public/styles.css | 37 ++++++++++++++++++++++++++++---------\n src/views/paste.pug | 31 +++++++++++++++++++++++++++++--\n 2 files changed, 57 insertions(+), 11 deletions(-)\n\ndiff --git a/src/public/styles.css b/src/public/styles.css\nindex 61d3019..32407f3 100644\n--- a/src/public/styles.css\n+++ b/src/public/styles.css\n@@ -5,6 +5,17 @@\n font-style: monospace;\n }\n \n+@media (prefers-color-scheme: dark) {\n+ .shiki,\n+ .shiki span {\n+ color: var(--shiki-dark) !important;\n+ background-color: var(--shiki-dark-bg) !important;\n+ font-style: var(--shiki-dark-font-style) !important;\n+ font-weight: var(--shiki-dark-font-weight) !important;\n+ text-decoration: var(--shiki-dark-text-decoration) !important;\n+ }\n+}\n+\n :root {\n /* Light mode colors */\n --bg-color: white;\n@@ -254,13 +265,21 @@ code .line::before {\n color: rgba(115,138,148,.4)\n }\n \n-@media (prefers-color-scheme: dark) {\n- .shiki,\n- .shiki span {\n- color: var(--shiki-dark) !important;\n- background-color: var(--shiki-dark-bg) !important;\n- font-style: var(--shiki-dark-font-style) !important;\n- font-weight: var(--shiki-dark-font-weight) !important;\n- text-decoration: var(--shiki-dark-text-decoration) !important;\n- }\n+#copy-btn {\n+ background: none;\n+ border: none;\n+ padding: 0;\n+ color: var(--text-color-muted);\n+ cursor: pointer;\n+ text-decoration: none;\n+ font-family: inherit;\n+ font-size: inherit;\n+}\n+\n+#copy-btn:hover {\n+ text-decoration: underline;\n+}\n+\n+#copy-btn:focus {\n+ outline: 1px solid var(--accent);\n }\n\\ No newline at end of file\ndiff --git a/src/views/paste.pug b/src/views/paste.pug\nindex aca4c07..f343674 100644\n--- a/src/views/paste.pug\n+++ b/src/views/paste.pug\n@@ -15,7 +15,11 @@ html\n | #{timeDifference(now, Date.parse(paste.createdAt))} ago ·\n | #{paste.lang} · \n | #{paste.code.split('\\n').length} loc · \n- a(href=`/r/${paste.shortUrl}`) raw \n+ a(href=`/r/${paste.shortUrl}`) raw\n+ | \u0026nbsp;·\u0026nbsp;\n+ button#copy-btn(type=\"button\" onclick=\"copyToClipboard()\" data-code=paste.code) copy\n+ | \u0026nbsp;·\n+ | #{comments.length} #{pluralize(comments.length, 'comment')}\n div.highlighted-code !{paste.highlightedCode}\n hr\n \n@@ -43,4 +47,27 @@ html\n a(href=\"/login\") login\n |\u0026nbsp;to post a comment\n \n- +footer()\n\\ No newline at end of file\n+ +footer()\n+ script.\n+ async function copyToClipboard() {\n+ const copyBtn = document.getElementById('copy-btn');\n+ const originalText = copyBtn.textContent;\n+ \n+ try {\n+ const code = copyBtn.dataset.code;\n+ await navigator.clipboard.writeText(code);\n+ copyBtn.textContent = 'copied!';\n+ copyBtn.style.color = 'var(--accent)';\n+ \n+ setTimeout(() =\u003e {\n+ copyBtn.textContent = originalText;\n+ copyBtn.style.color = '';\n+ }, 1500);\n+ } catch (err) {\n+ console.error('Failed to copy: ', err);\n+ copyBtn.textContent = 'copy failed';\n+ setTimeout(() =\u003e {\n+ copyBtn.textContent = originalText;\n+ }, 1500);\n+ }\n+ }\n\\ No newline at end of file\n-- \n2.43.0\n\n\n",
"pullId": 5,
"source": {
"branch": "main",
"repo": "at://did:plc:ttdrpj45ibqunmfhdsb4zdwq/sh.tangled.repo/3lqasfdqkgk22"
},
"targetBranch": "main",
"targetRepo": "at://did:plc:qfpnj4og54vl56wngdriaxug/sh.tangled.repo/3ljstrrobog22",
"title": "adds syntax highlighting from shiki which server side renders, and adds a copy text button"
}