discord-html-transcripts-fix 1.1.0 → 1.2.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.
- package/README.md +86 -78
- package/dist/downloader/images.js +60 -85
- package/dist/generator/index.js +194 -43
- package/dist/generator/renderers/attachment.js +27 -36
- package/dist/generator/renderers/components/Container.js +36 -14
- package/dist/generator/renderers/components/Media Gallery.js +24 -18
- package/dist/generator/renderers/components.js +7 -2
- package/dist/generator/renderers/content.js +48 -48
- package/dist/generator/renderers/embed.js +55 -34
- package/dist/generator/renderers/message.js +159 -46
- package/dist/generator/renderers/reply.js +47 -39
- package/dist/generator/renderers/systemMessage.js +131 -70
- package/dist/generator/transcript.js +42 -56
- package/dist/index.js +59 -84
- package/dist/static/client.js +216 -208
- package/dist/types.js +1 -0
- package/dist/utils/buildProfiles.js +51 -59
- package/dist/utils/utils.js +73 -26
- package/package.json +1 -1
package/dist/generator/index.js
CHANGED
|
@@ -1,76 +1,227 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
2
5
|
var __rest = (this && this.__rest) || function (s, e) {
|
|
3
6
|
var t = {};
|
|
4
|
-
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
|
|
5
|
-
t[p] = s[p];
|
|
7
|
+
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0) t[p] = s[p];
|
|
6
8
|
if (s != null && typeof Object.getOwnPropertySymbols === "function")
|
|
7
9
|
for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
|
|
8
|
-
if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
|
|
9
|
-
t[p[i]] = s[p[i]];
|
|
10
|
+
if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i])) t[p[i]] = s[p[i]];
|
|
10
11
|
}
|
|
11
12
|
return t;
|
|
12
13
|
};
|
|
13
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
14
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
15
|
-
};
|
|
16
14
|
var _a;
|
|
17
15
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
18
16
|
exports.default = render;
|
|
17
|
+
exports.renderToStream = renderToStream;
|
|
19
18
|
const jsx_runtime_1 = require("react/jsx-runtime");
|
|
20
19
|
const static_1 = require("react-dom/static");
|
|
21
20
|
const buildProfiles_1 = require("../utils/buildProfiles");
|
|
22
21
|
const client_1 = require("../static/client");
|
|
23
22
|
const fs_1 = require("fs");
|
|
24
23
|
const path_1 = __importDefault(require("path"));
|
|
25
|
-
//const ssr_1 = require("@lit-labs/ssr");
|
|
26
24
|
const transcript_1 = __importDefault(require("./transcript"));
|
|
27
25
|
const utils_1 = require("../utils/utils");
|
|
28
|
-
//const render_result_1 = require("@lit-labs/ssr/lib/render-result");
|
|
29
26
|
const styles_1 = require("./renderers/components/styles");
|
|
30
27
|
const DiscordImage_1 = require("./renderers/components/DiscordImage");
|
|
31
28
|
const DiscordHighlightedCode_1 = require("./renderers/components/DiscordHighlightedCode");
|
|
32
|
-
|
|
33
|
-
|
|
29
|
+
|
|
30
|
+
// Lock to an exact resolved version — semver ranges in src= aren't cacheable by CDNs.
|
|
31
|
+
let discordComponentsVersion = '4.0.2';
|
|
34
32
|
try {
|
|
35
33
|
const packagePath = path_1.default.join(__dirname, '..', '..', 'package.json');
|
|
36
34
|
const packageJSON = JSON.parse((0, fs_1.readFileSync)(packagePath, 'utf8'));
|
|
37
|
-
|
|
38
|
-
|
|
35
|
+
const v = (_a = packageJSON.dependencies['@skyra/discord-components-core']) !== null && _a !== void 0 ? _a : discordComponentsVersion;
|
|
36
|
+
const cleaned = String(v).replace(/^[\^~]/, '');
|
|
37
|
+
if (/^\d+\.\d+\.\d+/.test(cleaned)) discordComponentsVersion = cleaned;
|
|
38
|
+
} catch (_b) { /* ignore */ }
|
|
39
|
+
|
|
40
|
+
function computeStats(messages) {
|
|
41
|
+
let images = 0;
|
|
42
|
+
let humans = new Set();
|
|
43
|
+
for (const m of messages) {
|
|
44
|
+
if (m.attachments && m.attachments.forEach) {
|
|
45
|
+
m.attachments.forEach((a) => {
|
|
46
|
+
if (typeof a.contentType === 'string' && a.contentType.startsWith('image/')) images++;
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
if (m.author && !m.author.bot) humans.add(m.author.id);
|
|
50
|
+
}
|
|
51
|
+
return { messageCount: messages.length, participantCount: humans.size, imageCount: images };
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function snowflakeToDate(id) {
|
|
55
|
+
try {
|
|
56
|
+
const DISCORD_EPOCH = 1420070400000n;
|
|
57
|
+
const ms = (BigInt(id) >> 22n) + DISCORD_EPOCH;
|
|
58
|
+
return new Date(Number(ms));
|
|
59
|
+
} catch (_e) { return null; }
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function buildDateSeparators(messages, lang) {
|
|
63
|
+
// Returns an array same length as messages, each entry: separator string or null
|
|
64
|
+
const seps = new Array(messages.length).fill(null);
|
|
65
|
+
let lastYmd = null;
|
|
66
|
+
const fmt = new Intl.DateTimeFormat(lang === 'de' ? 'de-DE' : 'en-US', { weekday: 'long', day: '2-digit', month: 'long', year: 'numeric' });
|
|
67
|
+
for (let i = 0; i < messages.length; i++) {
|
|
68
|
+
const m = messages[i];
|
|
69
|
+
const d = m.createdAt instanceof Date ? m.createdAt : snowflakeToDate(m.id);
|
|
70
|
+
if (!d) continue;
|
|
71
|
+
const ymd = `${d.getUTCFullYear()}-${d.getUTCMonth()}-${d.getUTCDate()}`;
|
|
72
|
+
if (ymd !== lastYmd) {
|
|
73
|
+
seps[i] = fmt.format(d);
|
|
74
|
+
lastYmd = ymd;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
return seps;
|
|
39
78
|
}
|
|
40
|
-
|
|
79
|
+
|
|
41
80
|
async function render(_a) {
|
|
42
81
|
var _b;
|
|
43
82
|
var { messages, channel, callbacks } = _a, options = __rest(_a, ["messages", "channel", "callbacks"]);
|
|
44
|
-
|
|
45
|
-
const
|
|
46
|
-
|
|
47
|
-
|
|
83
|
+
|
|
84
|
+
const lang = options.language && client_1.defaultDicts[options.language] ? options.language : 'en';
|
|
85
|
+
const i18n = options.i18n
|
|
86
|
+
? Object.fromEntries(Object.keys(client_1.defaultDicts).map((k) => [k, Object.assign({}, client_1.defaultDicts[k], options.i18n[k] || {})]))
|
|
87
|
+
: client_1.defaultDicts;
|
|
88
|
+
|
|
89
|
+
// Single-pass collector (profiles + extended in one walk)
|
|
90
|
+
const allCtx = await (0, buildProfiles_1.buildAllContext)(messages, channel).catch((e) => {
|
|
91
|
+
console.warn('[discord-html-transcripts-fix] buildAllContext failed:', e?.message || e);
|
|
92
|
+
return { profiles: {}, users: {}, roles: {}, channels: {} };
|
|
48
93
|
});
|
|
94
|
+
|
|
95
|
+
const stats = computeStats(messages);
|
|
96
|
+
const dateSeps = buildDateSeparators(messages, lang);
|
|
97
|
+
|
|
49
98
|
const globalData = {
|
|
50
|
-
profiles,
|
|
51
|
-
users:
|
|
52
|
-
roles:
|
|
53
|
-
channels:
|
|
99
|
+
profiles: allCtx.profiles,
|
|
100
|
+
users: allCtx.users,
|
|
101
|
+
roles: allCtx.roles,
|
|
102
|
+
channels: allCtx.channels,
|
|
103
|
+
lang,
|
|
104
|
+
i18n,
|
|
105
|
+
stats,
|
|
54
106
|
};
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
107
|
+
|
|
108
|
+
const renderContext = Object.assign({ lang, i18n, dateSeps, stats }, callbacks ? { callbacks } : {});
|
|
109
|
+
|
|
110
|
+
const docTree = (0, jsx_runtime_1.jsxs)("html", { lang, children: [
|
|
111
|
+
(0, jsx_runtime_1.jsxs)("head", { children: [
|
|
112
|
+
(0, jsx_runtime_1.jsx)("meta", { charSet: "utf-8" }),
|
|
113
|
+
(0, jsx_runtime_1.jsx)("meta", { name: "viewport", content: "width=device-width, initial-scale=1" }),
|
|
114
|
+
(0, jsx_runtime_1.jsx)("link", { rel: "preconnect", href: "https://cdn.jsdelivr.net/" }),
|
|
115
|
+
(0, jsx_runtime_1.jsx)("link", { rel: "preconnect", href: "https://cdn.discordapp.com" }),
|
|
116
|
+
(0, jsx_runtime_1.jsx)("style", { dangerouslySetInnerHTML: { __html: client_1.ggSansFont } }),
|
|
117
|
+
(0, jsx_runtime_1.jsx)("style", { dangerouslySetInnerHTML: { __html: styles_1.globalStyles } }),
|
|
118
|
+
(0, jsx_runtime_1.jsx)("style", { dangerouslySetInnerHTML: { __html: client_1.mentionPopupStyles } }),
|
|
119
|
+
(0, jsx_runtime_1.jsx)("style", { dangerouslySetInnerHTML: { __html: client_1.transcriptUtils } }),
|
|
120
|
+
(0, jsx_runtime_1.jsx)("style", { dangerouslySetInnerHTML: { __html: client_1.searchBarStyles } }),
|
|
121
|
+
(0, jsx_runtime_1.jsx)("style", { dangerouslySetInnerHTML: { __html: client_1.lightboxStyles } }),
|
|
122
|
+
(0, jsx_runtime_1.jsx)("style", { dangerouslySetInnerHTML: { __html: client_1.tocStyles } }),
|
|
123
|
+
(0, jsx_runtime_1.jsx)("style", { dangerouslySetInnerHTML: { __html: client_1.i18nStyles } }),
|
|
124
|
+
(0, jsx_runtime_1.jsx)("style", { dangerouslySetInnerHTML: { __html: client_1.dateSeparatorStyles } }),
|
|
125
|
+
(0, jsx_runtime_1.jsx)("style", { dangerouslySetInnerHTML: { __html: DiscordImage_1.DiscordAttachmentStyles } }),
|
|
126
|
+
(0, jsx_runtime_1.jsx)("style", { dangerouslySetInnerHTML: { __html: DiscordHighlightedCode_1.DiscordHighlightStyles } }),
|
|
127
|
+
(0, jsx_runtime_1.jsx)("style", { dangerouslySetInnerHTML: { __html: extraStyles } }),
|
|
128
|
+
(0, jsx_runtime_1.jsx)("link", { rel: "icon", type: "image/png", href: options.favicon === 'guild'
|
|
129
|
+
? channel.isDMBased()
|
|
130
|
+
? undefined
|
|
131
|
+
: ((_b = channel.guild.iconURL({ size: 16, extension: 'png' })) !== null && _b !== void 0 ? _b : undefined)
|
|
132
|
+
: options.favicon }),
|
|
133
|
+
(0, jsx_runtime_1.jsx)("title", { children: channel.isDMBased() ? 'Direct Messages' : channel.name }),
|
|
134
|
+
(0, jsx_runtime_1.jsx)("script", { dangerouslySetInnerHTML: { __html: client_1.scrollToMessage } }),
|
|
135
|
+
// Non-blocking inline JSON: type="application/json" so the parser doesn't pause
|
|
136
|
+
(0, jsx_runtime_1.jsx)("script", { id: "dht-data", type: "application/json", dangerouslySetInnerHTML: { __html: (0, utils_1.safeJsonForScript)(globalData) } }),
|
|
137
|
+
(0, jsx_runtime_1.jsx)("script", { dangerouslySetInnerHTML: { __html: 'try{globalThis.$discordMessage=JSON.parse(document.getElementById("dht-data").textContent);}catch(e){globalThis.$discordMessage={};}' } }),
|
|
138
|
+
!options.hydrate && (0, jsx_runtime_1.jsx)("script", { type: "module", src: `https://cdn.jsdelivr.net/npm/@skyra/discord-components-core@${discordComponentsVersion}/+esm`, crossOrigin: "anonymous" }),
|
|
139
|
+
] }),
|
|
140
|
+
(0, jsx_runtime_1.jsxs)("body", { style: { margin: 0, minHeight: '100vh' }, children: [
|
|
141
|
+
(0, jsx_runtime_1.jsx)(transcript_1.default, Object.assign({ messages, channel, callbacks }, options, renderContext)),
|
|
142
|
+
(0, jsx_runtime_1.jsx)(StatsFooter, { stats, lang, i18n }),
|
|
143
|
+
(0, jsx_runtime_1.jsx)("script", { dangerouslySetInnerHTML: { __html: client_1.mentionPopup } }),
|
|
144
|
+
(0, jsx_runtime_1.jsx)("script", { dangerouslySetInnerHTML: { __html: client_1.backToTop } }),
|
|
145
|
+
(0, jsx_runtime_1.jsx)("script", { dangerouslySetInnerHTML: { __html: client_1.searchBar } }),
|
|
146
|
+
(0, jsx_runtime_1.jsx)("script", { dangerouslySetInnerHTML: { __html: client_1.lightbox } }),
|
|
147
|
+
(0, jsx_runtime_1.jsx)("script", { dangerouslySetInnerHTML: { __html: client_1.toc } }),
|
|
148
|
+
(0, jsx_runtime_1.jsx)("script", { dangerouslySetInnerHTML: { __html: client_1.i18nSwitcher } }),
|
|
149
|
+
options.hydrate && (0, jsx_runtime_1.jsx)("script", { dangerouslySetInnerHTML: { __html: client_1.revealSpoiler } }),
|
|
150
|
+
] })
|
|
151
|
+
] });
|
|
152
|
+
|
|
153
|
+
const { prelude } = await (0, static_1.prerenderToNodeStream)(docTree);
|
|
154
|
+
|
|
155
|
+
if (options.hydrate) {
|
|
156
|
+
const markup = await (0, utils_1.streamToString)(prelude);
|
|
157
|
+
const { render: renderLit } = await import('@lit-labs/ssr');
|
|
158
|
+
const result = renderLit(markup);
|
|
159
|
+
const { collectResult } = await import('@lit-labs/ssr/lib/render-result.js');
|
|
160
|
+
return await collectResult(result);
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
if (options.returnType === 'stream' || options.stream) {
|
|
164
|
+
// Return the underlying Node stream — caller is responsible for piping.
|
|
165
|
+
return prelude;
|
|
166
|
+
}
|
|
167
|
+
return await (0, utils_1.streamToString)(prelude);
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
async function renderToStream(args) {
|
|
171
|
+
return render(Object.assign({}, args, { returnType: 'stream' }));
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
function StatsFooter({ stats, lang, i18n }) {
|
|
175
|
+
const dict = (i18n && i18n[lang]) || (i18n && i18n.en) || {};
|
|
176
|
+
return (0, jsx_runtime_1.jsxs)("footer", { className: "dht-stats", children: [
|
|
177
|
+
(0, jsx_runtime_1.jsx)("span", { "data-i18n": "statsMessages", "data-i18n-params": JSON.stringify({ n: stats.messageCount }), children: (dict.statsMessages || '{n} messages').replace('{n}', stats.messageCount) }),
|
|
178
|
+
' · ',
|
|
179
|
+
(0, jsx_runtime_1.jsx)("span", { "data-i18n": "statsParticipants", "data-i18n-params": JSON.stringify({ n: stats.participantCount }), children: (dict.statsParticipants || '{n} participants').replace('{n}', stats.participantCount) }),
|
|
180
|
+
' · ',
|
|
181
|
+
(0, jsx_runtime_1.jsx)("span", { "data-i18n": "statsImages", "data-i18n-params": JSON.stringify({ n: stats.imageCount }), children: (dict.statsImages || '{n} images').replace('{n}', stats.imageCount) }),
|
|
182
|
+
] });
|
|
75
183
|
}
|
|
76
|
-
|
|
184
|
+
|
|
185
|
+
const extraStyles = `
|
|
186
|
+
.dht-pinned-indicator{position:absolute;top:6px;right:8px;font-size:12px;opacity:.6}
|
|
187
|
+
.dht-app-badge,.dht-popup-badge{background:#5865F2;color:#fff;font-size:10px;padding:1px 6px;border-radius:4px;margin-left:6px;vertical-align:middle;font-weight:600}
|
|
188
|
+
.dht-edit-marker{color:#949ba4;font-size:11px;margin-left:4px;cursor:help}
|
|
189
|
+
.dht-suppressed{color:#6e727a;font-size:11px;font-style:italic}
|
|
190
|
+
.dht-stickers{display:flex;gap:8px;flex-wrap:wrap;margin-top:4px}
|
|
191
|
+
.dht-sticker{width:160px;height:160px;border-radius:8px;object-fit:contain;background:rgba(255,255,255,.02)}
|
|
192
|
+
.dht-poll{border:1px solid #2b2d31;background:#1e1f22;border-radius:8px;padding:12px 14px;margin-top:6px;max-width:480px}
|
|
193
|
+
.dht-poll-q{font-weight:700;margin-bottom:8px;font-size:15px}
|
|
194
|
+
.dht-poll-answers{display:flex;flex-direction:column;gap:6px}
|
|
195
|
+
.dht-poll-a{background:#2b2d31;border-radius:6px;padding:6px 10px;position:relative;overflow:hidden}
|
|
196
|
+
.dht-poll-bar{position:absolute;top:0;left:0;height:100%;background:rgba(88,101,242,.25);z-index:0}
|
|
197
|
+
.dht-poll-line{position:relative;z-index:1;display:flex;justify-content:space-between;gap:8px}
|
|
198
|
+
.dht-poll-pct{color:#b5bac1;font-size:12px;white-space:nowrap}
|
|
199
|
+
.dht-poll-foot{margin-top:8px;font-size:11px;color:#949ba4}
|
|
200
|
+
.dht-voice{display:inline-flex;align-items:center;gap:8px;background:#2b2d31;padding:6px 12px;border-radius:8px;color:#dbdee1;font-size:13px;margin:4px 0}
|
|
201
|
+
.dht-voice-wave svg{display:block}
|
|
202
|
+
.dht-voice-dur{color:#b5bac1;font-size:11px}
|
|
203
|
+
.dht-forwarded-wrap{margin:4px 0}
|
|
204
|
+
.dht-forwarded{border-left:3px solid #5865F2;background:rgba(88,101,242,.06);padding:6px 10px;border-radius:0 6px 6px 0;margin-bottom:4px}
|
|
205
|
+
.dht-forwarded-head{font-size:12px;color:#949ba4;margin-bottom:2px}
|
|
206
|
+
.dht-forwarded-icon{margin-right:4px}
|
|
207
|
+
.dht-forwarded-body{color:#dbdee1}
|
|
208
|
+
.dht-forwarded-attachments{margin-top:4px;font-size:11px;color:#949ba4}
|
|
209
|
+
.dht-edit-history{font-size:12px;color:#b5bac1;margin-top:4px}
|
|
210
|
+
.dht-edit-history summary{cursor:pointer;color:#949ba4}
|
|
211
|
+
.dht-edit-history li{margin-top:4px}
|
|
212
|
+
.dht-edit-history time{display:block;color:#6e727a;font-size:10px;margin-bottom:2px}
|
|
213
|
+
.dht-thread-badge{display:inline-block;background:#3f4248;color:#dbdee1;font-size:10px;padding:1px 6px;border-radius:4px;margin-left:6px}
|
|
214
|
+
.dht-thread-badge--locked{background:#dc3545}
|
|
215
|
+
.dht-thread-badge--archived{background:#6c757d}
|
|
216
|
+
.dht-gifv{max-width:400px;max-height:300px;border-radius:6px}
|
|
217
|
+
.dht-stats{margin:24px auto;padding:12px;text-align:center;color:#b5bac1;font-size:13px;font-family:"gg sans",sans-serif;border-top:1px solid #2b2d31;max-width:800px}
|
|
218
|
+
.dht-mention--cmd{background:rgba(88,101,242,.22)!important}
|
|
219
|
+
.dht-embed-provider{color:#949ba4;font-size:11px;margin-bottom:2px}
|
|
220
|
+
.dht-embed-video{margin-top:6px;font-size:13px}
|
|
221
|
+
.dht-embed-video a{color:#8ab4ff;text-decoration:none}
|
|
222
|
+
.dht-embed-video a:hover{text-decoration:underline}
|
|
223
|
+
.dht-automod-rule{font-size:11px;color:#949ba4}
|
|
224
|
+
.dht-automod-content{margin:4px 0 0 12px;color:#dbdee1;border-left:3px solid #f23f42;padding-left:8px;font-size:13px}
|
|
225
|
+
.dht-reaction--burst{box-shadow:inset 0 0 0 1px gold}
|
|
226
|
+
`;
|
|
227
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -6,62 +6,53 @@ const jsx_runtime_1 = require("react/jsx-runtime");
|
|
|
6
6
|
const types_1 = require("../../types");
|
|
7
7
|
const utils_1 = require("../../utils/utils");
|
|
8
8
|
const DiscordImage_1 = require("./components/DiscordImage");
|
|
9
|
-
|
|
10
|
-
* Renders all attachments for a message
|
|
11
|
-
* @param message
|
|
12
|
-
* @param context
|
|
13
|
-
* @returns
|
|
14
|
-
*/
|
|
9
|
+
|
|
15
10
|
async function Attachments(props) {
|
|
16
|
-
if (props.message.attachments.size === 0)
|
|
17
|
-
return (0, jsx_runtime_1.jsx)(jsx_runtime_1.Fragment, {});
|
|
11
|
+
if (props.message.attachments.size === 0) return (0, jsx_runtime_1.jsx)(jsx_runtime_1.Fragment, {});
|
|
18
12
|
return props.message.attachments.map((attachment, id) => ((0, jsx_runtime_1.jsx)(Attachment, { attachment: attachment, message: props.message, context: props.context }, id)));
|
|
19
13
|
}
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
*/
|
|
24
|
-
async function Attachment({ attachment, context, message, }) {
|
|
25
|
-
let url = attachment.url;
|
|
14
|
+
|
|
15
|
+
async function Attachment({ attachment, context, message }) {
|
|
16
|
+
let url = (0, utils_1.safeHref)(attachment.url) === '#' ? '#' : attachment.url;
|
|
26
17
|
const attachmentType = getAttachmentType(attachment);
|
|
27
18
|
const [bytes, bytesUnit] = (0, utils_1.formatBytes)(attachment.size);
|
|
28
|
-
|
|
19
|
+
const altText = attachment.description || attachment.name;
|
|
20
|
+
|
|
21
|
+
// GIFV detection: animated content served as video but spec-typed as GIF
|
|
22
|
+
const isGifv = attachment.contentType === 'video/mp4' && /\.(gifv?|webm)$/i.test(attachment.name || '');
|
|
23
|
+
|
|
29
24
|
switch (attachmentType) {
|
|
30
25
|
case types_1.AttachmentTypes.Image: {
|
|
31
26
|
const downloaded = await context.callbacks.resolveImageSrc(attachment.toJSON(), message.toJSON());
|
|
32
27
|
if (downloaded !== null) {
|
|
33
|
-
url = downloaded
|
|
28
|
+
url = downloaded ?? url;
|
|
34
29
|
}
|
|
35
|
-
|
|
30
|
+
// GIF / animated content — Discord auto-plays. Render as <video> when needed.
|
|
31
|
+
if (attachment.contentType === 'image/gif' || isGifv) {
|
|
32
|
+
return (0, jsx_runtime_1.jsx)("video", { src: url, autoPlay: true, loop: true, muted: true, playsInline: true, className: "dht-gifv", "aria-label": altText }, attachment.id);
|
|
33
|
+
}
|
|
34
|
+
return (0, jsx_runtime_1.jsx)(DiscordImage_1.DiscordImageAttachment, { url: url, alt: altText, title: attachment.description || undefined }, attachment.id);
|
|
36
35
|
}
|
|
37
36
|
case types_1.AttachmentTypes.Video: {
|
|
38
37
|
return (0, jsx_runtime_1.jsx)("discord-video-attachment", { slot: "attachments", href: url }, attachment.id);
|
|
39
38
|
}
|
|
40
39
|
case types_1.AttachmentTypes.Audio: {
|
|
41
|
-
|
|
40
|
+
// Voice messages have waveform + duration_secs — message.js renders the indicator
|
|
41
|
+
return (0, jsx_runtime_1.jsx)("discord-audio-attachment", { slot: "attachments", href: url, bytes: bytes, bytesUnit: bytesUnit, name: attachment.name, title: attachment.description || undefined }, attachment.id);
|
|
42
42
|
}
|
|
43
43
|
case types_1.AttachmentTypes.File: {
|
|
44
|
-
return (
|
|
44
|
+
return (0, jsx_runtime_1.jsx)("discord-file-attachment", { slot: "attachments", href: url, bytes: bytes, "bytes-unit": bytesUnit, name: attachment.name, title: attachment.description || undefined }, attachment.id);
|
|
45
45
|
}
|
|
46
46
|
}
|
|
47
47
|
}
|
|
48
|
-
|
|
49
|
-
* Parses the attachment content type.
|
|
50
|
-
* @param attachment Discord.js attachment object
|
|
51
|
-
* @returns
|
|
52
|
-
*/
|
|
48
|
+
|
|
53
49
|
function getAttachmentType(attachment) {
|
|
54
|
-
|
|
55
|
-
switch (
|
|
56
|
-
case 'audio':
|
|
57
|
-
|
|
58
|
-
case '
|
|
59
|
-
|
|
60
|
-
case 'video':
|
|
61
|
-
return types_1.AttachmentTypes.Video;
|
|
62
|
-
case undefined:
|
|
63
|
-
default:
|
|
64
|
-
return types_1.AttachmentTypes.File;
|
|
50
|
+
const top = attachment.contentType?.split('/')?.[0];
|
|
51
|
+
switch (top) {
|
|
52
|
+
case 'audio': return types_1.AttachmentTypes.Audio;
|
|
53
|
+
case 'image': return types_1.AttachmentTypes.Image;
|
|
54
|
+
case 'video': return types_1.AttachmentTypes.Video;
|
|
55
|
+
default: return types_1.AttachmentTypes.File;
|
|
65
56
|
}
|
|
66
57
|
}
|
|
67
|
-
//# sourceMappingURL=attachment.js.map
|
|
58
|
+
//# sourceMappingURL=attachment.js.map
|
|
@@ -1,19 +1,41 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
const jsx_runtime_1 = require("react/jsx-runtime");
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
4
|
+
|
|
5
|
+
function colorToHex(color) {
|
|
6
|
+
if (color == null) return null;
|
|
7
|
+
if (typeof color === 'string') {
|
|
8
|
+
return color.startsWith('#') ? color : '#' + color.replace(/^0x/i, '');
|
|
9
|
+
}
|
|
10
|
+
if (typeof color === 'number') {
|
|
11
|
+
return '#' + color.toString(16).padStart(6, '0');
|
|
12
|
+
}
|
|
13
|
+
return null;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
function DiscordContainer({ children, accentColor, spoiler }) {
|
|
17
|
+
const hex = colorToHex(accentColor);
|
|
18
|
+
const style = {
|
|
19
|
+
display: 'flex',
|
|
20
|
+
width: '500px',
|
|
21
|
+
flexDirection: 'column',
|
|
22
|
+
backgroundColor: '#3f4248',
|
|
23
|
+
padding: '16px',
|
|
24
|
+
// Use accent color as a left border (Discord's V2 container style)
|
|
25
|
+
borderLeft: hex ? `4px solid ${hex}` : '1px solid #4f5359',
|
|
26
|
+
borderTop: '1px solid #4f5359',
|
|
27
|
+
borderRight: '1px solid #4f5359',
|
|
28
|
+
borderBottom: '1px solid #4f5359',
|
|
29
|
+
marginTop: '2px',
|
|
30
|
+
marginBottom: '2px',
|
|
31
|
+
borderRadius: '10px',
|
|
32
|
+
gap: '8px',
|
|
33
|
+
};
|
|
34
|
+
const node = (0, jsx_runtime_1.jsx)("div", { style: style, children: children });
|
|
35
|
+
if (spoiler) {
|
|
36
|
+
return (0, jsx_runtime_1.jsx)("span", { className: "discord-spoiler", children: node });
|
|
37
|
+
}
|
|
38
|
+
return node;
|
|
17
39
|
}
|
|
18
40
|
exports.default = DiscordContainer;
|
|
19
|
-
//# sourceMappingURL=Container.js.map
|
|
41
|
+
//# sourceMappingURL=Container.js.map
|
|
@@ -9,24 +9,30 @@ function DiscordMediaGallery({ component }) {
|
|
|
9
9
|
const count = component.items.length;
|
|
10
10
|
const imagesToShow = component.items.slice(0, 10);
|
|
11
11
|
const hasMore = component.items.length > 10;
|
|
12
|
-
return ((0, jsx_runtime_1.jsx)("div", { style: (0, utils_1.getGalleryLayout)(count), children: imagesToShow.map((media, idx) =>
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
12
|
+
return ((0, jsx_runtime_1.jsx)("div", { style: (0, utils_1.getGalleryLayout)(count), children: imagesToShow.map((media, idx) => {
|
|
13
|
+
const img = (0, jsx_runtime_1.jsx)("img", { src: media.media.url, alt: media.description || 'Media content', style: {
|
|
14
|
+
width: '100%',
|
|
15
|
+
height: '100%',
|
|
16
|
+
objectFit: 'cover',
|
|
17
|
+
} });
|
|
18
|
+
const wrapped = media.spoiler
|
|
19
|
+
? (0, jsx_runtime_1.jsx)("span", { className: "discord-spoiler", style: { display: 'block', width: '100%', height: '100%' }, children: img })
|
|
20
|
+
: img;
|
|
21
|
+
return ((0, jsx_runtime_1.jsxs)("div", { style: (0, utils_1.getImageStyle)(idx, count), children: [wrapped, hasMore && idx === imagesToShow.length - 1 && ((0, jsx_runtime_1.jsxs)("div", { style: {
|
|
22
|
+
position: 'absolute',
|
|
23
|
+
top: 0,
|
|
24
|
+
left: 0,
|
|
25
|
+
width: '100%',
|
|
26
|
+
height: '100%',
|
|
27
|
+
display: 'flex',
|
|
28
|
+
alignItems: 'center',
|
|
29
|
+
justifyContent: 'center',
|
|
30
|
+
backgroundColor: 'rgba(0, 0, 0, 0.7)',
|
|
31
|
+
color: 'white',
|
|
32
|
+
fontSize: '20px',
|
|
33
|
+
fontWeight: 'bold',
|
|
34
|
+
}, children: ["+", component.items.length - 10] }))] }, idx));
|
|
35
|
+
}) }));
|
|
30
36
|
}
|
|
31
37
|
exports.default = DiscordMediaGallery;
|
|
32
38
|
//# sourceMappingURL=Media%20Gallery.js.map
|
|
@@ -22,8 +22,13 @@ function ComponentRow({ component, id, context, }) {
|
|
|
22
22
|
switch (component.type) {
|
|
23
23
|
case discord_js_1.ComponentType.ActionRow:
|
|
24
24
|
return ((0, jsx_runtime_1.jsx)("discord-action-row", { children: (0, jsx_runtime_1.jsx)(jsx_runtime_1.Fragment, { children: component.components.map((nestedComponent, id) => ((0, jsx_runtime_1.jsx)(Component, { component: nestedComponent, id: id }, id))) }) }, id));
|
|
25
|
-
case discord_js_1.ComponentType.Container:
|
|
26
|
-
|
|
25
|
+
case discord_js_1.ComponentType.Container: {
|
|
26
|
+
const accentColor = component.accent_color !== undefined
|
|
27
|
+
? component.accent_color
|
|
28
|
+
: (component.accentColor !== undefined ? component.accentColor : null);
|
|
29
|
+
const spoiler = !!component.spoiler;
|
|
30
|
+
return ((0, jsx_runtime_1.jsx)(Container_1.default, { accentColor: accentColor, spoiler: spoiler, children: (0, jsx_runtime_1.jsx)(jsx_runtime_1.Fragment, { children: component.components.map((nestedComponent, id) => ((0, jsx_runtime_1.jsx)(ComponentRow, { component: nestedComponent, id: id, context: context }, id))) }) }, id));
|
|
31
|
+
}
|
|
27
32
|
case discord_js_1.ComponentType.File: {
|
|
28
33
|
const attachmentComponent = (0, jsx_runtime_1.jsx)("discord-file-attachment", { href: component.file.url });
|
|
29
34
|
if (component.spoiler) {
|