discord-html-transcripts-fix 1.0.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.
@@ -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
- * Renders one Discord Attachment
22
- * @param props - the attachment and rendering context
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
- // if the attachment is an image, download it to a data url
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 !== null && downloaded !== void 0 ? downloaded : url;
28
+ url = downloaded ?? url;
34
29
  }
35
- return (0, jsx_runtime_1.jsx)(DiscordImage_1.DiscordImageAttachment, { url: url, alt: attachment.name }, attachment.id);
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
- return ((0, jsx_runtime_1.jsx)("discord-audio-attachment", { slot: "attachments", href: url, bytes: bytes, bytesUnit: bytesUnit, name: attachment.name }, attachment.id));
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 ((0, jsx_runtime_1.jsx)("discord-file-attachment", { slot: "attachments", href: url, bytes: bytes, "bytes-unit": bytesUnit, name: attachment.name }, attachment.id));
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
- var _a, _b;
55
- switch ((_b = (_a = attachment.contentType) === null || _a === void 0 ? void 0 : _a.split('/')) === null || _b === void 0 ? void 0 : _b[0]) {
56
- case 'audio':
57
- return types_1.AttachmentTypes.Audio;
58
- case 'image':
59
- return types_1.AttachmentTypes.Image;
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
- function DiscordContainer({ children }) {
5
- return ((0, jsx_runtime_1.jsx)("div", { style: {
6
- display: 'flex',
7
- width: '500px',
8
- flexDirection: 'column',
9
- backgroundColor: '#3f4248',
10
- padding: '16px',
11
- border: '1px solid #4f5359',
12
- marginTop: '2px',
13
- marginBottom: '2px',
14
- borderRadius: '10px',
15
- gap: '8px',
16
- }, children: children }));
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) => ((0, jsx_runtime_1.jsxs)("div", { style: (0, utils_1.getImageStyle)(idx, count), children: [(0, jsx_runtime_1.jsx)("img", { src: media.media.url, alt: media.description || 'Media content', style: {
13
- width: '100%',
14
- height: '100%',
15
- objectFit: 'cover',
16
- } }), hasMore && idx === imagesToShow.length - 1 && ((0, jsx_runtime_1.jsxs)("div", { style: {
17
- position: 'absolute',
18
- top: 0,
19
- left: 0,
20
- width: '100%',
21
- height: '100%',
22
- display: 'flex',
23
- alignItems: 'center',
24
- justifyContent: 'center',
25
- backgroundColor: 'rgba(0, 0, 0, 0.7)',
26
- color: 'white',
27
- fontSize: '20px',
28
- fontWeight: 'bold',
29
- }, children: ["+", component.items.length - 10] }))] }, idx))) }));
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
- return ((0, jsx_runtime_1.jsx)(Container_1.default, { 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));
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) {
@@ -19,52 +19,63 @@ var RenderType;
19
19
  RenderType[RenderType["NORMAL"] = 2] = "NORMAL";
20
20
  RenderType[RenderType["WEBHOOK"] = 3] = "WEBHOOK";
21
21
  })(RenderType || (exports.RenderType = RenderType = {}));
22
- /**
23
- * Renders discord markdown content
24
- * @param content - The content to render
25
- * @param context - The context to render the content in
26
- * @returns
27
- */
22
+
28
23
  async function MessageContent({ content, context }) {
29
24
  if (context.type === RenderType.REPLY && content.length > 180)
30
25
  content = content.slice(0, 180) + '...';
31
- // parse the markdown
32
26
  const parsed = (0, discord_markdown_parser_1.default)(content, context.type === RenderType.EMBED || context.type === RenderType.WEBHOOK ? 'extended' : 'normal');
33
- // check if the parsed content is only emojis
34
27
  const isOnlyEmojis = parsed.every((node) => ['emoji', 'twemoji'].includes(node.type) || (node.type === 'text' && node.content.trim().length === 0));
35
28
  if (isOnlyEmojis) {
36
- // now check if there are less than or equal to 25 emojis
37
29
  const emojis = parsed.filter((node) => ['emoji', 'twemoji'].includes(node.type));
38
30
  if (emojis.length <= 25) {
39
- context._internal = {
40
- largeEmojis: true,
41
- };
31
+ context._internal = { largeEmojis: true };
42
32
  }
43
33
  }
44
34
  return (0, jsx_runtime_1.jsx)(MessageASTNodes, { nodes: parsed, context: context });
45
35
  }
