refactor(web): replace react-syntax-highlighter with shiki (#33473)

Co-authored-by: Matt Van Horn <455140+mvanhorn@users.noreply.github.com>
Co-authored-by: Stephen Zhou <38493346+hyoban@users.noreply.github.com>
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
This commit is contained in:
Matt Van Horn
2026-04-02 23:40:26 -07:00
committed by GitHub
parent 64ddec0d67
commit a9cf8f6c5d
10 changed files with 291 additions and 201 deletions

312
pnpm-lock.yaml generated
View File

@@ -213,9 +213,6 @@ catalogs:
'@types/react-dom':
specifier: 19.2.3
version: 19.2.3
'@types/react-syntax-highlighter':
specifier: 15.5.13
version: 15.5.13
'@types/react-window':
specifier: 1.8.8
version: 1.8.8
@@ -333,6 +330,9 @@ catalogs:
happy-dom:
specifier: 20.8.9
version: 20.8.9
hast-util-to-jsx-runtime:
specifier: 2.3.6
version: 2.3.6
hono:
specifier: 4.12.10
version: 4.12.10
@@ -450,9 +450,6 @@ catalogs:
react-sortablejs:
specifier: 6.1.4
version: 6.1.4
react-syntax-highlighter:
specifier: 15.6.6
version: 15.6.6
react-textarea-autosize:
specifier: 8.5.9
version: 8.5.9
@@ -474,6 +471,9 @@ catalogs:
sharp:
specifier: 0.34.5
version: 0.34.5
shiki:
specifier: 4.0.2
version: 4.0.2
sortablejs:
specifier: 1.15.7
version: 1.15.7
@@ -812,6 +812,9 @@ importers:
foxact:
specifier: 'catalog:'
version: 0.3.0(react-dom@19.2.4(react@19.2.4))(react@19.2.4)
hast-util-to-jsx-runtime:
specifier: 'catalog:'
version: 2.3.6
html-entities:
specifier: 'catalog:'
version: 2.6.0
@@ -914,9 +917,6 @@ importers:
react-sortablejs:
specifier: 'catalog:'
version: 6.1.4(@types/sortablejs@1.15.9)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(sortablejs@1.15.7)
react-syntax-highlighter:
specifier: 'catalog:'
version: 15.6.6(react@19.2.4)
react-textarea-autosize:
specifier: 'catalog:'
version: 8.5.9(@types/react@19.2.14)(react@19.2.4)
@@ -938,6 +938,9 @@ importers:
sharp:
specifier: 'catalog:'
version: 0.34.5
shiki:
specifier: 'catalog:'
version: 4.0.2
sortablejs:
specifier: 'catalog:'
version: 1.15.7
@@ -1095,9 +1098,6 @@ importers:
'@types/react-dom':
specifier: 'catalog:'
version: 19.2.3(@types/react@19.2.14)
'@types/react-syntax-highlighter':
specifier: 'catalog:'
version: 15.5.13
'@types/react-window':
specifier: 'catalog:'
version: 1.8.8
@@ -3621,6 +3621,37 @@ packages:
peerDependencies:
react: ^16.14.0 || 17.x || 18.x || 19.x
'@shikijs/core@4.0.2':
resolution: {integrity: sha512-hxT0YF4ExEqB8G/qFdtJvpmHXBYJ2lWW7qTHDarVkIudPFE6iCIrqdgWxGn5s+ppkGXI0aEGlibI0PAyzP3zlw==}
engines: {node: '>=20'}
'@shikijs/engine-javascript@4.0.2':
resolution: {integrity: sha512-7PW0Nm49DcoUIQEXlJhNNBHyoGMjalRETTCcjMqEaMoJRLljy1Bi/EGV3/qLBgLKQejdspiiYuHGQW6dX94Nag==}
engines: {node: '>=20'}
'@shikijs/engine-oniguruma@4.0.2':
resolution: {integrity: sha512-UpCB9Y2sUKlS9z8juFSKz7ZtysmeXCgnRF0dlhXBkmQnek7lAToPte8DkxmEYGNTMii72zU/lyXiCB6StuZeJg==}
engines: {node: '>=20'}
'@shikijs/langs@4.0.2':
resolution: {integrity: sha512-KaXby5dvoeuZzN0rYQiPMjFoUrz4hgwIE+D6Du9owcHcl6/g16/yT5BQxSW5cGt2MZBz6Hl0YuRqf12omRfUUg==}
engines: {node: '>=20'}
'@shikijs/primitive@4.0.2':
resolution: {integrity: sha512-M6UMPrSa3fN5ayeJwFVl9qWofl273wtK1VG8ySDZ1mQBfhCpdd8nEx7nPZ/tk7k+TYcpqBZzj/AnwxT9lO+HJw==}
engines: {node: '>=20'}
'@shikijs/themes@4.0.2':
resolution: {integrity: sha512-mjCafwt8lJJaVSsQvNVrJumbnnj1RI8jbUKrPKgE6E3OvQKxnuRoBaYC51H4IGHePsGN/QtALglWBU7DoKDFnA==}
engines: {node: '>=20'}
'@shikijs/types@4.0.2':
resolution: {integrity: sha512-qzbeRooUTPnLE+sHD/Z8DStmaDgnbbc/pMrU203950aRqjX/6AFHeDYT+j00y2lPdz0ywJKx7o/7qnqTivtlXg==}
engines: {node: '>=20'}
'@shikijs/vscode-textmate@10.0.2':
resolution: {integrity: sha512-83yeghZ2xxin3Nj8z1NMd/NCuca+gsYXswywDy5bHvwlWL8tpTQmzGeUuHd9FC3E/SBEMvzJRwWEOz5gGes9Qg==}
'@shuding/opentype.js@1.4.0-beta.0':
resolution: {integrity: sha512-3NgmNyH3l/Hv6EvsWJbsvpcpUba6R8IREQ83nH83cyakCw7uM1arZKNfHwv1Wz6jgqrF/j4x5ELvR6PnK9nTcA==}
engines: {node: '>= 8.0.0'}
@@ -4252,9 +4283,6 @@ packages:
'@types/geojson@7946.0.16':
resolution: {integrity: sha512-6C8nqWur3j98U6+lXDfTUWIfgvZU+EumvpHKcYjujKH7woYyLj2sUmff0tRhrqM7BohUw7Pz3ZB1jj2gW9Fvmg==}
'@types/hast@2.3.10':
resolution: {integrity: sha512-McWspRw8xx8J9HurkVBfYj0xKoE25tOFlHGdx4MJ5xORQrMGZNqJhVQWaIbm6Oyla5kYOXtDiopzKRJzEOkwJw==}
'@types/hast@3.0.4':
resolution: {integrity: sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==}
@@ -4299,9 +4327,6 @@ packages:
peerDependencies:
'@types/react': ^19.2.0
'@types/react-syntax-highlighter@15.5.13':
resolution: {integrity: sha512-uLGJ87j6Sz8UaBAooU0T6lWJ0dBmjZgN1PZTrj05TNql2/XpC6+4HhMT5syIdFUUt+FASfCeLLv4kBygNU+8qA==}
'@types/react-window@1.8.8':
resolution: {integrity: sha512-8Ls660bHR1AUA2kuRvVG9D/4XpRC6wjAaPT9dil7Ckc76eP9TKWZwwmgfq8Q1LANX3QNDnoU4Zp48A3w+zK69Q==}
@@ -5058,21 +5083,12 @@ packages:
character-entities-html4@2.1.0:
resolution: {integrity: sha512-1v7fgQRj6hnSwFpq1Eu0ynr/CDEw0rXo2B61qXrLNdHZmPKgb7fqS1a2JwF0rISo9q77jDI8VMEHoApn8qDoZA==}
character-entities-legacy@1.1.4:
resolution: {integrity: sha512-3Xnr+7ZFS1uxeiUDvV02wQ+QDbc55o97tIV5zHScSPJpcLm/r0DFPcoY3tYRp+VZukxuMeKgXYmsXQHO05zQeA==}
character-entities-legacy@3.0.0:
resolution: {integrity: sha512-RpPp0asT/6ufRm//AJVwpViZbGM/MkjQFxJccQRHmISF/22NBtsHqAWmL+/pmkPWoIUJdWyeVleTl1wydHATVQ==}
character-entities@1.2.4:
resolution: {integrity: sha512-iBMyeEHxfVnIakwOuDXpVkc54HijNgCyQB2w0VfGQThle6NXn50zU6V/u+LDhxHcDUPojn6Kpga3PTAD8W1bQw==}
character-entities@2.0.2:
resolution: {integrity: sha512-shx7oQ0Awen/BRIdkjkvz54PnEEI/EjwXDSIZp86/KKdbafHh1Df/RYGBhn4hbe2+uKC9FnT5UCEdyPz3ai9hQ==}
character-reference-invalid@1.1.4:
resolution: {integrity: sha512-mKKUkUbhPpQlCOfIuZkvSEgktjPFIsZKRRbC6KWVEMvlzblj3i3asQv5ODsrwt0N3pHAEvjP8KTQPHkp0+6jOg==}
character-reference-invalid@2.0.1:
resolution: {integrity: sha512-iBZ4F4wRbyORVsu0jPV7gXkOsGYjGHPmAyv+HiHG8gi5PtC9KI2j1+v8/tlibRvjoWX027ypmG/n0HtO5t7unw==}
@@ -5172,9 +5188,6 @@ packages:
color-name@1.1.4:
resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==}
comma-separated-tokens@1.0.8:
resolution: {integrity: sha512-GHuDRO12Sypu2cV70d1dkA2EUmXHgntrzbpvOB+Qy+49ypNfGgFQIC2fhhXbnyrJRynDCAARsT7Ou0M6hirpfw==}
comma-separated-tokens@2.0.3:
resolution: {integrity: sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg==}
@@ -6060,9 +6073,6 @@ packages:
fastq@1.20.1:
resolution: {integrity: sha512-GGToxJ/w1x32s/D2EKND7kTil4n8OVk/9mycTc4VDza13lOvpUZTGX3mFSCtV9ksdGBVzvsyAVLM6mHFThxXxw==}
fault@1.0.4:
resolution: {integrity: sha512-CJ0HCB5tL5fYTEA7ToAq5+kTwd++Borf1/bifxd9iT70QcXr4MRrO3Llf8Ifs70q+SJcGHFtnIE/Nw6giCtECA==}
fault@2.0.1:
resolution: {integrity: sha512-WtySTkS4OKev5JtpHXnib4Gxiurzh5NCGvWrFaZ34m6JehfTUhKZvn9njTfw48t6JumVQOmrKqpmGcdwxnhqBQ==}
@@ -6251,9 +6261,6 @@ packages:
hast-util-is-element@3.0.0:
resolution: {integrity: sha512-Val9mnv2IWpLbNPqc/pUem+a7Ipj2aHacCwgNfTiK0vJKl0LF+4Ba4+v1oPHFpf3bLYmreq0/l3Gud9S5OH42g==}
hast-util-parse-selector@2.2.5:
resolution: {integrity: sha512-7j6mrk/qqkSehsM92wQjdIgWM2/BW61u/53G6xmC8i1OmEdKLHbk419QKQUjz6LglWsfqoiHmyMRkP1BGjecNQ==}
hast-util-parse-selector@4.0.0:
resolution: {integrity: sha512-wkQCkSYoOGCRKERFWcxMVMOcYE2K1AaNLU8DXS9arxnLOUEWbOXKXiJUNzEpqZ3JOKpnha3jkFrumEjVliDe7A==}
@@ -6266,6 +6273,9 @@ packages:
hast-util-to-estree@3.1.3:
resolution: {integrity: sha512-48+B/rJWAp0jamNbAAf9M7Uf//UVqAoMmgXhBdxTDJLGKY+LRnZ99qcG+Qjl5HfMpYNzS5v4EAwVEF34LeAj7w==}
hast-util-to-html@9.0.5:
resolution: {integrity: sha512-OguPdidb+fbHQSU4Q4ZiLKnzWo8Wwsf5bZfbvu7//a9oTYoqD/fWpe96NuHkoS9h0ccGOTe0C4NGXdtS0iObOw==}
hast-util-to-jsx-runtime@2.3.6:
resolution: {integrity: sha512-zl6s8LwNyo1P9uw+XJGvZtdFF1GdAkOg8ujOw+4Pyb76874fLps4ueHXDhXWdk6YHQ6OgUtinliG7RsYvCbbBg==}
@@ -6278,9 +6288,6 @@ packages:
hast-util-whitespace@3.0.0:
resolution: {integrity: sha512-88JUN06ipLwsnv+dVn+OIYOvAuvBMy/Qoi6O7mQHxdPXpjy+Cd6xRkWwux7DKO+4sYILtLBRIKgsdpS2gQc7qw==}
hastscript@6.0.0:
resolution: {integrity: sha512-nDM6bvd7lIqDUiYEiu5Sl/+6ReP0BMk/2f4U/Rooccxkj0P5nm+acM5PrGJ/t5I8qPGiqZSE6hVAwZEdZIvP4w==}
hastscript@9.0.1:
resolution: {integrity: sha512-g7df9rMFX/SPi34tyGCyUBREQoKkapwdY/T04Qn9TDWfHhAYt4/I0gMVirzK5wEzeUqIjEB+LXC/ypb7Aqno5w==}
@@ -6288,12 +6295,6 @@ packages:
resolution: {integrity: sha512-Ox1pJVrDCyGHMG9CFg1tmrRUMRPRsAWYc/PinY0XzJU4K7y7vjNoLKIQ7BR5UJMCxNN8EM1MNDmHWA/B3aZUuw==}
engines: {node: '>=6'}
highlight.js@10.7.3:
resolution: {integrity: sha512-tzcUFauisWKNHaRkN4Wjl/ZA07gENAjFl3J/c480dprkGTg5EQstgaNFqBfUqCq54kZRIEcreTsAgF/m2quD7A==}
highlightjs-vue@1.0.0:
resolution: {integrity: sha512-PDEfEF102G23vHmPhLyPboFCD+BkMGu+GuJe2d9/eH4FsCwvgBpnc9n0pGE+ffKdph38s6foEZiEjdgHdzp+IA==}
hono@4.12.10:
resolution: {integrity: sha512-mx/p18PLy5og9ufies2GOSUqep98Td9q4i/EF6X7yJgAiIopxqdfIO3jbqsi3jRgTgw88jMDEzVKi+V2EF+27w==}
engines: {node: '>=16.9.0'}
@@ -6413,15 +6414,9 @@ packages:
resolution: {integrity: sha512-7m1vEcPCxXYI8HqnL8CKI6siDyD+eIWSwgB3DZA+ZTogxk9I4CDnj4wilt9x/+/QbHI4YG5YZNmC6458/e9Ktg==}
deprecated: The Intersection Observer polyfill is no longer needed and can safely be removed. Intersection Observer has been Baseline since 2019.
is-alphabetical@1.0.4:
resolution: {integrity: sha512-DwzsA04LQ10FHTZuL0/grVDk4rFoVH1pjAToYwBrHSxcrBIGQuXrQMtD5U1b0U2XVgKZCTLLP8u2Qxqhy3l2Vg==}
is-alphabetical@2.0.1:
resolution: {integrity: sha512-FWyyY60MeTNyeSRpkM2Iry0G9hpr7/9kD40mD/cGQEuilcZYS4okz8SN2Q6rLCJ8gbCt6fN+rC+6tMGS99LaxQ==}
is-alphanumerical@1.0.4:
resolution: {integrity: sha512-UzoZUr+XfVz3t3v4KyGEniVL9BDRoQtY7tOyrRybkVNjDFWyo1yhXNGrrBTQxp3ib9BLAWs7k2YKBQsFRkZG9A==}
is-alphanumerical@2.0.1:
resolution: {integrity: sha512-hmbYhX/9MUMF5uh7tOXyK/n0ZvWpad5caBA17GsC6vyuCqaWliRG5K1qS9inmUhEMaOBIW7/whAnSwveW/LtZw==}
@@ -6429,9 +6424,6 @@ packages:
resolution: {integrity: sha512-f4RqJKBUe5rQkJ2eJEJBXSticB3hGbN9j0yxxMQFqIW89Jp9WYFtzfTcRlstDKVUTRzSOTLKRfO9vIztenwtxA==}
engines: {node: '>=18.20'}
is-decimal@1.0.4:
resolution: {integrity: sha512-RGdriMmQQvZ2aqaQq3awNA6dCGtKpiDFcOzrTWrDAT2MiWrKQVPmxLGHl7Y2nNu6led0kEyoX0enY0qXYsv9zw==}
is-decimal@2.0.1:
resolution: {integrity: sha512-AAB9hiomQs5DXWcRB1rqsxGUstbRroFOPPVAomNk/3XHR5JyEZChOyTWe2oayKnsSsr/kcGqF+z6yuH6HHpN0A==}
@@ -6448,9 +6440,6 @@ packages:
resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==}
engines: {node: '>=0.10.0'}
is-hexadecimal@1.0.4:
resolution: {integrity: sha512-gyPJuv83bHMpocVYoqof5VDiZveEoGoFL8m3BXNb2VW8Xs+rz9kqO8LOQ5DH6EsuvilT1ApazU0pyl+ytbPtlw==}
is-hexadecimal@2.0.1:
resolution: {integrity: sha512-DgZQp241c8oO6cA1SbTEWiXeoxV42vlcJxgH+B3hi1AiqqKruZR3ZGF8In3fj4+/y/7rHvlOZLZtgJ/4ttYGZg==}
@@ -6781,9 +6770,6 @@ packages:
lower-case@2.0.2:
resolution: {integrity: sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg==}
lowlight@1.20.0:
resolution: {integrity: sha512-8Ktj+prEb1RoCPkEOrPMYUN/nCggB7qAWe3a7OpMjWQkh3l2RD5wKRQ+o8Q8YuI9RG/xs95waaI/E6ym/7NsTw==}
lru-cache@11.2.7:
resolution: {integrity: sha512-aY/R+aEsRelme17KGQa/1ZSIpLpNYYrhcrepKTZgE+W3WM16YMCaPwOHLHsmopZHELU0Ojin1lPVxKR0MihncA==}
engines: {node: 20 || >=22}
@@ -7234,6 +7220,12 @@ packages:
resolution: {integrity: sha512-VXJjc87FScF88uafS3JllDgvAm+c/Slfz06lorj2uAY34rlUu0Nt+v8wreiImcrgAjjIHp1rXpTDlLOGw29WwQ==}
engines: {node: '>=18'}
oniguruma-parser@0.12.1:
resolution: {integrity: sha512-8Unqkvk1RYc6yq2WBYRj4hdnsAxVze8i7iPfQr8e4uSP3tRv0rpZcbGUDvxfQQcdwHt/e9PrMvGCsa8OqG9X3w==}
oniguruma-to-es@4.3.5:
resolution: {integrity: sha512-Zjygswjpsewa0NLTsiizVuMQZbp0MDyM6lIt66OxsF21npUDlzpHi1Mgb/qhQdkb+dWFTzJmFbEWdvZgRho8eQ==}
open@10.2.0:
resolution: {integrity: sha512-YgBpdJHPyQ2UE5x+hlSXcnejzAvD0b22U2OuAP+8OnlJT+PjWPxtgmGqKKc+RgTM63U9gN0YzrYc71R2WT/hTA==}
engines: {node: '>=18'}
@@ -7307,9 +7299,6 @@ packages:
parse-css-color@0.2.1:
resolution: {integrity: sha512-bwS/GGIFV3b6KS4uwpzCFj4w297Yl3uqnSgIPsoQkx7GMLROXfMnWvxfNkL0oh8HVhZA4hvJoEoEIqonfJ3BWg==}
parse-entities@2.0.0:
resolution: {integrity: sha512-kkywGpCcRYhqQIchaWqZ875wzpS/bMKhz5HnN3p7wveJTkTtyAB/AlnS0f8DFSqYW1T82t6yEAkEcB+A1I3MbQ==}
parse-entities@4.0.2:
resolution: {integrity: sha512-GG2AQYWoLgL877gQIKeRPGO1xF9+eG1ujIb5soS5gPvLQ1y2o8FL90w2QWNdf9I361Mpp7726c+lj3U0qK1uGw==}
@@ -7476,10 +7465,6 @@ packages:
resolution: {integrity: sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==}
engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0}
prismjs@1.30.0:
resolution: {integrity: sha512-DEvV2ZF2r2/63V+tK8hQvrR2ZGn10srHbXviTlcv7Kpzw8jWiNTqbVgjO3IY8RxrrOUF8VPMQQFysYYYv0YZxw==}
engines: {node: '>=6'}
progress@2.0.3:
resolution: {integrity: sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==}
engines: {node: '>=0.4.0'}
@@ -7490,9 +7475,6 @@ packages:
property-expr@2.0.6:
resolution: {integrity: sha512-SVtmxhRE/CGkn3eZY1T6pC8Nln6Fr/lu1mKSgRud0eC73whjGfoAogbn78LkD8aFL0zz3bAFerKSnOl7NlErBA==}
property-information@5.6.0:
resolution: {integrity: sha512-YUHSPk+A30YPv+0Qf8i9Mbfe/C0hdPXk1s1jPVToV8pk8BQtpw10ct89Eo7OWkutrwqvT0eicAxlOg3dOAu8JA==}
property-information@7.1.0:
resolution: {integrity: sha512-TwEZ+X+yCJmYfL7TPUOcvBZ4QfoT5YenQiJuX//0th53DE6w0xxLEtfK3iyryQFddXuvkIk51EEgrJQ0WJkOmQ==}
@@ -7670,11 +7652,6 @@ packages:
'@types/react':
optional: true
react-syntax-highlighter@15.6.6:
resolution: {integrity: sha512-DgXrc+AZF47+HvAPEmn7Ua/1p10jNoVZVI/LoPiYdtY+OM+/nG5yefLHKJwdKqY1adMuHFbeyBaG9j64ML7vTw==}
peerDependencies:
react: '>= 0.14.0'
react-textarea-autosize@8.5.9:
resolution: {integrity: sha512-U1DGlIQN5AwgjTyOEnI1oCcMuEr1pv1qOtklB2l4nyMGbHzWrI0eFsYK0zos2YWqAolJyG0IWJaqWmWj5ETh0A==}
engines: {node: '>=10'}
@@ -7743,8 +7720,14 @@ packages:
reflect-metadata@0.2.2:
resolution: {integrity: sha512-urBwgfrvVP/eAyXx4hluJivBKzuEbSQs9rKWCrCkbSxNv8mxPcUZKeuoF3Uy4mJl3Lwprp6yy5/39VWigZ4K6Q==}
refractor@3.6.0:
resolution: {integrity: sha512-MY9W41IOWxxk31o+YvFCNyNzdkc9M20NoZK5vq6jkv4I/uh2zkWcfudj0Q1fovjUQJrNewS9NMzeTtqPf+n5EA==}
regex-recursion@6.0.2:
resolution: {integrity: sha512-0YCaSCq2VRIebiaUviZNs0cBz1kg5kVS2UKUfNIx8YVs1cN3AV7NTctO5FOKBA+UT2BPJIWZauYHPqJODG50cg==}
regex-utilities@2.3.0:
resolution: {integrity: sha512-8VhliFJAWRaUiVvREIiW2NXXTmHs4vMNnSzuJVhscgmGav3g9VDxLrQndI3dZZVVdp0ZO/5v0xmX516/7M9cng==}
regex@6.1.0:
resolution: {integrity: sha512-6VwtthbV4o/7+OaAF9I5L5V3llLEsoPyq9P1JVXkedTP33c7MfCG0/5NOPcSJn0TzXcG9YUrR0gQSWioew3LDg==}
regexp-ast-analysis@0.7.1:
resolution: {integrity: sha512-sZuz1dYW/ZsfG17WSAG7eS85r5a0dDsvg+7BiiYR5o6lKCAtUrEwdmRmaGF6rwVj3LcmAeYkOWKEPlbPzN3Y3A==}
@@ -7941,6 +7924,10 @@ packages:
resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==}
engines: {node: '>=8'}
shiki@4.0.2:
resolution: {integrity: sha512-eAVKTMedR5ckPo4xne/PjYQYrU3qx78gtJZ+sHlXEg5IHhhoQhMfZVzetTYuaJS0L2Ef3AcCRzCHV8T0WI6nIQ==}
engines: {node: '>=20'}
signal-exit@4.1.0:
resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==}
engines: {node: '>=14'}
@@ -7986,9 +7973,6 @@ packages:
resolution: {integrity: sha512-i5uvt8C3ikiWeNZSVZNWcfZPItFQOsYTUAOkcUPGd8DqDy1uOUikjt5dG+uRlwyvR108Fb9DOd4GvXfT0N2/uQ==}
engines: {node: '>= 12'}
space-separated-tokens@1.1.5:
resolution: {integrity: sha512-q/JSVd1Lptzhf5bkYm4ob4iWPjx0KiRe3sRFBNrVqbJkFaBm5vbbowy1mymoPNLRa52+oadOhJ+K49wsSeSjTA==}
space-separated-tokens@2.0.2:
resolution: {integrity: sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q==}
@@ -8772,10 +8756,6 @@ packages:
resolution: {integrity: sha512-yMqGBqtXyeN1e3TGYvgNgDVZ3j84W4cwkOXQswghol6APgZWaff9lnbvN7MHYJOiXsvGPXtjTYJEiC9J2wv9Eg==}
engines: {node: '>=8.0'}
xtend@4.0.2:
resolution: {integrity: sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==}
engines: {node: '>=0.4'}
yallist@3.1.1:
resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==}
@@ -11160,6 +11140,46 @@ snapshots:
'@sentry/core': 10.47.0
react: 19.2.4
'@shikijs/core@4.0.2':
dependencies:
'@shikijs/primitive': 4.0.2
'@shikijs/types': 4.0.2
'@shikijs/vscode-textmate': 10.0.2
'@types/hast': 3.0.4
hast-util-to-html: 9.0.5
'@shikijs/engine-javascript@4.0.2':
dependencies:
'@shikijs/types': 4.0.2
'@shikijs/vscode-textmate': 10.0.2
oniguruma-to-es: 4.3.5
'@shikijs/engine-oniguruma@4.0.2':
dependencies:
'@shikijs/types': 4.0.2
'@shikijs/vscode-textmate': 10.0.2
'@shikijs/langs@4.0.2':
dependencies:
'@shikijs/types': 4.0.2
'@shikijs/primitive@4.0.2':
dependencies:
'@shikijs/types': 4.0.2
'@shikijs/vscode-textmate': 10.0.2
'@types/hast': 3.0.4
'@shikijs/themes@4.0.2':
dependencies:
'@shikijs/types': 4.0.2
'@shikijs/types@4.0.2':
dependencies:
'@shikijs/vscode-textmate': 10.0.2
'@types/hast': 3.0.4
'@shikijs/vscode-textmate@10.0.2': {}
'@shuding/opentype.js@1.4.0-beta.0':
dependencies:
fflate: 0.7.4
@@ -11853,10 +11873,6 @@ snapshots:
'@types/geojson@7946.0.16': {}
'@types/hast@2.3.10':
dependencies:
'@types/unist': 2.0.11
'@types/hast@3.0.4':
dependencies:
'@types/unist': 3.0.3
@@ -11895,10 +11911,6 @@ snapshots:
dependencies:
'@types/react': 19.2.14
'@types/react-syntax-highlighter@15.5.13':
dependencies:
'@types/react': 19.2.14
'@types/react-window@1.8.8':
dependencies:
'@types/react': 19.2.14
@@ -12761,16 +12773,10 @@ snapshots:
character-entities-html4@2.1.0: {}
character-entities-legacy@1.1.4: {}
character-entities-legacy@3.0.0: {}
character-entities@1.2.4: {}
character-entities@2.0.2: {}
character-reference-invalid@1.1.4: {}
character-reference-invalid@2.0.1: {}
check-error@2.1.3: {}
@@ -12884,8 +12890,6 @@ snapshots:
color-name@1.1.4: {}
comma-separated-tokens@1.0.8: {}
comma-separated-tokens@2.0.3: {}
commander@14.0.0: {}
@@ -13991,10 +13995,6 @@ snapshots:
dependencies:
reusify: 1.1.0
fault@1.0.4:
dependencies:
format: 0.2.2
fault@2.0.1:
dependencies:
format: 0.2.2
@@ -14177,8 +14177,6 @@ snapshots:
dependencies:
'@types/hast': 3.0.4
hast-util-parse-selector@2.2.5: {}
hast-util-parse-selector@4.0.0:
dependencies:
'@types/hast': 3.0.4
@@ -14226,6 +14224,20 @@ snapshots:
transitivePeerDependencies:
- supports-color
hast-util-to-html@9.0.5:
dependencies:
'@types/hast': 3.0.4
'@types/unist': 3.0.3
ccount: 2.0.1
comma-separated-tokens: 2.0.3
hast-util-whitespace: 3.0.0
html-void-elements: 3.0.0
mdast-util-to-hast: 13.2.1
property-information: 7.1.0
space-separated-tokens: 2.0.2
stringify-entities: 4.0.4
zwitch: 2.0.4
hast-util-to-jsx-runtime@2.3.6:
dependencies:
'@types/estree': 1.0.8
@@ -14267,14 +14279,6 @@ snapshots:
dependencies:
'@types/hast': 3.0.4
hastscript@6.0.0:
dependencies:
'@types/hast': 2.3.10
comma-separated-tokens: 1.0.8
hast-util-parse-selector: 2.2.5
property-information: 5.6.0
space-separated-tokens: 1.1.5
hastscript@9.0.1:
dependencies:
'@types/hast': 3.0.4
@@ -14285,10 +14289,6 @@ snapshots:
hex-rgb@4.3.0: {}
highlight.js@10.7.3: {}
highlightjs-vue@1.0.0: {}
hono@4.12.10: {}
hosted-git-info@9.0.2:
@@ -14385,15 +14385,8 @@ snapshots:
intersection-observer@0.12.2: {}
is-alphabetical@1.0.4: {}
is-alphabetical@2.0.1: {}
is-alphanumerical@1.0.4:
dependencies:
is-alphabetical: 1.0.4
is-decimal: 1.0.4
is-alphanumerical@2.0.1:
dependencies:
is-alphabetical: 2.0.1
@@ -14403,8 +14396,6 @@ snapshots:
dependencies:
builtin-modules: 5.0.0
is-decimal@1.0.4: {}
is-decimal@2.0.1: {}
is-docker@3.0.0: {}
@@ -14415,8 +14406,6 @@ snapshots:
dependencies:
is-extglob: 2.1.1
is-hexadecimal@1.0.4: {}
is-hexadecimal@2.0.1: {}
is-in-ssh@1.0.0: {}
@@ -14692,11 +14681,6 @@ snapshots:
dependencies:
tslib: 2.8.1
lowlight@1.20.0:
dependencies:
fault: 1.0.4
highlight.js: 10.7.3
lru-cache@11.2.7: {}
lru-cache@5.1.1:
@@ -15440,6 +15424,14 @@ snapshots:
dependencies:
mimic-function: 5.0.1
oniguruma-parser@0.12.1: {}
oniguruma-to-es@4.3.5:
dependencies:
oniguruma-parser: 0.12.1
regex: 6.1.0
regex-recursion: 6.0.2
open@10.2.0:
dependencies:
default-browser: 5.5.0
@@ -15608,15 +15600,6 @@ snapshots:
color-name: 1.1.4
hex-rgb: 4.3.0
parse-entities@2.0.0:
dependencies:
character-entities: 1.2.4
character-entities-legacy: 1.1.4
character-reference-invalid: 1.1.4
is-alphanumerical: 1.0.4
is-decimal: 1.0.4
is-hexadecimal: 1.0.4
parse-entities@4.0.2:
dependencies:
'@types/unist': 2.0.11
@@ -15799,8 +15782,6 @@ snapshots:
ansi-styles: 5.2.0
react-is: 17.0.2
prismjs@1.30.0: {}
progress@2.0.3: {}
prop-types@15.8.1:
@@ -15811,10 +15792,6 @@ snapshots:
property-expr@2.0.6: {}
property-information@5.6.0:
dependencies:
xtend: 4.0.2
property-information@7.1.0: {}
pump@3.0.4:
@@ -15993,16 +15970,6 @@ snapshots:
optionalDependencies:
'@types/react': 19.2.14
react-syntax-highlighter@15.6.6(react@19.2.4):
dependencies:
'@babel/runtime': 7.29.2
highlight.js: 10.7.3
highlightjs-vue: 1.0.0
lowlight: 1.20.0
prismjs: 1.30.0
react: 19.2.4
refractor: 3.6.0
react-textarea-autosize@8.5.9(@types/react@19.2.14)(react@19.2.4):
dependencies:
'@babel/runtime': 7.29.2
@@ -16107,11 +16074,15 @@ snapshots:
reflect-metadata@0.2.2: {}
refractor@3.6.0:
regex-recursion@6.0.2:
dependencies:
hastscript: 6.0.0
parse-entities: 2.0.0
prismjs: 1.30.0
regex-utilities: 2.3.0
regex-utilities@2.3.0: {}
regex@6.1.0:
dependencies:
regex-utilities: 2.3.0
regexp-ast-analysis@0.7.1:
dependencies:
@@ -16427,6 +16398,17 @@ snapshots:
shebang-regex@3.0.0: {}
shiki@4.0.2:
dependencies:
'@shikijs/core': 4.0.2
'@shikijs/engine-javascript': 4.0.2
'@shikijs/engine-oniguruma': 4.0.2
'@shikijs/langs': 4.0.2
'@shikijs/themes': 4.0.2
'@shikijs/types': 4.0.2
'@shikijs/vscode-textmate': 10.0.2
'@types/hast': 3.0.4
signal-exit@4.1.0: {}
simple-concat@1.0.1:
@@ -16470,8 +16452,6 @@ snapshots:
source-map@0.7.6: {}
space-separated-tokens@1.1.5: {}
space-separated-tokens@2.0.2: {}
spdx-correct@3.2.0:
@@ -17341,8 +17321,6 @@ snapshots:
xmlbuilder@15.1.1: {}
xtend@4.0.2: {}
yallist@3.1.1: {}
yallist@5.0.0: {}

