discord.js-html-transcript 4.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 (102) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +470 -0
  3. package/dist/downloader/assets.d.ts +18 -0
  4. package/dist/downloader/assets.js +53 -0
  5. package/dist/downloader/assets.js.map +1 -0
  6. package/dist/downloader/images.d.ts +28 -0
  7. package/dist/downloader/images.js +119 -0
  8. package/dist/downloader/images.js.map +1 -0
  9. package/dist/generator/index.d.ts +24 -0
  10. package/dist/generator/index.js +112 -0
  11. package/dist/generator/index.js.map +1 -0
  12. package/dist/generator/renderers/attachment.d.ts +23 -0
  13. package/dist/generator/renderers/attachment.js +154 -0
  14. package/dist/generator/renderers/attachment.js.map +1 -0
  15. package/dist/generator/renderers/components/Attachment Gallery.d.ts +9 -0
  16. package/dist/generator/renderers/components/Attachment Gallery.js +26 -0
  17. package/dist/generator/renderers/components/Attachment Gallery.js.map +1 -0
  18. package/dist/generator/renderers/components/Button.d.ts +9 -0
  19. package/dist/generator/renderers/components/Button.js +23 -0
  20. package/dist/generator/renderers/components/Button.js.map +1 -0
  21. package/dist/generator/renderers/components/Container.d.ts +6 -0
  22. package/dist/generator/renderers/components/Container.js +26 -0
  23. package/dist/generator/renderers/components/Container.js.map +1 -0
  24. package/dist/generator/renderers/components/Media Gallery.d.ts +8 -0
  25. package/dist/generator/renderers/components/Media Gallery.js +45 -0
  26. package/dist/generator/renderers/components/Media Gallery.js.map +1 -0
  27. package/dist/generator/renderers/components/Select Menu.d.ts +10 -0
  28. package/dist/generator/renderers/components/Select Menu.js +34 -0
  29. package/dist/generator/renderers/components/Select Menu.js.map +1 -0
  30. package/dist/generator/renderers/components/Spacing.d.ts +7 -0
  31. package/dist/generator/renderers/components/Spacing.js +17 -0
  32. package/dist/generator/renderers/components/Spacing.js.map +1 -0
  33. package/dist/generator/renderers/components/Thumbnail.d.ts +7 -0
  34. package/dist/generator/renderers/components/Thumbnail.js +21 -0
  35. package/dist/generator/renderers/components/Thumbnail.js.map +1 -0
  36. package/dist/generator/renderers/components/poll.d.ts +7 -0
  37. package/dist/generator/renderers/components/poll.js +78 -0
  38. package/dist/generator/renderers/components/poll.js.map +1 -0
  39. package/dist/generator/renderers/components/section/Section.d.ts +11 -0
  40. package/dist/generator/renderers/components/section/Section.js +21 -0
  41. package/dist/generator/renderers/components/section/Section.js.map +1 -0
  42. package/dist/generator/renderers/components/section/SectionAccessory.d.ts +6 -0
  43. package/dist/generator/renderers/components/section/SectionAccessory.js +20 -0
  44. package/dist/generator/renderers/components/section/SectionAccessory.js.map +1 -0
  45. package/dist/generator/renderers/components/section/SectionContent.d.ts +6 -0
  46. package/dist/generator/renderers/components/section/SectionContent.js +21 -0
  47. package/dist/generator/renderers/components/section/SectionContent.js.map +1 -0
  48. package/dist/generator/renderers/components/styles.d.ts +21 -0
  49. package/dist/generator/renderers/components/styles.js +1639 -0
  50. package/dist/generator/renderers/components/styles.js.map +1 -0
  51. package/dist/generator/renderers/components/utils.d.ts +36 -0
  52. package/dist/generator/renderers/components/utils.js +98 -0
  53. package/dist/generator/renderers/components/utils.js.map +1 -0
  54. package/dist/generator/renderers/components/voice.d.ts +5 -0
  55. package/dist/generator/renderers/components/voice.js +50 -0
  56. package/dist/generator/renderers/components/voice.js.map +1 -0
  57. package/dist/generator/renderers/components.d.ts +13 -0
  58. package/dist/generator/renderers/components.js +78 -0
  59. package/dist/generator/renderers/components.js.map +1 -0
  60. package/dist/generator/renderers/content.d.ts +26 -0
  61. package/dist/generator/renderers/content.js +396 -0
  62. package/dist/generator/renderers/content.js.map +1 -0
  63. package/dist/generator/renderers/embed.d.ts +59 -0
  64. package/dist/generator/renderers/embed.js +216 -0
  65. package/dist/generator/renderers/embed.js.map +1 -0
  66. package/dist/generator/renderers/message.d.ts +7 -0
  67. package/dist/generator/renderers/message.js +106 -0
  68. package/dist/generator/renderers/message.js.map +1 -0
  69. package/dist/generator/renderers/reply.d.ts +7 -0
  70. package/dist/generator/renderers/reply.js +74 -0
  71. package/dist/generator/renderers/reply.js.map +1 -0
  72. package/dist/generator/renderers/systemMessage.d.ts +15 -0
  73. package/dist/generator/renderers/systemMessage.js +168 -0
  74. package/dist/generator/renderers/systemMessage.js.map +1 -0
  75. package/dist/generator/transcript.d.ts +3 -0
  76. package/dist/generator/transcript.js +148 -0
  77. package/dist/generator/transcript.js.map +1 -0
  78. package/dist/index.d.ts +26 -0
  79. package/dist/index.js +159 -0
  80. package/dist/index.js.map +1 -0
  81. package/dist/options.d.ts +37 -0
  82. package/dist/options.js +89 -0
  83. package/dist/options.js.map +1 -0
  84. package/dist/static/client.d.ts +6 -0
  85. package/dist/static/client.js +626 -0
  86. package/dist/static/client.js.map +1 -0
  87. package/dist/types.d.ts +193 -0
  88. package/dist/types.js +10 -0
  89. package/dist/types.js.map +1 -0
  90. package/dist/utils/buildProfiles.d.ts +14 -0
  91. package/dist/utils/buildProfiles.js +136 -0
  92. package/dist/utils/buildProfiles.js.map +1 -0
  93. package/dist/utils/embeds.d.ts +2 -0
  94. package/dist/utils/embeds.js +17 -0
  95. package/dist/utils/embeds.js.map +1 -0
  96. package/dist/utils/extend.d.ts +8 -0
  97. package/dist/utils/extend.js +8 -0
  98. package/dist/utils/extend.js.map +1 -0
  99. package/dist/utils/utils.d.ts +9 -0
  100. package/dist/utils/utils.js +44 -0
  101. package/dist/utils/utils.js.map +1 -0
  102. package/package.json +100 -0
