99 lines
2.9 KiB
C++
99 lines
2.9 KiB
C++
#ifndef OLLIE_CHAT_RENDERER_H
|
|
#define OLLIE_CHAT_RENDERER_H
|
|
|
|
#include <QString>
|
|
#include <QList>
|
|
#include <QStringList>
|
|
#include <QTextEdit>
|
|
#include <QRegularExpression>
|
|
|
|
namespace KSyntaxHighlighting {
|
|
class Repository;
|
|
}
|
|
|
|
// Parsed chat block
|
|
struct ChatBlock {
|
|
enum Type {
|
|
User,
|
|
Assistant,
|
|
Reasoning,
|
|
Call,
|
|
Tool,
|
|
Error,
|
|
Info
|
|
};
|
|
|
|
Type type;
|
|
QString name; // tool/call name
|
|
QString content; // raw text content
|
|
};
|
|
|
|
// Sub-block within a message (prose, code fence, diff)
|
|
struct SubBlock {
|
|
enum Type { Prose, Code, Diff, Interrupt };
|
|
|
|
Type type;
|
|
QString language;
|
|
QString fenceMeta; // extra text after language on the fence line (e.g., "/path:10-20")
|
|
QString content;
|
|
};
|
|
|
|
/**
|
|
* ChatRenderer parses ollie chat output into structured blocks
|
|
* and renders them as styled HTML in a QTextEdit.
|
|
*/
|
|
class ChatRenderer
|
|
{
|
|
public:
|
|
ChatRenderer();
|
|
~ChatRenderer();
|
|
|
|
/// Set external diff storage. When set, diffs are stored here and
|
|
/// rendered as clickable links rather than inline.
|
|
void setDiffStorage(QStringList *storage) { m_diffStorage = storage; }
|
|
|
|
/// Storage for apply-able code blocks. Each entry is the code content.
|
|
/// Rendered with a clickable "Apply" link.
|
|
struct ApplyBlock {
|
|
QString filePath;
|
|
QString code;
|
|
int startLine = -1; // 1-indexed, inclusive. -1 = insert at cursor
|
|
int endLine = -1; // 1-indexed, inclusive. -1 = insert at cursor
|
|
};
|
|
void setApplyStorage(QList<ApplyBlock> *storage) { m_applyStorage = storage; }
|
|
|
|
/// Parse raw chat text into blocks
|
|
QList<ChatBlock> parseBlocks(const QString &text) const;
|
|
|
|
/// Parse a block's content into sub-blocks (prose, code, diff)
|
|
QList<SubBlock> parseSubBlocks(const QString &content) const;
|
|
|
|
/// Render full chat text into the QTextEdit as styled HTML
|
|
void render(QTextEdit *view, const QString &rawText);
|
|
|
|
/// Render incrementally: assumes rawText is the full text and only
|
|
/// the tail has changed since last render. Returns true if it did
|
|
/// an incremental update, false if it did a full re-render.
|
|
bool renderIncremental(QTextEdit *view, const QString &rawText);
|
|
|
|
private:
|
|
QString blockToHtml(const ChatBlock &block) const;
|
|
QString subBlockToHtml(const SubBlock &sub, bool preserveWhitespace = false) const;
|
|
QString highlightCode(const QString &code, const QString &language) const;
|
|
QString renderDiff(const QString &diff) const;
|
|
QString renderProse(const QString &prose) const;
|
|
QString escapeHtml(const QString &text) const;
|
|
|
|
KSyntaxHighlighting::Repository *m_repo;
|
|
QStringList *m_diffStorage = nullptr;
|
|
QList<ApplyBlock> *m_applyStorage = nullptr;
|
|
QString m_lastRawText; // for incremental rendering
|
|
int m_lastBlockCount = 0;
|
|
|
|
// Colors (derived from palette)
|
|
mutable bool m_dark = false;
|
|
void detectTheme() const;
|
|
};
|
|
|
|
#endif // OLLIE_CHAT_RENDERER_H
|