diff --git a/docs/.vitepress/config.mts b/docs/.vitepress/config.mts
index 019284713..f5a7cb5ec 100644
--- a/docs/.vitepress/config.mts
+++ b/docs/.vitepress/config.mts
@@ -122,7 +122,9 @@ export default defineConfig({
output: ['console', 'terminal']
}),
UnoCSS({
- configFile: '../unocss.config.ts'
+ configFile: fileURLToPath(
+ new URL('../../unocss.config.ts', import.meta.url)
+ )
}),
AutoImport({
dts: '../.cache/imports.d.ts',
diff --git a/docs/.vitepress/theme/components/ThemeDropdown.vue b/docs/.vitepress/theme/components/ThemeDropdown.vue
index e7f2d6994..edc480ea7 100644
--- a/docs/.vitepress/theme/components/ThemeDropdown.vue
+++ b/docs/.vitepress/theme/components/ThemeDropdown.vue
@@ -1,12 +1,11 @@
-
-
-
-
-
-
+
+
+
+
+
+
+
diff --git a/docs/.vitepress/theme/components/VPLocalSearchBox.vue b/docs/.vitepress/theme/components/VPLocalSearchBox.vue
index 77bf06034..372f632e4 100644
--- a/docs/.vitepress/theme/components/VPLocalSearchBox.vue
+++ b/docs/.vitepress/theme/components/VPLocalSearchBox.vue
@@ -317,11 +317,16 @@ debouncedWatch(
await mergeNearbyMarks()
}
- const excerpts = Array.from(el.value?.querySelectorAll('.result .excerpt') ?? [])
+ const excerpts = Array.from(el.value?.querySelectorAll('.result .excerpt') ?? []) as HTMLElement[]
for (const excerpt of excerpts) {
- excerpt
- .querySelector('mark[data-markjs="true"]')
- ?.scrollIntoView({ block: 'center' })
+ const mark = excerpt.querySelector('mark[data-markjs="true"]') as HTMLElement | null
+ if (mark) {
+ const markTop = mark.offsetTop
+ const markHeight = mark.offsetHeight
+ const excerptHeight = excerpt.clientHeight
+
+ excerpt.scrollTop = markTop - excerptHeight / 2 + markHeight / 2
+ }
}
/**
@@ -343,8 +348,10 @@ debouncedWatch(
resultMarks.value = newResultMarks
currentMarkIndex.value = newCurrentMarkIndex
- // FIXME: without this whole page scrolls to the bottom
- resultsEl.value?.firstElementChild?.scrollIntoView({ block: 'start' })
+ // Reset scroll position to top
+ if (resultsEl.value) {
+ resultsEl.value.scrollTop = 0
+ }
},
{ debounce: 200, immediate: true }
)
diff --git a/docs/.vitepress/theme/index.ts b/docs/.vitepress/theme/index.ts
index 33b5ce876..379a302f3 100644
--- a/docs/.vitepress/theme/index.ts
+++ b/docs/.vitepress/theme/index.ts
@@ -38,6 +38,35 @@ export default {
app.component('Feedback', Feedback)
app.component('Tooltip', Tooltip)
loadProgress(router)
+
+ if (typeof window !== 'undefined') {
+ const originalBefore = router.onBeforeRouteChange
+ const originalAfter = router.onAfterRouteChanged
+
+ router.onBeforeRouteChange = (to) => {
+ try {
+ // Force scroll-behavior: auto (instant) when changing pages (path),
+ // preventing the "scroll to top" animation.
+ // Smooth scrolling is preserved for same-page hash/anchor changes.
+ const targetUrl = new URL(to, window.location.href)
+ if (targetUrl.pathname !== window.location.pathname) {
+ document.documentElement.style.scrollBehavior = 'auto'
+ }
+ } catch (e) {
+ // Fallback if URL parsing fails
+ }
+ originalBefore?.(to)
+ }
+
+ router.onAfterRouteChanged = (to) => {
+ originalAfter?.(to)
+ // Re-enable smooth scrolling shortly after navigation completes
+ setTimeout(() => {
+ document.documentElement.style.scrollBehavior = 'smooth'
+ }, 1)
+ }
+ }
+
// Initialize theme handler
useThemeHandler()
}
diff --git a/docs/.vitepress/theme/style.scss b/docs/.vitepress/theme/style.scss
index 950792f78..4c7f4db4f 100644
--- a/docs/.vitepress/theme/style.scss
+++ b/docs/.vitepress/theme/style.scss
@@ -1,4 +1,9 @@
+body {
+ transition: background-color 0.5s cubic-bezier(0.25, 0.8, 0.25, 1), color 0.5s cubic-bezier(0.25, 0.8, 0.25, 1);
+}
+
:root {
+ scroll-behavior: smooth;
/* Colors: Brand */
--vp-c-brand-1: theme('colors.swarm.500');
--vp-c-brand-2: theme('colors.swarm.600');
@@ -399,4 +404,52 @@ .VPLocalSearchBox .backdrop {
.dark .VPLocalSearchBox .backdrop {
background: rgba(0, 0, 0, 0.6);
+}
+
+/* Glassy Tooltips */
+.v-popper--theme-tooltip .v-popper__inner {
+ background: var(--vp-c-bg-elv) !important;
+ backdrop-filter: blur(2px);
+ -webkit-backdrop-filter: blur(2px);
+ border: 1px solid var(--vp-c-divider) !important;
+ color: var(--vp-c-text-1) !important;
+ box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15) !important;
+}
+
+.v-popper--theme-tooltip .v-popper__arrow {
+ display: none !important;
+}
+
+/* Global theme styles for the dropdown to ensure correct box appearance */
+.v-popper--theme-theme-selector .v-popper__inner {
+ background: var(--vp-c-bg-elv);
+ backdrop-filter: blur(12px);
+ -webkit-backdrop-filter: blur(12px);
+ border: 1px solid var(--vp-c-divider);
+ border-radius: 8px;
+ box-shadow: 0 4px 16px rgba(0, 0, 0, 0.1);
+ padding: 6px;
+ color: var(--vp-c-text-1);
+}
+
+.dark .v-popper--theme-theme-selector .v-popper__inner {
+ box-shadow: 0 4px 16px rgba(0, 0, 0, 0.3);
+}
+
+.v-popper--theme-theme-selector .v-popper__arrow {
+ display: none;
+}
+
+/* Override VPFlyout hover behavior when manually controlled */
+.VPFlyout.click-based-flyout:hover .menu {
+ opacity: 0 !important;
+ visibility: hidden !important;
+ transform: translateY(0) !important;
+ /* Reset transform if any */
+}
+
+.VPFlyout.click-based-flyout.open .menu {
+ opacity: 1 !important;
+ visibility: visible !important;
+ transform: translateY(0) !important;
}
\ No newline at end of file
diff --git a/docs/.vitepress/theme/themes/themeHandler.ts b/docs/.vitepress/theme/themes/themeHandler.ts
index c11c1601d..b74648d19 100644
--- a/docs/.vitepress/theme/themes/themeHandler.ts
+++ b/docs/.vitepress/theme/themes/themeHandler.ts
@@ -106,15 +106,21 @@ export class ThemeHandler {
private applyDOMClasses(mode: DisplayMode) {
const root = document.documentElement
- // Remove all mode classes
- root.classList.remove('dark', 'light', 'amoled')
+ const isDark = mode === 'dark'
+ const isAmoled = isDark && this.amoledEnabled.value
- // Add current mode class
- root.classList.add(mode)
+ if (isDark) {
+ if (!root.classList.contains('dark')) root.classList.add('dark')
+ if (root.classList.contains('light')) root.classList.remove('light')
+ } else {
+ if (!root.classList.contains('light')) root.classList.add('light')
+ if (root.classList.contains('dark')) root.classList.remove('dark')
+ }
- // Add amoled class if enabled in dark mode
- if (mode === 'dark' && this.amoledEnabled.value) {
- root.classList.add('amoled')
+ if (isAmoled) {
+ if (!root.classList.contains('amoled')) root.classList.add('amoled')
+ } else {
+ if (root.classList.contains('amoled')) root.classList.remove('amoled')
}
}
@@ -124,12 +130,12 @@ export class ThemeHandler {
const root = document.documentElement
// Clear ALL inline styles related to theming to ensure clean slate
- const allStyleProps = Array.from(root.style)
- allStyleProps.forEach(prop => {
- if (prop.startsWith('--vp-')) {
- root.style.removeProperty(prop)
- }
- })
+ // const allStyleProps = Array.from(root.style)
+ // allStyleProps.forEach(prop => {
+ // if (prop.startsWith('--vp-')) {
+ // root.style.removeProperty(prop)
+ // }
+ // })
let bgColor = colors.bg
let bgAltColor = colors.bgAlt
let bgElvColor = colors.bgElv
@@ -296,6 +302,14 @@ export class ThemeHandler {
this.setMode(newMode)
}
+ public setAppearance(mode: DisplayMode, amoled: boolean) {
+ this.state.value.currentMode = mode
+ this.amoledEnabled.value = amoled
+ localStorage.setItem(STORAGE_KEY_MODE, mode)
+ localStorage.setItem(STORAGE_KEY_AMOLED, amoled.toString())
+ this.applyTheme()
+ }
+
public setAmoledEnabled(enabled: boolean) {
this.amoledEnabled.value = enabled
localStorage.setItem(STORAGE_KEY_AMOLED, enabled.toString())
@@ -332,7 +346,6 @@ export class ThemeHandler {
public getState() {
return this.state
}
-
public getMode() {
return this.state.value.currentMode
}
@@ -395,6 +408,7 @@ export function useTheme() {
amoledEnabled: handler.getAmoledEnabledRef(),
setAmoledEnabled: (enabled: boolean) => handler.setAmoledEnabled(enabled),
toggleAmoled: () => handler.toggleAmoled(),
+ setAppearance: (mode: DisplayMode, amoled: boolean) => handler.setAppearance(mode, amoled),
state
}
}
\ No newline at end of file
diff --git a/unocss.config.ts b/unocss.config.ts
index 001b1f731..2d0160f43 100644
--- a/unocss.config.ts
+++ b/unocss.config.ts
@@ -59,7 +59,7 @@ const createColorRules = (type: 'text' | 'bg' | 'border'): Rule[] => {
export default defineConfig({
content: {
- filesystem: ['.vitepress/config.mts', '.vitepress/constants.ts']
+ filesystem: ['.vitepress/config.mts', '.vitepress/constants.ts', '.vitepress/shared.ts']
},
theme: {
colors: {