46
- // This function can probably be combined into the MessageSingleASTNode function
47
- async function MessageASTNodes({ nodes, context, }) {
36
+
37
+ async function MessageASTNodes({ nodes, context }) {
48
38
  if (Array.isArray(nodes)) {
49
39
  return ((0, jsx_runtime_1.jsx)(jsx_runtime_1.Fragment, { children: nodes.map((node, i) => ((0, jsx_runtime_1.jsx)(MessageSingleASTNode, { node: node, context: context }, i))) }));
50
40
  }
51
- else {
52
- return (0, jsx_runtime_1.jsx)(MessageSingleASTNode, { node: nodes, context: context });
53
- }
41
+ return (0, jsx_runtime_1.jsx)(MessageSingleASTNode, { node: nodes, context: context });
54
42
  }
43
+
55
44
  async function MessageSingleASTNode({ node, context }) {
56
- var _a, _b;
57
- if (!node)
45
+ if (!node) return null;
46
+ try {
47
+ return await renderASTNode(node, context);
48
+ } catch (err) {
49
+ console.warn('[discord-html-transcripts-fix] AST node render failed:', err && err.message ? err.message : err);
50
+ try {
51
+ if (typeof node.content === 'string') return node.content;
52
+ if (Array.isArray(node.content)) return (0, jsx_runtime_1.jsx)(MessageASTNodes, { nodes: node.content, context: context });
53
+ } catch (_e) {}
58
54
  return null;
55
+ }
56
+ }
57
+
58
+ // Detect Discord "guide" pseudo-channels: <id:guide>, <id:browse>, <id:customize>, <id:welcome>, <id:linked-roles>
59
+ const GUIDE_LABELS = {
60
+ 'guide': 'Server Guide',
61
+ 'browse': 'Channels & Roles',
62
+ 'customize': 'Customize',
63
+ 'welcome': 'Welcome',
64
+ 'linked-roles': 'Linked Roles',
65
+ 'home': 'Home',
66
+ };
67
+
68
+ async function renderASTNode(node, context) {
69
+ var _a, _b;
59
70
  const type = node.type;
60
71
  switch (type) {
61
72
  case 'text':
62
73
  return node.content;
63
74
  case 'link':
64
- return ((0, jsx_runtime_1.jsx)("discord-link", { href: node.target, children: (0, jsx_runtime_1.jsx)(MessageASTNodes, { nodes: node.content, context: context }) }));
75
+ return ((0, jsx_runtime_1.jsx)("discord-link", { href: (0, utils_1.safeHref)(node.target), children: (0, jsx_runtime_1.jsx)(MessageASTNodes, { nodes: node.content, context: context }) }));
65
76
  case 'url':
66
77
  case 'autolink':
67
- return ((0, jsx_runtime_1.jsx)("discord-link", { href: node.target, target: "_blank", rel: "noreferrer", children: (0, jsx_runtime_1.jsx)(MessageASTNodes, { nodes: node.content, context: context }) }));
78
+ return ((0, jsx_runtime_1.jsx)("discord-link", { href: (0, utils_1.safeHref)(node.target), target: "_blank", rel: "noreferrer", children: (0, jsx_runtime_1.jsx)(MessageASTNodes, { nodes: node.content, context: context }) }));
68
79
  case 'blockQuote':
69
80
  if (context.type === RenderType.REPLY) {
70
81
  return (0, jsx_runtime_1.jsx)(MessageASTNodes, { nodes: node.content, context: context });
@@ -72,23 +83,34 @@ async function MessageSingleASTNode({ node, context }) {
72
83
  return ((0, jsx_runtime_1.jsx)("discord-quote", { children: (0, jsx_runtime_1.jsx)(MessageASTNodes, { nodes: node.content, context: context }) }));
73
84
  case 'br':
74
85
  case 'newline':
75
- if (context.type === RenderType.REPLY)
76
- return ' ';
86
+ if (context.type === RenderType.REPLY) return ' ';
77
87
  return (0, jsx_runtime_1.jsx)("br", {});
78
88
  case 'channel': {
79
89
  const id = node.id;
90
+ // Discord pseudo channels (<id:guide>, <id:browse>, …) come through with letters as id
91
+ if (typeof id === 'string' && /^[a-z][a-z-]*$/.test(id)) {
92
+ const label = GUIDE_LABELS[id] || id;
93
+ return ((0, jsx_runtime_1.jsx)("discord-mention", { type: "channel", className: "dht-mention", children: label }));
94
+ }
80
95
  const channel = await context.callbacks.resolveChannel(id);
81
- return ((0, jsx_runtime_1.jsx)("discord-mention", { type: channel ? (channel.isDMBased() ? 'channel' : getChannelType(channel.type)) : 'channel', children: channel ? (channel.isDMBased() ? 'DM Channel' : channel.name) : `<#${id}>` }));
96
+ return ((0, jsx_runtime_1.jsx)("discord-mention", { type: channel ? (channel.isDMBased() ? 'channel' : getChannelType(channel.type)) : 'channel', "data-mention-type": "channel", "data-mention-id": id, className: "dht-mention", children: channel ? (channel.isDMBased() ? 'DM Channel' : channel.name) : `<#${id}>` }));
82
97
  }
83
98
  case 'role': {
84
99
  const id = node.id;
85
100
  const role = await context.callbacks.resolveRole(id);
86
- return ((0, jsx_runtime_1.jsx)("discord-mention", { type: "role", color: context.type === RenderType.REPLY ? undefined : role === null || role === void 0 ? void 0 : role.hexColor, children: role ? role.name : `<@&${id}>` }));
101
+ const color = role?.hexColor ? (0, utils_1.safeColor)(role.hexColor, undefined) : undefined;
102
+ return ((0, jsx_runtime_1.jsx)("discord-mention", { type: "role", color: context.type === RenderType.REPLY ? undefined : color, "data-mention-type": "role", "data-mention-id": id, className: "dht-mention", children: role ? role.name : `<@&${id}>` }));
87
103
  }
88
104
  case 'user': {
89
105
  const id = node.id;
90
106
  const user = await context.callbacks.resolveUser(id);
91
- return (0, jsx_runtime_1.jsx)("discord-mention", { type: "user", children: user ? ((_a = user.displayName) !== null && _a !== void 0 ? _a : user.username) : `<@${id}>` });
107
+ return (0, jsx_runtime_1.jsx)("discord-mention", { type: "user", "data-mention-type": "user", "data-mention-id": id, className: "dht-mention", children: user ? ((_a = user.displayName) !== null && _a !== void 0 ? _a : user.username) : `<@${id}>` });
108
+ }
109
+ case 'slashCommand':
110
+ case 'command': {
111
+ // </name:id> renders as blue pill like Discord
112
+ const name = node.name || node.content || 'command';
113
+ return ((0, jsx_runtime_1.jsx)("discord-mention", { type: "command", className: "dht-mention dht-mention--cmd", children: '/' + String(name) }));
92
114
  }
93
115
  case 'here':
94
116
  case 'everyone':
@@ -109,39 +131,46 @@ async function MessageSingleASTNode({ node, context }) {
109
131
  case 'strikethrough':
110
132
  return ((0, jsx_runtime_1.jsx)("s", { children: (0, jsx_runtime_1.jsx)(MessageASTNodes, { nodes: node.content, context: context }) }));
111
133
  case 'emoticon':
112
- return typeof node.content === 'string' ? (node.content) : ((0, jsx_runtime_1.jsx)(MessageASTNodes, { nodes: node.content, context: context }));
134
+ return typeof node.content === 'string' ? node.content : ((0, jsx_runtime_1.jsx)(MessageASTNodes, { nodes: node.content, context: context }));
113
135
  case 'spoiler':
114
136
  return ((0, jsx_runtime_1.jsx)("discord-spoiler", { children: (0, jsx_runtime_1.jsx)(MessageASTNodes, { nodes: node.content, context: context }) }));
115
137
  case 'emoji':
116
138
  case 'twemoji':
117
139
  return ((0, jsx_runtime_1.jsx)("discord-custom-emoji", { name: node.name, url: (0, utils_1.parseDiscordEmoji)(node), embedEmoji: context.type === RenderType.EMBED, jumbo: (_b = context._internal) === null || _b === void 0 ? void 0 : _b.largeEmojis }));
118
- case 'timestamp':
119
- // TODO: Make this reactive
120
- // https://github.com/ItzDerock/discord-components/blob/main/packages/core/src/components/discord-time/discord-time.tsx
121
- return (0, jsx_runtime_1.jsx)("discord-time", { children: new Date(node.timestamp).toISOString() });
122
- // return <DiscordTime timestamp={parseInt(node.timestamp) * 1000} format={node.format} />;
140
+ case 'timestamp': {
141
+ // Defensive: invalid <t:...> markers must not throw
142
+ const raw = node.timestamp;
143
+ let dateMs = NaN;
144
+ if (typeof raw === 'number') {
145
+ dateMs = raw < 1e12 ? raw * 1000 : raw;
146
+ } else if (typeof raw === 'string') {
147
+ const n = parseInt(raw, 10);
148
+ if (Number.isFinite(n)) dateMs = n < 1e12 ? n * 1000 : n;
149
+ }
150
+ const dt = new Date(dateMs);
151
+ let iso = '';
152
+ try { iso = Number.isFinite(dt.getTime()) ? dt.toISOString() : ''; } catch (_e) { iso = ''; }
153
+ return (0, jsx_runtime_1.jsx)("discord-time", { children: iso });
154
+ }
123
155
  default: {
124
- // console.log(`[discord-html-transcripts] Unknown node type: ${type}`, node);
125
- return typeof node.content === 'string' ? (node.content) : ((0, jsx_runtime_1.jsx)(MessageASTNodes, { nodes: node.content, context: context }));
156
+ return typeof node.content === 'string' ? node.content : ((0, jsx_runtime_1.jsx)(MessageASTNodes, { nodes: node.content, context: context }));
126
157
  }
127
158
  }
128
159
  }
160
+
129
161
  function getChannelType(channelType) {
130
162
  switch (channelType) {
131
163
  case discord_js_1.ChannelType.GuildCategory:
132
164
  case discord_js_1.ChannelType.GuildAnnouncement:
133
165
  case discord_js_1.ChannelType.GuildText:
134
166
  case discord_js_1.ChannelType.DM:
135
- case discord_js_1.ChannelType.GroupDM:
136
- case discord_js_1.ChannelType.GuildDirectory:
137
- case discord_js_1.ChannelType.GuildMedia:
138
167
  return 'channel';
139
168
  case discord_js_1.ChannelType.GuildVoice:
140
169
  case discord_js_1.ChannelType.GuildStageVoice:
141
170
  return 'voice';
171
+ case discord_js_1.ChannelType.AnnouncementThread:
142
172
  case discord_js_1.ChannelType.PublicThread:
143
173
  case discord_js_1.ChannelType.PrivateThread:
144
- case discord_js_1.ChannelType.AnnouncementThread:
145
174
  return 'thread';
146
175
  case discord_js_1.ChannelType.GuildForum:
147
176
  return 'forum';
@@ -149,4 +178,4 @@ function getChannelType(channelType) {
149
178
  return 'channel';
150
179
  }
151
180
  }
152
- //# sourceMappingURL=content.js.map
181
+ //# sourceMappingURL=content.js.map
@@ -1,44 +1,65 @@
1
1
  "use strict";
2
- var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
- if (k2 === undefined) k2 = k;
4
- var desc = Object.getOwnPropertyDescriptor(m, k);
5
- if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
- desc = { enumerable: true, get: function() { return m[k]; } };
7
- }
8
- Object.defineProperty(o, k2, desc);
9
- }) : (function(o, m, k, k2) {
10
- if (k2 === undefined) k2 = k;
11
- o[k2] = m[k];
12
- }));
13
- var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
- Object.defineProperty(o, "default", { enumerable: true, value: v });
15
- }) : function(o, v) {
16
- o["default"] = v;
17
- });
18
2
  var __importStar = (this && this.__importStar) || (function () {
19
- var ownKeys = function(o) {
20
- ownKeys = Object.getOwnPropertyNames || function (o) {
21
- var ar = [];
22
- for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
- return ar;
24
- };
25
- return ownKeys(o);
26
- };
27
- return function (mod) {
28
- if (mod && mod.__esModule) return mod;
29
- var result = {};
30
- if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
- __setModuleDefault(result, mod);
32
- return result;
33
- };
3
+ var ownKeys = function(o){ownKeys = Object.getOwnPropertyNames || function (o){var ar=[];for (var k in o) if (Object.prototype.hasOwnProperty.call(o,k)) ar[ar.length]=k;return ar;};return ownKeys(o);};
4
+ return function (mod){if (mod && mod.__esModule) return mod;var result={};if (mod != null) for (var k=ownKeys(mod), i=0; i<k.length; i++) if (k[i] !== "default") Object.defineProperty(result,k[i],{get:function(){return mod[k[i]];}});Object.defineProperty(result,"default",{value:mod,enumerable:true});return result;};
34
5
  })();