@@ -0,0 +1,89 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.resolveFeatureOptions = resolveFeatureOptions;
4
+ exports.resolveAssetOptions = resolveAssetOptions;
5
+ exports.hasSavedAssetsEnabled = hasSavedAssetsEnabled;
6
+ exports.shouldSaveAsset = shouldSaveAsset;
7
+ const DEFAULT_FEATURES = {
8
+ search: true,
9
+ imagePreview: true,
10
+ spoilerReveal: true,
11
+ messageLinks: true,
12
+ profileBadges: true,
13
+ embedTweaks: true,
14
+ };
15
+ const DEFAULT_ASSETS = {
16
+ attachmentImages: false,
17
+ attachmentVideos: false,
18
+ attachmentAudio: false,
19
+ attachmentFiles: false,
20
+ embedImages: false,
21
+ embedThumbnails: false,
22
+ embedVideos: false,
23
+ embedAuthorIcons: false,
24
+ embedFooterIcons: false,
25
+ componentImages: false,
26
+ componentThumbnails: false,
27
+ componentFiles: false,
28
+ avatars: false,
29
+ emojis: false,
30
+ guildIcons: false,
31
+ inviteIcons: false,
32
+ roleIcons: false,
33
+ serverTagBadges: false,
34
+ };
35
+ function resolveFeatureOptions(features) {
36
+ return Object.assign(Object.assign({}, DEFAULT_FEATURES), (features !== null && features !== void 0 ? features : {}));
37
+ }
38
+ function resolveAssetOptions({ saveImages, saveAssets, assets, }) {
39
+ if (saveAssets) {
40
+ return Object.fromEntries(Object.keys(DEFAULT_ASSETS).map((key) => [key, true]));
41
+ }
42
+ return Object.assign(Object.assign({}, DEFAULT_ASSETS), { attachmentImages: Boolean(saveImages || (assets === null || assets === void 0 ? void 0 : assets.attachments)), attachmentVideos: Boolean(assets === null || assets === void 0 ? void 0 : assets.attachments), attachmentAudio: Boolean(assets === null || assets === void 0 ? void 0 : assets.attachments), attachmentFiles: Boolean(assets === null || assets === void 0 ? void 0 : assets.attachments), embedImages: Boolean(assets === null || assets === void 0 ? void 0 : assets.embeds), embedThumbnails: Boolean(assets === null || assets === void 0 ? void 0 : assets.embeds), embedVideos: Boolean(assets === null || assets === void 0 ? void 0 : assets.embeds), embedAuthorIcons: Boolean(assets === null || assets === void 0 ? void 0 : assets.embeds), embedFooterIcons: Boolean(assets === null || assets === void 0 ? void 0 : assets.embeds), componentImages: Boolean(assets === null || assets === void 0 ? void 0 : assets.components), componentThumbnails: Boolean(assets === null || assets === void 0 ? void 0 : assets.components), componentFiles: Boolean(assets === null || assets === void 0 ? void 0 : assets.components), avatars: Boolean(assets === null || assets === void 0 ? void 0 : assets.avatars), emojis: Boolean(assets === null || assets === void 0 ? void 0 : assets.emojis), guildIcons: Boolean(assets === null || assets === void 0 ? void 0 : assets.guildIcons), inviteIcons: Boolean(assets === null || assets === void 0 ? void 0 : assets.inviteIcons), roleIcons: Boolean(assets === null || assets === void 0 ? void 0 : assets.roleIcons), serverTagBadges: Boolean(assets === null || assets === void 0 ? void 0 : assets.serverTagBadges) });
43
+ }
44
+ function hasSavedAssetsEnabled(options) {
45
+ return Object.values(options).some(Boolean);
46
+ }
47
+ function shouldSaveAsset(kind, options) {
48
+ switch (kind) {
49
+ case 'attachment-image':
50
+ return options.attachmentImages;
51
+ case 'attachment-video':
52
+ return options.attachmentVideos;
53
+ case 'attachment-audio':
54
+ return options.attachmentAudio;
55
+ case 'attachment-file':
56
+ return options.attachmentFiles;
57
+ case 'embed-image':
58
+ return options.embedImages;
59
+ case 'embed-thumbnail':
60
+ return options.embedThumbnails;
61
+ case 'embed-video':
62
+ return options.embedVideos;
63
+ case 'embed-author-icon':
64
+ return options.embedAuthorIcons;
65
+ case 'embed-footer-icon':
66
+ return options.embedFooterIcons;
67
+ case 'component-image':
68
+ return options.componentImages;
69
+ case 'component-thumbnail':
70
+ return options.componentThumbnails;
71
+ case 'component-file':
72
+ return options.componentFiles;
73
+ case 'avatar':
74
+ return options.avatars;
75
+ case 'emoji':
76
+ return options.emojis;
77
+ case 'guild-icon':
78
+ return options.guildIcons;
79
+ case 'invite-icon':
80
+ return options.inviteIcons;
81
+ case 'role-icon':
82
+ return options.roleIcons;
83
+ case 'server-tag-badge':
84
+ return options.serverTagBadges;
85
+ default:
86
+ return false;
87
+ }
88
+ }
89
+ //# sourceMappingURL=options.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"options.js","sourceRoot":"","sources":["../src/options.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,6 @@
1
+ export declare const scrollToMessage = "document.addEventListener(\"click\",t=>{let e=t.target;if(!e)return;let o=e?.getAttribute(\"data-goto\");if(o){let r=document.getElementById(`m-${o}`);r?(r.scrollIntoView({behavior:\"smooth\",block:\"center\"}),r.style.backgroundColor=\"rgba(148, 156, 247, 0.1)\",r.style.transition=\"background-color 0.5s ease\",setTimeout(()=>{r.style.backgroundColor=\"transparent\"},1e3)):console.warn(\"Message ${goto} not found.\")}});";
2
+ export declare const revealSpoiler = "document.addEventListener(\"click\",e=>{const t=e.target;if(!(t instanceof Element))return;const o=t.closest(\".discord-spoiler\");o&&o.classList.contains(\"discord-spoiler\")&&(o.classList.remove(\"discord-spoiler\"),o.classList.add(\"discord-spoiler--revealed\"))});";
3
+ export declare const applyServerTags = "\n(() => {\n const hostSelector = 'discord-message[profile], discord-reply[profile], discord-thread-message[profile], discord-command[profile]';\n const rootSelector = 'discord-message, discord-reply, discord-thread-message, discord-command';\n\n const getProfiles = () => window.$discordMessage?.profiles ?? {};\n const getOwningHost = (element) => element.closest(rootSelector);\n\n const getInjectedTag = (host) =>\n Array.from(host.querySelectorAll('.discord-server-tag[data-transcript-server-tag=\"true\"]')).find(\n (element) => getOwningHost(element) === host\n ) ?? null;\n\n const getInjectedRoleIcon = (host) =>\n Array.from(host.querySelectorAll('.discord-author-role-icon[data-transcript-role-icon=\"true\"]')).find(\n (element) => getOwningHost(element) === host\n ) ?? null;\n\n const getNativeRoleIcon = (host) =>\n Array.from(host.querySelectorAll('.discord-author-role-icon')).find(\n (element) => getOwningHost(element) === host && element.dataset.transcriptRoleIcon !== 'true'\n ) ?? null;\n\n const getUsernameElement = (host) => {\n const usernameSelector =\n host.tagName === 'DISCORD-MESSAGE' || host.tagName === 'DISCORD-THREAD-MESSAGE'\n ? '.discord-author-username'\n : '.discord-replied-message-username';\n\n return Array.from(host.querySelectorAll(usernameSelector)).find((element) => getOwningHost(element) === host) ?? null;\n };\n\n const createServerTag = (tagText, badgeUrl, guildId) => {\n const container = document.createElement('span');\n container.className = 'discord-server-tag';\n container.dataset.transcriptServerTag = 'true';\n container.dataset.tag = tagText;\n container.dataset.badge = badgeUrl ?? '';\n container.title = 'Server Tag: ' + tagText;\n\n if (guildId) {\n container.dataset.guildId = guildId;\n }\n\n if (badgeUrl) {\n const badge = document.createElement('img');\n badge.className = 'discord-server-tag-badge';\n badge.src = badgeUrl;\n badge.alt = '';\n badge.loading = 'lazy';\n badge.decoding = 'async';\n badge.draggable = false;\n container.appendChild(badge);\n }\n\n const label = document.createElement('span');\n label.className = 'discord-server-tag-text';\n label.textContent = tagText;\n container.appendChild(label);\n\n return container;\n };\n\n const createRoleIcon = (iconUrl, roleName) => {\n const icon = document.createElement('img');\n icon.className = 'discord-author-role-icon';\n icon.dataset.transcriptRoleIcon = 'true';\n icon.src = iconUrl;\n icon.alt = roleName || '';\n icon.loading = 'lazy';\n icon.decoding = 'async';\n icon.draggable = false;\n\n if (roleName) {\n icon.title = roleName;\n }\n\n return icon;\n };\n\n const applyToHost = (host) => {\n const profileId = host.getAttribute('profile');\n if (!profileId) {\n return;\n }\n\n const profile = getProfiles()[profileId];\n const tagText = typeof profile?.serverTagText === 'string' ? profile.serverTagText.trim() : '';\n const badgeUrl = typeof profile?.serverTagBadge === 'string' ? profile.serverTagBadge : '';\n const guildId = typeof profile?.serverTagGuildId === 'string' ? profile.serverTagGuildId : '';\n const existing = getInjectedTag(host);\n\n if (!tagText) {\n existing?.remove();\n return;\n }\n\n const username = getUsernameElement(host);\n if (!username) {\n return;\n }\n\n if (existing?.dataset.tag === tagText && (existing.dataset.badge ?? '') === badgeUrl) {\n return;\n }\n\n existing?.remove();\n username.insertAdjacentElement('afterend', createServerTag(tagText, badgeUrl || undefined, guildId || undefined));\n };\n\n const applyRoleIconToHost = (host) => {\n const profileId = host.getAttribute('profile');\n if (!profileId) {\n return;\n }\n\n const profile = getProfiles()[profileId];\n const roleIcon = typeof profile?.roleIcon === 'string' ? profile.roleIcon : '';\n const roleName = typeof profile?.roleName === 'string' ? profile.roleName : '';\n const existing = getInjectedRoleIcon(host);\n const nativeIcon = getNativeRoleIcon(host);\n\n if (!roleIcon) {\n existing?.remove();\n return;\n }\n\n if (nativeIcon) {\n if (nativeIcon.getAttribute('src') !== roleIcon) {\n nativeIcon.setAttribute('src', roleIcon);\n }\n\n nativeIcon.setAttribute('alt', roleName);\n if (roleName) {\n nativeIcon.setAttribute('title', roleName);\n }\n return;\n }\n\n const username = getUsernameElement(host);\n if (!username) {\n return;\n }\n\n const anchor = getInjectedTag(host) ?? username;\n if (existing) {\n if (existing.getAttribute('src') !== roleIcon) {\n existing.setAttribute('src', roleIcon);\n }\n\n existing.setAttribute('alt', roleName);\n if (roleName) {\n existing.setAttribute('title', roleName);\n } else {\n existing.removeAttribute('title');\n }\n\n if (existing.previousElementSibling !== anchor) {\n anchor.insertAdjacentElement('afterend', existing);\n }\n return;\n }\n\n anchor.insertAdjacentElement('afterend', createRoleIcon(roleIcon, roleName));\n };\n\n const normalizeApplicationTags = () => {\n document.querySelectorAll('.discord-application-tag').forEach((tag) => {\n const label = Array.from(tag.childNodes)\n .map((node) => node.textContent ?? '')\n .join('')\n .replace(/\\s+/g, ' ')\n .trim()\n .toLowerCase();\n\n if (label === 'bot') {\n tag.setAttribute('data-transcript-app-tag-kind', 'bot');\n return;\n }\n\n if (label === 'server') {\n tag.setAttribute('data-transcript-app-tag-kind', 'server');\n return;\n }\n\n tag.removeAttribute('data-transcript-app-tag-kind');\n });\n };\n\n let scheduled = false;\n\n const applyAll = () => {\n document.querySelectorAll(hostSelector).forEach((host) => applyToHost(host));\n document.querySelectorAll(hostSelector).forEach((host) => applyRoleIconToHost(host));\n normalizeApplicationTags();\n };\n\n const schedule = () => {\n if (scheduled) {\n return;\n }\n\n scheduled = true;\n requestAnimationFrame(() => {\n scheduled = false;\n applyAll();\n });\n };\n\n if (document.readyState === 'loading') {\n document.addEventListener('DOMContentLoaded', schedule, { once: true });\n } else {\n schedule();\n }\n\n window.addEventListener('load', schedule);\n new MutationObserver(schedule).observe(document.documentElement, { childList: true, subtree: true });\n})();\n";
4
+ export declare const enableImagePreview = "\n(() => {\n const overlay = document.createElement('div');\n overlay.className = 'discord-image-preview-overlay';\n overlay.innerHTML = `\n <button type=\"button\" class=\"discord-image-preview-close\" aria-label=\"Close image preview\">\n <svg width=\"20\" height=\"20\" viewBox=\"0 0 24 24\" aria-hidden=\"true\">\n <path fill=\"currentColor\" d=\"M17.66 6.34a1 1 0 0 0-1.41 0L12 10.59 7.76 6.34a1 1 0 1 0-1.41 1.41L10.59 12l-4.24 4.24a1 1 0 1 0 1.41 1.41L12 13.41l4.24 4.24a1 1 0 0 0 1.41-1.41L13.41 12l4.24-4.24a1 1 0 0 0 0-1.41Z\" />\n </svg>\n </button>\n <div class=\"discord-image-preview-stage\">\n <img class=\"discord-image-preview-image\" alt=\"\" />\n </div>\n `;\n\n const previewImage = overlay.querySelector('.discord-image-preview-image');\n const closeButton = overlay.querySelector('.discord-image-preview-close');\n const ignoredAncestors =\n '.discord-image-preview-overlay, .discord-author-avatar, .discord-replied-message, .discord-button, .discord-select-menu-option, .discord-server-tag, .discord-thread-preview-message, .discord-poll-emoji, .discord-invite';\n let previousOverflow = '';\n\n const closePreview = () => {\n overlay.classList.remove('is-open');\n previewImage.removeAttribute('src');\n previewImage.alt = '';\n document.body.style.overflow = previousOverflow;\n };\n\n const openPreview = (src, alt) => {\n if (!src) {\n return;\n }\n\n previousOverflow = document.body.style.overflow;\n previewImage.src = src;\n previewImage.alt = alt || 'Image preview';\n overlay.classList.add('is-open');\n document.body.style.overflow = 'hidden';\n };\n\n const getPreviewCandidate = (target) => {\n const attachment = target.closest('discord-attachment[type=\"image\"]');\n if (attachment) {\n return {\n src: attachment.getAttribute('url') || '',\n alt: attachment.getAttribute('alt') || 'Image preview',\n };\n }\n\n const image = target.closest('img');\n if (!image) {\n return null;\n }\n\n if (\n image.classList.contains('discord-button-emoji') ||\n image.classList.contains('discord-select-menu-option-emoji') ||\n image.classList.contains('discord-server-tag-badge') ||\n image.classList.contains('discord-author-role-icon') ||\n image.closest(ignoredAncestors)\n ) {\n return null;\n }\n\n const src = image.currentSrc || image.getAttribute('src') || '';\n if (!src) {\n return null;\n }\n\n return {\n src,\n alt: image.getAttribute('alt') || 'Image preview',\n };\n };\n\n document.addEventListener(\n 'click',\n (event) => {\n const target = event.target;\n if (!(target instanceof Element)) {\n return;\n }\n\n if (target === overlay || target.closest('.discord-image-preview-close')) {\n event.preventDefault();\n closePreview();\n return;\n }\n\n const candidate = getPreviewCandidate(target);\n if (!candidate) {\n return;\n }\n\n event.preventDefault();\n event.stopPropagation();\n openPreview(candidate.src, candidate.alt);\n },\n true\n );\n\n overlay.querySelector('.discord-image-preview-stage')?.addEventListener('click', (event) => {\n if (event.target === event.currentTarget) {\n closePreview();\n }\n });\n\n document.addEventListener('keydown', (event) => {\n if (event.key === 'Escape' && overlay.classList.contains('is-open')) {\n closePreview();\n }\n });\n\n if (document.readyState === 'loading') {\n document.addEventListener('DOMContentLoaded', () => document.body.appendChild(overlay), { once: true });\n } else {\n document.body.appendChild(overlay);\n }\n})();\n";
5
+ export declare const enableMessageSearch = "\n(() => {\n const SEARCH_SELECTOR = 'discord-message[id^=\"m-\"]';\n const MATCH_CLASS = 'discord-search-match';\n const ACTIVE_CLASS = 'discord-search-match--active';\n const SHELL_CLASS = 'discord-transcript-search-shell';\n const PANEL_CLASS = 'discord-transcript-search';\n const OPEN_CLASS = 'is-open';\n\n const normalize = (value) => value.toLowerCase().replace(/\\s+/g, ' ').trim();\n\n const extractMessageId = (value) => {\n const trimmed = value.trim();\n const directMatch = trimmed.match(/^(?:m-)?(\\d{15,21})$/);\n if (directMatch) {\n return directMatch[1];\n }\n\n const linkMatch = trimmed.match(/\\/(\\d{15,21})(?:\\/?(?:\\?.*)?)?$/);\n return linkMatch ? linkMatch[1] : null;\n };\n\n const getMessages = () =>\n Array.from(document.querySelectorAll(SEARCH_SELECTOR))\n .filter((element) => element instanceof HTMLElement)\n .map((element) => ({\n element,\n id: (element.id || '').replace(/^m-/, ''),\n text: normalize(element.textContent || ''),\n }));\n\n const shell = document.createElement('div');\n shell.className = SHELL_CLASS;\n shell.innerHTML =\n '<button class=\"discord-transcript-search-toggle\" type=\"button\" aria-label=\"Toggle transcript search\" aria-expanded=\"false\">' +\n '<svg width=\"18\" height=\"18\" viewBox=\"0 0 24 24\" aria-hidden=\"true\"><path fill=\"currentColor\" d=\"M10 2a8 8 0 1 1 0 16a8 8 0 0 1 0-16Zm0 2a6 6 0 1 0 0 12a6 6 0 0 0 0-12Zm11.71 15.29a1 1 0 0 1 0 1.42a1 1 0 0 1-1.42 0l-3.4-3.39a1 1 0 0 1 1.42-1.42l3.4 3.39Z\"/></svg>' +\n '</button>' +\n '<div class=\"' +\n PANEL_CLASS +\n '\" role=\"search\" aria-hidden=\"true\">' +\n '<label class=\"discord-transcript-search-bar\">' +\n '<input class=\"discord-transcript-search-input\" type=\"search\" aria-label=\"Search transcript messages\" />' +\n '<button class=\"discord-transcript-search-submit\" type=\"button\" data-action=\"next\" aria-label=\"Search transcript\">' +\n '<svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" aria-hidden=\"true\"><path fill=\"currentColor\" d=\"M10 2a8 8 0 1 1 0 16a8 8 0 0 1 0-16Zm0 2a6 6 0 1 0 0 12a6 6 0 0 0 0-12Zm11.71 15.29a1 1 0 0 1 0 1.42a1 1 0 0 1-1.42 0l-3.4-3.39a1 1 0 0 1 1.42-1.42l3.4 3.39Z\"/></svg>' +\n '</button>' +\n '</label>' +\n '<div class=\"discord-transcript-search-meta\">' +\n '<span class=\"discord-transcript-search-count\" aria-live=\"polite\">Ready</span>' +\n '<div class=\"discord-transcript-search-actions\">' +\n '<button class=\"discord-transcript-search-button\" type=\"button\" data-action=\"prev\" aria-label=\"Previous match\">\u2191</button>' +\n '<button class=\"discord-transcript-search-button\" type=\"button\" data-action=\"next\" aria-label=\"Next match\">\u2193</button>' +\n '<button class=\"discord-transcript-search-button discord-transcript-search-button--ghost\" type=\"button\" data-action=\"clear\" aria-label=\"Clear search\">\u2715</button>' +\n '</div>' +\n '</div>' +\n '</div>';\n\n const desktopMedia = window.matchMedia('(min-width: 901px)');\n const panel = shell.querySelector('.discord-transcript-search');\n const toggleButton = shell.querySelector('.discord-transcript-search-toggle');\n const input = shell.querySelector('.discord-transcript-search-input');\n const count = shell.querySelector('.discord-transcript-search-count');\n const previousButton = shell.querySelector('[data-action=\"prev\"]');\n const nextButton = shell.querySelectorAll('[data-action=\"next\"]')[1];\n const submitButton = shell.querySelector('.discord-transcript-search-submit');\n const clearButton = shell.querySelector('[data-action=\"clear\"]');\n const headerChannel =\n document.querySelector('.discord-transcript-header-bar')?.getAttribute('data-channel-name') ||\n document.querySelector('discord-header')?.getAttribute('channel') ||\n document.title ||\n 'transcript';\n\n let matches = [];\n let activeIndex = -1;\n let scheduled = false;\n let hasUserToggled = false;\n\n input.placeholder = 'Search ' + headerChannel;\n\n const setOpen = (next, focus = false) => {\n shell.classList.toggle(OPEN_CLASS, next);\n toggleButton.setAttribute('aria-expanded', String(next));\n panel.setAttribute('aria-hidden', String(!next));\n\n if (focus && next) {\n requestAnimationFrame(() => {\n input.focus();\n input.select();\n });\n }\n\n if (!next) {\n input.blur();\n }\n };\n\n const syncDefaultVisibility = (force = false) => {\n if (!force && hasUserToggled) {\n return;\n }\n\n setOpen(desktopMedia.matches, false);\n };\n\n const clearHighlights = () => {\n document.querySelectorAll(SEARCH_SELECTOR + '.' + MATCH_CLASS).forEach((message) => {\n message.classList.remove(MATCH_CLASS, ACTIVE_CLASS);\n });\n };\n\n const updateControls = () => {\n const total = matches.length;\n const query = (input.value || '').trim();\n panel.classList.toggle('has-query', Boolean(query));\n count.textContent =\n !query ? 'Ready' : total > 0 && activeIndex >= 0 ? activeIndex + 1 + ' of ' + total : 'No matches';\n previousButton.disabled = total === 0;\n nextButton.disabled = total === 0;\n clearButton.disabled = !(input.value || '').trim();\n };\n\n const focusMatch = (index, scroll = true) => {\n if (matches.length === 0) {\n activeIndex = -1;\n updateControls();\n return;\n }\n\n if (activeIndex >= 0 && matches[activeIndex]) {\n matches[activeIndex].element.classList.remove(ACTIVE_CLASS);\n }\n\n activeIndex = ((index % matches.length) + matches.length) % matches.length;\n const match = matches[activeIndex];\n match.element.classList.add(ACTIVE_CLASS);\n\n if (scroll) {\n match.element.scrollIntoView({ behavior: 'smooth', block: 'center' });\n }\n\n updateControls();\n };\n\n const runSearch = (scroll = true) => {\n clearHighlights();\n matches = [];\n activeIndex = -1;\n\n const rawQuery = (input.value || '').trim();\n if (!rawQuery) {\n updateControls();\n return;\n }\n\n const normalizedQuery = normalize(rawQuery.replace(/^m-/, ''));\n const exactMessageId = extractMessageId(rawQuery);\n const availableMessages = getMessages();\n\n matches = availableMessages.filter((message) => {\n if (exactMessageId) {\n return message.id === exactMessageId;\n }\n\n return message.id.includes(normalizedQuery) || message.text.includes(normalizedQuery);\n });\n\n matches.forEach((match) => match.element.classList.add(MATCH_CLASS));\n focusMatch(0, scroll);\n };\n\n const scheduleSearchRefresh = () => {\n if (scheduled || !(input.value || '').trim()) {\n return;\n }\n\n scheduled = true;\n requestAnimationFrame(() => {\n scheduled = false;\n runSearch(false);\n });\n };\n\n input.addEventListener('input', () => runSearch(false));\n input.addEventListener('keydown', (event) => {\n if (event.key === 'Enter') {\n event.preventDefault();\n if (matches.length > 0) {\n focusMatch(activeIndex + (event.shiftKey ? -1 : 1));\n } else {\n runSearch();\n }\n return;\n }\n\n if (event.key === 'Escape') {\n if ((input.value || '').trim()) {\n input.value = '';\n runSearch(false);\n } else {\n setOpen(false, false);\n }\n }\n });\n\n toggleButton.addEventListener('click', () => {\n hasUserToggled = true;\n setOpen(!shell.classList.contains(OPEN_CLASS), !shell.classList.contains(OPEN_CLASS));\n });\n\n previousButton.addEventListener('click', () => focusMatch(activeIndex - 1));\n nextButton.addEventListener('click', () => {\n if (matches.length > 0) {\n focusMatch(activeIndex + 1);\n return;\n }\n\n runSearch();\n });\n submitButton.addEventListener('click', () => {\n if (matches.length > 0) {\n focusMatch(activeIndex + 1);\n return;\n }\n\n runSearch();\n });\n clearButton.addEventListener('click', () => {\n input.value = '';\n runSearch(false);\n input.focus();\n });\n\n document.addEventListener('keydown', (event) => {\n const isFindShortcut = (event.ctrlKey || event.metaKey) && !event.altKey && !event.shiftKey && event.key.toLowerCase() === 'f';\n if (!isFindShortcut) {\n return;\n }\n\n event.preventDefault();\n setOpen(true, true);\n });\n\n document.addEventListener(\n 'pointerdown',\n (event) => {\n const target = event.target;\n if (!(target instanceof Element) || desktopMedia.matches || !shell.classList.contains(OPEN_CLASS)) {\n return;\n }\n\n if (!shell.contains(target)) {\n setOpen(false, false);\n }\n },\n true\n );\n\n desktopMedia.addEventListener('change', () => syncDefaultVisibility(false));\n\n const mount = () => {\n const mountTarget = document.querySelector('.discord-transcript-header-search-slot') || document.body;\n\n if (!mountTarget.contains(shell)) {\n mountTarget.appendChild(shell);\n syncDefaultVisibility(true);\n updateControls();\n }\n };\n\n if (document.readyState === 'loading') {\n document.addEventListener('DOMContentLoaded', mount, { once: true });\n } else {\n mount();\n }\n\n const transcriptRoot = document.querySelector('discord-messages');\n if (transcriptRoot) {\n new MutationObserver(scheduleSearchRefresh).observe(transcriptRoot, { childList: true, subtree: true });\n }\n})();\n";
6
+ export declare const fixEmbedBorders = "setTimeout(()=>{document.querySelectorAll(\".discord-embed-wrapper\").forEach(e=>{e.style.border=\"1px solid rgba(255, 255, 255, 0.06)\";e.style.borderRadius=\"4px\"});},500);setTimeout(()=>{document.querySelectorAll(\".discord-embed-wrapper\").forEach(e=>{e.style.border=\"1px solid rgba(255, 255, 255, 0.06)\";e.style.borderRadius=\"4px\"});},2000);";