@@ -16,6 +16,9 @@ const { publicRuntimeConfig } = getConfig();
1616
1717import styles from "./autofunction.module.css" ;
1818import { getThemedUrl , getThemeFromDOM } from "../../lib/next/ThemeContext" ;
19+ import languageDisplayNames , {
20+ getPrismLanguage ,
21+ } from "../../lib/languageDisplayNames" ;
1922
2023const LATEST_VERSION = publicRuntimeConfig . LATEST_VERSION ;
2124const DEFAULT_VERSION = publicRuntimeConfig . DEFAULT_VERSION ;
@@ -66,7 +69,7 @@ const Autofunction = ({
6669 } ) ;
6770 } ;
6871
69- const highlightWithPrism = ( ) => {
72+ const highlightWithPrism = async ( ) => {
7073 if ( isHighlighted ) {
7174 return ;
7275 }
@@ -78,28 +81,79 @@ const Autofunction = ({
7881 blockRef . current . getElementsByTagName ( "pre" ) ,
7982 ) ;
8083
81- // Important: keep this in sync with components/block/code.js
84+ // Collect unique languages and transform pre elements
85+ const languagesNeeded = new Set ( ) ;
86+ const ignoredClasses = new Set ( [
87+ "code" ,
88+ "literal-block" ,
89+ "last" ,
90+ "doctest-block" ,
91+ ] ) ;
92+
93+ // languageDisplayNames imported from ../../lib/languageDisplayNames
8294 pres . forEach ( ( ele ) => {
83- // Detect language based on pre element class
84- const isLiteralBlock = ele . classList . contains ( "literal-block" ) ;
85- const language = isLiteralBlock ? "bash" : "python" ;
86- const displayLanguage = isLiteralBlock ? "BASH" : "PYTHON" ;
95+ // Extract language from class list (e.g., "code toml literal-block" -> "toml")
96+ let language = "python" ; // default
97+
98+ for ( const cls of ele . classList ) {
99+ if ( ! ignoredClasses . has ( cls ) && cls in languageDisplayNames ) {
100+ language = cls ;
101+ break ;
102+ }
103+ }
104+
105+ // Map to Prism component name
106+ const prismLanguage = getPrismLanguage ( language ) ;
107+ languagesNeeded . add ( prismLanguage ) ;
108+
109+ // Check for filename from CSS class (set by stcode.py directive)
110+ // Filename is base64-encoded in a class like "stfilename-LnN0cmVhbWxpdC9zZWNyZXRzLnRvbWw"
111+ let filename = null ;
112+ for ( const cls of ele . classList ) {
113+ if ( cls . startsWith ( "stfilename-" ) ) {
114+ const encoded = cls . substring ( 11 ) ; // Remove "stfilename-" prefix
115+ // Add padding back for base64 decode
116+ const padded = encoded + "=" . repeat ( ( 4 - ( encoded . length % 4 ) ) % 4 ) ;
117+ try {
118+ // URL-safe base64 decode
119+ filename = atob ( padded . replace ( / - / g, "+" ) . replace ( / _ / g, "/" ) ) ;
120+ } catch ( e ) {
121+ console . error ( "Failed to decode filename:" , e ) ;
122+ }
123+ break ;
124+ }
125+ }
126+ const displayLanguage =
127+ languageDisplayNames [ language ] || language . toUpperCase ( ) ;
128+
129+ // Show language only if no filename (matching code.js behavior)
130+ const showLanguage = ! filename ;
87131
88132 const codeText = ele . innerHTML ;
89133 const preTag = ele . cloneNode ( true ) ;
90134 const codeWrap = document . createElement ( "div" ) ;
91135 codeWrap . setAttribute ( "class" , styles . CodeBlockContainer ) ;
92136
93- // Create language header
137+ // Create header with language and/or filename
94138 const header = document . createElement ( "div" ) ;
95139 header . setAttribute ( "class" , `${ styles . Header } code-block-header` ) ;
96- const langSpan = document . createElement ( "span" ) ;
97- langSpan . setAttribute ( "class" , styles . Language ) ;
98- langSpan . textContent = displayLanguage ;
99- header . appendChild ( langSpan ) ;
140+
141+ if ( showLanguage ) {
142+ const langSpan = document . createElement ( "span" ) ;
143+ langSpan . setAttribute ( "class" , styles . Language ) ;
144+ langSpan . textContent = displayLanguage ;
145+ header . appendChild ( langSpan ) ;
146+ }
147+
148+ if ( filename ) {
149+ const filenameSpan = document . createElement ( "span" ) ;
150+ filenameSpan . setAttribute ( "class" , styles . Filename ) ;
151+ filenameSpan . textContent = filename ;
152+ header . appendChild ( filenameSpan ) ;
153+ }
100154
101155 const codeTag = document . createElement ( "code" ) ;
102- codeTag . setAttribute ( "class" , `language-${ language } ` ) ;
156+ codeTag . setAttribute ( "class" , `language-${ prismLanguage } ` ) ;
103157 preTag . classList . add ( "line-numbers" ) ;
104158 codeTag . innerHTML = codeText ;
105159 preTag . textContent = null ;
@@ -110,6 +164,20 @@ const Autofunction = ({
110164 ele . replaceWith ( codeWrap ) ;
111165 } ) ;
112166
167+ // Dynamically import all needed Prism language modules
168+ for ( const lang of languagesNeeded ) {
169+ try {
170+ await import ( `prismjs/components/prism-${ lang } ` ) ;
171+ } catch ( error ) {
172+ console . error ( `Prism doesn't support this language: ${ lang } ` ) ;
173+ }
174+ }
175+
176+ // Guard against component unmounting during async imports
177+ if ( ! blockRef . current ) {
178+ return ;
179+ }
180+
113181 Prism . highlightAllUnder ( blockRef . current ) ;
114182
115183 setIsHighlighted ( true ) ;
0 commit comments