diff --git a/src/theme/book.js b/src/theme/book.js index 0939a29f689..284b89814ba 100644 --- a/src/theme/book.js +++ b/src/theme/book.js @@ -222,57 +222,88 @@ if (window.playground_copyable) { })(); (function clipboard() { - const clipButtons = document.querySelectorAll(".clip-button"); - - function hideTooltip(elem) { - elem.firstChild.textContent = ""; - elem.className = "fa far fa-clipboard clip-button"; - } - - function showTooltip(elem, msg) { - elem.firstChild.textContent = msg; - elem.className = "fa far fa-clipboard tooltipped"; - } - - const clipboardSnippets = new ClipboardJS(".clip-button", { - text: (trigger) => { - hideTooltip(trigger); - const playground = trigger.closest("pre"); - return playground.querySelector("code").textContent; - }, - }); - - clipButtons.forEach((clipButton) => { - clipButton.addEventListener("mouseout", (e) => { - hideTooltip(e.currentTarget); - }); - }); - - clipboardSnippets.on("success", (e) => { - e.clearSelection(); - showTooltip(e.trigger, "Copied!"); - }); - - clipboardSnippets.on("error", (e) => { - showTooltip(e.trigger, "Clipboard error!"); - }); + const clipButtons = document.querySelectorAll(".clip-button"); + + function hideTooltip(elem) { + elem.firstChild.textContent = ""; + elem.className = "fa far fa-clipboard clip-button"; + } + + function showTooltip(elem, msg) { + elem.firstChild.textContent = msg; + elem.className = "fa far fa-clipboard tooltipped"; + } + + const clipboardSnippets = new ClipboardJS(".clip-button", { + text: (trigger) => { + hideTooltip(trigger); + const playground = trigger.closest("pre"); + + const codeElement = playground.querySelector("code"); + const codeLines = codeElement.querySelectorAll(".code-line"); + + if (codeLines.length > 0) { + const lines = []; + codeLines.forEach(line => { + const text = line.textContent; + + if (text.trim() === '') { + lines.push(''); + } else { + lines.push(text); + } + }); + return lines.join('\n'); + } else { + return codeElement.textContent; + } + }, + }); + + clipButtons.forEach((clipButton) => { + clipButton.addEventListener("mouseout", (e) => { + hideTooltip(e.currentTarget); + }); + }); + + clipboardSnippets.on("success", (e) => { + e.clearSelection(); + showTooltip(e.trigger, "Copied!"); + }); + + clipboardSnippets.on("error", (e) => { + showTooltip(e.trigger, "Clipboard error!"); + }); })(); (function wrap() { - const wrapButtons = document.querySelectorAll(".wrap-button"); - wrapButtons.forEach((button) => { - button.addEventListener("click", (e) => { - const playground = button.closest("pre"); - const codeBlock = playground.querySelector("code"); - if (codeBlock.style.whiteSpace == "pre-wrap") { - codeBlock.style.whiteSpace = "pre"; - } else { - codeBlock.style.whiteSpace = "pre-wrap"; - } - }); - }); + const wrapButtons = document.querySelectorAll(".wrap-button"); + wrapButtons.forEach((button) => { + button.addEventListener("click", (e) => { + const playground = button.closest("pre"); + if (!playground) return; + + const codeContainer = playground.querySelector(".code-container") || playground.querySelector("code"); + if (!codeContainer) return; + + codeContainer.classList.toggle("wrap-enabled"); + + const lineNumbers = codeContainer.querySelectorAll(".line-number"); + const codeLines = codeContainer.querySelectorAll(".code-line"); + + if (lineNumbers.length > 0) { + codeLines.forEach((line, index) => { + if (lineNumbers[index]) { + const lineHeight = line.scrollHeight; + lineNumbers[index].style.height = lineHeight + 'px'; + } + }); + } + }); + }); })(); + (function syntax() { const syntaxButtons = document.querySelectorAll(".syntax-button"); syntaxButtons.forEach((hgButton) => { diff --git a/src/theme/css/chrome.css b/src/theme/css/chrome.css index 31d79a7cb0f..9f88685f4d5 100644 --- a/src/theme/css/chrome.css +++ b/src/theme/css/chrome.css @@ -3,7 +3,7 @@ @import "variables.css"; ::-webkit-scrollbar { - background: var(--card-bg); + background: var(--dark-bg); } ::-webkit-scrollbar-thumb { @@ -11,7 +11,7 @@ } html { - scrollbar-color: var(--card-hover) var(--card-bg); + scrollbar-color: var(--card-hover) var(--dark-bg); } /* ===== Page Layout ===== */ @@ -43,6 +43,25 @@ html { margin: 0 auto; } +.objectInfo { + border: 1px solid rgba(255, 255, 255, 0.05); + background-color: var(--card-bg); + border-radius: var(--border-radius); + padding: 1rem; + padding-left: 1.5rem; + margin-top: 2rem; +} + +.objectInfo h1 { + margin-top: -.1rem; + margin-bottom: -1.2rem; +} + +.objectInfo p { + color: var(--text-secondary); + margin-bottom: 1rem; +} + /* ===== Content Card ===== */ .content-card { background: var(--card-bg); @@ -176,6 +195,83 @@ html { text-decoration: none; } +/* ===== Last edited ===== */ + +.last_file_edit { + font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; + font-size: 1rem; + color: var(--text-secondary); + margin: 1rem 0; + padding: 1.3rem; + border: 1px solid rgba(255, 255, 255, 0.05); + background-color: var(--card-bg); + border-radius: var(--border-radius); +} + +.last_file_edit .edit-info { + display: flex; + align-items: center; + gap: 1rem; +} + +.last_file_edit .edit-avatar { + user-select: none; + -webkit-user-select: none; + width: 4rem; + height: 4rem; + border-radius: 50%; + object-fit: cover; + box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1); + flex-shrink: 0; +} + +.last_file_edit .edit-details { + display: flex; + flex-direction: column; + line-height: 1; + flex-grow: 1; +} + +.last_file_edit .edit-date-line { + margin-bottom: .45rem; +} + +.last_file_edit .edit-date { + color: var(--text-secondary); + font-weight: 600; + font-size: 1.45rem; +} + +.last_file_edit .edit-author-line { + margin-top: -1.5rem; + color: var(--text-secondary); +} + +.last_file_edit .edit-author { + font-size: 1.3rem; +} + +@media (max-width: 768px) { + .last_file_edit { + margin: 15px 0; + padding: 12px; + font-size: 12px; + } + + .last_file_edit .edit-avatar { + width: 32px; + height: 32px; + } + + .last_file_edit .edit-date { + font-size: 13px; + } + + .last_file_edit .edit-author { + font-size: 12px; + } +} + /* ===== Legacy Card Styles ===== */ .commandCard { display: flex; @@ -249,7 +345,8 @@ html { .functionTags { user-select: none; -webkit-user-select: none; - margin-top: -1rem; + margin-top: 1.5rem; + margin-bottom: -1rem; } .functionTags span { @@ -259,10 +356,9 @@ html { border: solid; border-width: 1px; border-radius: 10px; - font-size: 1.5rem; + font-size: 1rem; font-weight: bold; padding: 0.25rem 0.5rem 0.25rem 0.5rem; - transition: 0.3s; border-color: var(--link-color); color: var(--link-color); } @@ -295,11 +391,6 @@ html { content: "❗ "; } -.functionTags span:hover { - border-radius: 12.5px; - transition: 0.3s; -} - /* Playground */ .function-playground { @@ -630,40 +721,25 @@ a > .hljs { .editPage { user-select: none; -webkit-user-select: none; - border: solid; - border-width: 1px; border-radius: 10px; - border-color: var(--card-hover); - color: var(--link-color); + border: 1px solid rgba(255, 255, 255, 0.1); + color: rgba(255, 255, 255, 0.5) !important; width: fit-content; - padding: 5px; + padding: 1rem; text-decoration: none; - margin-top: -2.2rem; + margin-top: -2rem; text-align: center; display: block; margin-left: auto; - margin-right: 5px; font-size: 85%; transition: 0.3s; } .editPage:hover { - font-size: 87.5%; - border-radius: 8px; - border-color: var(--link-color); + border-color: rgba(255, 255, 255, 0.2); transition: 0.3s; } -@media (min-width: 750px) { - .editPage:after { - content: "Edit this page"; - } - - #editPageIcon { - padding-right: 0.4rem; - } -} - @media (max-width: 750px) { .editPage { visibility: hidden; @@ -676,19 +752,16 @@ a > .hljs { font-size: 2.5em; text-align: center; text-decoration: none; - position: fixed; top: 0; bottom: 0; margin: 0; max-width: 150px; min-width: 90px; - display: flex; justify-content: center; align-content: center; flex-direction: column; - transition: 0.5s; } @@ -699,51 +772,108 @@ a > .hljs { } .nav-wrapper { - margin-top: 20px; - display: none; + margin-top: 3rem; + display: block; + flex-wrap: wrap; + gap: 1rem; + width: 100%; + box-sizing: border-box; } +.nav-wide-wrapper { + display: none; +} .mobile-nav-chapters { - font-size: 2.5em; - text-align: center; - text-decoration: none; - width: 60px; - border-radius: 10px; - background-color: var(--card-bg) !important; - color: var(--link-color) !important; + height: 4.5rem; + font-size: 1.5rem; + text-align: center; + text-decoration: none; + border: 1px solid rgba(255, 255, 255, 0.05); + border-radius: var(--border-radius); + color: var(--text-secondary) !important; + transition: 0.4s; + display: flex; + align-items: center; + box-sizing: border-box; +} + +.mobile-nav-chapters i { + color: var(--link-color); + background-color: var(--card-bg); + padding: 1.15rem; + width: 4.5rem; + border-radius: var(--border-radius); + font-size: 2rem; + transition: 0.4s; +} + +.mobile-nav-chapters.previous .fa-angle-left { + margin-right: .75rem; +} + +.mobile-nav-chapters.next .fa-angle-right { + margin-left: .75rem; } .mobile-nav-chapters:hover i { - color: #ded; - transform: scale(1.05); - transition: 0.2s; + color: #ded !important; + border-radius: 20px; + transition: 0.4s; +} + +.mobile-nav-chapters:hover { + border: 1px solid rgba(255, 255, 255, 0.1); + border-radius: 20px; + transition: 0.4s; } .previous { - float: left; + justify-content: flex-start; + float: left; + padding-right: 1.5rem; + margin-left: -.25rem; } .next { - float: right; - right: var(--page-padding); + justify-content: flex-end; + padding-left: 1.5rem; + float: right; + right: var(--page-padding); + margin-right: -.25rem; } -@media only screen and (max-width: 1080px) { - .nav-wide-wrapper { - display: none; - } - .nav-wrapper { - display: block; - } +.prev-page-info, +.next-page-info { + color: var(--text-secondary); } -@media only screen and (max-width: 1380px) { - .sidebar-visible .nav-wide-wrapper { - display: none; - } - .sidebar-visible .nav-wrapper { - display: block; - } +.prev-page-info { + text-align: left; +} + +.next-page-info { + text-align: right; +} + +@media (max-width: 500px) { + .prev-page-info, + .next-page-info { + font-size: 1rem; + } +} + +@media (max-width: 500px) { + .mobile-nav-chapters { + width: auto; + flex: 0 1 auto; + } + .mobile-nav-chapters { + width: 100%; + } + + .mobile-nav-chapters.previous { + margin-bottom: 1rem; + } } /* Inline code */ @@ -770,14 +900,94 @@ pre { } code.hljs:not(.discord-message code) { - background: var(--card-bg); - padding: 0.8em; margin-bottom: -0.5rem; + padding: 0; + overflow-y: hidden; +} + +.code-container { + display: flex; + font-size: 14px; + line-height: 1.5; + background: #1e1e1e; + border-radius: 4px; + overflow: hidden; + margin-top: -35px; + margin-bottom: -35px; + padding-bottom: 2px; +} + +.line-numbers { + background: #2d2d2d; + color: #858585; + padding: 1em 0.5em; + text-align: right; + user-select: none; + -webkit-user-select: none; + min-width: 3em; + margin-bottom: -10px; + border-right: 1px solid #404040; +} + +.line-number { + padding: 0 0.5em; + min-height: 1.5em; + white-space: nowrap; + display: block; + height: 1.5em; + line-height: 1.5em; + text-align: right; +} + +.code-content { + flex: 1; + padding: 1em; + overflow-x: auto; + white-space: pre; + tab-size: 4; + line-height: 1.5; + scrollbar-color: #2d2d2d transparent; +} + +.code-content::-webkit-scrollbar { + background: transparent; +} + +.code-content::-webkit-scrollbar-thumb { + background: #2d2d2d; +} + +.code-line { + white-space: pre; + line-height: 1.5; + min-height: 1.5em; + display: block; +} + +.code-container.wrap-enabled .code-content { + white-space: pre-wrap; + word-wrap: break-word; + word-break: break-word; +} + +.code-container.wrap-enabled .code-line { + white-space: pre-wrap; + word-wrap: break-word; + word-break: break-word; + min-height: auto; + height: auto; +} + +.code-container.wrap-enabled .line-number { + min-height: auto; + height: auto; + line-height: 1.5em; + vertical-align: top; } .nostyle { /* Disabled Code HL style */ - color: inherit !important; + color: var(--text-secondary) !important; font-weight: inherit !important; font-style: normal !important; } @@ -800,6 +1010,10 @@ summary.admonition-title { margin-bottom: -1.75rem !important; } +.admonition-anchor-link { + display: none !important; +} + /* Summary */ summary { @@ -842,7 +1056,7 @@ pre > .buttons { position: absolute; z-index: 100; right: -3px; - margin: 1px; + margin: 5px; padding: 1px; visibility: hidden; opacity: 0; @@ -1118,7 +1332,7 @@ ul#searchresults span.teaser em { .sidebar { position: fixed; left: 0; - top: 70px; /* Account for fixed navbar height */ + top: 65px; /* Account for fixed navbar height */ bottom: 0; width: var(--sidebar-width); font-size: 95%; @@ -1132,7 +1346,7 @@ ul#searchresults span.teaser em { border-right: 1px solid rgba(255, 255, 255, 0.08); box-shadow: 4px 0 24px rgba(0, 0, 0, 0.3); scrollbar-color: var(--card-hover) rgba(255, 0, 0, 0) !important; - z-index: 999; + z-index: 2500; } .sidebar code { @@ -1146,7 +1360,8 @@ ul#searchresults span.teaser em { bottom: 0; left: 0; right: 0; - padding: 20px 16px; + padding: .1rem 16px; + padding-bottom: 2rem; } .sidebar-hidden .sidebar { @@ -1396,3 +1611,6 @@ ul#searchresults span.teaser em { + + + diff --git a/src/theme/css/general.css b/src/theme/css/general.css index 8e8991b7247..4df99f654d2 100644 --- a/src/theme/css/general.css +++ b/src/theme/css/general.css @@ -44,6 +44,10 @@ code { h1 { margin-top: 2%; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + width: 100%; } h2, @@ -117,23 +121,37 @@ h4 a.header:target { text-decoration: none; } +/* Tables */ + +.table-wrapper { + overflow-x: auto; +} + table { border-collapse: separate; border-spacing: 0; - border-radius: 10px; + border-radius: var(--border-radius); overflow: hidden; + width: 100%; box-shadow: 2px 10px 10px rgba(0, 0, 0, 0.3); + table-layout: auto; +} + +table td, table th { + max-width: 100rem; + word-wrap: break-word; + overflow-wrap: break-word; } table td { - padding: 6px 25px; + padding: 1rem 2rem; border: 0; border-radius: 0px !important; } table thead { background: var(--card-bg); - border-radius: 10px 10px 0 0; + border-radius: var(--border-radius) var(--border-radius) 0 0; } table thead td { @@ -141,12 +159,13 @@ table thead td { } table thead th { - padding: 3px 20px; + padding: 1rem; + border-bottom: 1px solid hsl(0deg 0% 100% / 10%); } table thead tr { border: 1px; - border-radius: 10px 10px 0 0; + border-radius: var(--border-radius) var(--border-radius) 0 0; } table tbody tr:nth-child(2n) { @@ -155,13 +174,15 @@ table tbody tr:nth-child(2n) { } table tbody tr:last-child td:first-child { - border-radius: 0 0 0 10px; + border-radius: 0 0 0 var(--border-radius); } table tbody tr:last-child td:last-child { - border-radius: 0 0 10px 0; + border-radius: 0 0 var(--border-radius) 0; } +/* Blockquote */ + blockquote { margin: 20px 0; padding: 0.1px 20px; @@ -206,3 +227,5 @@ blockquote { .tooltipped .tooltiptext { visibility: visible; } + + diff --git a/src/theme/css/home.css b/src/theme/css/home.css index 572c3a88322..31d5da4f529 100644 --- a/src/theme/css/home.css +++ b/src/theme/css/home.css @@ -9,7 +9,8 @@ } .editPage, -.breadcrumb { +.breadcrumb, +.last_file_edit { visibility: hidden; } @@ -37,6 +38,14 @@ box-shadow: 0 12px 15px rgba(0, 0, 0, 0.3); } +.bdfd-is h1 { + margin-top: 2%; + white-space: normal; + overflow: visible !important; + text-overflow: clip !important; + width: auto; +} + #bdfd-about { padding: 1rem; } @@ -207,3 +216,4 @@ button a { } } + diff --git a/src/theme/css/wiki-themes/discord-messages.css b/src/theme/css/wiki-themes/discord-messages.css index b9b486b455c..4edcc101928 100644 --- a/src/theme/css/wiki-themes/discord-messages.css +++ b/src/theme/css/wiki-themes/discord-messages.css @@ -1,7 +1,13 @@ -.discord-message .discord-message-markup, .discord-system-message { +.discord-message .discord-message-markup { font-size: 16px !important; } +.discord-system-message { + user-select: none !important; + -webkit-user-select: none !important; + font-size: 12px !important; +} + .discord-message a { color: #00aff4 !important; } @@ -45,12 +51,11 @@ color: #fff; font-size: 12px; margin-left: 4px; - border-radius: 3px; line-height: 100%; text-transform: uppercase; display: flex; align-items: center; - height: 2rem; + height: 1.5rem; padding: 0 0.275rem; margin-top: 0.075em; border-radius: 4px; @@ -64,6 +69,12 @@ margin-left: -0.25rem; } +.discord-embed-wrapper { + border-top: 1.5px solid hsl(0deg 0% 50% / 10%) !important; + border-bottom: 1.5px solid hsl(0deg 0% 50% / 10%) !important; + border-right: 1.5px solid hsl(0deg 0% 50% / 10%) !important; +} + .discord-embed .discord-embed-author { -webkit-box-align: center; align-items: center; @@ -208,6 +219,12 @@ transform: scale(1.4); } +.discord-button { + margin-top: -2px; + border-radius: 10px; + border: 1px solid hsl(0deg 0% 100% / 10%); +} + .discord-button.discord-button-secondary { color: #fff !important; } @@ -270,3 +287,8 @@ line-height: 19px; } + + + + + diff --git a/src/theme/index.hbs b/src/theme/index.hbs index 6b12f4e26fe..262fd8929a8 100644 --- a/src/theme/index.hbs +++ b/src/theme/index.hbs @@ -203,10 +203,6 @@ - - - - {{{ content }}} diff --git a/src/theme/settings/apply-settings.js b/src/theme/settings/apply-settings.js index d3de70d2486..fe38953c37e 100644 --- a/src/theme/settings/apply-settings.js +++ b/src/theme/settings/apply-settings.js @@ -1,173 +1,623 @@ +// Function Container +function createObjectInfo() { + if (/introduction/i.test(location.pathname)) return; + + if (/bdscript|callbacks/.test(location.pathname)) { + const h1 = document.querySelector('main h1'); + const p = Array.from(document.querySelectorAll('main > p')).find(el => !el.closest('.breadcrumb')); + const tags = document.querySelector('main .functionTags'); + + if (h1 && p) { + const container = document.createElement('div'); + container.className = 'objectInfo'; + h1.parentNode.insertBefore(container, h1); + container.appendChild(h1); + if (tags) container.appendChild(tags); + container.appendChild(p); + } + } +} + +// Example "Today at" +function removeTimestamp() { + const timestamps = document.querySelectorAll('.discord-message-timestamp'); + timestamps.forEach(timestamp => { + timestamp.remove(); + }); +} + +// Next & Prev function +function enhanceNavigationSimple() { + const navWrapper = document.querySelector('.nav-wrapper'); + if (!navWrapper) return; + + const prevLink = navWrapper.querySelector('a.previous'); + const nextLink = navWrapper.querySelector('a.next'); + + const supportedCategories = ['bdscript', 'callbacks', 'guides', 'resources', 'flowchart', 'tools', 'premium', 'javascript']; + + function getPathname(href) { + try { + return new URL(href, location.href).pathname; + } catch (e) { + return href || ''; + } + } + + function isSupportedCategory(href) { + const pathname = getPathname(href); + const hasCategory = supportedCategories.some(category => pathname.includes(`/${category}/`)); + const isRootPage = !supportedCategories.some(category => pathname.includes(`/${category}/`)) && + (pathname.endsWith('.html') && !pathname.includes('/category/')); + + return hasCategory || isRootPage; + } + + function getCategoryFromUrl(href) { + const pathname = getPathname(href); + for (const category of supportedCategories) { + if (pathname.includes(`/${category}/`)) { + return category; + } + } + return null; + } + + if (prevLink) { + const href = prevLink.getAttribute('href') || prevLink.href; + if (isSupportedCategory(href)) { + const pathname = getPathname(href); + const prevFileName = decodeURIComponent(pathname.split('/').pop().replace('.html', '')); + const category = getCategoryFromUrl(href); + prevLink.textContent = formatFunctionName(prevFileName, href, category); + prevLink.insertAdjacentHTML('afterbegin', ' '); + } + } + + if (nextLink) { + const href = nextLink.getAttribute('href') || nextLink.href; + if (isSupportedCategory(href)) { + const pathname = getPathname(href); + const nextFileName = decodeURIComponent(pathname.split('/').pop().replace('.html', '')); + const category = getCategoryFromUrl(href); + nextLink.textContent = formatFunctionName(nextFileName, href, category); + nextLink.insertAdjacentHTML('beforeend', ' '); + } + } +} + +function formatFunctionName(fileName, href, category = null) { + const lowerFileName = fileName.toLowerCase(); + + const customTitles = { + 'api': 'BDFD API', + '2fa': '2FA', + 'aboutselectmenu': 'Select Menus', + 'aboutmodals': 'Modals', + 'aboutbuttons': 'Buttons', + 'aboutslashcommands': 'Slash Commands', + 'discordidsystem': 'Discord ID System', + 'settings': 'Settings', + 'foreword': 'Home', + 'httprequests': 'HTTP Requests', + 'awaitedreactions': 'Awaited Reactions', + 'customimages': 'Custom Images', + 'customprefixes': 'Custom Prefixes', + 'embedbuilder': 'Embed Builder', + 'enablingjavascript': 'Enabling JavaScript', + 'objects': 'Objects', + 'tools': 'Tools' + }; + + if (customTitles[lowerFileName]) { + return customTitles[lowerFileName]; + } + + if (lowerFileName === 'introduction') { + if (category) { + const categoryMap = { + 'bdscript': 'BDScript', + 'callbacks': 'Callbacks', + 'guides': 'Guides', + 'resources': 'Resources', + 'flowchart': 'Flowchart', + 'tools': 'Tools', + 'premium': 'Premium', + 'javascript': 'JavaScript' + }; + + return categoryMap[category] || (category.charAt(0).toUpperCase() + category.slice(1)); + } + return 'Introduction'; + } + + if (category === 'javascript') { + return fileName; + } + + if (category === 'flowchart') { + let result = fileName; + result = result.replace(/([a-z])([A-Z])/g, '$1 $2'); + result = result.charAt(0).toUpperCase() + result.slice(1).toLowerCase(); + return result; + } + + const titleCategories = ['guides', 'resources', 'tools']; + const functionCategories = ['bdscript', 'callbacks', 'premium']; + + if (category && functionCategories.includes(category)) { + let result = '$' + fileName; + result = result.replace(/Complex$/i, '[]'); + return result; + } + + const isTitleCategory = category ? titleCategories.includes(category) : true; + + if (isTitleCategory) { + let result = fileName; + + result = result + .replace(/_/g, ' ') + .replace(/([a-z])([A-Z])/g, '$1 $2') + .replace(/([A-Z])([A-Z][a-z])/g, '$1 $2') + .replace(/^./, str => str.toUpperCase()); + + const words = result.split(' '); + const formattedWords = words.map(word => { + if (word === word.toUpperCase() && word.length > 1) { + return word.charAt(0).toUpperCase() + word.slice(1).toLowerCase(); + } + return word; + }); + + result = formattedWords.join(' '); + + result = result.replace(/\bBdfd\b/gi, 'BDFD'); + result = result.replace(/\b2fa\b/gi, '2FA'); + result = result.replace(/\bId\b/g, 'ID'); + result = result.replace(/\bAi\b/g, 'AI'); + result = result.replace(/\bI D\b/g, 'ID'); + result = result.replace(/\bA I\b/g, 'AI'); + result = result.replace(/\bU I\b/g, 'UI'); + result = result.replace(/\bF A Q\b/g, 'FAQ'); + result = result.replace(/\bChangelog\b/g, 'Changelog'); + result = result.replace(/\bHttp\b/gi, 'HTTP'); + + return result.trim(); + } + + return fileName; +} + +// Last edited +async function createAndUpdateLastEdit() { + try { + const currentPath = window.location.pathname; + const allowedPaths = ['premium', 'bdscript', 'guides', 'resources', 'flowchart', 'callbacks']; + let shouldAddBlock = false; + for (const path of allowedPaths) { + if (currentPath.includes(`/${path}/`) || + currentPath.endsWith(`/${path}`) || + currentPath.endsWith(`/${path}.html`)) { + shouldAddBlock = true; + break; + } + } + + if (!shouldAddBlock) return; + let pagePath = ''; + + if (currentPath.includes('/nightly/')) { + const nightlyIndex = currentPath.indexOf('/nightly/') + 8; + pagePath = currentPath.substring(nightlyIndex); + } else { + pagePath = currentPath.startsWith('/') ? currentPath.substring(1) : currentPath; + } + + if (!pagePath || pagePath === '' || pagePath === 'index.html') { + pagePath = 'index.md'; + } else { + pagePath = pagePath.replace('.html', '.md'); + if (!pagePath.startsWith('src/')) { + pagePath = 'src/' + pagePath; + } + } + + const owner = 'Rainb0wKey'; + const repo = 'bdfd-wiki'; + const apiUrl = `https://api.github.com/repos/${owner}/${repo}/commits?path=${encodeURIComponent(pagePath)}&per_page=1`; + const response = await fetch(apiUrl); + + if (!response.ok) { + // handle rate limits or permission issues gracefully + throw new Error(`API Error: ${response.status}`); + } + + const commits = await response.json(); + + if (commits && commits.length > 0) { + const lastCommit = commits[0]; + const lastModified = new Date(lastCommit.commit.committer.date); + + const formattedDate = lastModified.toLocaleDateString('en-US', { + year: 'numeric', + month: 'short', + day: 'numeric' + }); + + const authorName = lastCommit.author ? + (lastCommit.author.login || lastCommit.commit.author.name) : + lastCommit.commit.author.name; + + const avatarUrl = lastCommit.author ? + lastCommit.author.avatar_url : + 'https://github.com/identicons/identicon.png'; + + createEditBlock(formattedDate, authorName, avatarUrl, pagePath); + } else { + createFallbackBlock(pagePath); + } + } catch (error) { + console.error('Error loading last edit data:', error); + createFallbackBlock(); + } +} + +function createEditBlock(formattedDate, authorName, avatarUrl, pagePath) { + const container = document.createElement('div'); + container.className = 'last_file_edit'; + + const owner = 'Rainb0wKey'; + const repo = 'bdfd-wiki'; + const editUrl = `https://github.com/${owner}/${repo}/edit/dev/${encodeURIComponent(pagePath)}`; + + // Create elements using DOM methods instead of innerHTML + const editInfo = document.createElement('div'); + editInfo.className = 'edit-info'; + + const avatarImg = document.createElement('img'); + avatarImg.className = 'edit-avatar'; + avatarImg.src = avatarUrl || 'https://github.com/identicons/identicon.png'; + avatarImg.alt = ''; + + const editDetails = document.createElement('div'); + editDetails.className = 'edit-details'; + + const dateLine = document.createElement('div'); + dateLine.className = 'edit-date-line'; + + const dateSpan = document.createElement('span'); + dateSpan.className = 'edit-date'; + dateSpan.textContent = `Last edited at ${formattedDate}`; + + const editLink = document.createElement('a'); + editLink.href = editUrl; + editLink.className = 'editPage'; + + const editIcon = document.createElement('i'); + editIcon.className = 'fa fa-edit'; + editIcon.id = 'editPageIcon'; + + editLink.appendChild(editIcon); + dateLine.appendChild(dateSpan); + dateLine.appendChild(editLink); + + const authorLine = document.createElement('div'); + authorLine.className = 'edit-author-line'; + + const authorSpan = document.createElement('span'); + authorSpan.className = 'edit-author'; + authorSpan.textContent = authorName; + + authorLine.appendChild(authorSpan); + + editDetails.appendChild(dateLine); + editDetails.appendChild(authorLine); + editInfo.appendChild(avatarImg); + editInfo.appendChild(editDetails); + container.appendChild(editInfo); + + // Insert into DOM + const mainElement = document.querySelector('main'); + if (mainElement) { + mainElement.appendChild(container); + } else { + document.body.appendChild(container); + } +} + +function createFallbackBlock(pagePath = '') { + const container = document.createElement('div'); + container.className = 'last_file_edit'; + + const owner = 'Rainb0wKey'; + const repo = 'bdfd-wiki'; + const editUrl = pagePath ? `https://github.com/${owner}/${repo}/edit/dev/${encodeURIComponent(pagePath)}` : '#'; + + // Create elements using DOM methods + const editInfo = document.createElement('div'); + editInfo.className = 'edit-info'; + + const avatarImg = document.createElement('img'); + avatarImg.className = 'edit-avatar'; + avatarImg.src = 'https://github.com/identicons/identicon.png'; + avatarImg.alt = ''; + + const editDetails = document.createElement('div'); + editDetails.className = 'edit-details'; + + const dateLine = document.createElement('div'); + dateLine.className = 'edit-date-line'; + + const dateSpan = document.createElement('span'); + dateSpan.className = 'edit-date'; + dateSpan.textContent = 'Last edited at Failed to load'; + + const editLink = document.createElement('a'); + editLink.href = editUrl; + editLink.className = 'editPage'; + + const editIcon = document.createElement('i'); + editIcon.className = 'fa fa-edit'; + editIcon.id = 'editPageIcon'; + + editLink.appendChild(editIcon); + dateLine.appendChild(dateSpan); + dateLine.appendChild(editLink); + + const authorLine = document.createElement('div'); + authorLine.className = 'edit-author-line'; + + const authorSpan = document.createElement('span'); + authorSpan.className = 'edit-author'; + authorSpan.textContent = 'GitHub API may be experiencing issues loading data.'; + + authorLine.appendChild(authorSpan); + + editDetails.appendChild(dateLine); + editDetails.appendChild(authorLine); + editInfo.appendChild(avatarImg); + editInfo.appendChild(editDetails); + container.appendChild(editInfo); + + const mainElement = document.querySelector('main'); + if (mainElement) { + mainElement.appendChild(container); + } else { + document.body.appendChild(container); + } +} + const DiscordThemes = { - light: { - reactionColor: "#F2F3F5", - messageTextColor: "#313338", - background: "#FFF", - }, - dark: { - reactionColor: "#202226", - messageTextColor: "#C6C7CC", - background: "#1C1D22", - }, - redmoon: { - reactionColor: "#4e0505", - background: "linear-gradient(-25deg, #240000, #740606)", - }, - nightsapphire: { - reactionColor: "#180052", - background: "linear-gradient(-25deg, #000124, #260674)", - }, - emeraldearth: { - reactionColor: "#006d3f", - background: "linear-gradient(-25deg, #0c2400, #067446)", - }, - nightviolet: { - reactionColor: "#390085", - background: "linear-gradient(-25deg, #1d0024, #350674)", - }, - oldwood: { - reactionColor: "#714400", - background: "linear-gradient(-25deg, #240f00, #744806)", - }, - azuresky: { - reactionColor: "#007162", - background: "linear-gradient(-25deg, #001a24, #067465)", - }, - cherryvelvety: { - reactionColor: "#710049", - background: "linear-gradient(-25deg, #240017, #74064d)", - }, - forestdepth: { - reactionColor: "#616d00", - background: "linear-gradient(-25deg, #222400, #687406)", - }, - nightchestnut: { - reactionColor: "#4e0505", - background: "linear-gradient(-25deg, #190024, #740606)", - }, - mosscovered: { - reactionColor: "#4b6d11", - background: "linear-gradient(-25deg, #1c2400, #4c7406)", - }, - deepruby: { - reactionColor: "#74066e", - background: "linear-gradient(-25deg, #1f0024, #74066e)", - }, - fernvalley: { - reactionColor: "#1e6d00", - background: "linear-gradient(-25deg, #00240a, #247406)", - }, - forestshadows: { - reactionColor: "#086b00", - background: "linear-gradient(-25deg, #000624, #086700 , #0a7f01)", - }, - autumnblaze: { - reactionColor: "#742006", - background: "linear-gradient(-25deg, #240800, #742006)", - }, + light: { + reactionColor: "#F2F3F5", + messageTextColor: "#313338", + background: "#FFF", + }, + dark: { + reactionColor: "#202226", + messageTextColor: "#C6C7CC", + background: "#1C1D22", + }, + redmoon: { + reactionColor: "#4e0505", + background: "linear-gradient(-25deg, #240000, #740606)", + }, + nightsapphire: { + reactionColor: "#180052", + background: "linear-gradient(-25deg, #000124, #260674)", + }, + emeraldearth: { + reactionColor: "#006d3f", + background: "linear-gradient(-25deg, #0c2400, #067446)", + }, + nightviolet: { + reactionColor: "#390085", + background: "linear-gradient(-25deg, #1d0024, #350674)", + }, + oldwood: { + reactionColor: "#714400", + background: "linear-gradient(-25deg, #240f00, #744806)", + }, + azuresky: { + reactionColor: "#007162", + background: "linear-gradient(-25deg, #001a24, #067465)", + }, + cherryvelvety: { + reactionColor: "#710049", + background: "linear-gradient(-25deg, #240017, #74064d)", + }, + forestdepth: { + reactionColor: "#616d00", + background: "linear-gradient(-25deg, #222400, #687406)", + }, + nightchestnut: { + reactionColor: "#4e0505", + background: "linear-gradient(-25deg, #190024, #740606)", + }, + mosscovered: { + reactionColor: "#4b6d11", + background: "linear-gradient(-25deg, #1c2400, #4c7406)", + }, + deepruby: { + reactionColor: "#74066e", + background: "linear-gradient(-25deg, #1f0024, #74066e)", + }, + fernvalley: { + reactionColor: "#1e6d00", + background: "linear-gradient(-25deg, #00240a, #247406)", + }, + forestshadows: { + reactionColor: "#086b00", + background: "linear-gradient(-25deg, #000624, #086700 , #0a7f01)", + }, + autumnblaze: { + reactionColor: "#742006", + background: "linear-gradient(-25deg, #240800, #742006)", + }, }; function setDiscordTheme(colorId) { - const discordMessages = document.getElementsByTagName("discord-messages"); - - const styles = { - reactionColor: "#131318", - messageTextColor: "#DDDEE1", - background: "#000", - ...(DiscordThemes[colorId] || {}), - }; - - // Callback function to execute when mutations are observed - const callback = (mutationList, observer) => { - for (const mutation of mutationList) { - if ( - mutation.type === "attributes" && - mutation.attributeName === "class" - ) { - const reactions = - document.getElementsByTagName("discord-reaction"); - const messageColors = document.querySelectorAll( - ".discord-message .discord-message-markup" - ); - - const botToApp = - document.querySelectorAll('.discord-application-tag'); - - if (styles.background) - mutation.target.style.background = styles.background; - mutation.target.style.backgroundColor = styles.exampleColor; - for (const reaction of reactions) { - // change the div which is the actual reaction - reaction.children.item(0).style.backgroundColor = - styles.reactionColor; - } - messageColors.forEach((text) => { - text.style.color = styles.messageTextColor; - }); - - botToApp.forEach(tag => { - if (tag.textContent.includes("Bot")) { - tag.textContent = tag.textContent.replace("Bot", "App"); - tag.setAttribute("aria-label", "Verified App"); - } - }); - - // Changes "00/00/0000" to "Today at 00:00". - const timestamps = document.querySelectorAll( - ".discord-message-timestamp" - ); - timestamps.forEach((timestamp) => { - var time = new Date().getTime(); - var minuteExample = new Date().getMinutes(); - var hourExample = new Date().getHours(); - const formattedMinute = - minuteExample < 10 - ? `0${minuteExample}` - : minuteExample; - const formattedHour = - hourExample < 10 ? `0${hourExample}` : hourExample; - const formattedTime = `Today at ${formattedHour}:${formattedMinute}`; - timestamp.textContent = formattedTime; - }); - } + const discordMessages = document.getElementsByTagName("discord-messages"); + + const styles = { + reactionColor: "#131318", + messageTextColor: "#DDDEE1", + background: "#000", + ...(DiscordThemes[colorId] || {}), + }; + + // Disconnect previous observers to avoid duplicates/leaks + if (window.__discordThemeObservers && Array.isArray(window.__discordThemeObservers)) { + window.__discordThemeObservers.forEach(o => { + try { o.disconnect(); } catch (e) {} + }); + } + window.__discordThemeObservers = []; + + const callback = (mutationList) => { + for (const mutation of mutationList) { + if ( + mutation.type === "attributes" && + mutation.attributeName === "class" && + mutation.target && mutation.target.style + ) { + const reactions = document.getElementsByTagName("discord-reaction"); + const messageColors = document.querySelectorAll( + ".discord-message .discord-message-markup" + ); + + const botToApp = document.querySelectorAll('.discord-application-tag'); + + if (styles.background) { + mutation.target.style.background = styles.background; + if (styles.exampleColor) { + mutation.target.style.backgroundColor = styles.exampleColor; + } } - }; + for (const reaction of reactions) { + const child = reaction.children && reaction.children.item(0); + if (child && child.style) { + child.style.backgroundColor = styles.reactionColor; + } + } + messageColors.forEach((text) => { + if (text && text.style) text.style.color = styles.messageTextColor; + }); + + botToApp.forEach(tag => { + if (tag.textContent && tag.textContent.includes("Bot")) { + tag.textContent = tag.textContent.replace("Bot", "App"); + tag.setAttribute("aria-label", "Verified App"); + } + }); + + const timestamps = document.querySelectorAll( + ".discord-message-timestamp" + ); + if (timestamps && timestamps.length) { + const now = new Date(); + const minuteExample = now.getMinutes(); + const hourExample = now.getHours(); + const formattedMinute = minuteExample < 10 ? `0${minuteExample}` : minuteExample; + const formattedHour = hourExample < 10 ? `0${hourExample}` : hourExample; + const formattedTime = `Today at ${formattedHour}:${formattedMinute}`; + timestamps.forEach((timestamp) => { + timestamp.textContent = formattedTime; + }); + } + + removeTimestamp(); + } + } + }; + - for (message of discordMessages) { + for (const message of discordMessages) { const mutObv = new MutationObserver(callback); - mutObv.observe(message, {attributes: true}); + mutObv.observe(message, { attributes: true }); + window.__discordThemeObservers.push(mutObv); } } function applySettings() { - let data; + const defaultData = { + "discord-example-theme": "dark", + "text-size": "60%", + "language": "en", + "text-hg": "none", + "text-font": "Open Sans, sans-serif", + }; - try { - data = JSON.parse(localStorage.getItem("json")); - } catch { + let data; + let localData = {}; + try { + const ls = localStorage.getItem("json"); + if (ls) { + try { + localData = JSON.parse(ls) || {}; + } catch (e) { + localData = {}; + } } + data = Object.assign({}, defaultData, localData); + } catch { + data = defaultData; + } + + const html = document.querySelector("html"); - const defaultData = { - "discord-example-theme": "dark", - "text-size": "60%", - "language": "en", - "text-hg": "none", - "text-font": "Open Sans, sans-serif", - }; - - if (!data) localStorage.setItem("json", JSON.stringify(defaultData)); - data ??= defaultData; - - document.querySelectorAll('.chapter > li.chapter-item').forEach(el => { - if (el.querySelector('div')) { - const text = el.querySelector('div').textContent.trim(); - if (text === 'Functions' || text === 'Premium') { - el.classList.add('functions-section'); - } + html.style.fontFamily = data["text-font"]; + html.style.fontSize = data["text-size"]; + html.style.textShadow = data["text-hg"]; + + document.querySelectorAll('.chapter > li.chapter-item').forEach(el => { + const div = el.querySelector('div'); + if (div) { + const text = div.textContent.trim(); + if (text === 'Functions' || text === 'Premium') { + el.classList.add('functions-section'); + } + } + }); + + const currentPath = window.location.pathname; + const currentHref = window.location.href; + const owner = 'Rainb0wKey'; + const ghPagesBase = `https://${owner}.github.io/`; + + if (currentPath.includes('/tools/') && !currentHref.includes(`${owner}.github.io`)) { + const fileName = currentPath.split('/').pop(); + window.location.replace(ghPagesBase + fileName); + } + else if (currentPath.includes('/terms.html') || currentHref.includes('terms.html')) { + window.location.replace('https://botdesignerdiscord.com'); + } + else { + const allLinks = document.querySelectorAll('a[href]'); + + allLinks.forEach(link => { + const href = link.getAttribute('href'); + + if (href) { + if (href.indexOf('../tools/') === 0) { + const newHref = ghPagesBase + href.substring(9); + link.setAttribute('href', newHref); + } + + if (href.includes('terms.html')) { + link.setAttribute('href', 'https://botdesignerdiscord.com'); } + } }); - - setDiscordTheme(data["discord-example-theme"]); + } + + setDiscordTheme(data["discord-example-theme"]); } -applySettings(); +document.addEventListener('DOMContentLoaded', function() { + createAndUpdateLastEdit(); + enhanceNavigationSimple(); + createObjectInfo(); + applySettings(); +}); \ No newline at end of file diff --git a/src/theme/settings/code-hg.js b/src/theme/settings/code-hg.js index 8c357d181a5..7ab9576d572 100644 --- a/src/theme/settings/code-hg.js +++ b/src/theme/settings/code-hg.js @@ -105,7 +105,15 @@ function highlight(scheme) { } codeBlocks.forEach((codeBlock) => { - let code = escapeHtml(codeBlock.textContent); + let originalText = codeBlock.textContent; + originalText = originalText.replace(/\n+$/, ''); + + let code = originalText; + + code = code + .replace(/&/g, "&") + .replace(//g, ">"); code = code .replace(/\;/g, styling("semicolonHighlight", scheme)) @@ -124,8 +132,29 @@ function highlight(scheme) { ); }); - codeBlock.innerHTML = code; + const lines = code.split('\n'); + const lineCount = lines.length; + + let lineNumbersHtml = ''; + for (let i = 1; i <= lineCount; i++) { + const safeI = String(i).replace(/[&<>"']/g, ''); + lineNumbersHtml += `
${safeI}
`; + } + + const formattedCode = lines.map(line => { + if (line.trim() === '') { + return '
 
'; + } + return `
${line}
`; + }).join(''); + + codeBlock.innerHTML = ` +
+
${lineNumbersHtml}
+
${formattedCode}
+
+ `; }); } -highlight(scheme); +highlight(scheme); \ No newline at end of file diff --git a/src/theme/settings/style.css b/src/theme/settings/style.css index 6f79d0b787a..91d5af96d6e 100644 --- a/src/theme/settings/style.css +++ b/src/theme/settings/style.css @@ -8,46 +8,6 @@ body { visibility: hidden; } -/* Tag Beta Feature */ - -.tag-beta { - user-select: none; - -webkit-user-select: none; - border-style: solid; - border-color: rgba(255, 255, 255, 0.1); - margin: auto; - margin-top: 15px; - width: fit-content; - padding: 10px 10px; - border-width: 1px; - border-radius: 10px; - display: flex; - align-items: center; - background: var(--card-bg); -} - -.tag-beta .warn { - font-size: 2.25em; - margin-right: 7.5px; -} - -.tag-beta .text { - display: flex; - flex-direction: column; - align-items: flex-start; - margin-bottom: 5px; -} - -.tag-beta .head { - font-weight: bold; - margin-bottom: 2.5px; - font-size: 1.1em; -} - -.tag-beta .desc { - font-size: 0.9em; -} - /* Discord Message Preview*/ .discord-messages {