import { SidebarOverlay } from "./overlay.js";
// Global helper for code block copying in Markdown view
window.moonCopyCode = function(button) {
try {
const container = button.parentElement;
if (!container) return;
const codeEl = container.querySelector("code");
if (!codeEl) return;
const text = codeEl.innerText;
navigator.clipboard.writeText(text).then(() => {
button.innerText = "Copied!";
button.style.backgroundColor = "#4CAF50";
button.style.borderColor = "#45a049";
button.style.color = "#ffffff";
setTimeout(() => {
button.innerText = "Copy";
button.style.backgroundColor = "#333333";
button.style.borderColor = "#555555";
button.style.color = "#aaaaaa";
}, 1500);
}).catch(() => {
button.innerText = "Error";
setTimeout(() => button.innerText = "Copy", 1500);
});
} catch (e) {
button.innerText = "Error";
setTimeout(() => button.innerText = "Copy", 1500);
}
};
function parseInlineMarkdown(html) {
if (!html) return "";
html = html.replace(/`([^`]+)`/g, '$1');
html = html.replace(/\*\*\*(.*?)\*\*\*/g, '$1');
html = html.replace(/\*\*(.*?)\*\*/g, '$1');
html = html.replace(/__(.*?)__/g, '$1');
html = html.replace(/\*(.*?)\*/g, '$1');
html = html.replace(/_(.*?)_/g, '$1');
html = html.replace(/~~(.*?)~~/g, '$1');
html = html.replace(/\[([^\]]+)\]\(([^)]+)\)/g, '$1');
html = html.replace(/\$(?!\s)([^\$]+?)(?$1');
return html;
}
function parseMarkdown(text) {
if (!text) return "";
let rawLines = text.replace(/&/g, "&").replace(/"); inUl = false; } }
function closeOl() { if (inOl) { htmlLines.push(""); inOl = false; } }
function closeTable() {
if (inTable) {
let tableHtml = `
| ${parseInlineMarkdown(tableHeaders[i])} | `; } tableHtml += `
|---|
| ${parseInlineMarkdown(row[i] || '')} | `; } tableHtml += `
${rCode}${quote}`; } htmlLines.push(quote); } continue; } if (line.startsWith("### ")) { closeUl(); closeOl(); htmlLines.push(`
${parseInlineMarkdown(line)}
`); } } closeUl(); closeOl(); closeTable(); return htmlLines.join("\n"); } export function createTextReader(textData, onSwitchMedia = () => {}, onDestroy = () => {}) { let rawText = typeof textData === "object" ? (textData.text || "") : String(textData); let pid = typeof textData === "object" ? textData.pid : ""; let isMarkdownMode = true; const overlay = new SidebarOverlay({ onDestroy }); const readerBox = document.createElement("div"); Object.assign(readerBox.style, { background: "var(--comfy-menu-bg, rgba(18, 18, 24, 0.95))", backdropFilter: "blur(12px)", border: "1px solid var(--border-color, rgba(255, 255, 255, 0.15))", borderRadius: "12px", padding: "20px 24px", width: "85%", maxWidth: "840px", height: "80vh", boxShadow: "0 12px 40px rgba(0,0,0,0.4)", display: "flex", flexDirection: "column", gap: "14px", zIndex: "20", color: "var(--fg-color, #eee)" }); const headerRow = document.createElement("div"); Object.assign(headerRow.style, { display: "flex", alignItems: "center", justifyContent: "space-between", borderBottom: "1px solid rgba(255,255,255,0.1)", paddingBottom: "12px" }); const titleInfo = document.createElement("div"); Object.assign(titleInfo.style, { display: "flex", alignItems: "center", gap: "10px" }); const title = document.createElement("span"); title.textContent = pid ? `Text Output #${pid}` : "Text Output"; Object.assign(title.style, { fontSize: "14px", fontWeight: "bold", color: "#f8fafc", whiteSpace: "nowrap" }); const statsBadge = document.createElement("span"); Object.assign(statsBadge.style, { fontSize: "10px", color: "#94a3b8", background: "rgba(255,255,255,0.06)", padding: "3px 8px", borderRadius: "4px", fontFamily: "monospace", whiteSpace: "nowrap" }); titleInfo.append(title, statsBadge); const actionsRow = document.createElement("div"); Object.assign(actionsRow.style, { display: "flex", gap: "8px", alignItems: "center" }); const modeBtn = document.createElement("button"); Object.assign(modeBtn.style, { background: "#334155", border: "1px solid #64748b", color: "#f8fafc", borderRadius: "6px", padding: "5px 12px", fontSize: "11px", cursor: "pointer", display: "inline-flex", alignItems: "center", justifyContent: "center", gap: "6px", whiteSpace: "nowrap", minWidth: "96px", height: "28px", boxSizing: "border-box" }); modeBtn.innerHTML = `Markdown`; const copyBtn = document.createElement("button"); Object.assign(copyBtn.style, { background: "#1e293b", border: "1px solid #475569", color: "#e2e8f0", borderRadius: "6px", padding: "5px 10px", fontSize: "11px", cursor: "pointer", display: "inline-flex", alignItems: "center", justifyContent: "center", gap: "6px", whiteSpace: "nowrap", height: "28px", boxSizing: "border-box" }); copyBtn.innerHTML = `Copy`; const downloadBtn = document.createElement("button"); Object.assign(downloadBtn.style, { background: "#1e293b", border: "1px solid #475569", color: "#e2e8f0", borderRadius: "6px", padding: "5px 10px", fontSize: "11px", cursor: "pointer", display: "inline-flex", alignItems: "center", justifyContent: "center", gap: "6px", whiteSpace: "nowrap", height: "28px", boxSizing: "border-box" }); downloadBtn.innerHTML = `.txt`; actionsRow.append(modeBtn, copyBtn, downloadBtn); headerRow.append(titleInfo, actionsRow); const textArea = document.createElement("div"); Object.assign(textArea.style, { flex: "1", overflowY: "auto", fontSize: "13px", lineHeight: "1.6", color: "var(--fg-color, #e2e8f0)", wordBreak: "break-word", userSelect: "text", "-webkit-user-select": "text", background: "var(--comfy-input-bg, rgba(0, 0, 0, 0.2))", padding: "16px 20px", borderRadius: "6px", border: "1px solid var(--border-color, rgba(255, 255, 255, 0.08))", scrollbarWidth: "thin" }); const updateView = () => { const wc = rawText.trim() ? rawText.trim().split(/\s+/).length : 0; statsBadge.textContent = `${wc} words • ${rawText.length} chars`; if (isMarkdownMode) { modeBtn.innerHTML = `Markdown`; modeBtn.style.background = "#3b82f6"; modeBtn.style.borderColor = "#60a5fa"; textArea.style.fontFamily = "sans-serif"; textArea.style.whiteSpace = "normal"; textArea.innerHTML = parseMarkdown(rawText); } else { modeBtn.innerHTML = `Raw Text`; modeBtn.style.background = "#334155"; modeBtn.style.borderColor = "#64748b"; textArea.style.fontFamily = "monospace"; textArea.style.whiteSpace = "pre-wrap"; textArea.textContent = rawText; } }; modeBtn.onclick = () => { isMarkdownMode = !isMarkdownMode; updateView(); }; copyBtn.onclick = () => { navigator.clipboard.writeText(rawText); copyBtn.innerHTML = `Copied!`; setTimeout(() => { copyBtn.innerHTML = `Copy`; }, 1800); }; downloadBtn.onclick = () => { const blob = new Blob([rawText], { type: "text/plain;charset=utf-8" }); const url = URL.createObjectURL(blob); const a = document.createElement("a"); a.href = url; a.download = `output_${pid || "text"}.txt`; a.click(); URL.revokeObjectURL(url); }; updateView(); readerBox.append(headerRow, textArea); overlay.container.appendChild(readerBox); return { isText: true, loadTarget(targetData) { if (typeof targetData !== "object" || !targetData.text) { overlay.destroy(); onSwitchMedia(targetData); return; } rawText = targetData.text || ""; pid = targetData.pid || ""; title.textContent = pid ? `Text Output #${pid}` : "Text Output"; updateView(); }, destroy: () => overlay.destroy() }; }