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,119 @@
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.default = PinnedMessagesModal;
7
+ const jsx_runtime_1 = require("react/jsx-runtime");
8
+ const react_1 = __importDefault(require("react"));
9
+ const AudioPlayer_1 = __importDefault(require("./AudioPlayer"));
10
+ const Embed_1 = __importDefault(require("./Embed"));
11
+ const ChevronDownIcon_1 = __importDefault(require("./icons/ChevronDownIcon"));
12
+ const CloseIcon_1 = __importDefault(require("./icons/CloseIcon"));
13
+ const PinIcon_1 = __importDefault(require("./icons/PinIcon"));
14
+ const VerifiedIcon_1 = __importDefault(require("./icons/VerifiedIcon"));
15
+ const FileCodeIcon_1 = __importDefault(require("./icons/FileCodeIcon"));
16
+ const FileDocumentIcon_1 = __importDefault(require("./icons/FileDocumentIcon"));
17
+ const StickerPreview_1 = __importDefault(require("./StickerPreview"));
18
+ const date_1 = require("./utils/date");
19
+ const cdnHelpers_1 = require("../helpers/cdnHelpers");
20
+ function PinnedMessagesModal({ pinnedMessages, onClose, onMessageClick }) {
21
+ const handleMessageClick = (messageId) => {
22
+ onMessageClick(messageId);
23
+ onClose();
24
+ };
25
+ react_1.default.useEffect(() => {
26
+ const onKey = (e) => {
27
+ if (e.key === "Escape")
28
+ onClose();
29
+ };
30
+ window.addEventListener("keydown", onKey);
31
+ return () => window.removeEventListener("keydown", onKey);
32
+ }, [onClose]);
33
+ return ((0, jsx_runtime_1.jsx)("div", { className: "fixed inset-0 bg-black/50 z-50 flex items-start justify-end p-4 pt-16", onClick: onClose, children: (0, jsx_runtime_1.jsxs)("div", { onClick: (e) => e.stopPropagation(), className: "bg-[#313338] rounded-lg shadow-xl w-full sm:w-[420px] max-h-[calc(100vh-32px)] overflow-hidden flex flex-col", children: [(0, jsx_runtime_1.jsxs)("div", { className: "flex items-center justify-between gap-3 px-4 py-3 border-b border-[#3F4147]", children: [(0, jsx_runtime_1.jsxs)("div", { className: "flex items-center gap-2 flex-1 min-w-0", children: [(0, jsx_runtime_1.jsx)(PinIcon_1.default, { className: "text-[#949BA4] flex-shrink-0", width: 20, height: 20 }), (0, jsx_runtime_1.jsx)("h2", { className: "text-base font-semibold text-[#DBDEE1] truncate", children: "Pinned Messages" })] }), (0, jsx_runtime_1.jsx)("button", { type: "button", "aria-label": "Close pinned messages", className: "p-1.5 hover:bg-[#3d4148] rounded transition-colors flex-shrink-0", onClick: onClose, children: (0, jsx_runtime_1.jsx)(CloseIcon_1.default, { className: "text-[#DBDEE1]" }) })] }), (0, jsx_runtime_1.jsx)("div", { className: "flex-1 overflow-y-auto", children: pinnedMessages.length === 0 ? ((0, jsx_runtime_1.jsx)("div", { className: "text-center text-[#72767D] py-8 px-4", children: "No pinned messages" })) : ((0, jsx_runtime_1.jsx)("div", { className: "divide-y divide-[#404249]", children: pinnedMessages.map((msg) => ((0, jsx_runtime_1.jsxs)("div", { className: "p-4 cursor-pointer hover:bg-[#2E3035] transition-colors group relative", onClick: () => handleMessageClick(msg.id), children: [(0, jsx_runtime_1.jsxs)("div", { className: "flex items-center gap-3 mb-2", children: [(0, jsx_runtime_1.jsx)("img", { src: msg.author.avatar, alt: msg.author.username, className: "w-10 h-10 rounded-full flex-shrink-0" }), (0, jsx_runtime_1.jsx)("div", { className: "flex-1 min-w-0", children: (0, jsx_runtime_1.jsxs)("div", { className: "flex items-center gap-2 flex-wrap", children: [(0, jsx_runtime_1.jsx)("span", { className: "text-base font-semibold", style: { color: msg.author.color ?? "#F2F3F5" }, children: msg.author.username }), msg.author.guildTag && msg.author.guildTag.name ? ((0, jsx_runtime_1.jsxs)("span", { className: "text-xs text-[#B9BBBE] bg-[#2F3136] px-1 py-0.5 rounded flex items-center gap-1", children: [msg.author.guildTag.iconUrl ? ((0, jsx_runtime_1.jsx)("img", { src: msg.author.guildTag.iconUrl, alt: msg.author.guildTag.name, className: "w-3 h-3 rounded-full object-cover" })) : null, (0, jsx_runtime_1.jsx)("span", { children: msg.author.guildTag.name })] })) : null, msg.author.bot && ((0, jsx_runtime_1.jsxs)("span", { className: "text-[10px] bg-[#5865F2] text-white px-1.5 py-0.5 rounded font-medium flex-shrink-0 flex items-center gap-1", children: [msg.author.verified ? (0, jsx_runtime_1.jsx)(VerifiedIcon_1.default, { className: "w-3 h-3" }) : null, (0, jsx_runtime_1.jsx)("span", { children: "APP" })] })), (0, jsx_runtime_1.jsx)("span", { className: "text-xs text-[#949BA4]", children: msg.timestamp ? (0, date_1.formatTime)(msg.timestamp) : "" }), msg.editedAt ? (0, jsx_runtime_1.jsx)("span", { className: "text-xs text-[#72767D]", children: "(edited)" }) : null] }) }), (0, jsx_runtime_1.jsx)("button", { type: "button", onClick: (e) => {
34
+ e.stopPropagation();
35
+ handleMessageClick(msg.id);
36
+ }, className: "opacity-0 group-hover:opacity-100 transition-opacity bg-[#4E5058] hover:bg-[#5C5F69] text-white text-sm font-medium px-3 py-1.5 rounded flex-shrink-0", children: "Jump" })] }), (0, jsx_runtime_1.jsxs)("div", { className: "ml-[52px]", children: [typeof msg.content === "string" && msg.content && (0, jsx_runtime_1.jsx)("div", { className: "text-sm text-[#DBDEE1] mb-2", children: msg.content }), msg.embeds && msg.embeds.length > 0 && ((0, jsx_runtime_1.jsx)("div", { className: "mt-2 space-y-2", children: msg.embeds.map((e, i) => ((0, jsx_runtime_1.jsx)(Embed_1.default, { ...(e || {}), resolvedUsers: msg.resolvedUsers, resolvedRoles: msg.resolvedRoles, interaction: msg.interaction }, i))) })), msg.stickers && msg.stickers.length > 0 && ((0, jsx_runtime_1.jsx)("div", { className: "mt-2", children: msg.stickers.map((st, i) => ((0, jsx_runtime_1.jsx)(StickerPreview_1.default, { st: st, size: 160, className: "max-h-40 max-w-[240px] rounded object-contain" }, i))) })), msg.attachments && msg.attachments.length > 0 && ((0, jsx_runtime_1.jsx)("div", { className: "mt-2 flex flex-col gap-2", children: msg.attachments.map((attachment, idx) => {
37
+ const url = typeof attachment === "string" ? attachment : attachment?.url;
38
+ const filename = attachment && typeof attachment !== "string" ? attachment.filename : undefined;
39
+ const size = attachment && typeof attachment !== "string" ? attachment.size : undefined;
40
+ const isImage = url && /\.(png|jpe?g|gif|webp|svg|bmp|avif)(\?|$)/i.test(url);
41
+ const isAudio = url && /\.(mp3|wav|flac|ogg|m4a|aac|opus)(\?|$)/i.test(url);
42
+ if (isImage) {
43
+ return ((0, jsx_runtime_1.jsx)("img", { src: url, alt: "attachment", className: "max-h-60 max-w-full rounded object-contain" }, idx));
44
+ }
45
+ if (isAudio) {
46
+ const extractedFilename = filename || (url ? url.split("/").pop()?.split("?")[0] : "audio");
47
+ const filesize = size ? `${(size / 1024).toFixed(2)} KB` : "";
48
+ return ((0, jsx_runtime_1.jsx)("div", { children: (0, jsx_runtime_1.jsx)(AudioPlayer_1.default, { url: url, filename: extractedFilename ?? "file", filesize: filesize }) }, idx));
49
+ }
50
+ const getAttachmentKind = (name) => {
51
+ const target = (name || "").toLowerCase();
52
+ if (/(\\.js|\\.ts|\\.tsx|\\.jsx|\\.json|\\.py|\\.java|\\.c|\\.cpp|\\.cs|\\.rb|\\.php|\\.go|\\.rs|\\.swift|\\.kt|\\.kts|\\.sh|\\.ps1|\\.bat|\\.sql|\\.html|\\.css|\\.scss|\\.sass)$/.test(target))
53
+ return "code";
54
+ return "document";
55
+ };
56
+ const getAttachmentIcon = (kind) => {
57
+ switch (kind) {
58
+ case "code":
59
+ return FileCodeIcon_1.default;
60
+ default:
61
+ return FileDocumentIcon_1.default;
62
+ }
63
+ };
64
+ const kind = getAttachmentKind(filename || url);
65
+ const Icon = getAttachmentIcon(kind);
66
+ const filesize = size ? `${(size / 1024).toFixed(2)} KB` : "";
67
+ const extractedFilename = filename || (url ? url.split("/").pop()?.split("?")[0] : "file");
68
+ return ((0, jsx_runtime_1.jsx)("div", { className: "group relative inline-block max-w-[432px]", children: (0, jsx_runtime_1.jsxs)("div", { className: "flex items-center gap-3 px-4 py-3 bg-[#1e1f22] hover:bg-[#23252b] border border-[#111214] rounded-lg transition-colors", children: [(0, jsx_runtime_1.jsx)(Icon, { className: "h-10 w-8 flex-shrink-0" }), (0, jsx_runtime_1.jsxs)("div", { className: "flex-1 min-w-0", children: [(0, jsx_runtime_1.jsx)("a", { href: url, download: true, className: "text-[15px] font-medium text-[#00a8fc] truncate hover:underline block", children: extractedFilename }), filesize && (0, jsx_runtime_1.jsx)("div", { className: "text-xs text-[#b5bac1] mt-1", children: filesize })] })] }) }, idx));
69
+ }) })), msg.buttons && msg.buttons.length > 0 && ((0, jsx_runtime_1.jsx)("div", { className: "mt-2 flex flex-wrap gap-2", children: msg.buttons.map((b, i) => {
70
+ const styleId = Number(b.style || 1);
71
+ let classes = "px-3 py-2 rounded text-sm transition-colors inline-flex items-center gap-2 justify-start";
72
+ let isLinkStyle = false;
73
+ switch (styleId) {
74
+ case 1: // Primary
75
+ classes += " bg-[#6652ec] hover:bg-[#5846d1] text-white";
76
+ break;
77
+ case 2: // Secondary
78
+ classes += " bg-[#4E5058] hover:bg-[#5C5F69] text-white";
79
+ break;
80
+ case 3: // Success
81
+ classes += " bg-[#57F287] hover:bg-[#45c767] text-black";
82
+ break;
83
+ case 4: // Danger
84
+ classes += " bg-[#ED4245] hover:bg-[#C03537] text-white";
85
+ break;
86
+ case 5: // Link
87
+ classes += " bg-[#4E5058] hover:bg-[#5C5F69] text-white";
88
+ isLinkStyle = true;
89
+ break;
90
+ default:
91
+ classes += " bg-[#4E5058] hover:bg-[#5C5F69] text-white";
92
+ }
93
+ const renderEmoji = () => {
94
+ if (b.emojiUrl)
95
+ return ((0, jsx_runtime_1.jsx)("img", { src: b.emojiUrl, alt: "emoji", className: "inline w-4 h-4 align-text-bottom flex-shrink-0" }));
96
+ if (!b.emoji)
97
+ return null;
98
+ if (typeof b.emoji === "string")
99
+ return (0, jsx_runtime_1.jsx)("span", { children: b.emoji });
100
+ const em = b.emoji;
101
+ if (em.id) {
102
+ const animated = !!em.animated;
103
+ const src = (0, cdnHelpers_1.buildEmojiCdnUrl)(String(em.id), animated, 96) || "";
104
+ if (src) {
105
+ return ((0, jsx_runtime_1.jsx)("img", { src: src, alt: em.name || "emoji", className: "inline w-4 h-4 align-text-bottom flex-shrink-0" }));
106
+ }
107
+ }
108
+ return (0, jsx_runtime_1.jsx)("span", { children: em.name || "" });
109
+ };
110
+ if (isLinkStyle && b.url) {
111
+ return ((0, jsx_runtime_1.jsxs)("a", { href: b.url, target: "_blank", rel: "noopener noreferrer", className: classes, children: [renderEmoji(), (0, jsx_runtime_1.jsx)("span", { className: "flex-1 truncate text-left", children: b.label })] }, `btn-${i}`));
112
+ }
113
+ return ((0, jsx_runtime_1.jsxs)("button", { className: classes, onClick: (ev) => {
114
+ ev.preventDefault();
115
+ if (b.url)
116
+ window.open(b.url, "_blank");
117
+ }, type: "button", children: [renderEmoji(), (0, jsx_runtime_1.jsx)("span", { className: "flex-1 truncate text-left", children: b.label })] }, `btn-${i}`));
118
+ }) })), msg.selects && msg.selects.length > 0 && ((0, jsx_runtime_1.jsx)("div", { className: "mt-2 flex flex-col gap-2", children: msg.selects.map((s, si) => ((0, jsx_runtime_1.jsx)("div", { className: "flex w-auto max-w-[432px] my-2 min-w-0", children: (0, jsx_runtime_1.jsx)("div", { className: "flex-1 rounded-md px-3 py-2 bg-[#2C2F33] border border-[#1e2124]", children: (0, jsx_runtime_1.jsxs)("div", { className: "flex items-center justify-between text-sm text-[#B5BAC1] cursor-default", children: [(0, jsx_runtime_1.jsx)("div", { className: "truncate", children: s.placeholder || "Select" }), (0, jsx_runtime_1.jsx)(ChevronDownIcon_1.default, { className: "ml-2 text-[#949BA4] w-4 h-4 flex-shrink-0" })] }) }) }, `select-${si}`))) }))] })] }, msg.id))) })) })] }) }));
119
+ }
@@ -0,0 +1,5 @@
1
+ import type { MessageProps } from "../types/message";
2
+ export default function PinnedMessagesOverview({ pinnedMessages, onPinnedMessageClick }: {
3
+ pinnedMessages: MessageProps[];
4
+ onPinnedMessageClick: (messageId: string) => void;
5
+ }): import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,22 @@
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.default = PinnedMessagesOverview;
7
+ const jsx_runtime_1 = require("react/jsx-runtime");
8
+ const PinIcon_1 = __importDefault(require("./icons/PinIcon"));
9
+ const StickerPreview_1 = __importDefault(require("./StickerPreview"));
10
+ function PinnedMessagesOverview({ pinnedMessages, onPinnedMessageClick }) {
11
+ if (!pinnedMessages || pinnedMessages.length === 0) {
12
+ return null;
13
+ }
14
+ return ((0, jsx_runtime_1.jsxs)("div", { className: "w-full border-b border-[#2C2F33] bg-[#1e2124] py-4 px-4 sm:px-6 sticky top-0 z-20", children: [(0, jsx_runtime_1.jsxs)("div", { className: "flex items-center gap-3 mb-4", children: [(0, jsx_runtime_1.jsx)(PinIcon_1.default, { className: "text-[#949BA4]" }), (0, jsx_runtime_1.jsx)("h2", { className: "text-base font-semibold text-[#DBDEE1]", children: "Pinned Messages" })] }), (0, jsx_runtime_1.jsx)("div", { className: "grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-3", children: pinnedMessages.map((msg) => ((0, jsx_runtime_1.jsxs)("div", { className: "bg-[#2C2F33] rounded-lg p-3 cursor-pointer hover:bg-[#35393F] transition-colors border border-[#404249]", onClick: () => onPinnedMessageClick(msg.id), children: [(0, jsx_runtime_1.jsxs)("div", { className: "flex items-start gap-2 mb-2", children: [(0, jsx_runtime_1.jsx)("img", { src: msg.author.avatar, alt: msg.author.username, className: "w-6 h-6 rounded-full" }), (0, jsx_runtime_1.jsx)("div", { className: "flex-1 min-w-0", children: (0, jsx_runtime_1.jsxs)("div", { className: "flex items-center gap-1 flex-wrap", children: [(0, jsx_runtime_1.jsx)("span", { className: "text-sm font-semibold text-[#DBDEE1]", style: { color: msg.author.color ?? "#DBDEE1" }, children: msg.author.username }), msg.author.bot && (0, jsx_runtime_1.jsx)("span", { className: "text-xs bg-[#5865F2] text-white px-1.5 py-0.5 rounded", children: "APP" })] }) })] }), (0, jsx_runtime_1.jsx)("div", { className: "text-sm text-[#B5BAC1] truncate", children: typeof msg.content === "string" ? msg.content.substring(0, 100) : "No content" }), msg.stickers && msg.stickers.length > 0 && ((0, jsx_runtime_1.jsxs)("div", { className: "mt-2", children: [(0, jsx_runtime_1.jsx)("div", { className: "text-xs text-[#72767D] mb-1", children: "Sticker" }), (0, jsx_runtime_1.jsx)(StickerPreview_1.default, { st: msg.stickers[0], size: 96, className: "w-24 h-24 rounded object-contain" })] })), msg.attachments && msg.attachments.length > 0 && ((0, jsx_runtime_1.jsx)("div", { className: "mt-2", children: msg.attachments.slice(0, 1).map((attachment, idx) => {
15
+ const url = typeof attachment === "string" ? attachment : attachment?.url;
16
+ const isImage = url && /\.(png|jpe?g|gif|webp|svg|bmp|avif)(\?|$)/i.test(url);
17
+ if (isImage) {
18
+ return (0, jsx_runtime_1.jsx)("img", { src: url, alt: "attachment", className: "w-24 h-24 rounded object-cover" }, idx);
19
+ }
20
+ return null;
21
+ }) }))] }, msg.id))) })] }));
22
+ }
@@ -0,0 +1,2 @@
1
+ import type { ReplyProps } from "../types/props";
2
+ export default function Reply({ replyTo, onReplyClick, defaultAvatar, resolvedUsers, resolvedRoles, resolvedChannels }: ReplyProps): import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,42 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.default = Reply;
4
+ const jsx_runtime_1 = require("react/jsx-runtime");
5
+ const date_1 = require("./utils/date");
6
+ const markdown_1 = require("./utils/markdown");
7
+ function Reply({ replyTo, onReplyClick, defaultAvatar, resolvedUsers, resolvedRoles, resolvedChannels }) {
8
+ let previewSource = null;
9
+ let isAttachment = false;
10
+ if (replyTo?.embeds && Array.isArray(replyTo.embeds) && replyTo.embeds.length > 0) {
11
+ isAttachment = true;
12
+ }
13
+ else if (replyTo?.attachments && Array.isArray(replyTo.attachments) && replyTo.attachments.length > 0) {
14
+ isAttachment = true;
15
+ }
16
+ else if (replyTo?.stickers && Array.isArray(replyTo.stickers) && replyTo.stickers.length > 0) {
17
+ isAttachment = true;
18
+ }
19
+ else if (typeof replyTo?.preview === "string") {
20
+ previewSource = replyTo.preview;
21
+ }
22
+ else if (typeof replyTo?.content === "string") {
23
+ previewSource = replyTo.content;
24
+ }
25
+ const preview = isAttachment
26
+ ? "Click to see attachment"
27
+ : previewSource
28
+ ? (() => {
29
+ const raw = previewSource.length > 140 ? `${previewSource.slice(0, 137)}...` : previewSource;
30
+ return (0, markdown_1.parseMarkdown)(raw, resolvedUsers, resolvedRoles, resolvedChannels);
31
+ })()
32
+ : "Click to see attachment";
33
+ const timestamp = replyTo?.timestamp ? new Date(replyTo.timestamp) : null;
34
+ const shortTime = timestamp ? (0, date_1.formatDate)(timestamp) : "";
35
+ const handleKeyDown = (ev) => {
36
+ if (ev.key === "Enter" || ev.key === " ") {
37
+ ev.preventDefault();
38
+ onReplyClick(ev);
39
+ }
40
+ };
41
+ return ((0, jsx_runtime_1.jsx)("div", { className: "flex items-center gap-2 pl-3 text-[13px] cursor-pointer group leading-tight text-[#b5bac1] mb-1", onClick: onReplyClick, onKeyDown: handleKeyDown, role: "button", tabIndex: 0, children: (0, jsx_runtime_1.jsxs)("div", { className: "flex items-center gap-1 min-w-0", children: [(0, jsx_runtime_1.jsx)("img", { src: replyTo?.author?.avatar || defaultAvatar, alt: "", className: "w-4 h-4 rounded-full object-cover flex-shrink-0" }), (0, jsx_runtime_1.jsx)("span", { className: "font-semibold group-hover:underline flex-shrink-0 text-[#b5bac1] leading-snug", style: { color: "#b5bac1" }, children: replyTo?.author?.username || replyTo?.author?.name || replyTo?.authorName || "Unknown User" }), replyTo?.author?.guildTag?.name && ((0, jsx_runtime_1.jsxs)("div", { className: "flex items-center gap-0.5 bg-[#2b2d31] rounded px-1 py-0.5 text-[10px] font-medium flex-shrink-0", children: [replyTo?.author?.guildTag?.iconUrl && (0, jsx_runtime_1.jsx)("img", { src: replyTo.author.guildTag.iconUrl, alt: "", className: "w-3 h-3 rounded-sm" }), (0, jsx_runtime_1.jsx)("span", { className: "text-[#b5bac1]", children: replyTo.author.guildTag.name })] })), replyTo?.author?.bot && ((0, jsx_runtime_1.jsxs)("span", { className: "text-[9px] text-white bg-[#5865F2] rounded px-1 py-0.5 leading-none font-medium flex-shrink-0 flex items-center gap-0.5", children: [replyTo?.author?.verified && ((0, jsx_runtime_1.jsx)("svg", { viewBox: "0 0 24 24", className: "w-2.5 h-2.5", xmlns: "http://www.w3.org/2000/svg", "aria-hidden": true, children: (0, jsx_runtime_1.jsx)("path", { fill: "#fff", fillRule: "evenodd", d: "M19.06 6.94a1.5 1.5 0 0 1 0 2.12l-8 8a1.5 1.5 0 0 1-2.12 0l-4-4a1.5 1.5 0 0 1 2.12-2.12L10 13.88l6.94-6.94a1.5 1.5 0 0 1 2.12 0Z", clipRule: "evenodd" }) })), (0, jsx_runtime_1.jsx)("span", { children: "APP" })] })), timestamp && (0, jsx_runtime_1.jsx)("span", { className: "text-[11px] text-[#949ba4] flex-shrink-0", children: shortTime }), (replyTo?.editedAt || replyTo?.edited) && (0, jsx_runtime_1.jsx)("span", { className: "text-[10px] text-[#949ba4] flex-shrink-0", children: "(edited)" }), (0, jsx_runtime_1.jsxs)("span", { className: "text-[#b5b6b8] truncate whitespace-nowrap italic", children: [preview, isAttachment ? " 🖼️" : ""] })] }) }));
42
+ }
@@ -0,0 +1,6 @@
1
+ export default function StickerPreview({ st, size, className, alt }: {
2
+ st: any;
3
+ size?: number;
4
+ className?: string;
5
+ alt?: string;
6
+ }): import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,40 @@
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.default = StickerPreview;
7
+ const jsx_runtime_1 = require("react/jsx-runtime");
8
+ const react_1 = __importDefault(require("react"));
9
+ const cdnHelpers_1 = require("../helpers/cdnHelpers");
10
+ function StickerPreview({ st, size = 160, className = "", alt }) {
11
+ const id = String(st?.id || st?.sticker_id || st?.assetId || "") || "";
12
+ const initial = st?.url || (id ? (0, cdnHelpers_1.buildStickerCdnUrl)(id, size) : undefined);
13
+ const [url, setUrl] = react_1.default.useState(initial);
14
+ react_1.default.useEffect(() => {
15
+ let cancelled = false;
16
+ async function loadMapping() {
17
+ const win = window;
18
+ if (win.__stickerMap) {
19
+ const mapped = win.__stickerMap[id];
20
+ if (mapped && mapped !== url && !cancelled)
21
+ setUrl(mapped);
22
+ return;
23
+ }
24
+ const DEFAULT_STICKER_MAP = {
25
+ "751606379340365864": "/system/wave.gif"
26
+ };
27
+ win.__stickerMap = win.__stickerMap || DEFAULT_STICKER_MAP;
28
+ const mapped = win.__stickerMap[id];
29
+ if (mapped && mapped !== url && !cancelled)
30
+ setUrl(mapped);
31
+ }
32
+ loadMapping();
33
+ return () => {
34
+ cancelled = true;
35
+ };
36
+ }, [id]);
37
+ if (!url)
38
+ return null;
39
+ return (0, jsx_runtime_1.jsx)("img", { src: url, alt: alt || st?.name || "sticker", className: className });
40
+ }
@@ -0,0 +1,2 @@
1
+ import type { ThemeSwitcherProps } from "../types/props";
2
+ export default function ThemeSwitcher({ currentTheme, onThemeChange }: ThemeSwitcherProps): import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,54 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.default = ThemeSwitcher;
4
+ const jsx_runtime_1 = require("react/jsx-runtime");
5
+ const themeColors_1 = require("./themeColors");
6
+ const colorThemes = [
7
+ "mint_apple",
8
+ "citrus_sherbert",
9
+ "retro_raincloud",
10
+ "hanami",
11
+ "sunrise",
12
+ "cotton_candy",
13
+ "lofi_vibes",
14
+ "desert_khaki",
15
+ "sunset",
16
+ "chroma_glow",
17
+ "forest",
18
+ "crimson_moon",
19
+ "midnight_blurple",
20
+ "mars",
21
+ "dusk",
22
+ "under_the_sea",
23
+ "retro_storm",
24
+ "neon_nights",
25
+ "strawberry_lemonade",
26
+ "aurora",
27
+ "sepia",
28
+ "blurple_twilight"
29
+ ];
30
+ function prettyName(s) {
31
+ return s
32
+ .replace(/_/g, " ")
33
+ .split(" ")
34
+ .map((w) => w.charAt(0).toUpperCase() + w.slice(1))
35
+ .join(" ");
36
+ }
37
+ function ThemeSwitcher({ currentTheme, onThemeChange }) {
38
+ const defaultThemes = ["light", "ash", "dark", "onyx", "system"];
39
+ return ((0, jsx_runtime_1.jsxs)("div", { className: "space-y-3", children: [(0, jsx_runtime_1.jsx)("div", { className: "flex items-start justify-between", children: (0, jsx_runtime_1.jsxs)("div", { children: [(0, jsx_runtime_1.jsx)("h3", { className: "text-lg font-semibold text-white mb-0.5", children: "Theme" }), (0, jsx_runtime_1.jsx)("p", { className: "text-xs text-[#b5bac1]", children: "Adjust the color of the interface for better visibility." })] }) }), (0, jsx_runtime_1.jsxs)("div", { children: [(0, jsx_runtime_1.jsx)("div", { className: "text-xs font-semibold text-[#b5bac1] uppercase mb-3", children: "Default Themes" }), (0, jsx_runtime_1.jsx)("div", { className: "flex gap-4 flex-wrap", children: defaultThemes.map((theme) => {
40
+ const isSelected = currentTheme === theme;
41
+ const isSystem = theme === "system";
42
+ return ((0, jsx_runtime_1.jsxs)("button", { type: "button", onClick: () => onThemeChange(theme), role: "radio", "aria-label": `Select ${theme} theme`, "aria-checked": isSelected, className: isSelected
43
+ ? `relative group w-12 h-12 rounded-lg ring-2 ring-offset-1 ring-[#5865f2] transition-all`
44
+ : `relative group w-12 h-12 rounded-lg border-2 transition-all border-[#4e5058] hover:border-[#6d70d8]`, children: [(0, jsx_runtime_1.jsx)("div", { className: "absolute -top-9 left-1/2 transform -translate-x-1/2 hidden group-hover:flex items-center justify-center bg-[#0b0b0c] text-white text-xs px-2 py-1 rounded-md shadow z-50 whitespace-nowrap", children: prettyName(theme) }), (0, jsx_runtime_1.jsx)("div", { className: "w-full h-full rounded-md overflow-hidden", children: isSystem ? ((0, jsx_runtime_1.jsx)("div", { className: "w-full h-full bg-gradient-to-br from-[#ffffff] to-[#070709] flex items-center justify-center", children: (0, jsx_runtime_1.jsxs)("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", className: "text-[#5865f2]", children: [(0, jsx_runtime_1.jsx)("path", { d: "M12 2L2 7L12 12L22 7L12 2Z", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" }), (0, jsx_runtime_1.jsx)("path", { d: "M2 17L12 22L22 17", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" }), (0, jsx_runtime_1.jsx)("path", { d: "M2 12L12 17L22 12", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" })] }) })) : ((0, jsx_runtime_1.jsx)("div", { className: "w-full h-full", style: {
45
+ backgroundColor: theme === "light" ? "#ffffff" : theme === "ash" ? "#36393f" : "#070709"
46
+ } })) })] }, theme));
47
+ }) })] }), (0, jsx_runtime_1.jsxs)("div", { children: [(0, jsx_runtime_1.jsx)("div", { className: "text-xs font-semibold text-[#b5bac1] uppercase mb-3", children: "Color Themes" }), (0, jsx_runtime_1.jsx)("div", { className: "flex flex-wrap gap-4", children: colorThemes.map((theme) => {
48
+ const preview = themeColors_1.colorThemePreviews[theme];
49
+ const isSelected = currentTheme === theme;
50
+ return ((0, jsx_runtime_1.jsxs)("button", { type: "button", onClick: () => onThemeChange(theme), role: "radio", "aria-label": `Select ${prettyName(theme)} theme`, "aria-checked": isSelected, className: isSelected
51
+ ? `relative group w-14 h-14 rounded-lg ring-2 ring-offset-1 ring-[#5865f2] transition-all`
52
+ : `relative group w-14 h-14 rounded-lg border-2 transition-all border-[#4e5058] hover:border-[#6d70d8]`, children: [(0, jsx_runtime_1.jsx)("div", { className: "absolute -top-9 left-1/2 transform -translate-x-1/2 hidden group-hover:flex items-center justify-center bg-[#0b0b0c] text-white text-xs px-2 py-1 rounded-md shadow z-50 whitespace-nowrap", children: prettyName(theme) }), (0, jsx_runtime_1.jsx)("div", { className: "w-full h-full rounded-md overflow-hidden", children: preview.gradient && (0, jsx_runtime_1.jsx)("div", { className: "w-full h-full", style: { background: preview.gradient } }) })] }, theme));
53
+ }) })] })] }));
54
+ }
@@ -0,0 +1,2 @@
1
+ import type { TranscriptProps } from "../types/props";
2
+ export default function Transcript(props: TranscriptProps): import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,174 @@
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
+ 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
+ };
34
+ })();
35
+ var __importDefault = (this && this.__importDefault) || function (mod) {
36
+ return (mod && mod.__esModule) ? mod : { "default": mod };
37
+ };
38
+ Object.defineProperty(exports, "__esModule", { value: true });
39
+ exports.default = Transcript;
40
+ const jsx_runtime_1 = require("react/jsx-runtime");
41
+ const react_1 = __importStar(require("react"));
42
+ const DateSeperator_1 = __importDefault(require("./DateSeperator"));
43
+ const PinIcon_1 = __importDefault(require("./icons/PinIcon"));
44
+ const Message_1 = __importDefault(require("./Message"));
45
+ const PinnedMessagesModal_1 = __importDefault(require("./PinnedMessagesModal"));
46
+ const ThemeSwitcher_1 = __importDefault(require("./ThemeSwitcher"));
47
+ const themeColors_1 = require("./themeColors");
48
+ const transcriptHelpers_1 = require("./transcriptHelpers");
49
+ const date_1 = require("./utils/date");
50
+ function Transcript(props) {
51
+ const { channel = { name: "transcript" }, className = "" } = props;
52
+ const hasSSRData = Array.isArray(props.messages) && props.messages.length > 0;
53
+ const [resolvedUsers, setResolvedUsers] = (0, react_1.useState)(() => props.resolvedUsers || {});
54
+ const [resolvedRoles, setResolvedRoles] = (0, react_1.useState)(() => props.resolvedRoles || {});
55
+ const [resolvedChannels, setResolvedChannels] = (0, react_1.useState)(() => props.resolvedChannels || {});
56
+ const [localMessages, setLocalMessages] = (0, react_1.useState)(() => {
57
+ if (!hasSSRData || !props.messages)
58
+ return [];
59
+ const users = props.resolvedUsers || {};
60
+ const roles = props.resolvedRoles || {};
61
+ const rawMessages = props.messages;
62
+ const byOriginal = new Map();
63
+ const msgs = rawMessages.map((m) => {
64
+ const mapped = (0, transcriptHelpers_1.mapMessage)(m, new Map(), users, roles);
65
+ byOriginal.set(String(m.id), mapped);
66
+ return m;
67
+ });
68
+ return msgs.map((m) => (0, transcriptHelpers_1.mapMessage)(m, byOriginal, users, roles));
69
+ });
70
+ const [pinnedMessages, setPinnedMessages] = (0, react_1.useState)(() => {
71
+ if (!hasSSRData || !props.messages)
72
+ return [];
73
+ const users = props.resolvedUsers || {};
74
+ const roles = props.resolvedRoles || {};
75
+ const rawMessages = props.messages;
76
+ const byOriginal = new Map();
77
+ const msgs = rawMessages.map((m) => {
78
+ const mapped = (0, transcriptHelpers_1.mapMessage)(m, new Map(), users, roles);
79
+ byOriginal.set(String(m.id), mapped);
80
+ return m;
81
+ });
82
+ const resolved = msgs.map((m) => (0, transcriptHelpers_1.mapMessage)(m, byOriginal, users, roles));
83
+ return resolved.filter((m) => m.pinned === true);
84
+ });
85
+ const [guildName, setGuildName] = (0, react_1.useState)("");
86
+ const [exportedAt, setExportedAt] = (0, react_1.useState)(() => (typeof props.exportedAt === "string" ? props.exportedAt : ""));
87
+ const [channelName, setChannelName] = (0, react_1.useState)(channel.name);
88
+ const [channelTopic, setChannelTopic] = (0, react_1.useState)(channel.topic);
89
+ const [themeChoice, setThemeChoice] = (0, react_1.useState)(() => props.theme ?? "onyx");
90
+ const [isSettingsOpen, setIsSettingsOpen] = (0, react_1.useState)(false);
91
+ const [isPinnedModalOpen, setIsPinnedModalOpen] = (0, react_1.useState)(false);
92
+ const systemPrefersDark = typeof window !== "undefined" && window.matchMedia && window.matchMedia("(prefers-color-scheme: dark)").matches;
93
+ const colors = (0, themeColors_1.getThemeColors)(themeChoice, systemPrefersDark);
94
+ const themeGradient = (0, themeColors_1.getThemeGradient)(themeChoice);
95
+ (0, react_1.useEffect)(() => {
96
+ if (hasSSRData)
97
+ return;
98
+ (async () => {
99
+ const res = await fetch("export.json");
100
+ const data = await res.json();
101
+ const users = data.resolvedUsers || {};
102
+ const roles = data.resolvedRoles || {};
103
+ const channels = data.resolvedChannels || {};
104
+ setResolvedUsers((prev) => Object.assign({}, users, prev || {}));
105
+ setResolvedRoles((prev) => Object.assign({}, roles, prev || {}));
106
+ setResolvedChannels((prev) => Object.assign({}, channels, prev || {}));
107
+ const byOriginal = new Map();
108
+ const msgs = (data.messages || []).map((m) => {
109
+ const mapped = (0, transcriptHelpers_1.mapMessage)(m, new Map(), users, roles);
110
+ byOriginal.set(String(m.id), mapped);
111
+ return m;
112
+ });
113
+ const resolved = msgs.map((m) => (0, transcriptHelpers_1.mapMessage)(m, byOriginal, users, roles));
114
+ const pinned = resolved.filter((m) => m.pinned === true);
115
+ setLocalMessages(resolved);
116
+ setPinnedMessages(pinned);
117
+ setGuildName(data.guild?.name || "");
118
+ setExportedAt(data.exportedAt || new Date().toISOString());
119
+ setChannelName(data.meta?.channelName || channel.name);
120
+ setChannelTopic(data.meta?.channelTopic || channel.topic);
121
+ })();
122
+ }, [hasSSRData, channel.name, channel.topic]);
123
+ react_1.default.useEffect(() => {
124
+ if (!isSettingsOpen && !isPinnedModalOpen)
125
+ return;
126
+ const onKey = (e) => {
127
+ if (e.key === "Escape") {
128
+ setIsSettingsOpen(false);
129
+ setIsPinnedModalOpen(false);
130
+ }
131
+ };
132
+ window.addEventListener("keydown", onKey);
133
+ return () => window.removeEventListener("keydown", onKey);
134
+ }, [isSettingsOpen, isPinnedModalOpen]);
135
+ const handlePinnedMessageClick = (messageId) => {
136
+ setIsPinnedModalOpen(false);
137
+ const el = document.getElementById(messageId);
138
+ if (el) {
139
+ document.querySelectorAll(".message-highlight").forEach((e) => e.classList.remove("message-highlight"));
140
+ el.classList.add("message-highlight");
141
+ el.scrollIntoView({ behavior: "smooth", block: "center" });
142
+ setTimeout(() => {
143
+ el?.classList.remove("message-highlight");
144
+ }, 2000);
145
+ }
146
+ };
147
+ return ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsx)("div", { className: "fixed inset-0 -z-10", style: {
148
+ background: themeGradient || colors.bg,
149
+ transition: "background 0.3s ease"
150
+ } }), (0, jsx_runtime_1.jsxs)("div", { className: `font-sans min-h-screen flex flex-col relative ${className}`, style: {
151
+ color: colors.text,
152
+ transition: "color 0.3s ease"
153
+ }, children: [(0, jsx_runtime_1.jsx)("div", { className: "border-b py-3 px-6 sticky top-0 z-40 backdrop-blur-sm", style: { backgroundColor: colors.header, borderColor: colors.border }, children: (0, jsx_runtime_1.jsxs)("div", { className: "w-full flex justify-between items-center", children: [(0, jsx_runtime_1.jsxs)("div", { className: "flex items-center gap-3 min-w-0", children: [(0, jsx_runtime_1.jsxs)("div", { className: "flex items-baseline gap-2", children: [(0, jsx_runtime_1.jsx)("span", { className: "text-base", style: { color: colors.accent }, children: "#" }), (0, jsx_runtime_1.jsx)("h1", { className: "text-base font-semibold truncate", style: { color: colors.text, maxWidth: "60vw" }, children: channelName })] }), channelTopic && ((0, jsx_runtime_1.jsxs)("span", { className: "text-base opacity-70 hidden sm:inline", style: { color: colors.subtext }, children: ["\u2014 ", channelTopic] }))] }), (0, jsx_runtime_1.jsxs)("div", { className: "flex items-center gap-2 flex-shrink-0", children: [exportedAt && ((0, jsx_runtime_1.jsxs)("span", { className: "text-base hidden md:inline", style: { color: colors.subtext }, children: ["Exported: ", (0, date_1.formatDate)(exportedAt)] })), (0, jsx_runtime_1.jsxs)("span", { className: "text-base hidden md:inline ml-4", style: { color: colors.subtext }, children: ["Messages: ", localMessages.length] }), (0, jsx_runtime_1.jsx)("button", { type: "button", "aria-label": "Open pinned messages", className: "p-1.5 sm:p-2 rounded hover:opacity-80 border transition-opacity bg-transparent", style: { borderColor: colors.border, color: colors.text }, onClick: () => setIsPinnedModalOpen(true), children: (0, jsx_runtime_1.jsx)(PinIcon_1.default, { width: 20, height: 20 }) }), props.allowThemeSwitching !== false && ((0, jsx_runtime_1.jsx)("button", { type: "button", "aria-label": "Open settings", className: "p-1.5 sm:p-2 rounded hover:opacity-80 border transition-opacity bg-transparent", style: { borderColor: colors.border, color: colors.text }, onClick: () => setIsSettingsOpen(true), children: (0, jsx_runtime_1.jsxs)("svg", { xmlns: "http://www.w3.org/2000/svg", xmlnsXlink: "http://www.w3.org/1999/xlink", viewBox: "0 0 24 24", width: "20", height: "20", preserveAspectRatio: "xMidYMid meet", children: [(0, jsx_runtime_1.jsxs)("defs", { children: [(0, jsx_runtime_1.jsx)("clipPath", { id: "__lottie_element_97", children: (0, jsx_runtime_1.jsx)("rect", { width: "24", height: "24", x: "0", y: "0" }) }), (0, jsx_runtime_1.jsx)("clipPath", { id: "__lottie_element_99", children: (0, jsx_runtime_1.jsx)("path", { d: "M0,0 L600,0 L600,600 L0,600z" }) })] }), (0, jsx_runtime_1.jsx)("g", { clipPath: "url(#__lottie_element_97)", children: (0, jsx_runtime_1.jsx)("g", { clipPath: "url(#__lottie_element_99)", transform: "matrix(0.04,0,0,0.04,0,0)", opacity: "1", children: (0, jsx_runtime_1.jsx)("g", { transform: "matrix(25,0,0,25,300,300)", opacity: "1", children: (0, jsx_runtime_1.jsx)("g", { opacity: "1", transform: "matrix(1,0,0,1,0,0)", children: (0, jsx_runtime_1.jsx)("path", { fill: "currentColor", fillOpacity: "1", d: " M-1.4420000314712524,-10.906000137329102 C-1.8949999809265137,-10.847000122070312 -2.1470000743865967,-10.375 -2.078000068664551,-9.92300033569336 C-1.899999976158142,-8.756999969482422 -2.265000104904175,-7.7210001945495605 -3.061000108718872,-7.390999794006348 C-3.8570001125335693,-7.060999870300293 -4.8480000495910645,-7.534999847412109 -5.546000003814697,-8.484999656677246 C-5.816999912261963,-8.852999687194824 -6.329999923706055,-9.008999824523926 -6.691999912261963,-8.730999946594238 C-7.458000183105469,-8.142999649047852 -8.142999649047852,-7.458000183105469 -8.730999946594238,-6.691999912261963 C-9.008999824523926,-6.329999923706055 -8.852999687194824,-5.816999912261963 -8.484999656677246,-5.546000003814697 C-7.534999847412109,-4.8480000495910645 -7.060999870300293,-3.8570001125335693 -7.390999794006348,-3.061000108718872 C-7.7210001945495605,-2.265000104904175 -8.756999969482422,-1.899999976158142 -9.92300033569336,-2.078000068664551 C-10.375,-2.1470000743865967 -10.847000122070312,-1.8949999809265137 -10.906000137329102,-1.4420000314712524 C-10.968000411987305,-0.9700000286102295 -11,-0.48899999260902405 -11,0 C-11,0.48899999260902405 -10.968000411987305,0.9700000286102295 -10.906000137329102,1.4420000314712524 C-10.847000122070312,1.8949999809265137 -10.375,2.1470000743865967 -9.92300033569336,2.078000068664551 C-8.756999969482422,1.899999976158142 -7.7210001945495605,2.265000104904175 -7.390999794006348,3.061000108718872 C-7.060999870300293,3.8570001125335693 -7.534999847412109,4.8470001220703125 -8.484999656677246,5.546000003814697 C-8.852999687194824,5.816999912261963 -9.008999824523926,6.328999996185303 -8.730999946594238,6.691999912261963 C-8.142999649047852,7.458000183105469 -7.458000183105469,8.142999649047852 -6.691999912261963,8.730999946594238 C-6.329999923706055,9.008999824523926 -5.816999912261963,8.852999687194824 -5.546000003814697,8.484999656677246 C-4.8480000495910645,7.534999847412109 -3.8570001125335693,7.060999870300293 -3.061000108718872,7.390999794006348 C-2.265000104904175,7.7210001945495605 -1.899999976158142,8.756999969482422 -2.078000068664551,9.92300033569336 C-2.1470000743865967,10.375 -1.8949999809265137,10.847000122070312 -1.4420000314712524,10.906000137329102 C-0.9700000286102295,10.968000411987305 -0.48899999260902405,11 0,11 C0.48899999260902405,11 0.9700000286102295,10.968000411987305 1.4420000314712524,10.906000137329102 C1.8949999809265137,10.847000122070312 2.1470000743865967,10.375 2.078000068664551,9.92300033569336 C1.899999976158142,8.756999969482422 2.2660000324249268,7.7210001945495605 3.062000036239624,7.390999794006348 C3.8580000400543213,7.060999870300293 4.8480000495910645,7.534999847412109 5.546000003814697,8.484999656677246 C5.816999912261963,8.852999687194824 6.328999996185303,9.008999824523926 6.691999912261963,8.730999946594238 C7.458000183105469,8.142999649047852 8.142999649047852,7.458000183105469 8.730999946594238,6.691999912261963 C9.008999824523926,6.328999923706055 8.852999687194824,5.816999912261963 8.484999656677246,5.546000003814697 C7.534999847412109,4.8480000495910645 7.060999870300293,3.8570001125335693 7.390999794006348,3.061000108718872 C7.7210001945495605,2.265000104904175 8.756999969482422,1.899999976158142 9.92300033569336,2.078000068664551 C10.375,2.1470000743865967 10.847000122070312,1.8949999809265137 10.906000137329102,1.4420000314712524 C10.968000411987305,0.9700000286102295 11,0.48899999260902405 11,0 C11,-0.48899999260902405 10.968000411987305,-0.9700000286102295 10.906000137329102,-1.4420000314712524 C10.847000122070312,-1.8949999809265137 10.375,-2.1470000743865967 9.92300033569336,-2.078000068664551 C8.756999969482422,-1.899999976158142 7.7210001945495605,-2.265000104904175 7.390999794006348,-3.061000108718872 C7.060999870300293,-3.8570001125335693 7.534999847412109,-4.8480000495910645 8.484999656677246,-5.546000003814697 C8.852999687194824,-5.816999912261963 9.008999824523926,-6.329999923706055 8.730999946594238,-6.691999912261963 C8.142999649047852,-7.458000183105469 7.458000183105469,-8.142999649047852 6.691999912261963,-8.730999946594238 C6.328999996185303,-9.008999824523926 5.817999839782715,-8.852999687194824 5.546999931335449,-8.484999656677246 C4.848999977111816,-7.534999847412109 3.8580000400543213,-7.060999870300293 3.062000036239624,-7.390999794006348 C2.2660000324249268,-7.7210001945495605 1.9010000228881836,-8.756999969482422 2.0789999961853027,-9.92300033569336 C2.1480000019073486,-10.375 1.8949999809265137,-10.847000122070312 1.4420000314712524,-10.906000137329102 C0.9700000286102295,-10.968000411987305 0.48899999260902405,-11 0,-11 C-0.48899999260902405,-11 -0.9700000286102295,-10.968000411987305 -1.4420000314712524,-10.906000137329102z M4,0 C4,2.2090001106262207 2.2090001106262207,4 0,4 C-2.2090001106262207,4 -4,2.2090001106262207 -4,0 C-4,-2.2090001106262207 -2.2090001106262207,-4 0,-4 C2.2090001106262207,-4 4,-2.2090001106262207 4,0z" }) }) }) }) })] }) }))] })] }) }), (0, jsx_runtime_1.jsx)("div", { className: "w-full py-8 px-4 sm:px-6 flex flex-col items-start gap-0 flex-1", children: localMessages.length === 0 ? ((0, jsx_runtime_1.jsx)("div", { className: "text-center text-[#72767D] py-8", children: "No messages" })) : (localMessages.map((msg, idx, arr) => {
154
+ const prevMsg = idx > 0 ? arr[idx - 1] : null;
155
+ const showDateSeparator = idx === 0 || (prevMsg && new Date(prevMsg.timestamp || 0).toDateString() !== new Date(msg.timestamp || 0).toDateString());
156
+ const shouldGroup = !showDateSeparator &&
157
+ prevMsg &&
158
+ prevMsg.author.id === msg.author.id &&
159
+ !msg.replyTo &&
160
+ !prevMsg.replyTo &&
161
+ msg.messageType !== 7 &&
162
+ prevMsg.messageType !== 7 &&
163
+ prevMsg.timestamp &&
164
+ msg.timestamp &&
165
+ new Date(msg.timestamp).getTime() - new Date(prevMsg.timestamp).getTime() <= 300000;
166
+ return ((0, jsx_runtime_1.jsxs)(react_1.default.Fragment, { children: [showDateSeparator && (0, jsx_runtime_1.jsx)(DateSeperator_1.default, { date: msg.timestamp || "" }), (0, jsx_runtime_1.jsx)(Message_1.default, { ...msg, compact: !!shouldGroup, resolvedUsers: resolvedUsers, resolvedRoles: resolvedRoles, resolvedChannels: resolvedChannels, onPinIconClick: () => setIsPinnedModalOpen(true), onNavigateToMessage: handlePinnedMessageClick, referencedMessage: msg.messageType === 6
167
+ ? pinnedMessages.find((m) => String(m.id) === String(msg.referencedMessageId))
168
+ : undefined })] }, msg.id));
169
+ })) }), isSettingsOpen && props.allowThemeSwitching !== false && ((0, jsx_runtime_1.jsx)("div", { className: "fixed inset-0 bg-black/50 flex items-center justify-center z-50", onClick: () => setIsSettingsOpen(false), children: (0, jsx_runtime_1.jsxs)("div", { onClick: (e) => e.stopPropagation(), className: "bg-[#2b2d31] text-white rounded-lg shadow-xl p-4 w-full max-w-xl border border-[#1f1f23]", children: [(0, jsx_runtime_1.jsxs)("div", { className: "flex items-center justify-between mb-4", children: [(0, jsx_runtime_1.jsx)("h2", { className: "text-xl font-bold", children: "Settings" }), (0, jsx_runtime_1.jsx)("button", { type: "button", "aria-label": "Close settings", className: "p-1.5 hover:bg-[#3d4148] rounded transition-colors", onClick: () => setIsSettingsOpen(false), children: (0, jsx_runtime_1.jsxs)("svg", { width: "20", height: "20", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: [(0, jsx_runtime_1.jsx)("line", { x1: "18", y1: "6", x2: "6", y2: "18" }), (0, jsx_runtime_1.jsx)("line", { x1: "6", y1: "6", x2: "18", y2: "18" })] }) })] }), (0, jsx_runtime_1.jsx)(ThemeSwitcher_1.default, { currentTheme: themeChoice, onThemeChange: setThemeChoice })] }) })), isPinnedModalOpen && ((0, jsx_runtime_1.jsx)(PinnedMessagesModal_1.default, { pinnedMessages: pinnedMessages, onClose: () => setIsPinnedModalOpen(false), onMessageClick: handlePinnedMessageClick })), props.poweredBy && ((0, jsx_runtime_1.jsx)("div", { className: "w-full py-3 px-6 text-center text-xs opacity-90", style: {
170
+ color: colors.subtext,
171
+ backgroundColor: colors.header,
172
+ borderTop: `1px solid ${colors.border}`
173
+ }, children: "Powered by Transcriptify" }))] })] }));
174
+ }
@@ -0,0 +1,3 @@
1
+ export default function UserJoinMessage({ username }: {
2
+ username: string;
3
+ }): import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,33 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.default = UserJoinMessage;
4
+ const jsx_runtime_1 = require("react/jsx-runtime");
5
+ const JOIN_MESSAGES = [
6
+ "[username] just joined the server - glhf!",
7
+ "[username] just joined. Everyone, look busy!",
8
+ "[username] just joined. Can I get a heal?",
9
+ "[username] joined your party.",
10
+ "[username] joined. You must construct additional pylons.",
11
+ "Ermagherd. [username] is here.",
12
+ "Welcome, [username]. Stay awhile and listen.",
13
+ "Welcome, [username]. We were expecting you ( ͡° ͜ʖ ͡°)",
14
+ "Welcome, [username]. We hope you brought pizza.",
15
+ "Welcome [username]. Leave your weapons by the door.",
16
+ "A wild [username] appeared.",
17
+ "Swoooosh. [username] just landed.",
18
+ "Brace yourselves. [username] just joined the server.",
19
+ "[username] just joined. Hide your bananas.",
20
+ "[username] just arrived. Seems OP – please nerf.",
21
+ "[username] just slid into the server.",
22
+ "A [username] has spawned in the server.",
23
+ "Big [username] showed up!",
24
+ "Where's [username]? In the server!",
25
+ "[username] hopped into the server. Kangaroo!!",
26
+ "[username] just showed up. Hold my beer"
27
+ ];
28
+ function UserJoinMessage({ username }) {
29
+ const messageIndex = username.split("").reduce((acc, char) => acc + char.charCodeAt(0), 0) % JOIN_MESSAGES.length;
30
+ const template = JOIN_MESSAGES[messageIndex];
31
+ const parts = template.split("[username]");
32
+ return ((0, jsx_runtime_1.jsxs)("div", { className: "flex items-center gap-2 py-1 px-4 text-sm", children: [(0, jsx_runtime_1.jsx)("svg", { height: "18", width: "18", xmlns: "http://www.w3.org/2000/svg", className: "shrink-0", children: (0, jsx_runtime_1.jsxs)("g", { fill: "none", fillRule: "evenodd", children: [(0, jsx_runtime_1.jsx)("path", { d: "m18 0h-18v18h18z" }), (0, jsx_runtime_1.jsx)("path", { d: "m0 8h14.2l-3.6-3.6 1.4-1.4 6 6-6 6-1.4-1.4 3.6-3.6h-14.2", fill: "#3ba55c" })] }) }), (0, jsx_runtime_1.jsxs)("span", { className: "text-[#949ba4]", children: [parts[0], (0, jsx_runtime_1.jsx)("span", { className: "text-white font-medium hover:underline cursor-pointer", children: username }), parts[1]] })] }));
33
+ }
@@ -0,0 +1,6 @@
1
+ import React from "react";
2
+ export default function VideoPlayer({ url, filename, filesize }: {
3
+ url: string;
4
+ filename: string;
5
+ filesize: string;
6
+ }): React.ReactElement;