From 51778dffbf6322fa42d2dedc3ea1d4941342beba Mon Sep 17 00:00:00 2001 From: JackLee <809262979@qq.com> Date: Mon, 10 Mar 2025 17:09:01 +0800 Subject: [PATCH] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E6=96=87=E6=9C=AC=E5=86=85?= =?UTF-8?q?=E5=AE=B9=E5=AF=B9=E9=BD=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- res/qss/light_style.qss | 4 -- src/ctaiHistoryTextEdit.cpp | 85 +++++++++++++++++++++++++------------ src/ctaiHistoryTextEdit.h | 2 + src/ctaiMathConvert.cpp | 55 +++++++++++++++++++----- src/ctaiMathConvert.h | 9 +++- src/ctai_base.h | 2 + 6 files changed, 115 insertions(+), 42 deletions(-) diff --git a/res/qss/light_style.qss b/res/qss/light_style.qss index 18ac7ce..78a121c 100644 --- a/res/qss/light_style.qss +++ b/res/qss/light_style.qss @@ -69,10 +69,6 @@ QLineEdit#m_msg_header{ border:none; background-color: #FFFFFF; } -QTextEdit#m_msg_history{ - border:none; - background-color: #FFFFFF; -} QPushButton#m_msg_system_header_ico, QPushButton#m_msg_user_header_ico{ diff --git a/src/ctaiHistoryTextEdit.cpp b/src/ctaiHistoryTextEdit.cpp index 8e8593e..ff41df8 100644 --- a/src/ctaiHistoryTextEdit.cpp +++ b/src/ctaiHistoryTextEdit.cpp @@ -11,11 +11,11 @@ ctaiHistoryTextEdit::~ctaiHistoryTextEdit() void ctaiHistoryTextEdit::init_layout(msg_type msg_type_mode) { // 主垂直布局 - main_layout = new QVBoxLayout(); + main_layout = new QVBoxLayout(); init_msg_line(); init_msg_header_layout(msg_type_mode); init_msg_history_layout(); - //主布局 + // 主布局 main_layout->addLayout(header_layout); main_layout->addLayout(history_layout); main_layout->addItem(bottom_spacer); @@ -28,7 +28,7 @@ void ctaiHistoryTextEdit::init_layout(msg_type msg_type_mode) } void ctaiHistoryTextEdit::init_msg_line() { - //分割线区域 + // 分割线区域 msg_line = new QFrame; msg_line->setFrameShape(QFrame::HLine); // 关键属性 msg_line->setFrameShadow(QFrame::Sunken); // 凹陷效果 @@ -52,11 +52,11 @@ void ctaiHistoryTextEdit::init_msg_history_layout() } void ctaiHistoryTextEdit::init_msg_header_layout(msg_type msg_type_mode) { - //消息头左区域布局,ico,id,fp,time + // 消息头左区域布局,ico,id,fp,time header_info_layout = new QHBoxLayout(); - //消息区头整体垂直 + // 消息区头整体垂直 header_layout = new QVBoxLayout(); - header_opts_layout=new QHBoxLayout(); + header_opts_layout = new QHBoxLayout(); m_msg_header = new QLineEdit(); m_msg_header->setObjectName("m_msg_header"); m_msg_header->setReadOnly(true); @@ -95,7 +95,7 @@ void ctaiHistoryTextEdit::init_msg_header_layout(msg_type msg_type_mode) m_msg_user_header_ico->setObjectName("m_msg_system_header_ico"); header_info_layout->addWidget(m_msg_user_header_ico); header_info_layout->addWidget(m_msg_header); - header_info_layout->setContentsMargins(0, 0, 0, 0); + header_info_layout->setContentsMargins(0, 0, 0, 0); // USER DELETE BUTTON m_msg_user_del = new QPushButton(); m_msg_user_del->setObjectName("m_msg_user_del"); @@ -109,8 +109,9 @@ void ctaiHistoryTextEdit::init_msg_header_layout(msg_type msg_type_mode) header_layout->addLayout(header_info_layout); } } -void ctaiHistoryTextEdit::set_default_opts(){ - m_msg_history->setFont(QFont(m_font,m_font_size)); +void ctaiHistoryTextEdit::set_default_opts() +{ + m_msg_history->setFont(QFont(m_font, m_font_size)); } void ctaiHistoryTextEdit::connect_signals(msg_type msg_type_mode) { @@ -123,12 +124,12 @@ void ctaiHistoryTextEdit::connect_signals(msg_type msg_type_mode) connect(m_msg_tools, SIGNAL(on_signals_display_mode(QString)), this, SLOT(on_display_changed(QString))); connect(m_msg_tools, SIGNAL(on_signals_display_font(QString)), this, SLOT(on_display_font_changed(QString))); connect(m_msg_tools, SIGNAL(on_signals_display_font_size(QString)), this, SLOT(on_display_font_size_changed(QString))); - //save菜单功能 + // save菜单功能 connect(m_msg_tools, SIGNAL(on_signals_save_text()), this, SLOT(on_save_text())); connect(m_msg_tools, SIGNAL(on_signals_save_html()), this, SLOT(on_save_html())); connect(m_msg_tools, SIGNAL(on_signals_save_pdf()), this, SLOT(on_save_pdf())); connect(m_msg_tools, SIGNAL(on_signals_save_markdown()), this, SLOT(on_save_markdown())); - //其他功能菜单 + // 其他功能菜单 connect(m_msg_tools, SIGNAL(on_signals_menu()), this, SLOT(on_menu_slots())); } else @@ -139,16 +140,15 @@ void ctaiHistoryTextEdit::connect_signals(msg_type msg_type_mode) } void ctaiHistoryTextEdit::on_menu_slots() { - } void ctaiHistoryTextEdit::on_display_font_size_changed(QString font_size) { - m_font_size=font_size.toInt(); + m_font_size = font_size.toInt(); m_msg_history->setFont(QFont(m_font, m_font_size)); } void ctaiHistoryTextEdit::on_display_font_changed(QString font) { - m_font=font; + m_font = font; m_msg_history->setFont(QFont(m_font, m_font_size)); } void ctaiHistoryTextEdit::on_delete_slots() @@ -180,13 +180,50 @@ void ctaiHistoryTextEdit::on_save_text() } QTextStream out(&file); out << m_msg_history->toPlainText() << "\n\n"; + file.close(); } void ctaiHistoryTextEdit::on_save_html() { + QString fileName = QFileDialog::getSaveFileName(this, + tr("保存HTML"), + m_msg_sned_id + ".html", + tr("HTML 文件 (*.html)")); + if (fileName.isEmpty()) + return; + + QFile file(fileName); + if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) + { + QMessageBox::warning(this, tr("保存失败"), + tr("无法保存文件 %1:\n%2").arg(fileName).arg(file.errorString())); + return; + } + + QTextStream out(&file); + out << m_msg_history->toHtml(); + file.close(); } void ctaiHistoryTextEdit::on_save_markdown() { + QString fileName = QFileDialog::getSaveFileName(this, + tr("保存 Markdown"), + m_msg_sned_id + ".md", + tr("Markdown 文件 (*.md)")); + if (fileName.isEmpty()) + return; + + QFile file(fileName); + if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) + { + QMessageBox::warning(this, tr("保存失败"), + tr("无法保存文件 %1:\n%2").arg(fileName).arg(file.errorString())); + return; + } + + QTextStream out(&file); + out << m_msg_history->toMarkdown(); + file.close(); } void ctaiHistoryTextEdit::on_save_pdf() @@ -212,14 +249,13 @@ void ctaiHistoryTextEdit::on_display_changed(QString mode) } else if (mode == "HTML源码") { - - }else if(mode == "HTML渲染"){ - - }else if(mode == "Markdown源码"){ - - }else if(mode == "Markdown渲染"){ - m_msg_history->setMarkdown(m_math_convert->replace_tags_svg(m_current_content,m_font_size)); + } + else if (mode == "HTML渲染" || mode == "Markdown渲染") + { + m_msg_history->setHtml(m_math_convert->replace_tags_svg(m_current_content, m_font_size)); qDebug() << "LaTeX Markdown:" << m_msg_history->toMarkdown(); + on_save_html(); + on_save_markdown(); } } // tokens按钮实现功能的槽函数 @@ -307,27 +343,24 @@ void ctaiHistoryTextEdit::add_user_message(const model_data &message) QString disp_data; disp_data = QSL(message.send_user_data); m_msg_sned_id = QSL(message.send_user_id); - m_msg_history->setMarkdown(disp_data); + m_msg_history->setHtml(m_math_convert->replace_tags_svg(disp_data, m_font_size)); } void ctaiHistoryTextEdit::add_system_message(const model_data &message) { m_msg_sned_id = QSL(message.postback_send_id); std::lock_guard lock(m_mutex); - if (message.postback_stream_mode) { // 流式模式下追加内容 m_current_content += message.postback_model_data; - m_msg_history->setMarkdown(m_current_content); - // m_msg_history->setHtml(m_math_convert->replace_tags_svg(m_current_content)); } else { // 非流式模式直接设置全部内容 m_current_content = QSL(message.postback_model_data); - m_msg_history->setMarkdown(m_current_content); } + m_msg_history->setMarkdown(m_current_content); } // 增加tokens信息 void ctaiHistoryTextEdit::update_tokens_message(const model_data &message) diff --git a/src/ctaiHistoryTextEdit.h b/src/ctaiHistoryTextEdit.h index b0404fe..b4cd5fd 100644 --- a/src/ctaiHistoryTextEdit.h +++ b/src/ctaiHistoryTextEdit.h @@ -24,6 +24,8 @@ #include #include #include +#include +#include // Qt对话框 #include #include diff --git a/src/ctaiMathConvert.cpp b/src/ctaiMathConvert.cpp index 6c98e5e..f6b9962 100644 --- a/src/ctaiMathConvert.cpp +++ b/src/ctaiMathConvert.cpp @@ -19,11 +19,9 @@ void ctaiMathConvert::save_svg(QPixmap &img) } QByteArray ctaiMathConvert::svg_to_base64(QPixmap &pix) { - QByteArray data; QBuffer buffer(&data); QImage img = pix.toImage(); - img.save(&buffer, "PNG"); data = data.toBase64(); return data; @@ -49,7 +47,6 @@ void ctaiMathConvert::debug_latex_match(const QString &text, const QRegularExpre qDebug() << "匹配位置:" << match.capturedStart() << "-" << match.capturedEnd(); qDebug() << "匹配文本:" << match.captured(0); qDebug() << "数学内容:" << match.captured(1); - // 显示匹配位置的上下文 int contextSize = 20; int start = qMax(0, match.capturedStart() - contextSize); @@ -91,7 +88,6 @@ QString ctaiMathConvert::replace_tags_svg(const QString &text,int font_size) QList> replacements; QStringList svgResults; // 定义基础图片标签 - const QString html_base = ""; + QString cleanFormula = clean_latex_for_mula(matchedText); + QString svg_markdown = markdown_base + math_convert_svg(cleanFormula) + ")"; // 保存替换信息 replacements.prepend({match.capturedStart(), match.capturedLength()}); svgResults.prepend(svg_markdown); - //svgResults.prepend(svg_html); } // 从后向前执行替换,避免位置改变影响 @@ -115,7 +109,48 @@ QString ctaiMathConvert::replace_tags_svg(const QString &text,int font_size) const auto &rep = replacements[i]; result.replace(rep.first, rep.second, svgResults[i]); } - - return result; + return markdown_to_html(result); } +QString ctaiMathConvert::markdown_to_html(const QString& text){ + // 转换为UTF-8编码的字符串 + QByteArray markdown = text.toUtf8(); + // 启用所有 GFM 扩展选项 + int options = CMARK_OPT_SOURCEPOS | + CMARK_OPT_VALIDATE_UTF8 | + CMARK_OPT_SMART | + CMARK_OPT_GITHUB_PRE_LANG | + CMARK_OPT_LIBERAL_HTML_TAG | + CMARK_OPT_FOOTNOTES| + CMARK_OPT_STRIKETHROUGH_DOUBLE_TILDE| + CMARK_OPT_TABLE_PREFER_STYLE_ATTRIBUTES| + CMARK_OPT_FULL_INFO_STRING; + + // 使用cmark-gfm解析Markdown + char *html = cmark_markdown_to_html( + markdown.constData(), + markdown.size(), + options); + + // 转换回QString并处理代码块 + QString result = QString::fromUtf8(html); + free(html); + + return fix_img_str_line_height(result.toUtf8()); +} +QString ctaiMathConvert::fix_img_str_line_height(QString html) { + // 修正图片样式,使其与文本在同一行显示 + html.replace("
  • #include #include +#include +#include #include #include #include @@ -36,6 +38,7 @@ class TexGuard { }; using namespace tex; + class ctaiMathConvert :public QObject { Q_OBJECT @@ -51,14 +54,16 @@ private: QString clean_latex_for_mula(const QString &formula); QString math_convert_svg(const QString& text); QByteArray svg_to_base64(QPixmap& pix); + QString markdown_to_html(const QString& text); + QString fix_img_str_line_height(QString html); TexGuard texGuard; //宽度 int m_render_width=600; int m_render_height=64; //文本大小 - int m_text_size=20; + int m_text_size=16; //行距 - float m_lineSpace=20 / 3.f; + float m_lineSpace=1.5f; //颜色 tex::color m_color=0xff424242; //边距 diff --git a/src/ctai_base.h b/src/ctai_base.h index b0fdbcc..3c4bcdd 100644 --- a/src/ctai_base.h +++ b/src/ctai_base.h @@ -88,4 +88,6 @@ const QStringList latex_symbols = { "\\Psi", "\\Omega" }; + + #endif // CTAI_BASE_H \ No newline at end of file