diff --git a/assets/css/_core/_media.scss b/assets/css/_core/_media.scss
index bf55c170..5c29f081 100644
--- a/assets/css/_core/_media.scss
+++ b/assets/css/_core/_media.scss
@@ -40,12 +40,6 @@
}
}
-@media only screen and (min-width: 961px) {
- #toc-drawer-button {
- display: none;
- }
-}
-
@media only screen and (max-width: 960px) {
%page-style {
width: ROUND(80%, 2px) !important;
diff --git a/assets/css/_partials/_single/_toc.scss b/assets/css/_partials/_single/_toc.scss
index c0f39d48..55be0547 100644
--- a/assets/css/_partials/_single/_toc.scss
+++ b/assets/css/_partials/_single/_toc.scss
@@ -1,3 +1,4 @@
+// Common styles for all TOC types
.toc {
.toc-title {
font-size: $toc-title-font-size;
@@ -70,6 +71,48 @@
}
}
+// Common active state styles for auto and dialog TOC
+#toc-auto,
+#toc-dialog {
+ .toc-content {
+ li.has-active {
+ &::marker {
+ color: $single-link-color;
+
+ [data-theme='dark'] & {
+ color: $single-link-color-dark;
+ }
+ }
+
+ &:has(> .active)::marker {
+ font-weight: bold;
+ }
+
+ > a {
+ color: $single-link-color;
+
+ [data-theme='dark'] & {
+ color: $single-link-color-dark;
+ }
+ }
+ }
+
+ ul {
+ a.active::before {
+ color: $single-link-hover-color;
+
+ [data-theme='dark'] & {
+ color: $single-link-hover-color-dark;
+ }
+ }
+ }
+
+ a.active {
+ font-weight: bold;
+ }
+ }
+}
+
#toc-auto {
display: block;
position: sticky;
@@ -127,28 +170,6 @@
margin: 0;
}
- li.has-active {
- &::marker {
- color: $single-link-color;
-
- [data-theme='dark'] & {
- color: $single-link-color-dark;
- }
- }
-
- &:has(> .active)::marker {
- font-weight: bold;
- }
-
- > a {
- color: $single-link-color;
-
- [data-theme='dark'] & {
- color: $single-link-color-dark;
- }
- }
- }
-
// for unordered list automatically collapsed
ul {
ul {
@@ -158,14 +179,6 @@
.has-active > ul {
display: block;
}
-
- a.active::before {
- color: $single-link-hover-color;
-
- [data-theme='dark'] & {
- color: $single-link-hover-color-dark;
- }
- }
}
// for ordered list automatically collapsed
@@ -178,10 +191,6 @@
display: block;
}
}
-
- a.active {
- font-weight: bold;
- }
}
}
@@ -261,11 +270,11 @@
opacity 0.2s 0.4s;
opacity: 0;
translate: 100vw 0;
- width: 100%;
+ width: MIN(calc(100% - 4rem), 500px);
height: 100%;
max-width: 100%;
max-height: 100%;
- margin-left: 4rem;
+ margin-right: 0;
border: none;
.toc {
diff --git a/assets/js/theme.js b/assets/js/theme.js
index 0ecedb4a..14a3985c 100644
--- a/assets/js/theme.js
+++ b/assets/js/theme.js
@@ -171,7 +171,7 @@ class FixIt {
window.postChatUser.setSearchInput('');
}, false);
}
- return;
+ return;
}
if (isMobile) {
@@ -209,9 +209,9 @@ class FixIt {
$searchClear.style.display = 'none';
this._searchDesktop && this._searchDesktop.autocomplete.setVal('');
}, false);
- this._searchDesktopOnClickMask = this._searchDesktopOnClickMask ||(() => {
- this._resetSearchUI($header, $searchLoading, $searchClear, this._searchDesktop);
- });
+ this._searchDesktopOnClickMask = this._searchDesktopOnClickMask || (() => {
+ this._resetSearchUI($header, $searchLoading, $searchClear, this._searchDesktop);
+ });
this.clickMaskEventSet.add(this._searchDesktopOnClickMask);
}
$searchInput.addEventListener('input', () => {
@@ -342,7 +342,7 @@ class FixIt {
suggestion: ({ title, uri, date, context }) =>
`
${context}
`,
empty: ({ query }) => `${searchConfig.noResultsFound}: "${query}"
`,
- footer: ({}) => {
+ footer: ({ }) => {
let searchType, icon, href;
switch (searchConfig.type) {
case 'algolia':
@@ -547,7 +547,7 @@ class FixIt {
let code = stagingDOM.contentAsText();
try {
code = JSON.stringify(JSON.parse(code), null, 2);
- } catch {}
+ } catch { }
this.util.copyText(code).then(() => {
$btn.toggleAttribute('data-copied', true);
setTimeout(() => {
@@ -579,6 +579,46 @@ class FixIt {
// TODO
}
+ /**
+ * Helper method to update TOC active state based on scroll position
+ * @param {HTMLElement} $tocContainer - TOC container element for parent traversal
+ * @param {HTMLCollection} $headingElements - Heading elements to track
+ * @param {number} indexOffset - Offset for active state calculation
+ */
+ _updateTocActiveState($tocContainer, $headingElements, indexOffset) {
+ const $tocLinkElements = $tocContainer.querySelectorAll('a:first-child');
+ const $tocLiElements = $tocContainer.getElementsByTagName('li');
+
+ // Remove all active classes
+ this.util.forEach($tocLinkElements, ($tocLink) => {
+ $tocLink.classList.remove('active');
+ });
+ this.util.forEach($tocLiElements, ($tocLi) => {
+ $tocLi.classList.remove('has-active');
+ });
+
+ // Calculate active TOC index
+ let activeTocIndex = $headingElements.length - 1;
+ for (let i = 0; i < $headingElements.length - 1; i++) {
+ const thisTop = $headingElements[i].getBoundingClientRect().top;
+ const nextTop = $headingElements[i + 1].getBoundingClientRect().top;
+ if ((i == 0 && thisTop > indexOffset) || (thisTop <= indexOffset && nextTop > indexOffset)) {
+ activeTocIndex = i;
+ break;
+ }
+ }
+
+ // Add active classes
+ if (activeTocIndex !== -1 && $tocLinkElements[activeTocIndex]) {
+ $tocLinkElements[activeTocIndex].classList.add('active');
+ let $parent = $tocLinkElements[activeTocIndex].parentElement;
+ while ($parent !== $tocContainer) {
+ $parent.classList.add('has-active');
+ $parent = $parent.parentElement.parentElement;
+ }
+ }
+ }
+
/**
* init table of contents
*/
@@ -587,61 +627,53 @@ class FixIt {
if ($tocCore === null) {
return;
}
- if (document.getElementById('toc-static').dataset.kept === 'true' || this.util.isTocStatic()) {
+ const $headingElements = document.getElementsByClassName('heading-element');
+ const INDEX_OFFSET = 20 + this.breadcrumbHeight + (
+ document.body.dataset.headerDesktop !== 'normal' ? document.getElementById('header-desktop').offsetHeight : 0
+ );
+
+ // TOC Static and TOC Dialog
+ if (this.util.isTocStatic()) {
const $tocContentStatic = document.getElementById('toc-content-static');
if ($tocCore.parentElement !== $tocContentStatic) {
$tocCore.parentElement.removeChild($tocCore);
$tocContentStatic.appendChild($tocCore);
}
+ this._tocDialogOnScroll = this._tocDialogOnScroll || (() => {
+ this._updateTocActiveState(
+ document.querySelector('#toc-content-drawer>nav'),
+ $headingElements,
+ INDEX_OFFSET
+ );
+ });
+ this._tocDialogOnScroll();
+ this.scrollEventSet.add(this._tocDialogOnScroll);
this._tocOnScroll && this.scrollEventSet.delete(this._tocOnScroll);
- } else {
- const $tocContentAuto = document.getElementById('toc-content-auto');
- if ($tocCore.parentElement !== $tocContentAuto) {
- $tocCore.parentElement.removeChild($tocCore);
- $tocContentAuto.appendChild($tocCore);
- }
- const $toc = document.getElementById('toc-auto');
- $toc.style.visibility = 'visible';
- this.util.animateCSS($toc, ['animate__fadeIn', 'animate__faster'], true);
- const $postMeta = document.querySelector('.post-meta');
- $toc.style.marginTop = `${$postMeta.offsetTop + $postMeta.clientHeight}px`;
- const $tocLinkElements = $tocCore.querySelectorAll('a:first-child');
- const $tocLiElements = $tocCore.getElementsByTagName('li');
- const $headingElements = document.getElementsByClassName('heading-element');
- const headerHeight = document.getElementById('header-desktop').offsetHeight;
- document.querySelector('.fi-container').addEventListener('resize', () => {
- $toc.style.marginBottom = `${document.querySelector('.fi-container').clientHeight - document.querySelector('.post-footer').offsetTop}px`;
- });
- this._tocOnScroll = this._tocOnScroll || (() => {
- $toc.style.marginBottom = `${document.querySelector('.fi-container').clientHeight - document.querySelector('.post-footer').offsetTop}px`;
- this.util.forEach($tocLinkElements, ($tocLink) => {
- $tocLink.classList.remove('active');
- });
- this.util.forEach($tocLiElements, ($tocLi) => {
- $tocLi.classList.remove('has-active');
- });
- const INDEX_SPACING = 20 + (document.body.dataset.headerDesktop !== 'normal' ? headerHeight : 0) + this.breadcrumbHeight;
- let activeTocIndex = $headingElements.length - 1;
- for (let i = 0; i < $headingElements.length - 1; i++) {
- const thisTop = $headingElements[i].getBoundingClientRect().top;
- const nextTop = $headingElements[i + 1].getBoundingClientRect().top;
- if ((i == 0 && thisTop > INDEX_SPACING) || (thisTop <= INDEX_SPACING && nextTop > INDEX_SPACING)) {
- activeTocIndex = i;
- break;
- }
- }
- if (activeTocIndex !== -1 && $tocLinkElements[activeTocIndex]) {
- $tocLinkElements[activeTocIndex].classList.add('active');
- let $parent = $tocLinkElements[activeTocIndex].parentElement;
- while ($parent !== $tocCore) {
- $parent.classList.add('has-active');
- $parent = $parent.parentElement.parentElement;
- }
- }
- });
- this._tocOnScroll();
- this.scrollEventSet.add(this._tocOnScroll);
+ return;
}
+
+ // TOC Auto
+ const $tocContentAuto = document.getElementById('toc-content-auto');
+ if ($tocCore.parentElement !== $tocContentAuto) {
+ $tocCore.parentElement.removeChild($tocCore);
+ $tocContentAuto.appendChild($tocCore);
+ }
+ const $toc = document.getElementById('toc-auto');
+ $toc.style.visibility = 'visible';
+ this.util.animateCSS($toc, ['animate__fadeIn', 'animate__faster'], true);
+ const $postMeta = document.querySelector('.post-meta');
+ $toc.style.marginTop = `${$postMeta.offsetTop + $postMeta.clientHeight}px`;
+
+ document.querySelector('.fi-container').addEventListener('resize', () => {
+ $toc.style.marginBottom = `${document.querySelector('.fi-container').clientHeight - document.querySelector('.post-footer').offsetTop}px`;
+ });
+ this._tocOnScroll = this._tocOnScroll || (() => {
+ $toc.style.marginBottom = `${document.querySelector('.fi-container').clientHeight - document.querySelector('.post-footer').offsetTop}px`;
+ this._updateTocActiveState($tocCore, $headingElements, INDEX_OFFSET);
+ });
+ this._tocOnScroll();
+ this.scrollEventSet.add(this._tocOnScroll);
+ this._tocDialogOnScroll && this.scrollEventSet.delete(this._tocDialogOnScroll);
}
initTocListener() {
@@ -670,6 +702,7 @@ class FixIt {
if (!dialog || !openButton) {
return;
}
+ openButton.classList.toggle('d-none', !this.util.isTocStatic());
openButton.addEventListener("click", () => {
dialog.showModal();
document.activeElement?.blur();
@@ -798,7 +831,7 @@ class FixIt {
if (Array.isArray(markerArray) && markerArray.length > 0) {
markerArray.forEach(marker => {
const { lng: markerLng, lat: markerLat, description } = marker;
- const popup = new mapboxgl.Popup({ offset: 25 }).setText(description);
+ const popup = new mapboxgl.Popup({ offset: 25 }).setText(description);
new mapboxgl.Marker()
.setLngLat([markerLng, markerLat])
.setPopup(popup)
@@ -1023,7 +1056,7 @@ class FixIt {
if (this.config.comment.giscus) {
const giscusConfig = this.config.comment.giscus;
this._giscusOnSwitchTheme = this._giscusOnSwitchTheme || (() => {
- const message = { setConfig: { theme: this.isDark ? giscusConfig.darkTheme : giscusConfig.lightTheme }};
+ const message = { setConfig: { theme: this.isDark ? giscusConfig.darkTheme : giscusConfig.lightTheme } };
document.querySelector('.giscus-frame')?.contentWindow.postMessage({ giscus: message }, giscusConfig.origin);
});
this.switchThemeEventSet.add(this._giscusOnSwitchTheme);
@@ -1031,7 +1064,7 @@ class FixIt {
this._messageListener = (event) => {
if (event.origin !== giscusConfig.origin) return;
const $script = document.querySelector('#giscus>script');
- if ($script){
+ if ($script) {
$script.parentElement.removeChild($script);
}
this._giscusOnSwitchTheme()
@@ -1089,8 +1122,8 @@ class FixIt {
navigator.serviceWorker
.ready
.then(function (registration) {
- // console.log('Service Worker Ready');
- });
+ // console.log('Service Worker Ready');
+ });
}
}
@@ -1206,7 +1239,7 @@ class FixIt {
const scrollTop = Number(window.sessionStorage?.getItem(`fixit-bookmark/#${location.pathname}`));
// If the page opens with a specific hash, just jump out
if (scrollTop && location.hash === '') {
- window.scrollTo({
+ window.scrollTo({
top: scrollTop,
behavior: 'smooth'
});
@@ -1253,7 +1286,7 @@ class FixIt {
if (targetFrame) {
window.postChatUser.setPostChatTheme(isDark ? 'dark' : 'light');
} else {
- postChat_theme = isDark ? 'dark' : 'light';
+ postChat_theme = isDark ? 'dark' : 'light';
}
});
}
diff --git a/assets/js/util.js b/assets/js/util.js
index f6586cb9..57c9344e 100644
--- a/assets/js/util.js
+++ b/assets/js/util.js
@@ -20,7 +20,7 @@ export default class Util {
}
isTocStatic() {
- return window.matchMedia('only screen and (max-width: 960px)').matches;
+ return document.getElementById('toc-static').dataset.kept === 'true' || window.matchMedia('only screen and (max-width: 960px)').matches;
}
/**
diff --git a/layouts/_partials/init/index.html b/layouts/_partials/init/index.html
index 2a62b4fc..f1983b4d 100644
--- a/layouts/_partials/init/index.html
+++ b/layouts/_partials/init/index.html
@@ -1,4 +1,4 @@
-{{- hugo.Store.Set "version" "v0.4.0-alpha.3-20251119064345-c8223a75" -}}
+{{- hugo.Store.Set "version" "v0.4.0-alpha.3-20251119082959-b78f22b6" -}}
{{- .Store.Set "this" dict -}}
{{- partial "init/detection-env.html" . -}}
diff --git a/layouts/_partials/widgets.html b/layouts/_partials/widgets.html
index 9c303850..81786064 100644
--- a/layouts/_partials/widgets.html
+++ b/layouts/_partials/widgets.html
@@ -18,7 +18,7 @@
{{- end -}}
{{- /* toc dialog button */ -}}
{{- if .Page.Store.Get "enableTocDialog" -}}
-