35
6
  Object.defineProperty(exports, "__esModule", { value: true });
36
7
  exports.DiscordEmbed = DiscordEmbed;
37
8
  const jsx_runtime_1 = require("react/jsx-runtime");
38
9
  const embeds_1 = require("../../utils/embeds");
39
10
  const content_1 = __importStar(require("./content"));
11
+ const utils_1 = require("../../utils/utils");
12
+
40
13
  async function DiscordEmbed({ embed, context }) {
41
- var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r;
42
- return ((0, jsx_runtime_1.jsxs)("discord-embed", { "embed-title": (_a = embed.title) !== null && _a !== void 0 ? _a : undefined, slot: "embeds", "author-image": (_c = (_b = embed.author) === null || _b === void 0 ? void 0 : _b.proxyIconURL) !== null && _c !== void 0 ? _c : (_d = embed.author) === null || _d === void 0 ? void 0 : _d.iconURL, "author-name": (_e = embed.author) === null || _e === void 0 ? void 0 : _e.name, "author-url": (_f = embed.author) === null || _f === void 0 ? void 0 : _f.url, color: (_g = embed.hexColor) !== null && _g !== void 0 ? _g : undefined, image: (_j = (_h = embed.image) === null || _h === void 0 ? void 0 : _h.proxyURL) !== null && _j !== void 0 ? _j : (_k = embed.image) === null || _k === void 0 ? void 0 : _k.url, thumbnail: (_m = (_l = embed.thumbnail) === null || _l === void 0 ? void 0 : _l.proxyURL) !== null && _m !== void 0 ? _m : (_o = embed.thumbnail) === null || _o === void 0 ? void 0 : _o.url, url: (_p = embed.url) !== null && _p !== void 0 ? _p : undefined, children: [embed.description && ((0, jsx_runtime_1.jsx)("discord-embed-description", { slot: "description", children: (0, jsx_runtime_1.jsx)(content_1.default, { content: embed.description, context: Object.assign(Object.assign({}, context), { type: content_1.RenderType.EMBED }) }) })), embed.fields.length > 0 && ((0, jsx_runtime_1.jsx)("discord-embed-fields", { slot: "fields", children: embed.fields.map(async (field, id) => ((0, jsx_runtime_1.jsx)("discord-embed-field", { "field-title": field.name, inline: !!field.inline, "inline-index": field.inline ? (0, embeds_1.calculateInlineIndex)(embed.fields, id) : undefined, children: (0, jsx_runtime_1.jsx)(content_1.default, { content: field.value, context: Object.assign(Object.assign({}, context), { type: content_1.RenderType.EMBED }) }) }, `${context.message.id}-e-${context.index}-f-${id}`))) })), embed.footer && ((0, jsx_runtime_1.jsx)("discord-embed-footer", { slot: "footer", footerImage: (_q = embed.footer.proxyIconURL) !== null && _q !== void 0 ? _q : embed.footer.iconURL, timestamp: (_r = embed.timestamp) !== null && _r !== void 0 ? _r : undefined, children: embed.footer.text }))] }, `${context.message.id}-e-${context.index}`));
14
+ const title = embed.title ?? undefined;
15
+ const authorImage = embed.author?.proxyIconURL ?? embed.author?.iconURL;
16
+ const authorName = embed.author?.name;
17
+ const authorUrl = embed.author?.url ? (0, utils_1.safeHref)(embed.author.url) : undefined;
18
+ const color = embed.hexColor ? (0, utils_1.safeColor)(embed.hexColor, undefined) : undefined;
19
+ const image = embed.image?.proxyURL ?? embed.image?.url;
20
+ const thumb = embed.thumbnail?.proxyURL ?? embed.thumbnail?.url;
21
+ const url = embed.url ? (0, utils_1.safeHref)(embed.url) : undefined;
22
+ const provider = embed.provider?.name;
23
+ const video = embed.video?.url ? (0, utils_1.safeHref)(embed.video.url) : null;
24
+
25
+ return ((0, jsx_runtime_1.jsxs)("discord-embed", {
26
+ "embed-title": title,
27
+ slot: "embeds",
28
+ "author-image": authorImage,
29
+ "author-name": authorName,
30
+ "author-url": authorUrl,
31
+ color: color,
32
+ image: image,
33
+ thumbnail: thumb,
34
+ url: url,
35
+ children: [
36
+ provider && ((0, jsx_runtime_1.jsx)("div", { className: "dht-embed-provider", slot: "author", children: provider })),
37
+ embed.description && ((0, jsx_runtime_1.jsx)("discord-embed-description", {
38
+ slot: "description",
39
+ children: (0, jsx_runtime_1.jsx)(content_1.default, { content: embed.description, context: Object.assign({}, context, { type: content_1.RenderType.EMBED }) })
40
+ })),
41
+ embed.fields.length > 0 && ((0, jsx_runtime_1.jsx)("discord-embed-fields", {
42
+ slot: "fields",
43
+ children: embed.fields.map((field, id) => ((0, jsx_runtime_1.jsx)(EmbedField, { embed, field, id, context }, `${context.message.id}-e-${context.index}-f-${id}`)))
44
+ })),
45
+ video && ((0, jsx_runtime_1.jsx)("div", { className: "dht-embed-video", children: (0, jsx_runtime_1.jsx)("a", { href: video, target: "_blank", rel: "noreferrer", children: '▶ Open video' }) })),
46
+ embed.footer && ((0, jsx_runtime_1.jsx)("discord-embed-footer", {
47
+ slot: "footer",
48
+ footerImage: embed.footer.proxyIconURL ?? embed.footer.iconURL,
49
+ timestamp: embed.timestamp ?? undefined,
50
+ children: embed.footer.text
51
+ }))
52
+ ]
53
+ }, `${context.message.id}-e-${context.index}`));
43
54
  }
44
- //# sourceMappingURL=embed.js.map
55
+
56
+ // Extracted as proper async component (was inline async arrow inside .map() — undefined React behavior)
57
+ async function EmbedField({ embed, field, id, context }) {
58
+ return (0, jsx_runtime_1.jsx)("discord-embed-field", {
59
+ "field-title": field.name,
60
+ inline: !!field.inline,
61
+ "inline-index": field.inline ? (0, embeds_1.calculateInlineIndex)(embed.fields, id) : undefined,
62
+ children: (0, jsx_runtime_1.jsx)(content_1.default, { content: field.value, context: Object.assign({}, context, { type: content_1.RenderType.EMBED }) })
63
+ });
64
+ }
65
+ //# sourceMappingURL=embed.js.map