44import burp .modules .AiAnalyzer ;
55import burp .modules .AiClient ;
66import burp .ui .utils .CodeBlockPanel ;
7+ import burp .ui .utils .MarkdownRenderer ;
8+ import burp .ui .utils .ThemeColors ;
79import burp .utils .SettingsManager ;
810import burp .utils .AiProvider ;
911
1012import javax .swing .*;
1113import javax .swing .border .TitledBorder ;
1214import java .awt .*;
1315import java .text .SimpleDateFormat ;
14- import java .util .ArrayList ;
1516import java .util .Date ;
1617import java .util .List ;
1718import java .util .function .Supplier ;
@@ -166,6 +167,10 @@ public AiAssistantPanel(
166167 add (splitPane , BorderLayout .CENTER );
167168 }
168169
170+ public void shutdown () {
171+ aiClient .shutdown ();
172+ }
173+
169174 public void refreshSettings () {
170175 updateOpSecPanel ();
171176 }
@@ -374,31 +379,21 @@ private void displayMarkdown(String markdown) {
374379 private void addTextBlock (String md ) {
375380 if (md == null || md .trim ().isEmpty ()) return ;
376381
377- String html = convertMarkdownToHtml (md );
382+ String html = MarkdownRenderer . toHtml (md );
378383
379384 JTextPane textPane = new JTextPane ();
380385 textPane .setContentType ("text/html" );
381386 textPane .setEditable (false );
382387 textPane .setBackground (resultsContainer .getBackground ());
383388 textPane .setMargin (new Insets (6 , 6 , 6 , 6 ));
384389
385- // Dynamically style based on Burp theme color
386- Color bg = UIManager .getColor ("TextPane.background" );
387- if (bg == null ) bg = UIManager .getColor ("Panel.background" );
388- if (bg == null ) bg = Color .WHITE ;
389-
390- Color fg = UIManager .getColor ("TextPane.foreground" );
391- if (fg == null ) fg = UIManager .getColor ("Label.foreground" );
392- if (fg == null ) fg = Color .BLACK ;
393-
394- String bgHex = String .format ("#%02x%02x%02x" , bg .getRed (), bg .getGreen (), bg .getBlue ());
395- String fgHex = String .format ("#%02x%02x%02x" , fg .getRed (), fg .getGreen (), fg .getBlue ());
396-
397- boolean isDark = (bg .getRed () + bg .getGreen () + bg .getBlue ()) / 3 < 128 ;
398- String borderHex = isDark ? "#444444" : "#dddddd" ;
390+ boolean isDark = ThemeColors .isDark ();
391+ String bgHex = ThemeColors .toHex (ThemeColors .background ());
392+ String fgHex = ThemeColors .toHex (ThemeColors .foreground ());
393+ String borderHex = isDark ? "#444444" : "#dddddd" ;
399394 String headerBgHex = isDark ? "#2d2d2d" : "#f2f2f2" ;
400- String codeBgHex = isDark ? "#282828" : "#f5f5f5" ;
401- String evenRowBgHex = isDark ? "#232323" : "#fafafa" ;
395+ String codeBgHex = isDark ? "#282828" : "#f5f5f5" ;
396+ String evenRowHex = isDark ? "#232323" : "#fafafa" ;
402397
403398 String styledHtml = "<html><head><style>" +
404399 "body { font-family: sans-serif; font-size: 11px; color: " + fgHex + "; margin: 0; }" +
@@ -408,7 +403,7 @@ private void addTextBlock(String md) {
408403 "table { border-collapse: collapse; width: 100%; margin-bottom: 8px; margin-top: 4px; }" +
409404 "th { background-color: " + headerBgHex + "; border: 1px solid " + borderHex + "; padding: 4px; text-align: left; font-weight: bold; color: " + fgHex + "; }" +
410405 "td { border: 1px solid " + borderHex + "; padding: 4px; color: " + fgHex + "; }" +
411- "tr:nth-child(even) { background-color: " + evenRowBgHex + "; }" +
406+ "tr:nth-child(even) { background-color: " + evenRowHex + "; }" +
412407 "ul { margin-top: 2px; margin-bottom: 2px; padding-left: 16px; }" +
413408 "li { margin-bottom: 1px; }" +
414409 "p { margin-top: 2px; margin-bottom: 2px; }" +
@@ -438,125 +433,6 @@ private void addCodeBlock(String language, String code) {
438433 resultsContainer .add (Box .createVerticalStrut (4 ));
439434 }
440435
441- private String convertMarkdownToHtml (String md ) {
442- if (md == null ) return "" ;
443-
444- String [] lines = md .split ("\n " );
445- StringBuilder sb = new StringBuilder ();
446- boolean inList = false ;
447- boolean inTable = false ;
448- boolean hasTableHeader = false ;
449-
450- for (String line : lines ) {
451- String trimmed = line .trim ();
452-
453- // List items
454- if (trimmed .startsWith ("- " ) || trimmed .startsWith ("* " )) {
455- if (!inList ) {
456- sb .append ("<ul>" );
457- inList = true ;
458- }
459- String content = trimmed .substring (2 );
460- content = replaceMarkdownFormatting (content );
461- sb .append ("<li>" ).append (content ).append ("</li>" );
462- continue ;
463- } else {
464- if (inList ) {
465- sb .append ("</ul>" );
466- inList = false ;
467- }
468- }
469-
470- // Tables
471- if (trimmed .startsWith ("|" ) && trimmed .endsWith ("|" )) {
472- if (!inTable ) {
473- sb .append ("<table>" );
474- inTable = true ;
475- hasTableHeader = false ;
476- }
477-
478- if (trimmed .matches ("^\\ |[\\ s\\ -:\\ |]+$" )) {
479- continue ; // Skip separator line
480- }
481-
482- String [] cells = trimmed .split ("\\ |" );
483- List <String > validCells = new ArrayList <>();
484- int startIdx = trimmed .startsWith ("|" ) ? 1 : 0 ;
485- int endIdx = cells .length ;
486- if (trimmed .endsWith ("|" ) && cells .length > startIdx ) {
487- endIdx = cells .length - 1 ;
488- }
489- for (int i = startIdx ; i < endIdx ; i ++) {
490- validCells .add (cells [i ].trim ());
491- }
492-
493- sb .append ("<tr>" );
494- for (String cell : validCells ) {
495- String formattedCell = replaceMarkdownFormatting (cell );
496- if (!hasTableHeader ) {
497- sb .append ("<th>" ).append (formattedCell ).append ("</th>" );
498- } else {
499- sb .append ("<td>" ).append (formattedCell ).append ("</td>" );
500- }
501- }
502- sb .append ("</tr>" );
503-
504- if (!hasTableHeader ) {
505- hasTableHeader = true ;
506- }
507- continue ;
508- } else {
509- if (inTable ) {
510- sb .append ("</table>" );
511- inTable = false ;
512- }
513- }
514-
515- // Headers
516- if (trimmed .startsWith ("### " )) {
517- String content = trimmed .substring (4 );
518- sb .append ("<h3>" ).append (replaceMarkdownFormatting (content )).append ("</h3>" );
519- continue ;
520- } else if (trimmed .startsWith ("## " )) {
521- String content = trimmed .substring (3 );
522- sb .append ("<h2>" ).append (replaceMarkdownFormatting (content )).append ("</h2>" );
523- continue ;
524- } else if (trimmed .startsWith ("# " )) {
525- String content = trimmed .substring (2 );
526- sb .append ("<h1>" ).append (replaceMarkdownFormatting (content )).append ("</h1>" );
527- continue ;
528- }
529-
530- // Regular paragraph
531- if (!trimmed .isEmpty ()) {
532- sb .append ("<p>" ).append (replaceMarkdownFormatting (trimmed )).append ("</p>" );
533- }
534- }
535-
536- if (inList ) sb .append ("</ul>" );
537- if (inTable ) sb .append ("</table>" );
538-
539- return sb .toString ();
540- }
541-
542- private String replaceMarkdownFormatting (String text ) {
543- if (text == null ) return "" ;
544-
545- // Escape HTML
546- text = text .replace ("&" , "&" ).replace ("<" , "<" ).replace (">" , ">" );
547-
548- // Code code
549- text = text .replaceAll ("`([^`]+)`" , "<code>$1</code>" );
550-
551- // Bold
552- text = text .replaceAll ("\\ *\\ *([^\\ *]+)\\ *\\ *" , "<b>$1</b>" );
553-
554- // Italic
555- text = text .replaceAll ("\\ *([^\\ *]+)\\ *" , "<i>$1</i>" );
556-
557- return text ;
558- }
559-
560436 /**
561437 * Creates a non-editable JEditorPane that renders HTML properly and wraps text
562438 * to the container width (prevents horizontal scrollbar in the left panel).
0 commit comments