add retry block support: Retry type, header color, expanded default

This commit is contained in:
Levi Neely 2026-08-01 12:45:33 +02:00
parent 80b5b1dd5d
commit 3a33c4e42a
3 changed files with 68 additions and 61 deletions

View File

@ -44,7 +44,7 @@ private:
// --- Block Parsing --- // --- Block Parsing ---
const QRegularExpression ChatBlockModel::s_headerRe( const QRegularExpression ChatBlockModel::s_headerRe(
R"(^\[(user|assistant|reasoning|call|tool|error|info)(?::([^\]]*))?\]$)"); R"(^\[(user|assistant|reasoning|call|tool|error|info|retry)(?::([^\]]*))?\]$)");
ChatBlockModel::ChatBlockModel(QObject *parent) ChatBlockModel::ChatBlockModel(QObject *parent)
: QAbstractListModel(parent) : QAbstractListModel(parent)
@ -64,17 +64,18 @@ QVariant ChatBlockModel::data(const QModelIndex &index, int role) const
const ChatBlock &b = m_blocks[index.row()]; const ChatBlock &b = m_blocks[index.row()];
switch (role) { switch (role) {
case BlockTypeRole: case BlockTypeRole:
switch (b.type) { switch (b.type) {
case ChatBlock::User: return QStringLiteral("user"); case ChatBlock::User: return QStringLiteral("user");
case ChatBlock::Assistant: return QStringLiteral("assistant"); case ChatBlock::Assistant: return QStringLiteral("assistant");
case ChatBlock::Reasoning: return QStringLiteral("reasoning"); case ChatBlock::Reasoning: return QStringLiteral("reasoning");
case ChatBlock::Call: return QStringLiteral("call"); case ChatBlock::Call: return QStringLiteral("call");
case ChatBlock::Tool: return QStringLiteral("tool"); case ChatBlock::Tool: return QStringLiteral("tool");
case ChatBlock::Error: return QStringLiteral("error"); case ChatBlock::Error: return QStringLiteral("error");
case ChatBlock::Info: return QStringLiteral("info"); case ChatBlock::Info: return QStringLiteral("info");
case ChatBlock::Default: return QStringLiteral("default"); case ChatBlock::Retry: return QStringLiteral("retry");
} case ChatBlock::Default: return QStringLiteral("default");
}
return {}; return {};
case BlockNameRole: case BlockNameRole:
return b.name; return b.name;
@ -257,6 +258,7 @@ void ChatBlockModel::processLine(const QString &line)
else if (typeStr == "tool") type = ChatBlock::Tool; else if (typeStr == "tool") type = ChatBlock::Tool;
else if (typeStr == "error") type = ChatBlock::Error; else if (typeStr == "error") type = ChatBlock::Error;
else if (typeStr == "info") type = ChatBlock::Info; else if (typeStr == "info") type = ChatBlock::Info;
else if (typeStr == "retry") type = ChatBlock::Retry;
else type = ChatBlock::Default; else type = ChatBlock::Default;
beginInsertRows(QModelIndex(), m_blocks.size(), m_blocks.size()); beginInsertRows(QModelIndex(), m_blocks.size(), m_blocks.size());

View File

@ -15,6 +15,7 @@ struct ChatBlock {
Tool, Tool,
Error, Error,
Info, Info,
Retry,
Default Default
}; };

View File

@ -610,47 +610,50 @@ ApplicationWindow {
width: chatView.width - 8 width: chatView.width - 8
spacing: 2 spacing: 2
property bool blockExpanded: { property bool blockExpanded: {
switch (blockType) { switch (blockType) {
case "user": return true case "user": return true
case "assistant": return true case "assistant": return true
case "tool": return true case "tool": return true
case "info": return true case "info": return true
default: return false case "retry": return true
} default: return false
} }
}
// Clickable header // Clickable header
Rectangle { Rectangle {
Layout.fillWidth: true Layout.fillWidth: true
height: 22 height: 22
radius: 3 radius: 3
color: { color: {
var bg = theme.baseColor var bg = theme.baseColor
if (theme.isDark) { if (theme.isDark) {
switch (blockType) { switch (blockType) {
case "user": return Qt.lighter(bg, 1.8) case "user": return Qt.lighter(bg, 1.8)
case "assistant": return Qt.lighter(bg, 1.5) case "assistant": return Qt.lighter(bg, 1.5)
case "reasoning": return Qt.lighter(bg, 1.3) case "reasoning": return Qt.lighter(bg, 1.3)
case "call": return Qt.lighter(bg, 1.7) case "call": return Qt.lighter(bg, 1.7)
case "tool": return Qt.lighter(bg, 1.2) case "tool": return Qt.lighter(bg, 1.2)
case "error": return Qt.rgba(0.6, 0.15, 0.15, 0.7) case "error": return Qt.rgba(0.6, 0.15, 0.15, 0.7)
case "info": return Qt.lighter(bg, 1.4) case "info": return Qt.lighter(bg, 1.4)
default: return bg case "retry": return Qt.rgba(0.6, 0.5, 0.0, 0.5)
} default: return bg
} else { }
switch (blockType) { } else {
case "user": return Qt.darker(bg, 1.12) switch (blockType) {
case "assistant": return Qt.darker(bg, 1.06) case "user": return Qt.darker(bg, 1.12)
case "reasoning": return Qt.darker(bg, 1.03) case "assistant": return Qt.darker(bg, 1.06)
case "call": return Qt.darker(bg, 1.1) case "reasoning": return Qt.darker(bg, 1.03)
case "tool": return Qt.darker(bg, 1.04) case "call": return Qt.darker(bg, 1.1)
case "error": return Qt.rgba(1.0, 0.8, 0.8, 0.7) case "tool": return Qt.darker(bg, 1.04)
case "info": return Qt.darker(bg, 1.02) case "error": return Qt.rgba(1.0, 0.8, 0.8, 0.7)
default: return bg case "info": return Qt.darker(bg, 1.02)
} case "retry": return Qt.rgba(1.0, 0.9, 0.6, 0.5)
} default: return bg
} }
}
}
MouseArea { MouseArea {
anchors.fill: parent anchors.fill: parent
@ -669,20 +672,21 @@ ApplicationWindow {
opacity: 0.6 opacity: 0.6
} }
Label { Label {
Layout.fillWidth: true Layout.fillWidth: true
text: { text: {
switch (blockType) { switch (blockType) {
case "user": return "User" case "user": return "User"
case "assistant": return "Assistant" + (blockName ? " (" + blockName + ")" : "") case "assistant": return "Assistant" + (blockName ? " (" + blockName + ")" : "")
case "reasoning": return "Reasoning" case "reasoning": return "Reasoning"
case "call": return "Call: " + blockName case "call": return "Call: " + blockName
case "tool": return "Tool: " + blockName case "tool": return "Tool: " + blockName
case "error": return "Error" case "error": return "Error"
case "info": return "Info" case "info": return "Info"
default: return "" case "retry": return "Retry"
} default: return ""
} }
}
font.pointSize: theme.fontSize > 0 ? theme.fontSize * 0.85 : 11 font.pointSize: theme.fontSize > 0 ? theme.fontSize * 0.85 : 11
font.bold: true font.bold: true
color: theme.textColor color: theme.textColor