From 70c343974e4b802c1dd6d2388b0de4a87b57df6d Mon Sep 17 00:00:00 2001 From: Tom Butcher Date: Fri, 12 Dec 2025 22:08:46 +0000 Subject: [PATCH] Added codeblock support. --- assets/copyicon.svg | 13 ++ index.html | 2 +- package.json | 1 + src/components/CodeBlock.jsx | 244 +++++++++++++++++++++++++++++ src/components/ContentRenderer.jsx | 11 +- src/icons/CopyIcon.jsx | 6 + src/styles/components.css | 19 +++ yarn.lock | 172 +++++++++++++++++++- 8 files changed, 465 insertions(+), 3 deletions(-) create mode 100644 assets/copyicon.svg create mode 100644 src/components/CodeBlock.jsx create mode 100644 src/icons/CopyIcon.jsx diff --git a/assets/copyicon.svg b/assets/copyicon.svg new file mode 100644 index 0000000..233c66f --- /dev/null +++ b/assets/copyicon.svg @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/index.html b/index.html index b10bcb0..5f72716 100644 --- a/index.html +++ b/index.html @@ -13,7 +13,7 @@ diff --git a/package.json b/package.json index d4bb019..3466bbc 100644 --- a/package.json +++ b/package.json @@ -32,6 +32,7 @@ "react-router-dom": "^7.5.0", "react-scripts": "^5.0.1", "react-scroll": "^1.9.3", + "react-syntax-highlighter": "^16.1.0", "react-turnstile": "^1.1.4", "sass": "^1.86.3", "simplebar": "^6.3.2", diff --git a/src/components/CodeBlock.jsx b/src/components/CodeBlock.jsx new file mode 100644 index 0000000..9460a61 --- /dev/null +++ b/src/components/CodeBlock.jsx @@ -0,0 +1,244 @@ +import { Prism as SyntaxHighlighter } from "react-syntax-highlighter"; +import { useTheme } from "../contexts/ThemeContext"; +import { useMemo, useState } from "react"; +import CopyIcon from "../icons/CopyIcon"; +import CheckIcon from "../icons/CheckIcon"; + +// Helper function to parse hex color to RGB +const hexToRgb = (hex) => { + const cleanHex = hex.replace("#", ""); + return { + r: parseInt(cleanHex.slice(0, 2), 16), + g: parseInt(cleanHex.slice(2, 4), 16), + b: parseInt(cleanHex.slice(4, 6), 16), + }; +}; + +// Helper function to blend two colors +const blendColors = (color1, color2, ratio) => { + const rgb1 = hexToRgb(color1); + const rgb2 = hexToRgb(color2); + const r = Math.round(rgb1.r * ratio + rgb2.r * (1 - ratio)); + const g = Math.round(rgb1.g * ratio + rgb2.g * (1 - ratio)); + const b = Math.round(rgb1.b * ratio + rgb2.b * (1 - ratio)); + return `#${[r, g, b].map((x) => x.toString(16).padStart(2, "0")).join("")}`; +}; + +// Helper function to create a custom theme based on app theme colors +const createCustomTheme = (backgroundColor, textColor) => { + // Determine if theme is dark or light based on background color brightness + const bgRgb = hexToRgb(backgroundColor); + const brightness = (bgRgb.r * 299 + bgRgb.g * 587 + bgRgb.b * 114) / 1000; + const isDark = brightness < 128; + + // Base colors + const bg = backgroundColor; + const fg = textColor; + + // Create color variations by blending foreground and background + // For dark themes: use brighter accents + // For light themes: use darker accents + const accent1 = isDark ? blendColors(fg, bg, 0.8) : blendColors(fg, bg, 0.6); + const accent2 = isDark ? blendColors(fg, bg, 0.6) : blendColors(fg, bg, 0.4); + + // Syntax highlighting colors + // Strings: slightly warmer tone + const stringColor = isDark ? "#f1fa8c" : "#8b6914"; + // Keywords: slightly cooler tone + const keywordColor = isDark ? "#bd93f9" : "#6b46c1"; + // Comments: muted + const commentColor = isDark ? "#6272a4" : "#6b7280"; + // Numbers: accent color + const numberColor = isDark ? "#ff79c6" : "#c026d3"; + // Functions: primary text color + const functionColor = isDark ? "#50fa7b" : "#059669"; + // Operators: accent + const operatorColor = fg; + + return { + 'code[class*="language-"]': { + color: fg, + background: bg, + textShadow: "none", + }, + 'pre[class*="language-"]': { + color: fg, + background: bg, + textShadow: "none", + }, + comment: { + color: commentColor, + fontStyle: "italic", + }, + prolog: { + color: commentColor, + }, + doctype: { + color: commentColor, + }, + cdata: { + color: commentColor, + }, + punctuation: { + color: accent2, + }, + property: { + color: keywordColor, + }, + tag: { + color: keywordColor, + }, + boolean: { + color: keywordColor, + }, + number: { + color: numberColor, + }, + constant: { + color: numberColor, + }, + symbol: { + color: numberColor, + }, + deleted: { + color: numberColor, + }, + selector: { + color: functionColor, + }, + "attr-name": { + color: functionColor, + }, + string: { + color: stringColor, + }, + char: { + color: stringColor, + }, + builtin: { + color: stringColor, + }, + inserted: { + color: stringColor, + }, + operator: { + color: operatorColor, + }, + entity: { + color: accent1, + cursor: "help", + }, + url: { + color: accent1, + }, + ".language-css .token.string": { + color: accent1, + }, + ".style .token.string": { + color: accent1, + }, + variable: { + color: accent1, + }, + atrule: { + color: keywordColor, + }, + "attr-value": { + color: stringColor, + }, + function: { + color: functionColor, + }, + "class-name": { + color: functionColor, + }, + keyword: { + color: keywordColor, + }, + regex: { + color: stringColor, + }, + important: { + color: keywordColor, + fontWeight: "bold", + }, + bold: { + fontWeight: "bold", + }, + italic: { + fontStyle: "italic", + }, + }; +}; + +export default function CodeBlock({ code, language }) { + const { theme } = useTheme(); + + // Create custom theme based on app theme + const customTheme = useMemo(() => { + const bg = theme?.backgroundColor || "#ffffff"; + const text = theme?.textColor || "#000000"; + return createCustomTheme(bg, text); + }, [theme?.backgroundColor, theme?.textColor]); + + // Determine line number color based on theme + const lineNumberColor = useMemo(() => { + if (!theme?.backgroundColor || !theme?.textColor) { + return "#6272a4"; + } + const bgRgb = hexToRgb(theme.backgroundColor); + const brightness = (bgRgb.r * 299 + bgRgb.g * 587 + bgRgb.b * 114) / 1000; + const isDark = brightness < 128; + // Use a muted version of the text color for line numbers + return isDark + ? blendColors(theme.textColor, theme.backgroundColor, 0.4) + : blendColors(theme.textColor, theme.backgroundColor, 0.3); + }, [theme?.backgroundColor, theme?.textColor]); + + const [isCopied, setIsCopied] = useState(false); + const handleCopy = () => { + navigator.clipboard.writeText(code); + setIsCopied(true); + setTimeout(() => { + setIsCopied(false); + }, 2000); + }; + + return ( +
+ + + + {code} + +
+ ); +} diff --git a/src/components/ContentRenderer.jsx b/src/components/ContentRenderer.jsx index a7ba61e..5c0a6d9 100644 --- a/src/components/ContentRenderer.jsx +++ b/src/components/ContentRenderer.jsx @@ -11,7 +11,8 @@ import RightIcon from "../icons/RightIcon"; import LeftIcon from "../icons/LeftIcon"; import Video from "./Video"; import VisitIcon from "../icons/VisitIcon"; - +import CodeBlock from "./CodeBlock"; +import CopyIcon from "../icons/CopyIcon"; const gradientHeight = 60; const renderAnnotation = (textObject = {}, navigate, index) => { @@ -378,6 +379,14 @@ const renderContentElement = ( ); } + case "code": { + return ( + + ); + } default: console.log("Unknown type:", type); return ( diff --git a/src/icons/CopyIcon.jsx b/src/icons/CopyIcon.jsx new file mode 100644 index 0000000..16a2340 --- /dev/null +++ b/src/icons/CopyIcon.jsx @@ -0,0 +1,6 @@ +import CopyIconSvg from "../../assets/copyicon.svg?react"; +const CopyIcon = () => { + return ; +}; + +export default CopyIcon; diff --git a/src/styles/components.css b/src/styles/components.css index 6f0e457..35e9b24 100644 --- a/src/styles/components.css +++ b/src/styles/components.css @@ -3308,3 +3308,22 @@ textarea.tb-form-input { .tb-video-volume-button > .tb-icon-button > svg { width: auto; } + +.tb-code { + font-family: "Sono", sans-serif; + position: relative; + padding: 20px 65px 5px 10px; + border: 1px solid color-mix(in srgb, var(--tb-textColor) 15%, transparent); + border-radius: 22px; +} + +.tb-code span { + font-family: "Sono", sans-serif; +} + +.tb-code-copy-btn { + position: absolute; + top: 0; + right: 0; + margin: 9px 10px; +} diff --git a/yarn.lock b/yarn.lock index a7b2f56..108a2f9 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1161,7 +1161,7 @@ "@babel/plugin-transform-modules-commonjs" "^7.27.1" "@babel/plugin-transform-typescript" "^7.27.1" -"@babel/runtime@^7.10.1", "@babel/runtime@^7.10.4", "@babel/runtime@^7.11.1", "@babel/runtime@^7.11.2", "@babel/runtime@^7.12.5", "@babel/runtime@^7.16.3", "@babel/runtime@^7.16.7", "@babel/runtime@^7.18.0", "@babel/runtime@^7.18.3", "@babel/runtime@^7.20.0", "@babel/runtime@^7.20.7", "@babel/runtime@^7.21.0", "@babel/runtime@^7.22.5", "@babel/runtime@^7.23.2", "@babel/runtime@^7.23.6", "@babel/runtime@^7.23.9", "@babel/runtime@^7.24.4", "@babel/runtime@^7.24.7", "@babel/runtime@^7.24.8", "@babel/runtime@^7.25.7", "@babel/runtime@^7.26.0": +"@babel/runtime@^7.10.1", "@babel/runtime@^7.10.4", "@babel/runtime@^7.11.1", "@babel/runtime@^7.11.2", "@babel/runtime@^7.12.5", "@babel/runtime@^7.16.3", "@babel/runtime@^7.16.7", "@babel/runtime@^7.18.0", "@babel/runtime@^7.18.3", "@babel/runtime@^7.20.0", "@babel/runtime@^7.20.7", "@babel/runtime@^7.21.0", "@babel/runtime@^7.22.5", "@babel/runtime@^7.23.2", "@babel/runtime@^7.23.6", "@babel/runtime@^7.23.9", "@babel/runtime@^7.24.4", "@babel/runtime@^7.24.7", "@babel/runtime@^7.24.8", "@babel/runtime@^7.25.7", "@babel/runtime@^7.26.0", "@babel/runtime@^7.28.4": version "7.28.4" resolved "https://registry.npmjs.org/@babel/runtime/-/runtime-7.28.4.tgz" integrity sha512-Q/N6JNWvIvPnLDvjlE1OUBLPQHH6l3CltCEsHIujp45zQUSSh8K+gHnaEX45yAT1nyngnINhvWtzN+Nb9D8RAQ== @@ -2882,6 +2882,13 @@ dependencies: "@types/node" "*" +"@types/hast@^3.0.0": + version "3.0.4" + resolved "https://registry.yarnpkg.com/@types/hast/-/hast-3.0.4.tgz#1d6b39993b82cea6ad783945b0508c25903e15aa" + integrity sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ== + dependencies: + "@types/unist" "*" + "@types/html-minifier-terser@^6.0.0": version "6.1.0" resolved "https://registry.npmjs.org/@types/html-minifier-terser/-/html-minifier-terser-6.1.0.tgz" @@ -2957,6 +2964,11 @@ resolved "https://registry.npmjs.org/@types/prettier/-/prettier-2.7.3.tgz" integrity sha512-+68kP9yzs4LMp7VNh8gdzMSPZFL44MLGqiHWvttYJe+6qnuVr4Ek9wSBQoveqY/r+LwjCcU29kNVkidwim+kYA== +"@types/prismjs@^1.0.0": + version "1.26.5" + resolved "https://registry.yarnpkg.com/@types/prismjs/-/prismjs-1.26.5.tgz#72499abbb4c4ec9982446509d2f14fb8483869d6" + integrity sha512-AUZTa7hQ2KY5L7AmtSiqxlhWxb4ina0yd8hNbl4TWuqnv/pFP0nDMb3YrfSBf4hJVGLh2YEIBfKaBW/9UEl6IQ== + "@types/q@^1.5.1": version "1.5.8" resolved "https://registry.npmjs.org/@types/q/-/q-1.5.8.tgz" @@ -3042,6 +3054,16 @@ resolved "https://registry.npmjs.org/@types/trusted-types/-/trusted-types-2.0.7.tgz" integrity sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw== +"@types/unist@*": + version "3.0.3" + resolved "https://registry.yarnpkg.com/@types/unist/-/unist-3.0.3.tgz#acaab0f919ce69cce629c2d4ed2eb4adc1b6c20c" + integrity sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q== + +"@types/unist@^2.0.0": + version "2.0.11" + resolved "https://registry.yarnpkg.com/@types/unist/-/unist-2.0.11.tgz#11af57b127e32487774841f7a4e54eab166d03c4" + integrity sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA== + "@types/ws@^8.5.5": version "8.18.1" resolved "https://registry.npmjs.org/@types/ws/-/ws-8.18.1.tgz" @@ -4124,6 +4146,21 @@ char-regex@^2.0.0: resolved "https://registry.npmjs.org/char-regex/-/char-regex-2.0.2.tgz" integrity sha512-cbGOjAptfM2LVmWhwRFHEKTPkLwNddVmuqYZQt895yXwAsWsXObCG+YN4DGQ/JBtT4GP1a1lPPdio2z413LmTg== +character-entities-legacy@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/character-entities-legacy/-/character-entities-legacy-3.0.0.tgz#76bc83a90738901d7bc223a9e93759fdd560125b" + integrity sha512-RpPp0asT/6ufRm//AJVwpViZbGM/MkjQFxJccQRHmISF/22NBtsHqAWmL+/pmkPWoIUJdWyeVleTl1wydHATVQ== + +character-entities@^2.0.0: + version "2.0.2" + resolved "https://registry.yarnpkg.com/character-entities/-/character-entities-2.0.2.tgz#2d09c2e72cd9523076ccb21157dff66ad43fcc22" + integrity sha512-shx7oQ0Awen/BRIdkjkvz54PnEEI/EjwXDSIZp86/KKdbafHh1Df/RYGBhn4hbe2+uKC9FnT5UCEdyPz3ai9hQ== + +character-reference-invalid@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/character-reference-invalid/-/character-reference-invalid-2.0.1.tgz#85c66b041e43b47210faf401278abf808ac45cb9" + integrity sha512-iBZ4F4wRbyORVsu0jPV7gXkOsGYjGHPmAyv+HiHG8gi5PtC9KI2j1+v8/tlibRvjoWX027ypmG/n0HtO5t7unw== + check-types@^11.2.3: version "11.2.3" resolved "https://registry.npmjs.org/check-types/-/check-types-11.2.3.tgz" @@ -4259,6 +4296,11 @@ combined-stream@^1.0.8: dependencies: delayed-stream "~1.0.0" +comma-separated-tokens@^2.0.0: + version "2.0.3" + resolved "https://registry.yarnpkg.com/comma-separated-tokens/-/comma-separated-tokens-2.0.3.tgz#4e89c9458acb61bc8fef19f4529973b2392839ee" + integrity sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg== + commander@^2.20.0: version "2.20.3" resolved "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz" @@ -4747,6 +4789,13 @@ decimal.js@^10.2.1: resolved "https://registry.npmjs.org/decimal.js/-/decimal.js-10.6.0.tgz" integrity sha512-YpgQiITW3JXGntzdUmyUR1V812Hn8T1YVXhCu+wO3OpS4eU9l4YdD3qjyiKdV6mvV29zapkMeD390UVEf2lkUg== +decode-named-character-reference@^1.0.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/decode-named-character-reference/-/decode-named-character-reference-1.2.0.tgz#25c32ae6dd5e21889549d40f676030e9514cc0ed" + integrity sha512-c6fcElNV6ShtZXmsgNgFFV5tVX2PaV4g+MOAkb8eXHvn6sryJBrZa9r0zV6+dtTyoCKxtDy5tyQ5ZwQuidtd+Q== + dependencies: + character-entities "^2.0.0" + dedent@^0.7.0: version "0.7.0" resolved "https://registry.npmjs.org/dedent/-/dedent-0.7.0.tgz" @@ -5721,6 +5770,13 @@ fastq@^1.6.0: dependencies: reusify "^1.0.4" +fault@^1.0.0: + version "1.0.4" + resolved "https://registry.yarnpkg.com/fault/-/fault-1.0.4.tgz#eafcfc0a6d214fc94601e170df29954a4f842f13" + integrity sha512-CJ0HCB5tL5fYTEA7ToAq5+kTwd++Borf1/bifxd9iT70QcXr4MRrO3Llf8Ifs70q+SJcGHFtnIE/Nw6giCtECA== + dependencies: + format "^0.2.0" + faye-websocket@^0.11.3: version "0.11.4" resolved "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.11.4.tgz" @@ -5894,6 +5950,11 @@ form-data@^4.0.4: hasown "^2.0.2" mime-types "^2.1.12" +format@^0.2.0: + version "0.2.2" + resolved "https://registry.yarnpkg.com/format/-/format-0.2.2.tgz#d6170107e9efdc4ed30c9dc39016df942b5cb58b" + integrity sha512-wzsgA6WOq+09wrU1tsJ09udeR/YZRaeArL9e1wPbFg3GG2yDnC2ldKpxs4xunpFF9DgqCqOIra3bc1HWrJ37Ww== + forwarded@0.2.0: version "0.2.0" resolved "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz" @@ -6199,11 +6260,39 @@ hasown@^2.0.2: dependencies: function-bind "^1.1.2" +hast-util-parse-selector@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/hast-util-parse-selector/-/hast-util-parse-selector-4.0.0.tgz#352879fa86e25616036037dd8931fb5f34cb4a27" + integrity sha512-wkQCkSYoOGCRKERFWcxMVMOcYE2K1AaNLU8DXS9arxnLOUEWbOXKXiJUNzEpqZ3JOKpnha3jkFrumEjVliDe7A== + dependencies: + "@types/hast" "^3.0.0" + +hastscript@^9.0.0: + version "9.0.1" + resolved "https://registry.yarnpkg.com/hastscript/-/hastscript-9.0.1.tgz#dbc84bef6051d40084342c229c451cd9dc567dff" + integrity sha512-g7df9rMFX/SPi34tyGCyUBREQoKkapwdY/T04Qn9TDWfHhAYt4/I0gMVirzK5wEzeUqIjEB+LXC/ypb7Aqno5w== + dependencies: + "@types/hast" "^3.0.0" + comma-separated-tokens "^2.0.0" + hast-util-parse-selector "^4.0.0" + property-information "^7.0.0" + space-separated-tokens "^2.0.0" + he@^1.2.0: version "1.2.0" resolved "https://registry.npmjs.org/he/-/he-1.2.0.tgz" integrity sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw== +highlight.js@^10.4.1, highlight.js@~10.7.0: + version "10.7.3" + resolved "https://registry.yarnpkg.com/highlight.js/-/highlight.js-10.7.3.tgz#697272e3991356e40c3cac566a74eef681756531" + integrity sha512-tzcUFauisWKNHaRkN4Wjl/ZA07gENAjFl3J/c480dprkGTg5EQstgaNFqBfUqCq54kZRIEcreTsAgF/m2quD7A== + +highlightjs-vue@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/highlightjs-vue/-/highlightjs-vue-1.0.0.tgz#fdfe97fbea6354e70ee44e3a955875e114db086d" + integrity sha512-PDEfEF102G23vHmPhLyPboFCD+BkMGu+GuJe2d9/eH4FsCwvgBpnc9n0pGE+ffKdph38s6foEZiEjdgHdzp+IA== + hoopy@^0.1.4: version "0.1.4" resolved "https://registry.npmjs.org/hoopy/-/hoopy-0.1.4.tgz" @@ -6462,6 +6551,19 @@ ipaddr.js@^2.0.1: resolved "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-2.2.0.tgz" integrity sha512-Ag3wB2o37wslZS19hZqorUnrnzSkpOVy+IiiDEiTqNubEYpYuHWIf6K4psgN2ZWKExS4xhVCrRVfb/wfW8fWJA== +is-alphabetical@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/is-alphabetical/-/is-alphabetical-2.0.1.tgz#01072053ea7c1036df3c7d19a6daaec7f19e789b" + integrity sha512-FWyyY60MeTNyeSRpkM2Iry0G9hpr7/9kD40mD/cGQEuilcZYS4okz8SN2Q6rLCJ8gbCt6fN+rC+6tMGS99LaxQ== + +is-alphanumerical@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/is-alphanumerical/-/is-alphanumerical-2.0.1.tgz#7c03fbe96e3e931113e57f964b0a368cc2dfd875" + integrity sha512-hmbYhX/9MUMF5uh7tOXyK/n0ZvWpad5caBA17GsC6vyuCqaWliRG5K1qS9inmUhEMaOBIW7/whAnSwveW/LtZw== + dependencies: + is-alphabetical "^2.0.0" + is-decimal "^2.0.0" + is-array-buffer@^3.0.4, is-array-buffer@^3.0.5: version "3.0.5" resolved "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.5.tgz" @@ -6538,6 +6640,11 @@ is-date-object@^1.0.5, is-date-object@^1.1.0: call-bound "^1.0.2" has-tostringtag "^1.0.2" +is-decimal@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/is-decimal/-/is-decimal-2.0.1.tgz#9469d2dc190d0214fd87d78b78caecc0cc14eef7" + integrity sha512-AAB9hiomQs5DXWcRB1rqsxGUstbRroFOPPVAomNk/3XHR5JyEZChOyTWe2oayKnsSsr/kcGqF+z6yuH6HHpN0A== + is-docker@^2.0.0, is-docker@^2.1.1: version "2.2.1" resolved "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz" @@ -6582,6 +6689,11 @@ is-glob@^4.0.0, is-glob@^4.0.1, is-glob@^4.0.3, is-glob@~4.0.1: dependencies: is-extglob "^2.1.1" +is-hexadecimal@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/is-hexadecimal/-/is-hexadecimal-2.0.1.tgz#86b5bf668fca307498d319dfc03289d781a90027" + integrity sha512-DgZQp241c8oO6cA1SbTEWiXeoxV42vlcJxgH+B3hi1AiqqKruZR3ZGF8In3fj4+/y/7rHvlOZLZtgJ/4ttYGZg== + is-map@^2.0.3: version "2.0.3" resolved "https://registry.npmjs.org/is-map/-/is-map-2.0.3.tgz" @@ -7635,6 +7747,14 @@ lower-case@^2.0.2: dependencies: tslib "^2.0.3" +lowlight@^1.17.0: + version "1.20.0" + resolved "https://registry.yarnpkg.com/lowlight/-/lowlight-1.20.0.tgz#ddb197d33462ad0d93bf19d17b6c301aa3941888" + integrity sha512-8Ktj+prEb1RoCPkEOrPMYUN/nCggB7qAWe3a7OpMjWQkh3l2RD5wKRQ+o8Q8YuI9RG/xs95waaI/E6ym/7NsTw== + dependencies: + fault "^1.0.0" + highlight.js "~10.7.0" + lru-cache@^10.2.0: version "10.4.3" resolved "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz" @@ -8191,6 +8311,19 @@ parent-module@^1.0.0: dependencies: callsites "^3.0.0" +parse-entities@^4.0.0: + version "4.0.2" + resolved "https://registry.yarnpkg.com/parse-entities/-/parse-entities-4.0.2.tgz#61d46f5ed28e4ee62e9ddc43d6b010188443f159" + integrity sha512-GG2AQYWoLgL877gQIKeRPGO1xF9+eG1ujIb5soS5gPvLQ1y2o8FL90w2QWNdf9I361Mpp7726c+lj3U0qK1uGw== + dependencies: + "@types/unist" "^2.0.0" + character-entities-legacy "^3.0.0" + character-reference-invalid "^2.0.0" + decode-named-character-reference "^1.0.0" + is-alphanumerical "^2.0.0" + is-decimal "^2.0.0" + is-hexadecimal "^2.0.0" + parse-json@^5.0.0, parse-json@^5.2.0: version "5.2.0" resolved "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz" @@ -8918,6 +9051,11 @@ pretty-format@^28.1.3: ansi-styles "^5.0.0" react-is "^18.0.0" +prismjs@^1.30.0: + version "1.30.0" + resolved "https://registry.yarnpkg.com/prismjs/-/prismjs-1.30.0.tgz#d9709969d9d4e16403f6f348c63553b19f0975a9" + integrity sha512-DEvV2ZF2r2/63V+tK8hQvrR2ZGn10srHbXviTlcv7Kpzw8jWiNTqbVgjO3IY8RxrrOUF8VPMQQFysYYYv0YZxw== + process-nextick-args@~2.0.0: version "2.0.1" resolved "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz" @@ -8947,6 +9085,11 @@ prop-types@^15.6.1, prop-types@^15.7.2, prop-types@^15.8.1: object-assign "^4.1.1" react-is "^16.13.1" +property-information@^7.0.0: + version "7.1.0" + resolved "https://registry.yarnpkg.com/property-information/-/property-information-7.1.0.tgz#b622e8646e02b580205415586b40804d3e8bfd5d" + integrity sha512-TwEZ+X+yCJmYfL7TPUOcvBZ4QfoT5YenQiJuX//0th53DE6w0xxLEtfK3iyryQFddXuvkIk51EEgrJQ0WJkOmQ== + proxy-addr@~2.0.7: version "2.0.7" resolved "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz" @@ -9563,6 +9706,18 @@ react-scroll@^1.9.3: lodash.throttle "^4.1.1" prop-types "^15.7.2" +react-syntax-highlighter@^16.1.0: + version "16.1.0" + resolved "https://registry.yarnpkg.com/react-syntax-highlighter/-/react-syntax-highlighter-16.1.0.tgz#ebe0bb5ae7a3540859212cedafd767f0189c516c" + integrity sha512-E40/hBiP5rCNwkeBN1vRP+xow1X0pndinO+z3h7HLsHyjztbyjfzNWNKuAsJj+7DLam9iT4AaaOZnueCU+Nplg== + dependencies: + "@babel/runtime" "^7.28.4" + highlight.js "^10.4.1" + highlightjs-vue "^1.0.0" + lowlight "^1.17.0" + prismjs "^1.30.0" + refractor "^5.0.0" + react-turnstile@^1.1.4: version "1.1.4" resolved "https://registry.npmjs.org/react-turnstile/-/react-turnstile-1.1.4.tgz" @@ -9643,6 +9798,16 @@ reflect.getprototypeof@^1.0.6, reflect.getprototypeof@^1.0.9: get-proto "^1.0.1" which-builtin-type "^1.2.1" +refractor@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/refractor/-/refractor-5.0.0.tgz#85daf0448a6d947f5361796eb22c31733d61d904" + integrity sha512-QXOrHQF5jOpjjLfiNk5GFnWhRXvxjUVnlFxkeDmewR5sXkr3iM46Zo+CnRR8B+MDVqkULW4EcLVcRBNOPXHosw== + dependencies: + "@types/hast" "^3.0.0" + "@types/prismjs" "^1.0.0" + hastscript "^9.0.0" + parse-entities "^4.0.0" + regenerate-unicode-properties@^10.2.2: version "10.2.2" resolved "https://registry.npmjs.org/regenerate-unicode-properties/-/regenerate-unicode-properties-10.2.2.tgz" @@ -10295,6 +10460,11 @@ sourcemap-codec@^1.4.8: resolved "https://registry.npmjs.org/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz" integrity sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA== +space-separated-tokens@^2.0.0: + version "2.0.2" + resolved "https://registry.yarnpkg.com/space-separated-tokens/-/space-separated-tokens-2.0.2.tgz#1ecd9d2350a3844572c3f4a312bceb018348859f" + integrity sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q== + spdy-transport@^3.0.0: version "3.0.0" resolved "https://registry.npmjs.org/spdy-transport/-/spdy-transport-3.0.0.tgz"