transcriptify 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (135) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +78 -0
  3. package/dist/src/generateHtml.d.ts +3 -0
  4. package/dist/src/generateHtml.js +303 -0
  5. package/dist/src/index.d.ts +5 -0
  6. package/dist/src/index.js +252 -0
  7. package/dist/src/transcript.d.ts +12 -0
  8. package/dist/src/transcript.js +179 -0
  9. package/dist/src/types/entities.d.ts +30 -0
  10. package/dist/src/types/entities.js +2 -0
  11. package/dist/src/utils/assetManager.d.ts +12 -0
  12. package/dist/src/utils/assetManager.js +295 -0
  13. package/dist/src/utils/authors.d.ts +4 -0
  14. package/dist/src/utils/authors.js +115 -0
  15. package/dist/src/utils/cache.d.ts +16 -0
  16. package/dist/src/utils/cache.js +29 -0
  17. package/dist/src/utils/extractors.d.ts +112 -0
  18. package/dist/src/utils/extractors.js +223 -0
  19. package/dist/src/utils/polls.d.ts +2 -0
  20. package/dist/src/utils/polls.js +91 -0
  21. package/dist/src/utils/transformer.d.ts +6 -0
  22. package/dist/src/utils/transformer.js +78 -0
  23. package/dist/src/utils/user.d.ts +4 -0
  24. package/dist/src/utils/user.js +56 -0
  25. package/dist/src/web/client.d.ts +20 -0
  26. package/dist/src/web/client.js +21 -0
  27. package/dist/src/web/discord-components/AudioPlayer.d.ts +5 -0
  28. package/dist/src/web/discord-components/AudioPlayer.js +231 -0
  29. package/dist/src/web/discord-components/Button.d.ts +3 -0
  30. package/dist/src/web/discord-components/Button.js +27 -0
  31. package/dist/src/web/discord-components/ChannelPinnedMessage.d.ts +7 -0
  32. package/dist/src/web/discord-components/ChannelPinnedMessage.js +11 -0
  33. package/dist/src/web/discord-components/DateSeperator.d.ts +3 -0
  34. package/dist/src/web/discord-components/DateSeperator.js +19 -0
  35. package/dist/src/web/discord-components/Embed.d.ts +2 -0
  36. package/dist/src/web/discord-components/Embed.js +78 -0
  37. package/dist/src/web/discord-components/ForwardedMessage.d.ts +2 -0
  38. package/dist/src/web/discord-components/ForwardedMessage.js +44 -0
  39. package/dist/src/web/discord-components/Message.d.ts +2 -0
  40. package/dist/src/web/discord-components/Message.js +543 -0
  41. package/dist/src/web/discord-components/PinnedMessagesModal.d.ts +6 -0
  42. package/dist/src/web/discord-components/PinnedMessagesModal.js +119 -0
  43. package/dist/src/web/discord-components/PinnedMessagesOverview.d.ts +5 -0
  44. package/dist/src/web/discord-components/PinnedMessagesOverview.js +22 -0
  45. package/dist/src/web/discord-components/Reply.d.ts +2 -0
  46. package/dist/src/web/discord-components/Reply.js +42 -0
  47. package/dist/src/web/discord-components/StickerPreview.d.ts +6 -0
  48. package/dist/src/web/discord-components/StickerPreview.js +40 -0
  49. package/dist/src/web/discord-components/ThemeSwitcher.d.ts +2 -0
  50. package/dist/src/web/discord-components/ThemeSwitcher.js +54 -0
  51. package/dist/src/web/discord-components/Transcript.d.ts +2 -0
  52. package/dist/src/web/discord-components/Transcript.js +174 -0
  53. package/dist/src/web/discord-components/UserJoinMessage.d.ts +3 -0
  54. package/dist/src/web/discord-components/UserJoinMessage.js +33 -0
  55. package/dist/src/web/discord-components/VideoPlayer.d.ts +6 -0
  56. package/dist/src/web/discord-components/VideoPlayer.js +222 -0
  57. package/dist/src/web/discord-components/icons/ChevronDownIcon.d.ts +1 -0
  58. package/dist/src/web/discord-components/icons/ChevronDownIcon.js +7 -0
  59. package/dist/src/web/discord-components/icons/CloseIcon.d.ts +1 -0
  60. package/dist/src/web/discord-components/icons/CloseIcon.js +7 -0
  61. package/dist/src/web/discord-components/icons/ExternalLinkIcon.d.ts +1 -0
  62. package/dist/src/web/discord-components/icons/ExternalLinkIcon.js +7 -0
  63. package/dist/src/web/discord-components/icons/FileAudioIcon.d.ts +1 -0
  64. package/dist/src/web/discord-components/icons/FileAudioIcon.js +7 -0
  65. package/dist/src/web/discord-components/icons/FileCodeIcon.d.ts +1 -0
  66. package/dist/src/web/discord-components/icons/FileCodeIcon.js +7 -0
  67. package/dist/src/web/discord-components/icons/FileDocumentIcon.d.ts +1 -0
  68. package/dist/src/web/discord-components/icons/FileDocumentIcon.js +7 -0
  69. package/dist/src/web/discord-components/icons/PinIcon.d.ts +1 -0
  70. package/dist/src/web/discord-components/icons/PinIcon.js +7 -0
  71. package/dist/src/web/discord-components/icons/VerifiedIcon.d.ts +1 -0
  72. package/dist/src/web/discord-components/icons/VerifiedIcon.js +7 -0
  73. package/dist/src/web/discord-components/index.d.ts +11 -0
  74. package/dist/src/web/discord-components/index.js +24 -0
  75. package/dist/src/web/discord-components/messageHelpers.d.ts +8 -0
  76. package/dist/src/web/discord-components/messageHelpers.js +72 -0
  77. package/dist/src/web/discord-components/themeColors.d.ts +9 -0
  78. package/dist/src/web/discord-components/themeColors.js +320 -0
  79. package/dist/src/web/discord-components/transcriptHelpers.d.ts +19 -0
  80. package/dist/src/web/discord-components/transcriptHelpers.js +120 -0
  81. package/dist/src/web/discord-components/types.d.ts +1 -0
  82. package/dist/src/web/discord-components/types.js +2 -0
  83. package/dist/src/web/discord-components/utils/date.d.ts +3 -0
  84. package/dist/src/web/discord-components/utils/date.js +50 -0
  85. package/dist/src/web/discord-components/utils/markdown.d.ts +11 -0
  86. package/dist/src/web/discord-components/utils/markdown.js +538 -0
  87. package/dist/src/web/discord-components/utils/markdownUtils.d.ts +12 -0
  88. package/dist/src/web/discord-components/utils/markdownUtils.js +140 -0
  89. package/dist/src/web/helpers/avatarHelpers.d.ts +2 -0
  90. package/dist/src/web/helpers/avatarHelpers.js +15 -0
  91. package/dist/src/web/helpers/cdnHelpers.d.ts +5 -0
  92. package/dist/src/web/helpers/cdnHelpers.js +48 -0
  93. package/dist/src/web/helpers/contentHelpers.d.ts +9 -0
  94. package/dist/src/web/helpers/contentHelpers.js +41 -0
  95. package/dist/src/web/helpers/renderContent.d.ts +2 -0
  96. package/dist/src/web/helpers/renderContent.js +15 -0
  97. package/dist/src/web/helpers/scrollHelpers.d.ts +2 -0
  98. package/dist/src/web/helpers/scrollHelpers.js +31 -0
  99. package/dist/src/web/helpers/timestampHelpers.d.ts +6 -0
  100. package/dist/src/web/helpers/timestampHelpers.js +66 -0
  101. package/dist/src/web/hooks/useMessageContent.d.ts +5 -0
  102. package/dist/src/web/hooks/useMessageContent.js +37 -0
  103. package/dist/src/web/index.d.ts +1 -0
  104. package/dist/src/web/index.js +17 -0
  105. package/dist/src/web/types/attachment.d.ts +6 -0
  106. package/dist/src/web/types/attachment.js +2 -0
  107. package/dist/src/web/types/author.d.ts +14 -0
  108. package/dist/src/web/types/author.js +2 -0
  109. package/dist/src/web/types/channel.d.ts +8 -0
  110. package/dist/src/web/types/channel.js +2 -0
  111. package/dist/src/web/types/embed.d.ts +52 -0
  112. package/dist/src/web/types/embed.js +2 -0
  113. package/dist/src/web/types/interaction.d.ts +8 -0
  114. package/dist/src/web/types/interaction.js +2 -0
  115. package/dist/src/web/types/markdown.d.ts +5 -0
  116. package/dist/src/web/types/markdown.js +2 -0
  117. package/dist/src/web/types/message.d.ts +73 -0
  118. package/dist/src/web/types/message.js +2 -0
  119. package/dist/src/web/types/poll.d.ts +11 -0
  120. package/dist/src/web/types/poll.js +2 -0
  121. package/dist/src/web/types/props.d.ts +155 -0
  122. package/dist/src/web/types/props.js +2 -0
  123. package/dist/src/web/types/reaction.d.ts +6 -0
  124. package/dist/src/web/types/reaction.js +2 -0
  125. package/dist/src/web/types/theme.d.ts +14 -0
  126. package/dist/src/web/types/theme.js +2 -0
  127. package/dist/src/web/types/ui.d.ts +10 -0
  128. package/dist/src/web/types/ui.js +2 -0
  129. package/dist/types/download.d.ts +12 -0
  130. package/dist/types/download.js +2 -0
  131. package/dist/types/exportableTranscript.d.ts +169 -0
  132. package/dist/types/exportableTranscript.js +2 -0
  133. package/dist/types/general.d.ts +90 -0
  134. package/dist/types/general.js +2 -0
  135. package/package.json +46 -0
