Блоки кода как в Telegram (скрин): тёмно-синий пузырь #3b5a7a, время и галочки

- Bubble: background #3b5a7a (как на скрине #2e4d6a), radius 12, padding 10/14/6, max-width 88%, shadow, язык uppercase #8a9ba8 10px слева, кнопка 📋 справа (rgba 0.15, radius 12, border 0.2)
- Код: контейнер rgba(0,0,0,0.18) radius 8, padding 10/12, JetBrains Mono 13px/1.45/tab-size 4, color #fff, подсветка форсирована тёмной темой (highlight(...,true) -> Breeze Dark)
- Время: QTime::currentTime hh:mm + ✓✓ #5eb1f6 внизу справа, как в сообщении ТГ (20:31 ✓✓)
- Сохранено автовыравнивание/скобки/отступы из предыдущего коммита (Tab 4 пробела, Enter сохраняет indent, Backspace удаляет 4, авто-закрытие ()[]{}''""<> и </tag>)
This commit is contained in:
2026-09-01 20:39:34 +03:00
parent a24f782205
commit c861fcc763
+33 -32
View File
@@ -23,6 +23,7 @@
#include <QGuiApplication>
#include <QImageReader>
#include <QSettings>
#include <QTime>
#include <QUrl>
using KSyntaxHighlighting::AbstractHighlighter;
@@ -194,7 +195,7 @@ public:
m_segments.append(seg);
}
QString highlight(const QString &code, const QString &lang)
QString highlight(const QString &code, const QString &lang, bool forceDark = false)
{
m_segments.clear();
Definition def = m_repo->definitionForName(lang);
@@ -203,10 +204,11 @@ public:
setDefinition(def);
// Выбираем тему подсветки в зависимости от темы приложения (тёмная/светлая)
Theme chosen = m_repo->defaultTheme();
// Попытка получить тему из настроек (ui/theme)
// Попытка получить тему из настроек (ui/theme) или форсировать тёмную для пузырей
{
QSettings s;
if (s.value("ui/theme").toString() == "dark") {
bool wantDark = forceDark || s.value("ui/theme").toString() == "dark";
if (wantDark) {
for (auto t : m_repo->themes()) {
if (t.name().contains("dark", Qt::CaseInsensitive) || t.name().contains("Dark")) {
chosen = t;
@@ -476,47 +478,46 @@ private:
const char *info = cmark_node_get_fence_info(n);
const QString lang = normalizeLang(QString::fromUtf8(info ? info : ""));
const QString code = QString::fromUtf8(cmark_node_get_literal(n));
CodeHighlighter hl(m_repo);
const QString highlighted = hl.highlight(code, lang);
// сохраняем оригинал для копирования
// Telegram-стиль как на скрине: тёмно-синий пузырь (#2e4d6a) с кодом, время и галочки внизу справа
// Сохраняем оригинал для копирования
QString codeId;
if (m_impl) {
codeId = QStringLiteral("code_%1").arg(m_impl->codeCounter++);
m_impl->codeBlocks.insert(codeId, code);
}
// Telegram-стиль: пузырь сообщения с кодом, как в мессенджере
QSettings s;
const bool isDark = s.value("ui/theme").toString() == "dark";
// Telegram-like цвета: светлая — #f5f5f7, тёмная — #1a1a1a / #212121
const QString bg = isDark ? "#212121" : "#f5f5f7";
const QString headerBg = isDark ? "#2a2a2a" : "#e8e8ea";
const QString headerBorder = isDark ? "#333333" : "#d0d0d0";
const QString headerText = isDark ? "#8e8e93" : "#6d6d72";
const QString codeTextColor = isDark ? "#e0e0e0" : "#24292f";
// Форсируем тёмную тему подсветки для контраста на синем фоне Telegram-пузыря
CodeHighlighter hl(m_repo);
const QString highlighted = hl.highlight(code, lang, true);
// Переопределяем lang после highlight (highlight уже сделал)
const QString langDisplay = lang.isEmpty() ? "text" : esc(lang);
const QString cls = lang.isEmpty() ? QString() : QStringLiteral(" class=\"language-%1\"").arg(esc(lang));
// Кнопка копирования - Telegram стиль: маленькая, с hover/active
const QString copyBtnStyle = isDark
? "display:inline-block;background:#333333;color:#8e8e93;text-decoration:none;font-size:12px;padding:4px 10px;border-radius:6px;border:1px solid #404040;font-family: -apple-system, sans-serif;"
: "display:inline-block;background:#ffffff;color:#0088cc;text-decoration:none;font-size:12px;padding:4px 10px;border-radius:6px;border:1px solid #d0d0d0;font-family: -apple-system, sans-serif;";
const QString copyLink = codeId.isEmpty() ? QString()
: QStringLiteral("<a href=\"flora://copycode/%1\" style=\"%2\" title=\"Копировать\">📋 Копировать</a>").arg(codeId, copyBtnStyle);
// Пузырь как в Telegram: скругления 12px, тень, граница, header отделен
// Время как в Telegram — текущее время рендера (можно заменить на lastModified)
const QString timeStr = QTime::currentTime().toString("hh:mm");
// Telegram bubble: #3a5068 или #2e4d6a, скругления 12, тень, max-width 85%
const QString bubbleBg = "#3b5a7a"; // тёмно-синий как на скрине #2e4d6a → #3b5a7a чуть светлее
const QString codeColor = "#ffffff";
// Кнопка копирования — в правом верхнем углу пузыря, как в ТГ при наведении
// Делаем её заметной: полупрозрачная, скруглённая, с hover/active через CSS
const QString copyLink = codeId.isEmpty() ? QString()
: QStringLiteral("<a href=\"flora://copycode/%1\" style=\"display:inline-block;background:rgba(255,255,255,0.15);color:#ffffff;text-decoration:none;font-size:12px;padding:3px 8px;border-radius:12px;border:1px solid rgba(255,255,255,0.2);font-family:-apple-system,sans-serif;\" title=\"Копировать\">📋</a>").arg(codeId);
// Пузырь Telegram: используем таблицу для совместимости с QTextDocument (не поддерживает position:absolute)
// Верхняя строка — язык слева и кнопка справа, средняя — код, нижняя — время и галочки справа
return QStringLiteral(
"<div style=\"background:%1;border:1px solid %2;border-radius:12px;overflow:hidden;margin:16px 0;box-shadow:0 1px 2px rgba(0,0,0,0.08)\">"
"<div style=\"background:%3;padding:7px 12px;border-bottom:1px solid %2\">"
"<table width=\"100%\" cellspacing=\"0\" cellpadding=\"0\"><tr>"
"<td style=\"text-align:left;color:%4;font-size:11px;font-weight:600;letter-spacing:0.3px;text-transform:uppercase;\">%5</td>"
"<td style=\"text-align:right\">%6</td>"
"<div style=\"background:%1;border-radius:12px;padding:10px 14px 6px 14px;margin:12px 0;max-width:88%%;box-shadow:0 1px 2px rgba(0,0,0,0.15)\">"
"<table width=\"100%%\" cellspacing=\"0\" cellpadding=\"0\" style=\"margin-bottom:6px\">"
"<tr>"
"<td style=\"text-align:left;color:#8a9ba8;font-size:10px;font-weight:600;letter-spacing:0.4px;text-transform:uppercase;opacity:0.9\">%2</td>"
"<td style=\"text-align:right\">%3</td>"
"</tr></table>"
"<div style=\"background:rgba(0,0,0,0.18);border-radius:8px;padding:10px 12px;margin:6px 0 8px 0\">"
"<pre style=\"margin:0;font-family:'JetBrains Mono','Fira Code','Menlo',Consolas,monospace;font-size:13px;line-height:1.45;tab-size:4;color:%4;white-space:pre-wrap;word-wrap:break-word\">"
"<code%6>%5</code></pre>"
"</div>"
"<div style=\"background:%1;padding:12px 14px;overflow-x:auto\"><pre style=\"margin:0;font-family:'JetBrains Mono','Fira Code','Menlo','Consolas',monospace;font-size:13px;line-height:1.5;tab-size:4;color:%7;white-space:pre-wrap;word-wrap:break-word\">"
"<code%9>%8</code></pre></div>"
"<div style=\"text-align:right;color:#8a9ba8;font-size:11px;margin-top:2px;opacity:0.95\">%7 <span style=\"color:#5eb1f6\">✓✓</span></div>"
"</div>"
).arg(bg, headerBorder, headerBg, headerText, langDisplay, copyLink, codeTextColor, highlighted, cls);
).arg(bubbleBg, langDisplay, copyLink, codeColor, highlighted, cls, timeStr);
}
QString inlineAll(cmark_node *n)