mirror of
https://github.com/fmhy/edit.git
synced 2026-03-11 08:55:38 +00:00
UI fix (#4694)
* blur to social media tooltip * fix theme selector * better transitions when switching to and from amoled * fix mobile search scrolling down * scroll instead of instant when navigating to section * fix unocss in dev mode * fix unocss in dev mode, again * prevent scroll transition when changing pages
This commit is contained in:
parent
e7fe537a73
commit
a6114818b9
7 changed files with 225 additions and 86 deletions
|
|
@ -122,7 +122,9 @@ export default defineConfig({
|
||||||
output: ['console', 'terminal']
|
output: ['console', 'terminal']
|
||||||
}),
|
}),
|
||||||
UnoCSS({
|
UnoCSS({
|
||||||
configFile: '../unocss.config.ts'
|
configFile: fileURLToPath(
|
||||||
|
new URL('../../unocss.config.ts', import.meta.url)
|
||||||
|
)
|
||||||
}),
|
}),
|
||||||
AutoImport({
|
AutoImport({
|
||||||
dts: '../.cache/imports.d.ts',
|
dts: '../.cache/imports.d.ts',
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,11 @@
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ref, computed, onMounted, onUnmounted } from 'vue'
|
import { computed, ref, onMounted, onUnmounted } from 'vue'
|
||||||
import { useTheme } from '../themes/themeHandler'
|
import { useTheme } from '../themes/themeHandler'
|
||||||
import type { DisplayMode } from '../themes/types'
|
import type { DisplayMode } from '../themes/types'
|
||||||
|
|
||||||
const { mode, setMode, state, amoledEnabled, setAmoledEnabled } = useTheme()
|
const { mode, amoledEnabled, setAppearance } = useTheme()
|
||||||
|
|
||||||
const isOpen = ref(false)
|
const wrapperRef = ref<HTMLElement | null>(null)
|
||||||
const dropdownRef = ref<HTMLElement | null>(null)
|
|
||||||
|
|
||||||
interface ModeChoice {
|
interface ModeChoice {
|
||||||
mode: DisplayMode
|
mode: DisplayMode
|
||||||
|
|
@ -29,19 +28,12 @@ const currentChoice = computed(() => {
|
||||||
return modeChoices.find(choice => choice.mode === current && !choice.isAmoled) || modeChoices[0]
|
return modeChoices.find(choice => choice.mode === current && !choice.isAmoled) || modeChoices[0]
|
||||||
})
|
})
|
||||||
|
|
||||||
const toggleDropdown = () => {
|
|
||||||
isOpen.value = !isOpen.value
|
|
||||||
}
|
|
||||||
|
|
||||||
const selectMode = (choice: ModeChoice) => {
|
const selectMode = (choice: ModeChoice) => {
|
||||||
if (choice.isAmoled) {
|
if (choice.isAmoled) {
|
||||||
setMode('dark')
|
setAppearance('dark', true)
|
||||||
setAmoledEnabled(true)
|
|
||||||
} else {
|
} else {
|
||||||
setMode(choice.mode)
|
setAppearance(choice.mode, false)
|
||||||
setAmoledEnabled(false)
|
|
||||||
}
|
}
|
||||||
isOpen.value = false
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const isActiveChoice = (choice: ModeChoice) => {
|
const isActiveChoice = (choice: ModeChoice) => {
|
||||||
|
|
@ -52,56 +44,112 @@ const isActiveChoice = (choice: ModeChoice) => {
|
||||||
return choice.mode === current && !choice.isAmoled && !amoledEnabled.value
|
return choice.mode === current && !choice.isAmoled && !amoledEnabled.value
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleClickOutside = (event: MouseEvent) => {
|
// Logic to override the parent VPFlyout behavior to be click-based
|
||||||
if (dropdownRef.value && !dropdownRef.value.contains(event.target as Node)) {
|
const setupParentFlyoutOverride = () => {
|
||||||
isOpen.value = false
|
if (!wrapperRef.value) return
|
||||||
|
|
||||||
|
const flyout = wrapperRef.value.closest('.VPFlyout')
|
||||||
|
if (!flyout) return
|
||||||
|
|
||||||
|
// Add class to disable CSS hover via global style
|
||||||
|
flyout.classList.add('click-based-flyout')
|
||||||
|
|
||||||
|
// Find the toggle button
|
||||||
|
const button = flyout.querySelector('button')
|
||||||
|
if (!button) return
|
||||||
|
|
||||||
|
// Click handler for toggle
|
||||||
|
const toggleFlyout = (e: MouseEvent) => {
|
||||||
|
flyout.classList.toggle('open')
|
||||||
|
}
|
||||||
|
|
||||||
|
button.addEventListener('click', toggleFlyout)
|
||||||
|
|
||||||
|
// Global click listener to close when clicking outside
|
||||||
|
const closeFlyout = (e: MouseEvent) => {
|
||||||
|
if (!flyout.contains(e.target as Node)) {
|
||||||
|
flyout.classList.remove('open')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
document.addEventListener('click', closeFlyout)
|
||||||
|
|
||||||
|
;(wrapperRef.value as any)._cleanup = () => {
|
||||||
|
flyout.classList.remove('click-based-flyout')
|
||||||
|
button.removeEventListener('click', toggleFlyout)
|
||||||
|
document.removeEventListener('click', closeFlyout)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
document.addEventListener('click', handleClickOutside)
|
// defer slightly to ensuring DOM is ready
|
||||||
|
setTimeout(setupParentFlyoutOverride, 100)
|
||||||
})
|
})
|
||||||
|
|
||||||
onUnmounted(() => {
|
onUnmounted(() => {
|
||||||
document.removeEventListener('click', handleClickOutside)
|
if (wrapperRef.value && (wrapperRef.value as any)._cleanup) {
|
||||||
|
;(wrapperRef.value as any)._cleanup()
|
||||||
|
}
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div ref="dropdownRef" class="theme-dropdown">
|
<div ref="wrapperRef" class="theme-dropdown-wrapper">
|
||||||
<button
|
<VDropdown
|
||||||
type="button"
|
class="theme-dropdown"
|
||||||
class="theme-dropdown-toggle"
|
theme="theme-selector"
|
||||||
:title="currentChoice.label"
|
:distance="12"
|
||||||
@click="toggleDropdown"
|
placement="bottom-end"
|
||||||
|
:triggers="['click']"
|
||||||
|
:popper-triggers="['click']"
|
||||||
|
:auto-hide="true"
|
||||||
>
|
>
|
||||||
<ClientOnly>
|
|
||||||
<div :class="[currentChoice.icon, 'text-xl']" />
|
|
||||||
</ClientOnly>
|
|
||||||
</button>
|
|
||||||
|
|
||||||
<Transition name="dropdown">
|
|
||||||
<div v-if="isOpen" class="theme-dropdown-menu">
|
|
||||||
<button
|
<button
|
||||||
v-for="(choice, index) in modeChoices"
|
type="button"
|
||||||
:key="index"
|
class="theme-dropdown-toggle"
|
||||||
class="theme-dropdown-item"
|
:title="currentChoice.label"
|
||||||
:class="{ active: isActiveChoice(choice) }"
|
|
||||||
@click="selectMode(choice)"
|
|
||||||
>
|
>
|
||||||
<div :class="[choice.icon, 'text-lg']" />
|
<ClientOnly>
|
||||||
<span>{{ choice.label }}</span>
|
<Transition name="fade" mode="out-in">
|
||||||
<div v-if="isActiveChoice(choice)" class="i-ph-check text-lg ml-auto" />
|
<div :key="currentChoice.label" :class="[currentChoice.icon, 'text-xl']" />
|
||||||
|
</Transition>
|
||||||
|
</ClientOnly>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
|
||||||
</Transition>
|
<template #popper>
|
||||||
|
<div class="theme-dropdown-content">
|
||||||
|
<button
|
||||||
|
v-for="(choice, index) in modeChoices"
|
||||||
|
:key="index"
|
||||||
|
class="theme-dropdown-item"
|
||||||
|
:class="{ active: isActiveChoice(choice) }"
|
||||||
|
@click="selectMode(choice)"
|
||||||
|
v-close-popper
|
||||||
|
>
|
||||||
|
<Transition name="fade" mode="out-in">
|
||||||
|
<div :key="choice.label" :class="[choice.icon, 'text-lg']" />
|
||||||
|
</Transition>
|
||||||
|
<span>{{ choice.label }}</span>
|
||||||
|
<div v-if="isActiveChoice(choice)" class="i-ph-check text-lg ml-auto" />
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</VDropdown>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
|
.theme-dropdown-wrapper {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
.theme-dropdown {
|
.theme-dropdown {
|
||||||
position: relative;
|
display: flex;
|
||||||
display: inline-block;
|
align-items: center;
|
||||||
|
height: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.theme-dropdown-toggle {
|
.theme-dropdown-toggle {
|
||||||
|
|
@ -121,25 +169,12 @@ onUnmounted(() => {
|
||||||
color: var(--vp-c-text-1);
|
color: var(--vp-c-text-1);
|
||||||
background: var(--vp-c-bg-elv);
|
background: var(--vp-c-bg-elv);
|
||||||
transition: color 0.25s, background 0.25s;
|
transition: color 0.25s, background 0.25s;
|
||||||
|
backdrop-filter: blur(12px);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.theme-dropdown-menu {
|
.theme-dropdown-content {
|
||||||
position: absolute;
|
|
||||||
top: calc(100% + 8px);
|
|
||||||
right: 0;
|
|
||||||
min-width: 180px;
|
min-width: 180px;
|
||||||
background: var(--vp-c-bg-elv);
|
|
||||||
border: 1px solid var(--vp-c-divider);
|
|
||||||
border-radius: 8px;
|
|
||||||
box-shadow: 0 4px 16px rgba(0, 0, 0, 0.1);
|
|
||||||
padding: 6px;
|
|
||||||
z-index: 1000;
|
|
||||||
backdrop-filter: blur(12px);
|
|
||||||
|
|
||||||
.dark & {
|
|
||||||
box-shadow: 0 4px 16px rgba(0, 0, 0, 0.3);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.theme-dropdown-item {
|
.theme-dropdown-item {
|
||||||
|
|
@ -171,14 +206,13 @@ onUnmounted(() => {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.dropdown-enter-active,
|
.fade-enter-active,
|
||||||
.dropdown-leave-active {
|
.fade-leave-active {
|
||||||
transition: opacity 0.15s ease, transform 0.15s ease;
|
transition: opacity 0.25s ease;
|
||||||
}
|
}
|
||||||
|
|
||||||
.dropdown-enter-from,
|
.fade-enter-from,
|
||||||
.dropdown-leave-to {
|
.fade-leave-to {
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
transform: translateY(-8px);
|
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
||||||
|
|
@ -317,11 +317,16 @@ debouncedWatch(
|
||||||
await mergeNearbyMarks()
|
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) {
|
for (const excerpt of excerpts) {
|
||||||
excerpt
|
const mark = excerpt.querySelector('mark[data-markjs="true"]') as HTMLElement | null
|
||||||
.querySelector('mark[data-markjs="true"]')
|
if (mark) {
|
||||||
?.scrollIntoView({ block: 'center' })
|
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
|
resultMarks.value = newResultMarks
|
||||||
currentMarkIndex.value = newCurrentMarkIndex
|
currentMarkIndex.value = newCurrentMarkIndex
|
||||||
|
|
||||||
// FIXME: without this whole page scrolls to the bottom
|
// Reset scroll position to top
|
||||||
resultsEl.value?.firstElementChild?.scrollIntoView({ block: 'start' })
|
if (resultsEl.value) {
|
||||||
|
resultsEl.value.scrollTop = 0
|
||||||
|
}
|
||||||
},
|
},
|
||||||
{ debounce: 200, immediate: true }
|
{ debounce: 200, immediate: true }
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -38,6 +38,35 @@ export default {
|
||||||
app.component('Feedback', Feedback)
|
app.component('Feedback', Feedback)
|
||||||
app.component('Tooltip', Tooltip)
|
app.component('Tooltip', Tooltip)
|
||||||
loadProgress(router)
|
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
|
// Initialize theme handler
|
||||||
useThemeHandler()
|
useThemeHandler()
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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 {
|
:root {
|
||||||
|
scroll-behavior: smooth;
|
||||||
/* Colors: Brand */
|
/* Colors: Brand */
|
||||||
--vp-c-brand-1: theme('colors.swarm.500');
|
--vp-c-brand-1: theme('colors.swarm.500');
|
||||||
--vp-c-brand-2: theme('colors.swarm.600');
|
--vp-c-brand-2: theme('colors.swarm.600');
|
||||||
|
|
@ -399,4 +404,52 @@ .VPLocalSearchBox .backdrop {
|
||||||
|
|
||||||
.dark .VPLocalSearchBox .backdrop {
|
.dark .VPLocalSearchBox .backdrop {
|
||||||
background: rgba(0, 0, 0, 0.6);
|
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;
|
||||||
}
|
}
|
||||||
|
|
@ -106,15 +106,21 @@ export class ThemeHandler {
|
||||||
private applyDOMClasses(mode: DisplayMode) {
|
private applyDOMClasses(mode: DisplayMode) {
|
||||||
const root = document.documentElement
|
const root = document.documentElement
|
||||||
|
|
||||||
// Remove all mode classes
|
const isDark = mode === 'dark'
|
||||||
root.classList.remove('dark', 'light', 'amoled')
|
const isAmoled = isDark && this.amoledEnabled.value
|
||||||
|
|
||||||
// Add current mode class
|
if (isDark) {
|
||||||
root.classList.add(mode)
|
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 (isAmoled) {
|
||||||
if (mode === 'dark' && this.amoledEnabled.value) {
|
if (!root.classList.contains('amoled')) root.classList.add('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
|
const root = document.documentElement
|
||||||
|
|
||||||
// Clear ALL inline styles related to theming to ensure clean slate
|
// Clear ALL inline styles related to theming to ensure clean slate
|
||||||
const allStyleProps = Array.from(root.style)
|
// const allStyleProps = Array.from(root.style)
|
||||||
allStyleProps.forEach(prop => {
|
// allStyleProps.forEach(prop => {
|
||||||
if (prop.startsWith('--vp-')) {
|
// if (prop.startsWith('--vp-')) {
|
||||||
root.style.removeProperty(prop)
|
// root.style.removeProperty(prop)
|
||||||
}
|
// }
|
||||||
})
|
// })
|
||||||
let bgColor = colors.bg
|
let bgColor = colors.bg
|
||||||
let bgAltColor = colors.bgAlt
|
let bgAltColor = colors.bgAlt
|
||||||
let bgElvColor = colors.bgElv
|
let bgElvColor = colors.bgElv
|
||||||
|
|
@ -296,6 +302,14 @@ export class ThemeHandler {
|
||||||
this.setMode(newMode)
|
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) {
|
public setAmoledEnabled(enabled: boolean) {
|
||||||
this.amoledEnabled.value = enabled
|
this.amoledEnabled.value = enabled
|
||||||
localStorage.setItem(STORAGE_KEY_AMOLED, enabled.toString())
|
localStorage.setItem(STORAGE_KEY_AMOLED, enabled.toString())
|
||||||
|
|
@ -332,7 +346,6 @@ export class ThemeHandler {
|
||||||
public getState() {
|
public getState() {
|
||||||
return this.state
|
return this.state
|
||||||
}
|
}
|
||||||
|
|
||||||
public getMode() {
|
public getMode() {
|
||||||
return this.state.value.currentMode
|
return this.state.value.currentMode
|
||||||
}
|
}
|
||||||
|
|
@ -395,6 +408,7 @@ export function useTheme() {
|
||||||
amoledEnabled: handler.getAmoledEnabledRef(),
|
amoledEnabled: handler.getAmoledEnabledRef(),
|
||||||
setAmoledEnabled: (enabled: boolean) => handler.setAmoledEnabled(enabled),
|
setAmoledEnabled: (enabled: boolean) => handler.setAmoledEnabled(enabled),
|
||||||
toggleAmoled: () => handler.toggleAmoled(),
|
toggleAmoled: () => handler.toggleAmoled(),
|
||||||
|
setAppearance: (mode: DisplayMode, amoled: boolean) => handler.setAppearance(mode, amoled),
|
||||||
state
|
state
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -59,7 +59,7 @@ const createColorRules = (type: 'text' | 'bg' | 'border'): Rule[] => {
|
||||||
|
|
||||||
export default defineConfig({
|
export default defineConfig({
|
||||||
content: {
|
content: {
|
||||||
filesystem: ['.vitepress/config.mts', '.vitepress/constants.ts']
|
filesystem: ['.vitepress/config.mts', '.vitepress/constants.ts', '.vitepress/shared.ts']
|
||||||
},
|
},
|
||||||
theme: {
|
theme: {
|
||||||
colors: {
|
colors: {
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue