@@ -8,6 +8,8 @@ import settings from "lib/settings";
88import markdownIt from "markdown-it" ;
99import styles from "./assistant.module.scss" ;
1010
11+ let aiTabInstance ;
12+
1113export default function openAIAssistantPage ( ) {
1214 // References
1315 const profileBtnRef = new Ref ( ) ;
@@ -265,7 +267,7 @@ export default function openAIAssistantPage() {
265267 `#message-${ assistantMsgId } .message-content` ,
266268 ) ;
267269 if ( messageEl ) {
268- messageEl . innerHTML += `<span style="color: var(--error-text-color);background-color: var(--danger-color); ">Cancelled by user.</span >` ;
270+ messageEl . innerHTML += `<div class="badge badge-red ">Cancelled by user.</div >` ;
269271 }
270272 } else {
271273 const messageEl = messageContainerRef . el . querySelector (
@@ -276,57 +278,110 @@ export default function openAIAssistantPage() {
276278 }
277279 }
278280 } finally {
279- // add copy button to code blocks
280- const codeBlocks = messageContainerRef . el
281- . querySelector ( `#message-${ assistantMsgId } .message-content` )
282- . querySelectorAll ( "pre" ) ;
283- codeBlocks . forEach ( ( pre ) => {
284- pre . style . position = "relative" ;
285- const copyButton = document . createElement ( "button" ) ;
286- copyButton . className = "copy-button" ;
287- copyButton . textContent = "Copy" ;
288-
289- const codeElement = pre . querySelector ( "code" ) ;
281+ // add custom code blocks with syntax highlighting
282+ const messageContent = messageContainerRef . el . querySelector (
283+ `#message-${ assistantMsgId } .message-content` ,
284+ ) ;
285+
286+ // Replace markdown code blocks with custom components
287+ messageContent . innerHTML = messageContent . innerHTML . replace (
288+ / < p r e > < c o d e c l a s s = " l a n g u a g e - ( \w + ) " > ( [ \s \S ] * ?) < \/ c o d e > < \/ p r e > / g,
289+ ( match , language , code ) => {
290+ language = language || "plaintext" ;
291+
292+ return `
293+ <div class="code-block">
294+ <div class="code-header">
295+ <div class="code-language">
296+ <i class="icon code"></i>
297+ <span>${ language } </span>
298+ </div>
299+ <div class="code-actions">
300+ <button class="btn btn-icon code-copy" title="Copy code">
301+ <i class="icon copy"></i>
302+ </button>
303+ </div>
304+ </div>
305+ <div class="code-content">
306+ <pre><code class="language-${ language } ">${ code } </code></pre>
307+ </div>
308+ <div class="code-expand">
309+ <i class="icon keyboard_arrow_down"></i>
310+ <span>Show more</span>
311+ </div>
312+ </div>
313+ ` ;
314+ } ,
315+ ) ;
316+
317+ // Process all code blocks
318+ const codeBlocks = messageContent . querySelectorAll ( ".code-block" ) ;
319+ codeBlocks . forEach ( ( codeBlock ) => {
320+ const codeContent = codeBlock . querySelector ( ".code-content" ) ;
321+ const codeElement = codeBlock . querySelector ( "code" ) ;
322+ const copyButton = codeBlock . querySelector ( ".code-copy" ) ;
323+ const expandButton = codeBlock . querySelector ( ".code-expand" ) ;
324+
325+ // Apply Ace highlighting
290326 if ( codeElement ) {
291- const langMatch = codeElement . className . match (
292- / l a n g u a g e - ( \w + ) | ( j a v a s c r i p t ) / ,
293- ) ;
327+ const langMatch = codeElement . className . match ( / l a n g u a g e - ( \w + ) / ) ;
294328 if ( langMatch ) {
295329 const langMap = {
296330 bash : "sh" ,
297331 shell : "sh" ,
298332 } ;
299- const lang = langMatch [ 1 ] || langMatch [ 2 ] ;
333+ const lang = langMatch [ 1 ] ;
300334 const mappedLang = langMap [ lang ] || lang ;
301335 const highlight = ace . require ( "ace/ext/static_highlight" ) ;
302- highlight ( codeElement , {
303- mode : `ace/mode/${ mappedLang } ` ,
304- theme : settings . value . editorTheme . startsWith ( "ace/theme/" )
336+ highlight . render (
337+ codeElement . textContent ,
338+ `ace/mode/${ mappedLang } ` ,
339+ settings . value . editorTheme . startsWith ( "ace/theme/" )
305340 ? settings . value . editorTheme
306341 : "ace/theme/" + settings . value . editorTheme ,
307- } ) ;
342+ 1 ,
343+ true ,
344+ ( highlighted ) => {
345+ aiTabInstance ?. addStyle ( highlighted . css ) ;
346+ codeElement . innerHTML = highlighted . html ;
347+ } ,
348+ ) ;
308349 }
309350 }
310351
352+ // copy functionality
311353 copyButton . addEventListener ( "click" , async ( ) => {
312- const code =
313- pre . querySelector ( "code" ) ?. textContent || pre . textContent ;
354+ const code = codeElement ?. textContent || "" ;
314355 try {
315356 cordova . plugins . clipboard . copy ( code ) ;
316- copyButton . textContent = "Copied! " ;
357+ copyButton . querySelector ( "i" ) . className = "icon check " ;
317358 setTimeout ( ( ) => {
318- copyButton . textContent = "Copy " ;
359+ copyButton . querySelector ( "i" ) . className = "icon copy " ;
319360 } , 2000 ) ;
320361 } catch ( err ) {
321- copyButton . textContent = "Failed to copy" ;
362+ copyButton . querySelector ( "i" ) . className =
363+ "icon warningreport_problem" ;
322364 setTimeout ( ( ) => {
323- copyButton . textContent = "Copy " ;
365+ copyButton . querySelector ( "i" ) . className = "icon copy " ;
324366 } , 2000 ) ;
325367 }
326368 } ) ;
327369
328- pre . appendChild ( copyButton ) ;
370+ // expand/collapse functionality
371+ expandButton . addEventListener ( "click" , ( ) => {
372+ const isExpanded = codeContent . classList . contains ( "expanded" ) ;
373+ codeContent . classList . toggle ( "expanded" , ! isExpanded ) ;
374+ expandButton . innerHTML = isExpanded
375+ ? `<i class="icon keyboard_arrow_down"></i> <span>Show more</span>`
376+ : `<i class="icon keyboard_arrow_up"></i> <span>Show less</span>` ;
377+ } ) ;
378+
379+ // Only show expand button if content overflows
380+ if ( codeContent . scrollHeight <= codeContent . clientHeight ) {
381+ expandButton . style . display = "none" ;
382+ }
329383 } ) ;
384+
330385 currentController = null ;
331386 sendBtnRef . el . style . display = "block" ;
332387 stopBtnRef . el . style . display = "none" ;
@@ -491,7 +546,7 @@ export default function openAIAssistantPage() {
491546 }
492547
493548 // Create a new EditorFile instance for the AI Assistant tab
494- new EditorFile ( "AI Assistant" , {
549+ aiTabInstance = new EditorFile ( "AI Assistant" , {
495550 uri : uri ,
496551 type : "page" ,
497552 tabIcon : "file file_type_assistant" ,
0 commit comments