diff --git a/GeminiTest/Myc.Api.MarkdownStream.pas b/GeminiTest/Myc.Api.MarkdownStream.pas
index 9d77d3b..de6a95a 100644
--- a/GeminiTest/Myc.Api.MarkdownStream.pas
+++ b/GeminiTest/Myc.Api.MarkdownStream.pas
@@ -14,12 +14,9 @@ uses
type
TBlockType = (btMarkdown, btRawText, btHtml);
- ///
- /// Manages an incremental Markdown/HTML stream within a TWebBrowser.
- /// Supports block-based rendering and collapsible raw text sections.
- ///
+ // Manages an incremental Markdown/HTML stream within a TWebBrowser.
+ // Supports block-based rendering and collapsible raw text sections.
IMarkdownStream = interface
- ['{A1B2C3D4-E5F6-4788-9900-112233445566}']
procedure InitializeBrowser(const Browser: TWebBrowser);
procedure BeginBlock(const AType: TBlockType; const ATitle: string = '');
procedure Append(const Content: string);
@@ -202,6 +199,7 @@ begin
+
@@ -282,8 +280,10 @@ begin
let currentTitle = "";
let currentBuffer = "";
- // Inline SVG für das Icon
+ // Icons
const chevronSvg = '';
+ const iconCopy = '';
+ const iconCheck = '';
function toggleBlock(header) {
const wrapper = header.closest('.raw-wrapper');
@@ -294,6 +294,35 @@ begin
}
}
+ function addCopyButtons(container) {
+ const pres = container.querySelectorAll("pre");
+ pres.forEach(function(preBlock) {
+ if (preBlock.parentNode.classList.contains("code-wrapper")) return;
+
+ const wrapper = document.createElement("div");
+ wrapper.className = "code-wrapper";
+ preBlock.parentNode.insertBefore(wrapper, preBlock);
+ wrapper.appendChild(preBlock);
+
+ const button = document.createElement("button");
+ button.className = "copy-btn";
+ button.innerHTML = iconCopy;
+ button.title = "Copy to Clipboard";
+
+ button.addEventListener("click", function() {
+ const code = preBlock.querySelector("code");
+ if (!code) return;
+ const text = code.innerText;
+ navigator.clipboard.writeText(text).then(function() {
+ button.innerHTML = iconCheck;
+ setTimeout(function() { button.innerHTML = iconCopy; }, 2000);
+ }, function(err) { console.error("Clipboard failed", err); });
+ });
+
+ wrapper.insertBefore(button, preBlock);
+ });
+ }
+
function renderTo(target, content, mode, title, isFinal) {
target.innerHTML = "";
if (mode === "markdown") {
@@ -306,6 +335,13 @@ begin
const c = document.createElement("div");
c.innerHTML = (typeof marked !== "undefined") ? marked.parse(content) : content;
target.appendChild(c);
+
+ // Highlight and add Copy Buttons
+ if (typeof hljs !== "undefined") {
+ c.querySelectorAll("pre code").forEach((block) => { hljs.highlightElement(block); });
+ }
+ addCopyButtons(c);
+
} else if (mode === "text") {
const wrapper = document.createElement("div");
wrapper.className = "raw-wrapper " + (isFinal ? "collapsed" : "expanded");
@@ -313,7 +349,6 @@ begin
const header = document.createElement("div");
header.className = "raw-header";
header.onclick = function() { toggleBlock(this); };
-
header.innerHTML = chevronSvg + '' + (title || "Raw Output") + '';
const code = document.createElement("pre");