1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
import { marked } from "https://esm.sh/marked";
export function main() {
document.getElementById("copy-markdownOutput").addEventListener("click", () => {
copyToClipboard("#markdownOutput", { html: false });
});
document.getElementById("copy-renderedMarkdown").addEventListener("click", () => {
copyToClipboard("#renderedMarkdown", { html: true });
});
document.getElementById("paste-htmlInput").addEventListener("click", () => {
pasteFromClipboard();
});
document.getElementById("htmlInput").addEventListener("keyup", () => {
convertToMarkdown();
});
}
export function slackHTMLToMarkdown(html) {
removeAbsolutePositioning();
const parser = new DOMParser();
const doc = parser.parseFromString(html, "text/html");
let lastSender = "";
let lastTimestamp = "";
let markdown = "";
function convertMessage(messageElement) {
function safeGetAttribute(selector, attribute) {
const element = messageElement.querySelector(selector);
return element ? element.getAttribute(attribute) : "";
}
const senderElement = messageElement.querySelector(`[data-qa="message_sender_name"]`);
const sender = senderElement ? senderElement.textContent.trim() : lastSender;
const ariaLabel = safeGetAttribute(".c-timestamp", "aria-label") || "";
const contentElements = messageElement.querySelectorAll(`.c-message_kit__blocks, [data-qa="file_name"]`);
const date = parseTimestamp(ariaLabel);
const isoDate = date.toISOString().split("T")[0];
const formattedTime = date.toLocaleTimeString("en-US", { hour: "2-digit", minute: "2-digit", hour12: true });
const formattedTimestamp = `${isoDate} ${formattedTime} EST`;
let messageMarkdown = "";
function processNode(node) {
if (node.nodeType === Node.TEXT_NODE) {
return node.textContent;
} else if (node.nodeName === "CODE") {
const codeContent = node.textContent;
if (node.parentNode.nodeName === "PRE") {
const language = node.className.replace("language-", "").split(" ")[0] || "";
return `\n\`\`\`${language}\n${codeContent}\n\`\`\`\n`;
} else {
return "`" + codeContent + "`";
}
} else if (node.nodeName === "PRE") {
const codeElement = node.querySelector("code");
if (codeElement) {
return processNode(codeElement);
} else {
return `\n\`\`\`\n${node.textContent}\n\`\`\`\n`;
}
} else if (node.nodeName === "OL" || node.nodeName === "UL") {
const listType = node.nodeName === "OL" ? "ordered" : "unordered";
const indent = parseInt(node.getAttribute("data-indent") || "0");
const listItems = Array.from(node.querySelectorAll("li")).map((item, index) => {
const indentation = " ".repeat(indent);
const bullet = listType === "ordered" ? `${index + 1}.` : "-";
return `${indentation}${bullet} ${processNode(item).trim()}`;
}).join("\n");
return `\n${listItems}\n`;
} else {
return Array.from(node.childNodes).map(processNode).join("");
}
}
if (contentElements.length > 0) {
messageMarkdown = Array.from(contentElements).map(processNode).join("\n");
}
if (messageMarkdown.trim()) {
if (sender !== lastSender || formattedTimestamp !== lastTimestamp) {
messageMarkdown = `__${sender}__ [${formattedTimestamp}]:\n${messageMarkdown}`;
lastSender = sender;
lastTimestamp = formattedTimestamp;
}
// Split the content by newlines and add blockquotes, except for code blocks
const lines = messageMarkdown.replaceAll(/\n\n+/g, "\n\n").split("\n");
let inCodeBlock = false;
let inList = false;
messageMarkdown = lines.map(line => {
if (line.startsWith("```")) {
inCodeBlock = !inCodeBlock;
return line;
} else if (inCodeBlock || line.startsWith("__")) {
inList = false;
return line;