View File

@@ -147,7 +147,6 @@ catalog:
"@types/qs": 6.15.0
"@types/react": 19.2.14
"@types/react-dom": 19.2.3
"@types/react-syntax-highlighter": 15.5.13
"@types/react-window": 1.8.8
"@types/sortablejs": 1.15.9
"@typescript-eslint/eslint-plugin": 8.58.0
@@ -188,6 +187,7 @@ catalog:
fast-deep-equal: 3.1.3
foxact: 0.3.0
happy-dom: 20.8.9
hast-util-to-jsx-runtime: 2.3.6
hono: 4.12.10
html-entities: 2.6.0
html-to-image: 1.11.13
@@ -228,7 +228,6 @@ catalog:
react-pdf-highlighter: 8.0.0-rc.0
react-server-dom-webpack: 19.2.4
react-sortablejs: 6.1.4
react-syntax-highlighter: 15.6.6
react-textarea-autosize: 8.5.9
react-window: 1.8.11
reactflow: 11.11.4
@@ -236,6 +235,7 @@ catalog:
remark-directive: 4.0.0
scheduler: 0.27.0
sharp: 0.34.5
shiki: 4.0.2
sortablejs: 1.15.7
std-semver: 1.0.8
storybook: 10.3.4

View File

@@ -3,7 +3,6 @@ import { defineConfig } from 'taze'
export default defineConfig({
exclude: [
// We are going to replace these
'react-syntax-highlighter',
'react-window',
'@types/react-window',
],

View File

@@ -5,6 +5,10 @@ import { Theme } from '@/types/app'
import CodeBlock from '../code-block'
const { mockHighlightCode } = vi.hoisted(() => ({
mockHighlightCode: vi.fn(),
}))
type UseThemeReturn = {
theme: Theme
}
@@ -70,6 +74,10 @@ vi.mock('@/hooks/use-theme', () => ({
default: () => mockUseTheme(),
}))
vi.mock('../shiki-highlight', () => ({
highlightCode: mockHighlightCode,
}))
vi.mock('echarts', () => ({
getInstanceByDom: mockEcharts.getInstanceByDom,
}))
@@ -130,6 +138,11 @@ describe('CodeBlock', () => {
beforeEach(() => {
vi.clearAllMocks()
mockUseTheme.mockReturnValue({ theme: Theme.light })
mockHighlightCode.mockImplementation(async ({ code, language }) => (
<pre className="shiki">
<code className={`language-${language}`}>{code}</code>
</pre>
))
consoleErrorSpy = vi.spyOn(console, 'error').mockImplementation(() => {})
consoleWarnSpy = vi.spyOn(console, 'warn').mockImplementation(() => {})
clientWidthSpy = vi.spyOn(HTMLElement.prototype, 'clientWidth', 'get').mockReturnValue(900)
@@ -198,12 +211,14 @@ describe('CodeBlock', () => {
expect(container.querySelector('code')?.textContent).toBe('plain text')
})
it('should render syntax-highlighted output when language is standard', () => {
it('should render syntax-highlighted output when language is standard', async () => {
render(<CodeBlock className="language-javascript">const x = 1;</CodeBlock>)
expect(screen.getByText('JavaScript')).toBeInTheDocument()
await waitFor(() => {
expect(document.querySelector('code.language-javascript')?.textContent).toContain('const x = 1;')
})
})
it('should format unknown language labels with capitalized fallback when language is not in map', () => {
render(<CodeBlock className="language-ruby">puts "ok"</CodeBlock>)
@@ -242,16 +257,29 @@ describe('CodeBlock', () => {
expect(screen.queryByText(/Error rendering SVG/i)).not.toBeInTheDocument()
})
it('should render syntax-highlighted output when language is standard and app theme is dark', () => {
it('should render syntax-highlighted output when language is standard and app theme is dark', async () => {
mockUseTheme.mockReturnValue({ theme: Theme.dark })
render(<CodeBlock className="language-javascript">const y = 2;</CodeBlock>)
expect(screen.getByText('JavaScript')).toBeInTheDocument()
await waitFor(() => {
expect(document.querySelector('code.language-javascript')?.textContent).toContain('const y = 2;')
})
})
it('should fall back to plain code block when shiki highlighting fails', async () => {
mockHighlightCode.mockRejectedValueOnce(new Error('highlight failed'))
render(<CodeBlock className="language-javascript">const z = 3;</CodeBlock>)
await waitFor(() => {
expect(screen.getByText('const z = 3;')).toBeInTheDocument()
})
expect(document.querySelector('code.language-javascript')).toBeNull()
})
})
// ECharts behaviors for loading, parsing, and chart lifecycle updates.
describe('ECharts', () => {
it('should show loading indicator when echarts content is empty', () => {

View File

@@ -1,10 +1,7 @@
import type { JSX } from 'react'
import type { BundledLanguage, BundledTheme } from 'shiki/bundle/web'
import ReactEcharts from 'echarts-for-react'
import { memo, useCallback, useEffect, useMemo, useRef, useState } from 'react'
import SyntaxHighlighter from 'react-syntax-highlighter'
import {
atelierHeathDark,
atelierHeathLight,
} from 'react-syntax-highlighter/dist/esm/styles/hljs'
import { memo, useCallback, useEffect, useLayoutEffect, useMemo, useRef, useState } from 'react'
import ActionButton from '@/app/components/base/action-button'
import CopyIcon from '@/app/components/base/copy-icon'
import MarkdownMusic from '@/app/components/base/markdown-blocks/music'
@@ -14,6 +11,7 @@ import useTheme from '@/hooks/use-theme'
import dynamic from '@/next/dynamic'
import { Theme } from '@/types/app'
import SVGRenderer from '../svg-gallery' // Assumes svg-gallery.tsx is in /base directory
import { highlightCode } from './shiki-highlight'
const Flowchart = dynamic(() => import('@/app/components/base/mermaid'), { ssr: false })
@@ -64,6 +62,61 @@ const getCorrectCapitalizationLanguageName = (language: string) => {
// visit https://reactjs.org/docs/error-decoder.html?invariant=185 for the full message
// or use the non-minified dev environment for full errors and additional helpful warnings.
const ShikiCodeBlock = memo(({ code, language, theme, initial }: { code: string, language: string, theme: BundledTheme, initial?: JSX.Element }) => {
const [nodes, setNodes] = useState(initial)
useLayoutEffect(() => {
let cancelled = false
void highlightCode({
code,
language: language as BundledLanguage,
theme,
}).then((result) => {
if (!cancelled)
setNodes(result)
}).catch((error) => {
console.error('Shiki highlighting failed:', error)
if (!cancelled)
setNodes(undefined)
})
return () => {
cancelled = true
}
}, [code, language, theme])
if (!nodes) {
return (
<pre style={{
paddingLeft: 12,
borderBottomLeftRadius: '10px',
borderBottomRightRadius: '10px',
backgroundColor: 'var(--color-components-input-bg-normal)',
margin: 0,
overflow: 'auto',
}}
>
<code>{code}</code>
</pre>
)
}
return (
<div
style={{
borderBottomLeftRadius: '10px',
borderBottomRightRadius: '10px',
overflow: 'auto',
}}
className="shiki-line-numbers [&_pre]:m-0! [&_pre]:rounded-t-none! [&_pre]:rounded-b-[10px]! [&_pre]:bg-components-input-bg-normal! [&_pre]:py-2!"
>
{nodes}
</div>
)
})
ShikiCodeBlock.displayName = 'ShikiCodeBlock'
// Define ECharts event parameter types
type EChartsEventParams = {
type: string
@@ -416,20 +469,11 @@ const CodeBlock: any = memo(({ inline, className, children = '', ...props }: any
)
default:
return (
<SyntaxHighlighter
{...props}
style={theme === Theme.light ? atelierHeathLight : atelierHeathDark}
customStyle={{
paddingLeft: 12,
borderBottomLeftRadius: '10px',
borderBottomRightRadius: '10px',
backgroundColor: 'var(--color-components-input-bg-normal)',
}}
language={match?.[1]}
showLineNumbers
>
{content}
</SyntaxHighlighter>
<ShikiCodeBlock
code={content}
language={match?.[1] || 'text'}
theme={isDarkMode ? 'github-dark' : 'github-light'}
/>
)
}
}, [children, language, isSVG, finalChartOption, props, theme, match, chartState, isDarkMode, echartsStyle, echartsOpts, handleChartReady, echartsEvents])
@@ -440,7 +484,7 @@ const CodeBlock: any = memo(({ inline, className, children = '', ...props }: any
return (
<div className="relative">
<div className="flex h-8 items-center justify-between rounded-t-[10px] border-b border-divider-subtle bg-components-input-bg-normal p-1 pl-3">
<div className="text-text-secondary system-xs-semibold-uppercase">{languageShowName}</div>
<div className="system-xs-semibold-uppercase text-text-secondary">{languageShowName}</div>
<div className="flex items-center gap-1">
{language === 'svg' && <SVGBtn isSVG={isSVG} setIsSVG={setIsSVG} />}
<ActionButton>

View File

@@ -0,0 +1,29 @@
import type { JSX } from 'react'
import type { BundledLanguage, BundledTheme } from 'shiki/bundle/web'
import { toJsxRuntime } from 'hast-util-to-jsx-runtime'
import { Fragment } from 'react'
import { jsx, jsxs } from 'react/jsx-runtime'
import { codeToHast } from 'shiki/bundle/web'
type HighlightCodeOptions = {
code: string
language: BundledLanguage
theme: BundledTheme
}
export const highlightCode = async ({
code,
language,
theme,
}: HighlightCodeOptions): Promise<JSX.Element> => {
const hast = await codeToHast(code, {
lang: language,
theme,
})
return toJsxRuntime(hast, {
Fragment,
jsx,
jsxs,
}) as JSX.Element
}

View File

@@ -61,7 +61,7 @@ vi.mock('@/app/components/datasets/common/image-list', () => ({
),
}))
// Markdown uses next/dynamic and react-syntax-highlighter (ESM)
// Markdown uses next/dynamic and shiki (ESM)
vi.mock('@/app/components/base/markdown', () => ({
Markdown: ({ content, className }: { content: string, className?: string }) => (
<div data-testid="markdown" className={`markdown-body ${className || ''}`}>{content}</div>

View File

@@ -909,4 +909,19 @@
[data-theme='light'] [data-hide-on-theme='light'] {
display: none;
}
/* Shiki code block line numbers */
.shiki-line-numbers code {
counter-reset: line;
}
.shiki-line-numbers .line::before {
counter-increment: line;
content: counter(line);
display: inline-block;
width: 1rem;
margin-right: 0.75rem;
text-align: right;
color: var(--color-text-quaternary);
user-select: none;
}
}

View File

@@ -3142,9 +3142,6 @@
"react/set-state-in-effect": {
"count": 7
},
"tailwindcss/enforce-consistent-class-order": {
"count": 1
},
"ts/no-explicit-any": {
"count": 9
}

View File

@@ -100,6 +100,7 @@
"es-toolkit": "catalog:",
"fast-deep-equal": "catalog:",
"foxact": "catalog:",
"hast-util-to-jsx-runtime": "catalog:",
"html-entities": "catalog:",
"html-to-image": "catalog:",
"i18next": "catalog:",
@@ -134,7 +135,6 @@
"react-papaparse": "catalog:",
"react-pdf-highlighter": "catalog:",
"react-sortablejs": "catalog:",
"react-syntax-highlighter": "catalog:",
"react-textarea-autosize": "catalog:",
"react-window": "catalog:",
"reactflow": "catalog:",
@@ -142,6 +142,7 @@
"remark-directive": "catalog:",
"scheduler": "catalog:",
"sharp": "catalog:",
"shiki": "catalog:",
"sortablejs": "catalog:",
"std-semver": "catalog:",
"streamdown": "catalog:",
@@ -196,7 +197,6 @@
"@types/qs": "catalog:",
"@types/react": "catalog:",
"@types/react-dom": "catalog:",
"@types/react-syntax-highlighter": "catalog:",
"@types/react-window": "catalog:",
"@types/sortablejs": "catalog:",
"@typescript-eslint/parser": "catalog:",