2626
2727# ==================== Markdown 解析器(使用 mistune + Pygments)====================
2828
29-
30-
3129class PygmentsRenderer (mistune .HTMLRenderer ):
3230 """
3331 使用 Pygments 进行代码高亮的 mistune 渲染器
@@ -107,7 +105,11 @@ def _init_parser(self):
107105 plugins = ['table' , 'strikethrough' , 'url' ]
108106 )
109107 # Token 解析器(用于分离内容片段)- mistune 3.x 使用 Markdown 类
108+ # 必须手动添加表格插件,否则表格会被解析为普通段落
110109 self .token_parser = mistune .Markdown ()
110+ # 添加表格插件支持
111+ from mistune .plugins .table import table as table_plugin
112+ table_plugin (self .token_parser )
111113
112114 def parse_to_html (self , text ):
113115 """将 Markdown 转换为 HTML"""
@@ -1090,7 +1092,30 @@ def update_height(tb):
10901092 QTimer .singleShot (50 , lambda tb = text_browser : update_height (tb ))
10911093
10921094 def process_inline_code (self , text : str , user : bool ) -> str :
1093- """处理行内代码和格式"""
1095+ """处理行内代码和格式,支持表格渲染"""
1096+ color = "#1e40af" if user else "#1a202c"
1097+
1098+ # 检测是否包含表格格式(至少两行且包含 | 分隔符)
1099+ # 表格格式:| col1 | col2 | 后跟 |---|---| 分隔行
1100+ table_pattern = r'^\|.+\|\s*\n\|[-\s|:]+\|\s*\n(\|.+\|\s*\n?)+'
1101+ if re .search (table_pattern , text , re .MULTILINE ):
1102+ # 包含表格,使用完整的 Markdown 解析器
1103+ html = self .parser .parse_to_html (text )
1104+ # 为表格添加样式
1105+ styled_html = self ._add_table_styles (html , color )
1106+ return f'<div style="line-height: 1.7; color: { color } ;">{ styled_html } </div>'
1107+
1108+ # 检测简化的表格格式(仅包含多行 | 分隔的内容)
1109+ lines = text .strip ().split ('\n ' )
1110+ table_lines = [line for line in lines if '|' in line and line .strip ().startswith ('|' )]
1111+ if len (table_lines ) >= 2 :
1112+ # 可能是表格,尝试用 Markdown 解析
1113+ html = self .parser .parse_to_html (text )
1114+ if '<table' in html :
1115+ styled_html = self ._add_table_styles (html , color )
1116+ return f'<div style="line-height: 1.7; color: { color } ;">{ styled_html } </div>'
1117+
1118+ # 普通文本处理
10941119 # 先转义 HTML 特殊字符
10951120 text = text .replace ('&' , '&' ).replace ('<' , '<' ).replace ('>' , '>' )
10961121
@@ -1105,9 +1130,56 @@ def process_inline_code(self, text: str, user: bool) -> str:
11051130 text = re .sub (r'\*\*([^*]+)\*\*' , r'<b>\1</b>' , text )
11061131 text = re .sub (r'\*([^*]+)\*' , r'<i>\1</i>' , text )
11071132 text = text .replace ('\n ' , '<br>' )
1108- color = "#1e40af" if user else "#1a202c"
11091133 return f'<div style="line-height: 1.7; color: { color } ;">{ text } </div>'
11101134
1135+ def _add_table_styles (self , html : str , text_color : str ) -> str :
1136+ """为表格 HTML 添加样式"""
1137+ # 表格样式
1138+ table_style = (
1139+ 'border-collapse: collapse; '
1140+ 'width: 100%; '
1141+ 'margin: 10px 0; '
1142+ 'font-size: 14px; '
1143+ 'background: #fafafa; '
1144+ 'border-radius: 8px; '
1145+ 'overflow: hidden; '
1146+ 'box-shadow: 0 1px 3px rgba(0,0,0,0.1);'
1147+ )
1148+
1149+ # 表头样式
1150+ th_style = (
1151+ 'background: #667eea; '
1152+ 'color: white; '
1153+ 'padding: 12px 16px; '
1154+ 'text-align: left; '
1155+ 'font-weight: 600; '
1156+ 'border-bottom: 2px solid #5a67d8;'
1157+ )
1158+
1159+ # 单元格样式
1160+ td_style = (
1161+ f'color: { text_color } ; '
1162+ 'padding: 10px 16px; '
1163+ 'border-bottom: 1px solid #e2e8f0;'
1164+ )
1165+
1166+ # 行样式
1167+ tr_style = 'background: #ffffff;'
1168+
1169+ # 替换 <table>
1170+ html = re .sub (r'<table>' , f'<table style="{ table_style } ">' , html )
1171+
1172+ # 替换 <th> - 在标签名后插入 style
1173+ html = re .sub (r'<th>' , f'<th style="{ th_style } ">' , html )
1174+
1175+ # 替换 <td> - 在标签名后插入 style
1176+ html = re .sub (r'<td>' , f'<td style="{ td_style } ">' , html )
1177+
1178+ # 替换 <tr>
1179+ html = re .sub (r'<tr>' , f'<tr style="{ tr_style } ">' , html )
1180+
1181+ return html
1182+
11111183 def get_all_text (self ) -> str :
11121184 """获取消息的所有文本内容(纯文本格式)"""
11131185 # 直接返回原始内容,因为 self.content 保存了原始 markdown 文本
@@ -1165,7 +1237,7 @@ class ChatWindow(QMainWindow):
11651237
11661238 def __init__ (self ):
11671239 super ().__init__ ()
1168- self .setWindowTitle ("AI 聊天机器人 V0.4.2 " )
1240+ self .setWindowTitle ("AI 聊天机器人 V0.4.3 " )
11691241 self .resize (1200 , 800 )
11701242 self .setMinimumSize (1000 , 600 )
11711243
@@ -2195,4 +2267,4 @@ def closeEvent(self, event):
21952267
21962268 window = ChatWindow ()
21972269 window .show ()
2198- sys .exit (app .exec ())
2270+ sys .exit (app .exec ())
0 commit comments