@@ -0,0 +1,538 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.parseMarkdown = parseMarkdown;
7
+ const jsx_runtime_1 = require("react/jsx-runtime");
8
+ const react_1 = __importDefault(require("react"));
9
+ const common_1 = __importDefault(require("highlight.js/lib/common"));
10
+ const cdnHelpers_1 = require("../../helpers/cdnHelpers");
11
+ const markdownUtils_1 = require("./markdownUtils");
12
+ let __parseMarkdownId = 0;
13
+ function normalizeCodeLang(lang) {
14
+ if (!lang)
15
+ return undefined;
16
+ const s = lang.toLowerCase();
17
+ if (s === "py")
18
+ return "python";
19
+ if (s === "js")
20
+ return "javascript";
21
+ if (s === "ts")
22
+ return "typescript";
23
+ if (s === "jsx")
24
+ return "javascript";
25
+ if (s === "tsx")
26
+ return "typescript";
27
+ return s;
28
+ }
29
+ function getHighlightedHtml(code, lang) {
30
+ const normalized = normalizeCodeLang(lang);
31
+ const useLang = normalized && common_1.default.getLanguage(normalized) ? normalized : undefined;
32
+ const result = useLang ? common_1.default.highlight(code, { language: useLang, ignoreIllegals: true }) : common_1.default.highlightAuto(code);
33
+ return result.value || (0, markdownUtils_1.escapeHtml)(code);
34
+ }
35
+ function Spoiler({ children }) {
36
+ const [revealed, setRevealed] = react_1.default.useState(false);
37
+ return ((0, jsx_runtime_1.jsx)("span", { className: "rounded px-0.5 cursor-pointer transition-colors", style: {
38
+ backgroundColor: revealed ? "#2b2d31" : "#202225",
39
+ color: revealed ? "#dcddde" : "transparent",
40
+ userSelect: revealed ? "text" : "none"
41
+ }, onClick: () => setRevealed(!revealed), title: revealed ? "Hide" : "Click to reveal spoiler", children: children }));
42
+ }
43
+ function renderTokens(tokens, keyBase = 0) {
44
+ const nodes = [];
45
+ tokens.forEach((t, i) => {
46
+ const key = `${keyBase}-${i}`;
47
+ const rawVal = t.value ?? "";
48
+ let v = "";
49
+ if (typeof rawVal === "string") {
50
+ v = rawVal;
51
+ }
52
+ else if (rawVal && typeof rawVal === "object") {
53
+ v = rawVal.name || rawVal.text || rawVal.value || "";
54
+ }
55
+ if (rawVal && typeof rawVal === "object" && Array.isArray(rawVal.tokens)) {
56
+ nodes.push((0, jsx_runtime_1.jsx)(react_1.default.Fragment, { children: renderTokens(rawVal.tokens, Number(`${keyBase}${i}`)) }, key));
57
+ return;
58
+ }
59
+ switch (t.type) {
60
+ case "text":
61
+ nodes.push(v);
62
+ break;
63
+ case "strike":
64
+ case "s":
65
+ case "strikethrough":
66
+ case "del":
67
+ case "deleted":
68
+ nodes.push((0, jsx_runtime_1.jsx)("s", { style: { textDecoration: "line-through", opacity: 0.8 }, children: v }, key));
69
+ break;
70
+ case "inlineCode":
71
+ nodes.push((0, jsx_runtime_1.jsx)("code", { className: "bg-[#1e1f22] text-[#dbdee1] px-1 rounded text-[0.875rem] font-mono", children: v }, key));
72
+ break;
73
+ case "spoiler":
74
+ nodes.push((0, jsx_runtime_1.jsx)(Spoiler, { children: v }, key));
75
+ break;
76
+ case "codeBlock": {
77
+ const lang = t.meta?.lang || undefined;
78
+ const codeContent = typeof rawVal === "string" ? rawVal : (rawVal && rawVal.raw) || "";
79
+ const langForHighlight = normalizeCodeLang(lang);
80
+ const highlighted = getHighlightedHtml(codeContent, langForHighlight);
81
+ nodes.push((0, jsx_runtime_1.jsxs)("div", { className: "relative my-2 rounded bg-[#2B2D31] border border-[#1E1F22]", children: [(0, jsx_runtime_1.jsx)("div", { className: "absolute top-1 right-2 px-2 py-0.5 text-xs text-[#B5BAC1]", children: lang || "code" }), (0, jsx_runtime_1.jsx)("pre", { className: "p-3 pt-6 text-sm overflow-x-auto text-[#DBDEE1]", children: (0, jsx_runtime_1.jsx)("code", { className: langForHighlight ? `language-${langForHighlight} hljs` : "hljs", dangerouslySetInnerHTML: { __html: highlighted } }) })] }, key));
82
+ break;
83
+ }
84
+ default:
85
+ nodes.push(v);
86
+ }
87
+ });
88
+ return nodes;
89
+ }
90
+ function parseMarkdown(text, resolvedUsers, resolvedRoles, resolvedChannels, currentGuildId) {
91
+ if (!text && text !== 0)
92
+ return null;
93
+ if (text && typeof text === "object" && Array.isArray(text.tokens)) {
94
+ const tokens = text.tokens;
95
+ const allTextOnly = tokens.every((t) => t.type === "text");
96
+ if (allTextOnly) {
97
+ return parseMarkdown(tokens.map((t) => t.value ?? "").join(""), resolvedUsers, resolvedRoles, resolvedChannels, currentGuildId);
98
+ }
99
+ return (0, jsx_runtime_1.jsx)(react_1.default.Fragment, { children: react_1.default.Children.toArray(renderTokens(tokens)) }, `pm-${++__parseMarkdownId}`);
100
+ }
101
+ let raw = typeof text === "string"
102
+ ? text
103
+ : text && typeof text.raw === "string"
104
+ ? text.raw
105
+ : text && typeof text.content === "string"
106
+ ? text.content
107
+ : text && typeof text.text === "string"
108
+ ? text.text
109
+ : typeof text === "number"
110
+ ? String(text)
111
+ : "";
112
+ raw = raw.replace(/<@!?(\d+)>/g, (_match, id, offset, src) => {
113
+ const resolveUserName = (map, uid) => {
114
+ if (!map || !uid)
115
+ return null;
116
+ const direct = map[uid];
117
+ if (direct && (direct.displayName || direct.username))
118
+ return direct.displayName || direct.username || null;
119
+ const nId = Number(uid);
120
+ if (!Number.isNaN(nId)) {
121
+ for (const k of Object.keys(map)) {
122
+ if (Number(k) === nId) {
123
+ const v = map[k];
124
+ if (v && (v.displayName || v.username))
125
+ return v.displayName || v.username || null;
126
+ }
127
+ }
128
+ }
129
+ for (const k of Object.keys(map)) {
130
+ if (String(k).trim() === String(uid).trim()) {
131
+ const v = map[k];
132
+ if (v && (v.displayName || v.username))
133
+ return v.displayName || v.username || null;
134
+ }
135
+ }
136
+ return null;
137
+ };
138
+ const name = resolveUserName(resolvedUsers, id) || "unknown-user";
139
+ const safe = (0, markdownUtils_1.escapeHtml)(String(name));
140
+ const nextChar = src.charAt(offset + _match.length) || "";
141
+ const needsSpace = nextChar && !/\s/.test(nextChar);
142
+ return `<user>${safe}</user>${needsSpace ? " " : ""}`;
143
+ });
144
+ raw = raw.replace(/<@&(\d+)>/g, (_match, roleId) => {
145
+ const role = resolvedRoles?.[roleId];
146
+ if (role && role.name) {
147
+ return `<role${role.color && ` data-color="${role.color}"`}>${(0, markdownUtils_1.escapeHtml)(String(role.name))}</role>`;
148
+ }
149
+ return "unknown-role";
150
+ });
151
+ const resolveChannelName = (map, id) => {
152
+ if (!map || !id)
153
+ return null;
154
+ const direct = map[id];
155
+ if (direct && direct.name)
156
+ return direct.name;
157
+ const nId = Number(id);
158
+ if (!Number.isNaN(nId)) {
159
+ for (const k of Object.keys(map)) {
160
+ if (Number(k) === nId) {
161
+ const v = map[k];
162
+ if (v && v.name)
163
+ return v.name;
164
+ }
165
+ }
166
+ }
167
+ for (const k of Object.keys(map)) {
168
+ if (String(k).trim() === String(id).trim()) {
169
+ const v = map[k];
170
+ if (v && v.name)
171
+ return v.name;
172
+ }
173
+ }
174
+ return null;
175
+ };
176
+ raw = raw.replace(/<#(\d+)>/g, (_match, channelId) => {
177
+ const name = resolveChannelName(resolvedChannels, channelId);
178
+ if (name)
179
+ return `<channel data-id="${channelId}">${(0, markdownUtils_1.escapeHtml)(String(name))}</channel>`;
180
+ return `<channel data-id="${channelId}">unknown</channel>`;
181
+ });
182
+ raw = raw.replace(/https:\/\/discord\.com\/channels\/(\d+)\/(\d+)(?:\/(\d+))?/g, (_match, _guildId, channelId) => {
183
+ const name = resolveChannelName(resolvedChannels, channelId);
184
+ if (name)
185
+ return `<channel data-id="${channelId}">${(0, markdownUtils_1.escapeHtml)(String(name))}</channel>`;
186
+ return `<channel data-id="${channelId}">unknown</channel>`;
187
+ });
188
+ raw = raw
189
+ .replace(/<br\s*\/?>(\r?\n)?/gi, "\n")
190
+ .replace(/<strong>([\s\S]*?)<\/strong>/gi, "**$1**")
191
+ .replace(/<b>([\s\S]*?)<\/b>/gi, "**$1**")
192
+ .replace(/<u>([\s\S]*?)<\/u>/gi, "__$1__")
193
+ .replace(/<em>([\s\S]*?)<\/em>/gi, "*$1*")
194
+ .replace(/<i>([\s\S]*?)<\/i>/gi, "*$1*")
195
+ .replace(/<s>([\s\S]*?)<\/s>/gi, "~~$1~~");
196
+ raw = raw.replace(/\\([*_~`\\_])/g, "$1");
197
+ const linesForIndentCalc = raw.split("\n");
198
+ let minIndent = null;
199
+ for (const l of linesForIndentCalc) {
200
+ if (!l.trim())
201
+ continue;
202
+ const m = l.match(/^\s*/);
203
+ const indent = m ? m[0].length : 0;
204
+ if (minIndent === null || indent < minIndent)
205
+ minIndent = indent;
206
+ }
207
+ if (minIndent && minIndent > 0) {
208
+ raw = linesForIndentCalc.map((l) => (l.startsWith(" ".repeat(minIndent)) ? l.slice(minIndent) : l)).join("\n");
209
+ }
210
+ const segments = (0, markdownUtils_1.splitFencedCodeBlocks)(raw);
211
+ const outputNodes = [];
212
+ const processTextSegment = (txt) => {
213
+ const lines = txt.split("\n");
214
+ const processedLines = [];
215
+ let quoteBuffer = [];
216
+ lines.forEach((line, idx) => {
217
+ const trimmedLine = line.trimStart();
218
+ const isQuote = trimmedLine.startsWith(">");
219
+ if (isQuote) {
220
+ const quoteLine = trimmedLine.replace(/^>\s?/, "");
221
+ quoteBuffer.push(quoteLine);
222
+ }
223
+ else {
224
+ if (quoteBuffer.length > 0) {
225
+ processedLines.push((0, jsx_runtime_1.jsx)("div", { className: "my-1 pl-3 border-l-4 border-[#4e505880] text-[#dbdee1]", children: quoteBuffer.map((qLine, qIdx) => ((0, jsx_runtime_1.jsxs)(react_1.default.Fragment, { children: [processSimpleMarkdown(qLine, resolvedChannels), qIdx < quoteBuffer.length - 1 ? (0, jsx_runtime_1.jsx)("br", {}) : null] }, qIdx))) }, `quote-${idx}`));
226
+ quoteBuffer = [];
227
+ }
228
+ const headingMatch = trimmedLine.match(/^(#{1,6})\s+(.+)$/);
229
+ if (headingMatch) {
230
+ const level = headingMatch[1].length;
231
+ const text = headingMatch[2];
232
+ const HeadingTag = `h${level}`;
233
+ processedLines.push((0, jsx_runtime_1.jsx)(HeadingTag, { style: { fontWeight: 600, margin: "0.5rem 0" }, children: processSimpleMarkdown(text, resolvedChannels) }, idx));
234
+ }
235
+ else if (trimmedLine.startsWith("-#")) {
236
+ let text = trimmedLine.slice(2);
237
+ if (text.startsWith(" "))
238
+ text = text.slice(1);
239
+ const inner = processSimpleMarkdown(text, resolvedChannels);
240
+ const smallContent = scaleImagesInNode(inner, true);
241
+ processedLines.push((0, jsx_runtime_1.jsx)("small", { style: { fontSize: "0.875rem", opacity: 0.8 }, children: (0, jsx_runtime_1.jsx)("span", { children: smallContent }) }, idx));
242
+ if (idx < lines.length - 1)
243
+ processedLines.push((0, jsx_runtime_1.jsx)("br", {}, `br-${idx}`));
244
+ }
245
+ else if (trimmedLine.startsWith("- ")) {
246
+ const text = trimmedLine.slice(2);
247
+ processedLines.push((0, jsx_runtime_1.jsx)("li", { style: { marginLeft: "1.5rem" }, children: processSimpleMarkdown(text, resolvedChannels) }, idx));
248
+ }
249
+ else {
250
+ processedLines.push((0, jsx_runtime_1.jsxs)(react_1.default.Fragment, { children: [processSimpleMarkdown(line, resolvedChannels), idx < lines.length - 1 ? (0, jsx_runtime_1.jsx)("br", {}) : null] }, idx));
251
+ }
252
+ }
253
+ });
254
+ if (quoteBuffer.length > 0) {
255
+ processedLines.push((0, jsx_runtime_1.jsx)("div", { className: "my-1 pl-3 border-l-4 border-[#4e505880] text-[#dbdee1]", children: quoteBuffer.map((qLine, qIdx) => ((0, jsx_runtime_1.jsxs)(react_1.default.Fragment, { children: [processSimpleMarkdown(qLine, resolvedChannels), qIdx < quoteBuffer.length - 1 ? (0, jsx_runtime_1.jsx)("br", {}) : null] }, qIdx))) }, "quote-end"));
256
+ }
257
+ return (0, jsx_runtime_1.jsx)(jsx_runtime_1.Fragment, { children: react_1.default.Children.toArray(processedLines) });
258
+ };
259
+ segments.forEach((seg, sIdx) => {
260
+ if (seg.type === "text") {
261
+ outputNodes.push((0, jsx_runtime_1.jsx)(react_1.default.Fragment, { children: processTextSegment(seg.value) }, `seg-${sIdx}`));
262
+ }
263
+ else if (seg.type === "code") {
264
+ const lang = seg.lang;
265
+ const code = seg.value || "";
266
+ const codeText = code || "";
267
+ const detectedLang = (lang || "").toLowerCase();
268
+ const langForHighlight = normalizeCodeLang(detectedLang);
269
+ const highlighted = getHighlightedHtml(codeText, langForHighlight);
270
+ outputNodes.push((0, jsx_runtime_1.jsxs)("div", { className: "relative my-2 rounded bg-[#2B2D31] border border-[#1E1F22]", children: [(0, jsx_runtime_1.jsx)("div", { className: "absolute top-1 right-2 px-2 py-0.5 text-xs text-[#B5BAC1]", children: lang || "code" }), (0, jsx_runtime_1.jsx)("pre", { className: "p-3 pt-6 text-sm overflow-x-auto text-[#DBDEE1]", children: (0, jsx_runtime_1.jsx)("code", { className: langForHighlight ? `language-${langForHighlight} hljs` : "hljs", dangerouslySetInnerHTML: { __html: highlighted } }) })] }, `code-${sIdx}`));
271
+ }
272
+ });
273
+ return (0, jsx_runtime_1.jsx)(react_1.default.Fragment, { children: react_1.default.Children.toArray(outputNodes) }, `pm-${++__parseMarkdownId}`);
274
+ }
275
+ function processSimpleMarkdown(line, resolvedChannels) {
276
+ const timestampMatches = [];
277
+ const processed = line.replace(/<t:(\d+)(?::([tTdDfFR]))?>/g, (match, timestamp, style) => {
278
+ const placeholder = `__TIMESTAMP_${timestampMatches.length}__`;
279
+ timestampMatches.push({ timestamp, style: style || "f", formatted: (0, markdownUtils_1.parseDiscordTimestamp)(parseInt(timestamp, 10), style || "f") });
280
+ return placeholder;
281
+ });
282
+ const regex = /(`[^`]+`)|(\|\|([^|]+)\|\|)|(~~(.+?)~~)|(\*\*\*(.+?)\*\*\*)|(\*\*(.+?)\*\*)|(__TIMESTAMP_(\d+)__)|(__([\s\S]+?)__)|\*(.+?)\*|(<\/([A-Za-z0-9_-]+):(\d+)>)|(<user>[^<]+<\/user>)|(<role[^>]*>[^<]+<\/role>)|(<channel[^>]*>[^<]+<\/channel>)/g;
283
+ let lastIndex = 0;
284
+ let match;
285
+ const parts = [];
286
+ while ((match = regex.exec(processed)) !== null) {
287
+ if (match.index > lastIndex)
288
+ parts.push((0, jsx_runtime_1.jsx)(react_1.default.Fragment, { children: processLinks(processed.substring(lastIndex, match.index)) }, `text-${lastIndex}`));
289
+ if (match[1]) {
290
+ const code = match[1].slice(1, -1);
291
+ parts.push((0, jsx_runtime_1.jsx)("code", { className: "bg-[#1e1f22] text-[#dbdee1] px-1 rounded text-[0.875rem] font-mono", children: code }, match.index));
292
+ }
293
+ else if (match[2]) {
294
+ const text = match[3];
295
+ parts.push((0, jsx_runtime_1.jsx)(Spoiler, { children: parseInline(text) }, match.index));
296
+ }
297
+ else if (match[4]) {
298
+ const text = match[5];
299
+ parts.push((0, jsx_runtime_1.jsx)("s", { style: { textDecoration: "line-through", opacity: 0.8 }, children: parseInline(text) }, match.index));
300
+ }
301
+ else if (match[6]) {
302
+ const text = match[7];
303
+ parts.push((0, jsx_runtime_1.jsx)("strong", { style: { fontWeight: 600 }, children: (0, jsx_runtime_1.jsx)("em", { style: { fontStyle: "italic" }, children: parseInline(text) }) }, match.index));
304
+ }
305
+ else if (match[8]) {
306
+ const text = match[9];
307
+ parts.push((0, jsx_runtime_1.jsx)("strong", { style: { fontWeight: 600 }, children: parseInline(text) }, match.index));
308
+ }
309
+ else if (match[10]) {
310
+ const idx = parseInt(match[11], 10);
311
+ const { formatted } = timestampMatches[idx];
312
+ parts.push((0, jsx_runtime_1.jsx)("span", { className: "bg-[#5865f21a] text-[#00a8fc] px-0.5 rounded cursor-default", title: formatted, children: formatted }, match.index));
313
+ }
314
+ else if (match[12]) {
315
+ const text = match[13];
316
+ parts.push((0, jsx_runtime_1.jsx)("u", { style: { textDecoration: "underline", textDecorationThickness: "1px" }, children: parseInline(text) }, match.index));
317
+ }
318
+ else if (match[14]) {
319
+ const text = match[14].slice(1, -1);
320
+ parts.push((0, jsx_runtime_1.jsx)("em", { style: { fontStyle: "italic" }, children: parseInline(text) }, match.index));
321
+ }
322
+ else if (match[15]) {
323
+ const commandName = match[16];
324
+ parts.push((0, jsx_runtime_1.jsx)("span", { className: "inline-flex items-center rounded px-2 py-0.5 text-sm font-medium mr-1", style: { backgroundColor: "#1d193f", color: "#9697ec" }, children: `/${commandName}` }, match.index));
325
+ }
326
+ else if (match[18]) {
327
+ const userTag = match[18];
328
+ let userName = userTag.replace(/<\/?user>/g, "");
329
+ userName = String(userName).replace(/\s+/g, " ").trim();
330
+ const display = `@${userName}`;
331
+ parts.push((0, jsx_runtime_1.jsx)("span", { className: "bg-[#5865f233] text-[#c9d1ff] px-0.5 rounded cursor-pointer hover:bg-[#5865f24d] transition-colors", style: { fontWeight: 500 }, children: display }, match.index));
332
+ try {
333
+ const nextChar = processed.charAt(regex.lastIndex) || "";
334
+ if (nextChar && !/\s/.test(nextChar)) {
335
+ parts.push((0, jsx_runtime_1.jsx)(react_1.default.Fragment, { children: " " }, `sp-${match.index}`));
336
+ }
337
+ }
338
+ catch (_) { }
339
+ }
340
+ else if (match[19]) {
341
+ const roleTag = match[19];
342
+ const colorMatch = roleTag.match(/data-color="([^"]+)"/);
343
+ const roleName = roleTag.replace(/<role[^>]*>|<\/role>/g, "");
344
+ if (colorMatch) {
345
+ const rawColor = colorMatch[1];
346
+ let colorInt = 0;
347
+ if (/^#?[0-9a-fA-F]{6}$/.test(rawColor)) {
348
+ colorInt = parseInt(rawColor.replace("#", ""), 16);
349
+ }
350
+ else if (/^0x[0-9a-fA-F]+$/.test(rawColor)) {
351
+ colorInt = parseInt(rawColor.replace("0x", ""), 16);
352
+ }
353
+ else {
354
+ colorInt = parseInt(rawColor, 10) || 0;
355
+ }
356
+ const r = (colorInt >> 16) & 0xff;
357
+ const g = (colorInt >> 8) & 0xff;
358
+ const b = colorInt & 0xff;
359
+ const textColor = `rgb(${r}, ${g}, ${b})`;
360
+ const bgColor = `rgba(${r}, ${g}, ${b}, 0.12)`;
361
+ parts.push((0, jsx_runtime_1.jsx)("span", { className: "px-0.5 rounded cursor-pointer transition-opacity hover:opacity-80", style: { color: textColor, backgroundColor: bgColor, fontWeight: 500 }, children: `@${roleName}` }, match.index));
362
+ }
363
+ else {
364
+ parts.push((0, jsx_runtime_1.jsx)("span", { children: `@${roleName}` }, match.index));
365
+ }
366
+ }
367
+ else if (match[20]) {
368
+ const channelTag = match[20];
369
+ const idMatch = channelTag.match(/data-id=\"([^\"]+)\"/);
370
+ const channelIdAttr = idMatch ? idMatch[1] : undefined;
371
+ const innerText = channelTag.replace(/<channel[^>]*>|<\/channel>/g, "");
372
+ let displayName = innerText;
373
+ if (channelIdAttr) {
374
+ const resolved = (0, markdownUtils_1.resolveChannelNameGlobal)(resolvedChannels, channelIdAttr);
375
+ if (resolved)
376
+ displayName = `#${resolved}`;
377
+ }
378
+ parts.push((0, jsx_runtime_1.jsx)("span", { className: "bg-[#5865f21a] text-[#00a8fc] px-0.5 rounded cursor-pointer hover:bg-[#5865f233] transition-colors", style: { fontWeight: 500 }, "data-channel-id": channelIdAttr, children: displayName }, match.index));
379
+ }
380
+ lastIndex = regex.lastIndex;
381
+ }
382
+ if (lastIndex < processed.length)
383
+ parts.push((0, jsx_runtime_1.jsx)(react_1.default.Fragment, { children: processLinks(processed.substring(lastIndex)) }, `text-${lastIndex}`));
384
+ return (0, jsx_runtime_1.jsx)(jsx_runtime_1.Fragment, { children: react_1.default.Children.toArray(parts) });
385
+ }
386
+ function processLinks(str) {
387
+ const mdRegex = /\[([^\]]+)\]\(\s*(?:<\s*(https?:\/\/[^>\s]+)\s*>|(https?:\/\/[^\s)]+))\s*\)/g;
388
+ const segments = [];
389
+ let lastIdx = 0;
390
+ let mdMatch;
391
+ while ((mdMatch = mdRegex.exec(str)) !== null) {
392
+ if (mdMatch.index > lastIdx) {
393
+ segments.push((0, jsx_runtime_1.jsx)(react_1.default.Fragment, { children: processBareLinks(str.substring(lastIdx, mdMatch.index)) }, `seg-${lastIdx}`));
394
+ }
395
+ const label = mdMatch[1];
396
+ const url = (mdMatch[2] || mdMatch[3]);
397
+ if (!url || !/^https?:\/\//i.test(url)) {
398
+ segments.push((0, jsx_runtime_1.jsx)(react_1.default.Fragment, { children: label }, `mdlink-${mdMatch.index}`));
399
+ }
400
+ else {
401
+ segments.push((0, jsx_runtime_1.jsx)("a", { href: url, style: { color: "blue" }, target: "_blank", rel: "noreferrer", children: label }, `mdlink-${mdMatch.index}`));
402
+ }
403
+ lastIdx = mdRegex.lastIndex;
404
+ }
405
+ if (lastIdx < str.length) {
406
+ segments.push((0, jsx_runtime_1.jsx)(react_1.default.Fragment, { children: processBareLinks(str.substring(lastIdx)) }, `seg-${lastIdx}`));
407
+ }
408
+ return (0, jsx_runtime_1.jsx)(jsx_runtime_1.Fragment, { children: react_1.default.Children.toArray(segments) });
409
+ }
410
+ function processBareLinks(str) {
411
+ const urlRegex = /<\s*(https?:\/\/[^>\s]+)\s*>|(https?:\/\/[^\s)]+)/g;
412
+ const parts = [];
413
+ let last = 0;
414
+ let match;
415
+ while ((match = urlRegex.exec(str)) !== null) {
416
+ if (match.index > last)
417
+ parts.push((0, jsx_runtime_1.jsx)(react_1.default.Fragment, { children: str.substring(last, match.index) }, `text-${last}`));
418
+ const url = (match[1] || match[2]);
419
+ const isSticker = /\/stickers\/\d+\.webp(\?.*)?$/i.test(url) || url.includes("/stickers/");
420
+ if (isSticker) {
421
+ parts.push((0, jsx_runtime_1.jsx)("img", { src: url, alt: "sticker", className: "inline h-10 w-10 align-text-bottom mr-0.5" }, `sticker-${match.index}`));
422
+ }
423
+ else {
424
+ parts.push((0, jsx_runtime_1.jsx)("a", { href: url, style: { color: "blue" }, target: "_blank", rel: "noreferrer", children: url }, `u-${match.index}`));
425
+ }
426
+ last = urlRegex.lastIndex;
427
+ }
428
+ if (last < str.length)
429
+ parts.push((0, jsx_runtime_1.jsx)(react_1.default.Fragment, { children: str.substring(last) }, `text-${last}`));
430
+ const rendered = parts.map((p, i) => {
431
+ if (typeof p === "string") {
432
+ return (0, jsx_runtime_1.jsx)(react_1.default.Fragment, { children: processEmojis(p) }, `emoji-${i}`);
433
+ }
434
+ if (react_1.default.isValidElement(p) && p.props && typeof p.props.children === "string") {
435
+ return (0, jsx_runtime_1.jsx)(react_1.default.Fragment, { children: processEmojis(p.props.children) }, `emoji-frag-${i}`);
436
+ }
437
+ return p;
438
+ });
439
+ return (0, jsx_runtime_1.jsx)(jsx_runtime_1.Fragment, { children: react_1.default.Children.toArray(rendered) });
440
+ }
441
+ function processEmojis(str) {
442
+ const nodes = [];
443
+ const emojiRegex = /<(a)?:([^:>]+):(\d+)>/g;
444
+ let last = 0;
445
+ let m;
446
+ while ((m = emojiRegex.exec(str)) !== null) {
447
+ if (m.index > last)
448
+ nodes.push(str.substring(last, m.index));
449
+ const animated = !!m[1];
450
+ const name = m[2];
451
+ const id = m[3];
452
+ const src = (0, cdnHelpers_1.buildEmojiCdnUrl)(id, animated, 96) || "";
453
+ if (src) {
454
+ nodes.push((0, jsx_runtime_1.jsx)("img", { src: src, alt: name, className: "inline h-5 w-5 align-text-bottom mr-0.5" }, `e-${id}-${m.index}`));
455
+ }
456
+ last = emojiRegex.lastIndex;
457
+ }
458
+ if (last < str.length)
459
+ nodes.push(str.substring(last));
460
+ return (0, jsx_runtime_1.jsx)(jsx_runtime_1.Fragment, { children: react_1.default.Children.toArray(nodes) });
461
+ }
462
+ function scaleImagesInNode(node, small) {
463
+ if (!small)
464
+ return node;
465
+ if (node === null || node === undefined)
466
+ return node;
467
+ if (typeof node === "string" || typeof node === "number")
468
+ return node;
469
+ if (Array.isArray(node)) {
470
+ const mapped = node.map((n, i) => {
471
+ const scaled = scaleImagesInNode(n, small);
472
+ if (Array.isArray(scaled)) {
473
+ return react_1.default.Children.toArray(scaled).map((c, j) => react_1.default.isValidElement(c) ? react_1.default.cloneElement(c, { key: `si-${i}-${j}` }) : react_1.default.createElement(react_1.default.Fragment, { key: `si-${i}-${j}` }, c));
474
+ }
475
+ if (react_1.default.isValidElement(scaled)) {
476
+ if (scaled.key == null)
477
+ return react_1.default.cloneElement(scaled, { key: `si-${i}` });
478
+ return scaled;
479
+ }
480
+ return react_1.default.createElement(react_1.default.Fragment, { key: `si-txt-${i}` }, scaled);
481
+ });
482
+ return react_1.default.Children.toArray(mapped.flat());
483
+ }
484
+ if (react_1.default.isValidElement(node)) {
485
+ const el = node;
486
+ const props = el.props || {};
487
+ const children = props.children;
488
+ const newChildren = scaleImagesInNode(children, small);
489
+ if (el.type === react_1.default.Fragment) {
490
+ return (0, jsx_runtime_1.jsx)(jsx_runtime_1.Fragment, { children: newChildren });
491
+ }
492
+ const isImageLike = el.type === "img" || (props && typeof props.src === "string");
493
+ if (isImageLike) {
494
+ const newStyle = Object.assign({}, props.style || {}, { height: "0.875rem", width: "0.875rem" });
495
+ return react_1.default.cloneElement(el, Object.assign({}, props, { style: newStyle }));
496
+ }
497
+ return react_1.default.cloneElement(el, Object.assign({}, props), newChildren);
498
+ }
499
+ return node;
500
+ }
501
+ function parseInline(text) {
502
+ const nodes = [];
503
+ const regex = /(~~([\s\S]+?)~~)|(\*\*__|__\*\*)([\s\S]+?)(__\*\*|\*\*__)|__([\s\S]+?)__|(\*\*\*([\s\S]+?)\*\*\*)|(\*\*(.+?)\*\*)|(\*([^*]+)\*)/g;
504
+ let lastIndex = 0;
505
+ let m;
506
+ while ((m = regex.exec(text)) !== null) {
507
+ if (m.index > lastIndex)
508
+ nodes.push((0, jsx_runtime_1.jsx)(react_1.default.Fragment, { children: processLinks(text.substring(lastIndex, m.index)) }, `link-${lastIndex}`));
509
+ if (m[1]) {
510
+ // ~~strike~~
511
+ nodes.push((0, jsx_runtime_1.jsx)("s", { style: { textDecoration: "line-through", opacity: 0.8 }, children: parseInline(m[2]) }, `st-${m.index}`));
512
+ }
513
+ else if (m[3]) {
514
+ // **__bold+underline__** or __**__ combo
515
+ nodes.push((0, jsx_runtime_1.jsx)("strong", { style: { fontWeight: 600 }, children: (0, jsx_runtime_1.jsx)("u", { style: { textDecoration: "underline" }, children: processLinks(m[4]) }) }, `bu-${m.index}`));
516
+ }
517
+ else if (m[6]) {
518
+ // __underline__
519
+ nodes.push((0, jsx_runtime_1.jsx)("u", { style: { textDecoration: "underline", textDecorationThickness: "1px" }, children: processLinks(m[6]) }, `u-${m.index}`));
520
+ }
521
+ else if (m[7]) {
522
+ // ***bold italic***
523
+ nodes.push((0, jsx_runtime_1.jsx)("strong", { style: { fontWeight: 600 }, children: (0, jsx_runtime_1.jsx)("em", { children: processLinks(m[8]) }) }, `bui-${m.index}`));
524
+ }
525
+ else if (m[9]) {
526
+ // **bold**
527
+ nodes.push((0, jsx_runtime_1.jsx)("strong", { style: { fontWeight: 600 }, children: processLinks(m[10]) }, `b-${m.index}`));
528
+ }
529
+ else if (m[11]) {
530
+ // *italic*
531
+ nodes.push((0, jsx_runtime_1.jsx)("em", { style: { fontStyle: "italic" }, children: processLinks(m[12]) }, `i-${m.index}`));
532
+ }
533
+ lastIndex = regex.lastIndex;
534
+ }
535
+ if (lastIndex < text.length)
536
+ nodes.push((0, jsx_runtime_1.jsx)(react_1.default.Fragment, { children: processLinks(text.substring(lastIndex)) }, `link-${lastIndex}`));
537
+ return (0, jsx_runtime_1.jsx)(jsx_runtime_1.Fragment, { children: react_1.default.Children.toArray(nodes) });
538
+ }
@@ -0,0 +1,12 @@
1
+ export declare function parseDiscordTimestamp(unixTimestamp: number, style?: string): string;
2
+ export declare function formatRelativeTime(date: Date): string;
3
+ export declare function escapeHtml(s: string): string;
4
+ export declare function splitFencedCodeBlocks(input: string): Array<{
5
+ type: "text" | "code";
6
+ value: string;
7
+ lang?: string;
8
+ }>;
9
+ export declare const resolveChannelNameGlobal: (map?: Record<string, {
10
+ name?: string | null;
11
+ guildId?: string | null;
12
+ }>, id?: string) => string | null;