\n {title}\n
\n )}\n\n {description}\n
\n )}\ndiff --git a/assets/compose-qZBynDN_.js b/assets/compose-Dh41_fT-.js similarity index 82% rename from assets/compose-qZBynDN_.js rename to assets/compose-Dh41_fT-.js index 124e43e..e7a6345 100644 --- a/assets/compose-qZBynDN_.js +++ b/assets/compose-Dh41_fT-.js @@ -1,2 +1,2 @@ -import{W as u,G as p}from"./useTitle-60DQEO3r.js";import{h as w,p as f,a as e,K as m}from"./vendor-jabuMAqb.js";window.opener&&(console=window.opener.console);function S(){var s,a;const[t,i]=w("default"),{editStatus:n,replyToStatus:o,draftStatus:d}=window.__COMPOSE__||{};return u(n?"Editing source status":o?`Replying to @${((s=o.account)==null?void 0:s.acct)||((a=o.account)==null?void 0:a.username)}`:"Compose"),f(()=>{if(t==="closed"){try{window.opener.focus()}catch{}window.close()}},[t]),t==="closed"?e("div",{class:"box",children:[e("p",{children:"You may close this page now."}),e("p",{children:e("button",{onClick:()=>{window.close()},children:"Close window"})})]}):e(p,{editStatus:n,replyToStatus:o,draftStatus:d,standalone:!0,hasOpener:window.opener,onClose:c=>{const{newStatus:r,fn:l=()=>{}}=c||{};try{r&&window.opener.__STATES__.reloadStatusPage++,l(),i("closed")}catch{}}})}m(e(S,{}),document.getElementById("app-standalone")); -//# sourceMappingURL=compose-qZBynDN_.js.map +import{W as u,G as p}from"./useTitle-sx0MJWtS.js";import{h as w,p as f,a as e,K as m}from"./vendor-f6XEe9UY.js";window.opener&&(console=window.opener.console);function S(){var s,a;const[t,i]=w("default"),{editStatus:n,replyToStatus:o,draftStatus:d}=window.__COMPOSE__||{};return u(n?"Editing source status":o?`Replying to @${((s=o.account)==null?void 0:s.acct)||((a=o.account)==null?void 0:a.username)}`:"Compose"),f(()=>{if(t==="closed"){try{window.opener.focus()}catch{}window.close()}},[t]),t==="closed"?e("div",{class:"box",children:[e("p",{children:"You may close this page now."}),e("p",{children:e("button",{onClick:()=>{window.close()},children:"Close window"})})]}):e(p,{editStatus:n,replyToStatus:o,draftStatus:d,standalone:!0,hasOpener:window.opener,onClose:c=>{const{newStatus:r,fn:l=()=>{}}=c||{};try{r&&window.opener.__STATES__.reloadStatusPage++,l(),i("closed")}catch{}}})}m(e(S,{}),document.getElementById("app-standalone")); +//# sourceMappingURL=compose-Dh41_fT-.js.map diff --git a/assets/compose-qZBynDN_.js.map b/assets/compose-Dh41_fT-.js.map similarity index 97% rename from assets/compose-qZBynDN_.js.map rename to assets/compose-Dh41_fT-.js.map index eefe7d8..cc4fc28 100644 --- a/assets/compose-qZBynDN_.js.map +++ b/assets/compose-Dh41_fT-.js.map @@ -1 +1 @@ -{"version":3,"file":"compose-qZBynDN_.js","sources":["../../src/compose.jsx"],"sourcesContent":["import './index.css';\n\nimport './app.css';\n\nimport { render } from 'preact';\nimport { useEffect, useState } from 'preact/hooks';\n\nimport Compose from './components/compose';\nimport useTitle from './utils/useTitle';\n\nif (window.opener) {\n console = window.opener.console;\n}\n\nfunction App() {\n const [uiState, setUIState] = useState('default');\n\n const { editStatus, replyToStatus, draftStatus } = window.__COMPOSE__ || {};\n\n useTitle(\n editStatus\n ? 'Editing source status'\n : replyToStatus\n ? `Replying to @${\n replyToStatus.account?.acct || replyToStatus.account?.username\n }`\n : 'Compose',\n );\n\n useEffect(() => {\n if (uiState === 'closed') {\n try {\n // Focus parent window\n window.opener.focus();\n } catch (e) {}\n window.close();\n }\n }, [uiState]);\n\n if (uiState === 'closed') {\n return (\n
You may close this page now.
\n\n \n
\nYou may close this page now.
\n\n \n
\n{action} | \n{keys} | \n
---|
\n \n
\n \n Note: Default account will always be used for first load.\n Switched accounts will persist during the session.\n \n
\n )}\n\n
\n Hide \"Translate\" button for\n {snapStates.settings.contentTranslationHideLanguages.length >\n 0 && (\n <>\n {' '}\n (\n {\n snapStates.settings.contentTranslationHideLanguages\n .length\n }\n )\n >\n )}\n :\n
\n \n Note: This feature uses external translation services,\n powered by{' '}\n \n Lingva API\n {' '}\n &{' '}\n \n Lingva Translate\n \n .\n \n
\n\n \n Automatically show translation for posts in timeline. Only\n works for short posts without content warning,\n media and poll.\n \n
\n\n \n Sponsor\n {' '}\n ·{' '}\n \n Donate\n {' '}\n ·{' '}\n \n Privacy Policy\n \n
\n {__BUILD_TIME__ && (\n\n {WEBSITE && (\n <>\n Site:{' '}\n {WEBSITE.replace(/https?:\\/\\//g, '').replace(/\\/$/, '')}\n
\n >\n )}\n Version:{' '}\n {\n e.target.select();\n // Copy to clipboard\n try {\n navigator.clipboard.writeText(e.target.value);\n showToast('Version string copied');\n } catch (e) {\n console.warn(e);\n showToast('Unable to copy version string');\n }\n }}\n />{' '}\n {!__FAKE_COMMIT_HASH__ && (\n \n (\n \n
Unable to load account.
\n\n \n Go to account page
███████ ████ ████
\n████ ████████ ██████ █████████ ████ ██
\n\n {displayName} has indicated that their new account is\n now:\n
\n\n {text}\n
\n\n
Unable to load lists.
\n ) : (\nNo lists.
\n )}\n \n\n
No drafts found.
\n )}\nThe end.
\n )\n ) : (\n uiState === 'loading' && (\n\n
\n
Error loading accounts
\n ) : (\nNothing to show
\n )}\n\n {alt}\n
\n {(differentLanguage || forceTranslate) && (\nSpecify a list of shortcuts that'll appear as:
\nNo shortcuts yet. Tap on the Add shortcut button.
\n\n Not sure what to add?\n
\n Try adding{' '}\n {\n e.preventDefault();\n states.shortcuts = [\n {\n type: 'following',\n },\n {\n type: 'notifications',\n },\n ];\n }}\n >\n Home / Following and Notifications\n {' '}\n first.\n
\n {shortcuts.length >= SHORTCUTS_LIMIT &&\n `Max ${SHORTCUTS_LIMIT} shortcuts`}\n
\n\n \n \n
\n\n {\n setImportShortcutStr(e.target.value);\n }}\n />\n
\n {!!parsedImportShortcutStr &&\n Array.isArray(parsedImportShortcutStr) && (\n <>\n\n {parsedImportShortcutStr.length} shortcut\n {parsedImportShortcutStr.length > 1 ? 's' : ''}{' '}\n \n ({importShortcutStr.length} characters)\n \n
\n\n * Exists in current shortcuts\n
\n \n ⚠️ List may not work if it's from a different account.\n \n
\n ⚠️ Invalid settings format\n
\n )}\n\n {hasCurrentSettings && (\n <>\n
\n {\n if (!e.target.value) return;\n e.target.select();\n // Copy url to clipboard\n try {\n navigator.clipboard.writeText(e.target.value);\n showToast('Shortcuts copied');\n } catch (e) {\n console.error(e);\n showToast('Unable to copy shortcuts');\n }\n }}\n />\n
\n\n {' '}\n {navigator?.share &&\n navigator?.canShare?.({\n text: shortcutsStr,\n }) && (\n \n )}{' '}\n {shortcutsStr.length > 0 && (\n \n {shortcutsStr.length} characters\n \n )}\n
\n {!!shortcutsStr && (\n\n {!/poll|update/i.test(type) && (\n <>\n {_accounts?.length > 1 ? (\n <>\n \n \n {shortenNumber(_accounts.length)}\n {' '}\n people\n {' '}\n >\n ) : (\n <>\n
\n {_accounts.slice(0, AVATARS_LIMIT).map((account) => (\n
This notification is from your other account.
\n )}\nThe end.
\n ))}\n >\n ) : uiState === 'loading' ? (\n{emptyText}
\n )}\n {uiState === 'error' && (\n\n {errorText}\n
\n
\n \n
\n {uiState === 'default' ? \"You're all caught up.\" : <>…>}\n
\n )}\n {snapStates.notifications.length ? (\n <>\n {snapStates.notifications\n // This is leaked from Notifications popover\n .filter((n) => n.type !== 'follow_request')\n .map((notification) => {\n if (onlyMentions && notification.type !== 'mention') {\n return null;\n }\n const notificationDay = new Date(notification.createdAt);\n const differentDay =\n notificationDay.toDateString() !== currentDay.toDateString();\n if (differentDay) {\n currentDay = notificationDay;\n }\n // if notificationDay is yesterday, show \"Yesterday\"\n // if notificationDay is before yesterday, show date\n const heading =\n notificationDay.toDateString() ===\n yesterdayDate.toDateString()\n ? 'Yesterday'\n : niceDateTime(currentDay, {\n hideTime: true,\n });\n return (\n███████████ ████
\n\n Unable to load notifications\n
\n
\n \n
\n \n {updatedAt && updatedAtText !== publishedDateText && (\n <>\n {' '}\n •{' '}\n \n Updated{' '}\n \n \n >\n )}\n
\n\n
No accounts found.
\n ))\n )}\n >\n )}\n {(!type || type === 'hashtags') && (\n <>\n {type !== 'hashtags' && (\n\n
No hashtags found.
\n ))\n )}\n >\n )}\n {(!type || type === 'statuses') && (\n <>\n {type !== 'statuses' && (\n\n
No posts found.
\n ))\n )}\n >\n )}\n {!!type &&\n (uiState === 'default' ? (\n showMore ? (\nThe end.
\n )\n ) : (\n uiState === 'loading' && (\n\n
\n
\n Enter your search term or paste a URL above to get started.\n
\n )}\nUnable to fetch notifications.
\n\n \n
\n\n \n {url}\n \n
\n >\n ) : (\n <>\n\n \n {url}\n \n
\n >\n )}\n\n Go home\n
\n\n
Unable to load lists.
\n ) : (\nNo lists yet.
\n )}\n\n Unable to load post\n
\n
\n \n
A minimalistic opinionated Mastodon web client.
\n\n \n {DEFAULT_INSTANCE ? 'Log in' : 'Log in with Mastodon'}\n \n
\n {DEFAULT_INSTANCE && DEFAULT_INSTANCE_REGISTRATION_URL && (\n\n \n Sign up\n \n
\n )}\n {!DEFAULT_INSTANCE && (\n\n \n Connect your existing Mastodon/Fediverse account.\n
\n Your credentials are not stored on this server.\n \n
\n \n {appSite} {appVersion}\n \n
\n )}\n\n \n Built\n {' '}\n by{' '}\n {\n e.preventDefault();\n states.showAccount = 'cheeaun@mastodon.social';\n }}\n >\n @cheeaun\n \n .{' '}\n \n Privacy Policy\n \n .\n
\n\n Visually separate original posts and re-shared posts (boosted\n posts).\n
\nEffortlessly follow conversations. Semi-collapsible replies.
\n\n Similar notifications are grouped and collapsed to reduce clutter.\n
\n\n By default, single column for zen-mode seekers. Configurable\n multi-column for power users.\n
\nUp to 5 hashtags combined into a single timeline.
\n{action} | \n{keys} | \n
---|
\n \n
\n \n Note: Default account will always be used for first load.\n Switched accounts will persist during the session.\n \n
\n )}\n\n
\n Hide \"Translate\" button for\n {snapStates.settings.contentTranslationHideLanguages.length >\n 0 && (\n <>\n {' '}\n (\n {\n snapStates.settings.contentTranslationHideLanguages\n .length\n }\n )\n >\n )}\n :\n
\n \n Note: This feature uses external translation services,\n powered by{' '}\n \n Lingva API\n {' '}\n &{' '}\n \n Lingva Translate\n \n .\n \n
\n\n \n Automatically show translation for posts in timeline. Only\n works for short posts without content warning,\n media and poll.\n \n
\n\n \n Sponsor\n {' '}\n ·{' '}\n \n Donate\n {' '}\n ·{' '}\n \n Privacy Policy\n \n
\n {__BUILD_TIME__ && (\n\n {WEBSITE && (\n <>\n Site:{' '}\n {WEBSITE.replace(/https?:\\/\\//g, '').replace(/\\/$/, '')}\n
\n >\n )}\n Version:{' '}\n {\n e.target.select();\n // Copy to clipboard\n try {\n navigator.clipboard.writeText(e.target.value);\n showToast('Version string copied');\n } catch (e) {\n console.warn(e);\n showToast('Unable to copy version string');\n }\n }}\n />{' '}\n {!__FAKE_COMMIT_HASH__ && (\n \n (\n \n
Unable to load account.
\n\n \n Go to account page
███████ ████ ████
\n████ ████████ ██████ █████████ ████ ██
\n\n {displayName} has indicated that their new account is\n now:\n
\n\n {text}\n
\n\n
Unable to load lists.
\n ) : (\nNo lists.
\n )}\n \n\n
No drafts found.
\n )}\nThe end.
\n )\n ) : (\n uiState === 'loading' && (\n\n
\n
Error loading accounts
\n ) : (\nNothing to show
\n )}\n\n {alt}\n
\n {(differentLanguage || forceTranslate) && (\nSpecify a list of shortcuts that'll appear as:
\nNo shortcuts yet. Tap on the Add shortcut button.
\n\n Not sure what to add?\n
\n Try adding{' '}\n {\n e.preventDefault();\n states.shortcuts = [\n {\n type: 'following',\n },\n {\n type: 'notifications',\n },\n ];\n }}\n >\n Home / Following and Notifications\n {' '}\n first.\n
\n {shortcuts.length >= SHORTCUTS_LIMIT &&\n `Max ${SHORTCUTS_LIMIT} shortcuts`}\n
\n\n \n \n
\n\n {\n setImportShortcutStr(e.target.value);\n }}\n />\n
\n {!!parsedImportShortcutStr &&\n Array.isArray(parsedImportShortcutStr) && (\n <>\n\n {parsedImportShortcutStr.length} shortcut\n {parsedImportShortcutStr.length > 1 ? 's' : ''}{' '}\n \n ({importShortcutStr.length} characters)\n \n
\n\n * Exists in current shortcuts\n
\n \n ⚠️ List may not work if it's from a different account.\n \n
\n ⚠️ Invalid settings format\n
\n )}\n\n {hasCurrentSettings && (\n <>\n
\n {\n if (!e.target.value) return;\n e.target.select();\n // Copy url to clipboard\n try {\n navigator.clipboard.writeText(e.target.value);\n showToast('Shortcuts copied');\n } catch (e) {\n console.error(e);\n showToast('Unable to copy shortcuts');\n }\n }}\n />\n
\n\n {' '}\n {navigator?.share &&\n navigator?.canShare?.({\n text: shortcutsStr,\n }) && (\n \n )}{' '}\n {shortcutsStr.length > 0 && (\n \n {shortcutsStr.length} characters\n \n )}\n
\n {!!shortcutsStr && (\n\n {!/poll|update/i.test(type) && (\n <>\n {_accounts?.length > 1 ? (\n <>\n \n \n {shortenNumber(_accounts.length)}\n {' '}\n people\n {' '}\n >\n ) : (\n <>\n
\n {_accounts.slice(0, AVATARS_LIMIT).map((account) => (\n
This notification is from your other account.
\n )}\nThe end.
\n ))}\n >\n ) : uiState === 'loading' ? (\n{emptyText}
\n )}\n {uiState === 'error' && (\n\n {errorText}\n
\n
\n \n
\n {uiState === 'default' ? \"You're all caught up.\" : <>…>}\n
\n )}\n {snapStates.notifications.length ? (\n <>\n {snapStates.notifications\n // This is leaked from Notifications popover\n .filter((n) => n.type !== 'follow_request')\n .map((notification) => {\n if (onlyMentions && notification.type !== 'mention') {\n return null;\n }\n const notificationDay = new Date(notification.createdAt);\n const differentDay =\n notificationDay.toDateString() !== currentDay.toDateString();\n if (differentDay) {\n currentDay = notificationDay;\n }\n // if notificationDay is yesterday, show \"Yesterday\"\n // if notificationDay is before yesterday, show date\n const heading =\n notificationDay.toDateString() ===\n yesterdayDate.toDateString()\n ? 'Yesterday'\n : niceDateTime(currentDay, {\n hideTime: true,\n });\n return (\n███████████ ████
\n\n Unable to load notifications\n
\n
\n \n
\n \n {updatedAt && updatedAtText !== publishedDateText && (\n <>\n {' '}\n •{' '}\n \n Updated{' '}\n \n \n >\n )}\n
\n\n
No accounts found.
\n ))\n )}\n >\n )}\n {(!type || type === 'hashtags') && (\n <>\n {type !== 'hashtags' && (\n\n
No hashtags found.
\n ))\n )}\n >\n )}\n {(!type || type === 'statuses') && (\n <>\n {type !== 'statuses' && (\n\n
No posts found.
\n ))\n )}\n >\n )}\n {!!type &&\n (uiState === 'default' ? (\n showMore ? (\nThe end.
\n )\n ) : (\n uiState === 'loading' && (\n\n
\n
\n Enter your search term or paste a URL above to get started.\n
\n )}\nUnable to fetch notifications.
\n\n \n
\n\n \n {url}\n \n
\n >\n ) : (\n <>\n\n \n {url}\n \n
\n >\n )}\n\n Go home\n
\n\n
Unable to load lists.
\n ) : (\nNo lists yet.
\n )}\n\n Unable to load post\n
\n
\n \n
A minimalistic opinionated Mastodon web client.
\n\n \n {DEFAULT_INSTANCE ? 'Log in' : 'Log in with Mastodon'}\n \n
\n {DEFAULT_INSTANCE && DEFAULT_INSTANCE_REGISTRATION_URL && (\n\n \n Sign up\n \n
\n )}\n {!DEFAULT_INSTANCE && (\n\n \n Connect your existing Mastodon/Fediverse account.\n
\n Your credentials are not stored on this server.\n \n
\n \n {appSite} {appVersion}\n \n
\n )}\n\n \n Built\n {' '}\n by{' '}\n {\n e.preventDefault();\n states.showAccount = 'cheeaun@mastodon.social';\n }}\n >\n @cheeaun\n \n .{' '}\n \n Privacy Policy\n \n .\n
\n\n Visually separate original posts and re-shared posts (boosted\n posts).\n
\nEffortlessly follow conversations. Semi-collapsible replies.
\n\n Similar notifications are grouped and collapsed to reduce clutter.\n
\n\n By default, single column for zen-mode seekers. Configurable\n multi-column for power users.\n
\nUp to 5 hashtags combined into a single timeline.
\n${h.innerHTML.trim()}
`,h.replaceWith(p)}),u&&[...r.querySelectorAll("p")].filter(p=>/^```/g.test(p.innerText)).forEach(p=>{const m=[p];let g=!1,_=p;for(;_.nextElementSibling;){const b=_.nextElementSibling;if(b&&b.tagName==="P")if(/```$/g.test(b.innerText)){m.push(b),g=!0;break}else m.push(b);else break;_=b}if(g){const b=document.createElement("pre");m.forEach(v=>{v.querySelectorAll("br").forEach(E=>E.replaceWith(`
-`))});const y=m.map(v=>v.innerHTML).join(`
-
-`);b.innerHTML=`${y}
`,p.replaceWith(b),m.forEach(v=>v.remove())}}),o.includes("`")&&(d=an(r),d.forEach(f=>{let h=f.nodeValue.replace(/&/g,"&").replace(/$1
")),tt.innerHTML=h,f.replaceWith(...tt.childNodes)})),/twitter\.com/i.test(o)&&(d=an(r,{rejectFilter:["A"]}),d.forEach(f=>{let h=f.nodeValue.replace(/&/g,"&").replace(//g,">");/@[a-zA-Z0-9_]+@twitter\.com/g.test(h)&&(h=h.replaceAll(/(@([a-zA-Z0-9_]+)@twitter\.com)/g,'$1')),tt.innerHTML=h,f.replaceWith(...tt.childNodes)})),o.includes("#")){let f=null;const h=[...r.querySelectorAll("p")].filter((p,m)=>{let g=0;for(let _=0;_code
\n if (hasCodeBlock) {\n const blocks = [...dom.querySelectorAll('p')].filter((p) =>\n /^```[^]+```$/g.test(p.innerText.trim()),\n );\n blocks.forEach((block) => {\n const pre = document.createElement('pre');\n // Replace ${block.innerHTML.trim()}
`;\n block.replaceWith(pre);\n });\n }\n\n // Convert multi-paragraph code blocks to code
\n if (hasCodeBlock) {\n const paragraphs = [...dom.querySelectorAll('p')];\n // Filter out paragraphs with ``` in beginning only\n const codeBlocks = paragraphs.filter((p) => /^```/g.test(p.innerText));\n // For each codeBlocks, get all paragraphs until the last paragraph with ``` at the end only\n codeBlocks.forEach((block) => {\n const nextParagraphs = [block];\n let hasCodeBlock = false;\n let currentBlock = block;\n while (currentBlock.nextElementSibling) {\n const next = currentBlock.nextElementSibling;\n if (next && next.tagName === 'P') {\n if (/```$/g.test(next.innerText)) {\n nextParagraphs.push(next);\n hasCodeBlock = true;\n break;\n } else {\n nextParagraphs.push(next);\n }\n } else {\n break;\n }\n currentBlock = next;\n }\n if (hasCodeBlock) {\n const pre = document.createElement('pre');\n nextParagraphs.forEach((p) => {\n // Replace ${codeText}
`;\n block.replaceWith(pre);\n nextParagraphs.forEach((p) => p.remove());\n }\n });\n }\n\n // INLINE CODE\n // ===========\n // Convert `code` to code
\n if (enhancedContent.includes('`')) {\n textNodes = extractTextNodes(dom);\n textNodes.forEach((node) => {\n let html = node.nodeValue\n .replace(/&/g, '&')\n .replace(//g, '>');\n if (/`[^`]+`/g.test(html)) {\n html = html.replaceAll(/(`[^]+?`)/g, '$1
');\n }\n fauxDiv.innerHTML = html;\n // const nodes = [...fauxDiv.childNodes];\n node.replaceWith(...fauxDiv.childNodes);\n });\n }\n\n // TWITTER USERNAMES\n // =================\n // Convert @username@twitter.com to @username@twitter.com\n if (/twitter\\.com/i.test(enhancedContent)) {\n textNodes = extractTextNodes(dom, {\n rejectFilter: ['A'],\n });\n textNodes.forEach((node) => {\n let html = node.nodeValue\n .replace(/&/g, '&')\n .replace(//g, '>');\n if (/@[a-zA-Z0-9_]+@twitter\\.com/g.test(html)) {\n html = html.replaceAll(\n /(@([a-zA-Z0-9_]+)@twitter\\.com)/g,\n '$1',\n );\n }\n fauxDiv.innerHTML = html;\n // const nodes = [...fauxDiv.childNodes];\n node.replaceWith(...fauxDiv.childNodes);\n });\n }\n\n // HASHTAG STUFFING\n // ================\n // Get the that contains a lot of hashtags, add a class to it\n if (enhancedContent.includes('#')) {\n let prevIndex = null;\n const hashtagStuffedParagraphs = [...dom.querySelectorAll('p')].filter(\n (p, index) => {\n let hashtagCount = 0;\n for (let i = 0; i < p.childNodes.length; i++) {\n const node = p.childNodes[i];\n\n if (node.nodeType === Node.TEXT_NODE) {\n const text = node.textContent.trim();\n if (text !== '') {\n return false;\n }\n } else if (node.tagName === 'BR') {\n // Ignore
\n } else if (node.tagName === 'A') {\n const linkText = node.textContent.trim();\n if (!linkText || !linkText.startsWith('#')) {\n return false;\n } else {\n hashtagCount++;\n }\n } else {\n return false;\n }\n }\n // Only consider \"stuffing\" if:\n // - there are more than 3 hashtags\n // - there are more than 1 hashtag in adjacent paragraphs\n if (hashtagCount > 3) {\n prevIndex = index;\n return true;\n }\n if (hashtagCount > 1 && prevIndex && index === prevIndex + 1) {\n prevIndex = index;\n return true;\n }\n },\n );\n if (hashtagStuffedParagraphs?.length) {\n hashtagStuffedParagraphs.forEach((p) => {\n p.classList.add('hashtag-stuffing');\n p.title = p.innerText;\n });\n }\n }\n\n if (postEnhanceDOM) {\n queueMicrotask(() => postEnhanceDOM(dom));\n // postEnhanceDOM(dom); // mutate dom\n }\n\n enhancedContent = dom.innerHTML;\n\n return enhancedContent;\n}\nconst enhanceContent = mem(_enhanceContent);\n\nconst defaultRejectFilter = [\n // Document metadata\n 'STYLE',\n // Image and multimedia\n 'IMG',\n 'VIDEO',\n 'AUDIO',\n 'AREA',\n 'MAP',\n 'TRACK',\n // Embedded content\n 'EMBED',\n 'IFRAME',\n 'OBJECT',\n 'PICTURE',\n 'PORTAL',\n 'SOURCE',\n // SVG and MathML\n 'SVG',\n 'MATH',\n // Scripting\n 'CANVAS',\n 'NOSCRIPT',\n 'SCRIPT',\n // Forms\n 'INPUT',\n 'OPTION',\n 'TEXTAREA',\n // Web Components\n 'SLOT',\n 'TEMPLATE',\n];\nconst defaultRejectFilterMap = Object.fromEntries(\n defaultRejectFilter.map((nodeName) => [nodeName, true]),\n);\nfunction extractTextNodes(dom, opts = {}) {\n const textNodes = [];\n const rejectFilterMap = Object.assign(\n {},\n defaultRejectFilterMap,\n opts.rejectFilter?.reduce((acc, cur) => {\n acc[cur] = true;\n return acc;\n }, {}),\n );\n const walk = document.createTreeWalker(\n dom,\n NodeFilter.SHOW_TEXT,\n {\n acceptNode(node) {\n if (rejectFilterMap[node.parentNode.nodeName]) {\n return NodeFilter.FILTER_REJECT;\n }\n return NodeFilter.FILTER_ACCEPT;\n },\n },\n false,\n );\n let node;\n while ((node = walk.nextNode())) {\n textNodes.push(node);\n }\n return textNodes;\n}\n\nexport default enhanceContent;\n","import mem from './mem';\n\nconst div = document.createElement('div');\nfunction getHTMLText(html) {\n if (!html) return '';\n div.innerHTML = html\n .replace(/<\\/p>/g, '
Failed to translate
\n ) : (\n !!translatedContent && (\n <>\n \n {!!pronunciationContent && (\n \n )}\n >\n )\n )}\n████ ████████
\n\n
Failed to load history
}\n {uiState === 'loading' && (\n\n
\n {attachment.description || No description}\n
\nError loading custom emojis
\n${m.innerHTML.trim()}
`,m.replaceWith(p)}),l&&[...r.querySelectorAll("p")].filter(p=>/^```/g.test(p.innerText)).forEach(p=>{const h=[p];let g=!1,y=p;for(;y.nextElementSibling;){const v=y.nextElementSibling;if(v&&v.tagName==="P")if(/```$/g.test(v.innerText)){h.push(v),g=!0;break}else h.push(v);else break;y=v}if(g){const v=document.createElement("pre");h.forEach(_=>{_.querySelectorAll("br").forEach(b=>b.replaceWith(`
+`))});const k=h.map(_=>_.innerHTML).join(`
+
+`);v.innerHTML=`${k}
`,p.replaceWith(v),h.forEach(_=>_.remove())}}),o.includes("`")&&(d=rn(r),d.forEach(f=>{let m=f.nodeValue.replace(/&/g,"&").replace(//g,">");/`[^`]+`/g.test(m)&&(m=m.replaceAll(/(`[^]+?`)/g,"$1
")),tt.innerHTML=m,f.replaceWith(...tt.childNodes)})),/twitter\.com/i.test(o)&&(d=rn(r,{rejectFilter:["A"]}),d.forEach(f=>{let m=f.nodeValue.replace(/&/g,"&").replace(//g,">");/@[a-zA-Z0-9_]+@twitter\.com/g.test(m)&&(m=m.replaceAll(/(@([a-zA-Z0-9_]+)@twitter\.com)/g,'$1')),tt.innerHTML=m,f.replaceWith(...tt.childNodes)})),o.includes("#")){let f=null;const m=[...r.querySelectorAll("p")].filter((p,h)=>{let g=0;for(let y=0;y