diff --git a/docs/themes/default/extending.md b/docs/themes/default/extending.md index c4b45f2504..857d3ae34a 100644 --- a/docs/themes/default/extending.md +++ b/docs/themes/default/extending.md @@ -87,7 +87,7 @@ export default defineUserConfig({ ## Modifying Behavior -Most of the core behaviors of the default theme have been extracted into a composable API, and also provide [aliases](https://v2.vuepress.vuejs.org/zh/reference/plugin-api.html#alias) with the `@theme` prefix. +Most of the core behaviors of the default theme have been extracted into a composable API or util function, and also provide [aliases](https://v2.vuepress.vuejs.org/zh/reference/plugin-api.html#alias) with the `@theme` prefix. For example, if you want to add some default values ​​to the theme data of the default theme, you can override the `useThemeData` function of `@theme/useThemeData`. diff --git a/docs/tools/helper/shared.md b/docs/tools/helper/shared.md index bd0ba49636..310a3f31b1 100644 --- a/docs/tools/helper/shared.md +++ b/docs/tools/helper/shared.md @@ -190,6 +190,7 @@ Return `false` if `a` is not a string. - `isLinkWithProtocol(x)`: Check if `x` is a valid URL with protocol. - `isLinkExternal(x)`: Check if `x` is a valid external URL. - `isLinkAbsolute(x)`: Check if `x` is a valid absolute URL. +- `isLinkRelative(x)`: Check if `x` is not absolute or external URL. - `ensureEndingSlash(x)`: Ensure `x` ends with a slash. - `ensureLeadingSlash(x)`: Ensure `x` starts with a slash. - `removeEndingSlash(x)`: Ensure `x` does not end with a slash. diff --git a/docs/zh/themes/default/extending.md b/docs/zh/themes/default/extending.md index 3c409790d8..1228ef1a66 100644 --- a/docs/zh/themes/default/extending.md +++ b/docs/zh/themes/default/extending.md @@ -87,7 +87,7 @@ export default defineUserConfig({ ## 修改行为 -默认主题的核心行为大多都被抽离成可组合式 API,并同样提供了 `@theme` 前缀的 [alias](https://v2.vuepress.vuejs.org/zh/reference/plugin-api.html#alias)。 +默认主题的核心行为大多都被抽离成可组合式 API 或工具函数,并同样提供了 `@theme` 前缀的 [alias](https://v2.vuepress.vuejs.org/zh/reference/plugin-api.html#alias)。 比如,如果你想为默认主题的主题数据添加一些默认值,你可以通过覆盖 `@theme/useThemeData` 的 `useThemeData` 函数来实现。 diff --git a/docs/zh/tools/helper/shared.md b/docs/zh/tools/helper/shared.md index 59073fb577..f403a3374e 100644 --- a/docs/zh/tools/helper/shared.md +++ b/docs/zh/tools/helper/shared.md @@ -186,6 +186,7 @@ encodeURIComponent(content) // '%0A%7B%0A%20%20%22type%22%3A%20%22bar%22%2C%0A%2 - `isLinkWithProtocol(x)`: x 是否是有效的带有协议的 URL。 - `isLinkExternal(x)`: x 是否是有效的外部 URL。 - `isLinkAbsolute(x)`: x 是否是有效的绝对 URL。 +- `isLinkRelative(x)`: x 是否不是外部 URL 或绝对 URL。 - `ensureEndingSlash(x)`: 确保 x 以斜杠结尾。 - `ensureLeadingSlash(x)`: 确保 x 以斜杠开头。 - `removeEndingSlash(x)`: 确保 x 不以斜杠结尾。 diff --git a/plugins/features/plugin-icon/src/client/components/VPIcon.ts b/plugins/features/plugin-icon/src/client/components/VPIcon.ts index 1bbdccd784..79be80a223 100644 --- a/plugins/features/plugin-icon/src/client/components/VPIcon.ts +++ b/plugins/features/plugin-icon/src/client/components/VPIcon.ts @@ -137,7 +137,7 @@ export const VPIcon = defineComponent({ }) } - return h('span', { + return h('i', { key: icon, class: [ 'vp-icon', diff --git a/plugins/features/plugin-icon/src/client/styles/vp-icon.scss b/plugins/features/plugin-icon/src/client/styles/vp-icon.scss index 5afe1c75d5..cee2703696 100644 --- a/plugins/features/plugin-icon/src/client/styles/vp-icon.scss +++ b/plugins/features/plugin-icon/src/client/styles/vp-icon.scss @@ -3,6 +3,11 @@ width: 1em; height: 1em; + &:is(i) { + font-size: 1em; + line-height: 1; + } + &:not(iconify-icon, i) { vertical-align: -0.125em; } diff --git a/themes/theme-default/src/client/composables/useNavbarConfig.ts b/themes/theme-default/src/client/composables/useNavbarConfig.ts index c5ce00576c..979e652324 100644 --- a/themes/theme-default/src/client/composables/useNavbarConfig.ts +++ b/themes/theme-default/src/client/composables/useNavbarConfig.ts @@ -1,7 +1,7 @@ -import { isLinkInternal } from '@theme/isLinkInternal' import { resolveAutoLink } from '@theme/resolveAutoLink' import { resolvePrefix } from '@theme/resolvePrefix' import { useThemeLocaleData } from '@theme/useThemeData' +import { isLinkRelative } from '@vuepress/helper/client' import type { ComputedRef } from 'vue' import { computed } from 'vue' import { isString } from 'vuepress/shared' @@ -30,7 +30,7 @@ const resolveNavbarItem = ( return { ...item, - link: isLinkInternal(item.link) + link: isLinkRelative(item.link) ? resolveAutoLink(resolvePrefix(prefix, item.link)).link : item.link, } diff --git a/themes/theme-default/src/client/composables/useSidebarItems.ts b/themes/theme-default/src/client/composables/useSidebarItems.ts index 79899f3f86..c0c1ebc39e 100644 --- a/themes/theme-default/src/client/composables/useSidebarItems.ts +++ b/themes/theme-default/src/client/composables/useSidebarItems.ts @@ -1,9 +1,13 @@ -import { isLinkInternal } from '@theme/isLinkInternal' import { resolveAutoLink } from '@theme/resolveAutoLink' import { resolvePrefix } from '@theme/resolvePrefix' import { useThemeLocaleData } from '@theme/useThemeData' import type { MenuItem } from '@vuepress/helper/client' -import { getHeaders, keys, startsWith } from '@vuepress/helper/client' +import { + getHeaders, + isLinkRelative, + keys, + startsWith, +} from '@vuepress/helper/client' import type { ComputedRef, InjectionKey, Ref } from 'vue' import { computed, inject, onMounted, provide, ref, watch } from 'vue' import type { PageData } from 'vuepress/client' @@ -110,7 +114,7 @@ export const resolveArraySidebarItems = ( : isString(item.link) ? { ...item, - link: isLinkInternal(item.link) + link: isLinkRelative(item.link) ? resolveAutoLink(resolvePrefix(pathPrefix, item.link)).link : item.link, } diff --git a/themes/theme-default/src/client/utils/index.ts b/themes/theme-default/src/client/utils/index.ts index 928bac4865..5069fa58f3 100644 --- a/themes/theme-default/src/client/utils/index.ts +++ b/themes/theme-default/src/client/utils/index.ts @@ -1,5 +1,4 @@ export * from './isActiveSidebarItem.js' -export * from './isLinkInternal.js' export * from './resolveAutoLink.js' export * from './resolveEditLink.js' export * from './resolvePrefix.js' diff --git a/themes/theme-default/src/client/utils/isLinkInternal.ts b/themes/theme-default/src/client/utils/isLinkInternal.ts deleted file mode 100644 index c0cef97b0c..0000000000 --- a/themes/theme-default/src/client/utils/isLinkInternal.ts +++ /dev/null @@ -1,4 +0,0 @@ -import { isLinkExternal, isLinkWithProtocol } from 'vuepress/shared' - -export const isLinkInternal = (link: string): boolean => - !isLinkExternal(link) && !isLinkWithProtocol(link) diff --git a/tools/helper/src/shared/link.ts b/tools/helper/src/shared/link.ts index c0fe9b0022..7db1388d8c 100644 --- a/tools/helper/src/shared/link.ts +++ b/tools/helper/src/shared/link.ts @@ -1,3 +1,4 @@ +import { isLinkExternal, isLinkWithProtocol } from 'vuepress/shared' import { startsWith } from './helper.js' export { isLinkExternal, isLinkHttp, isLinkWithProtocol } from 'vuepress/shared' @@ -5,4 +6,8 @@ export { isLinkExternal, isLinkHttp, isLinkWithProtocol } from 'vuepress/shared' /** * Check if a value is a valid absolute url */ -export const isLinkAbsolute = (test: unknown): boolean => startsWith(test, '/') +export const isLinkAbsolute = (test: unknown): boolean => + startsWith(test, '/') && (test as string)[1] !== '/' + +export const isLinkRelative = (link: string): boolean => + !isLinkExternal(link) && !isLinkWithProtocol(link) diff --git a/tools/highlighter-helper/src/client/styles/collapsed-lines.scss b/tools/highlighter-helper/src/client/styles/collapsed-lines.scss index 1e300c526b..c294f1e724 100644 --- a/tools/highlighter-helper/src/client/styles/collapsed-lines.scss +++ b/tools/highlighter-helper/src/client/styles/collapsed-lines.scss @@ -1,16 +1,29 @@ /* stylelint-disable scss/operator-no-newline-after */ -// collapsed lines -div[class*='language-'].has-collapsed-lines { - &.collapsed { - overflow-y: hidden; - height: calc( - var(--vp-collapsed-lines) * var(--code-line-height) * - var(--code-font-size) + var(--code-padding-y) + 28px - ); + +@property --vp-collapsed-lines-bg { + inherits: false; + initial-value: #fff; + syntax: ''; +} + +@keyframes code-collapsed-lines { + 0% { + opacity: 0.3; + transform: translateY(-2px) rotate(var(--vp-collapsed-lines-rotate)); } + 100% { + opacity: 1; + transform: translateY(2px) rotate(var(--vp-collapsed-lines-rotate)); + } +} + +// collapsed lines +div[class*='language-'].has-collapsed-lines { .collapsed-lines { --vp-collapsed-lines-bg: var(--code-c-bg); + --vp-collapsed-lines-icon: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='1em' height='1em' viewBox='0 0 24 24'%3E%3Cpath fill='none' stroke='%23000' stroke-width='2' d='m18 12l-6 6l-6-6m12-6l-6 6l-6-6'/%3E%3C/svg%3E"); + --vp-collapsed-lines-rotate: 0deg; position: absolute; right: 0; @@ -36,7 +49,27 @@ div[class*='language-'].has-collapsed-lines { transition: --vp-collapsed-lines-bg var(--vp-t-color); &:hover { - --vp-collapsed-lines-bg: rgb(0 0 0 / 10%) !important; + --vp-collapsed-lines-bg: var(--code-c-highlight-bg); + } + + &::before { + content: ''; + + display: inline-block; + + width: 24px; + height: 24px; + + background-color: var(--code-c-text); + + mask-image: var(--vp-collapsed-lines-icon); + mask-position: 50%; + mask-size: 20px; + mask-repeat: no-repeat; + pointer-events: none; + + animation: code-collapsed-lines 1.2s infinite alternate-reverse + ease-in-out; } } @@ -49,26 +82,12 @@ div[class*='language-'].has-collapsed-lines { } } - .collapsed-lines::before { - --icon: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='1em' height='1em' viewBox='0 0 24 24'%3E%3Cpath fill='none' stroke='%23000' stroke-width='2' d='m18 12l-6 6l-6-6m12-6l-6 6l-6-6'/%3E%3C/svg%3E"); - --vp-collapsed-lines-rotate: 0deg; - - content: ''; - - display: inline-block; - - width: 24px; - height: 24px; - - background-color: var(--code-c-text); - - mask-image: var(--icon); - mask-position: 50%; - mask-size: 20px; - mask-repeat: no-repeat; - pointer-events: none; - - animation: code-collapsed-lines 1.2s infinite alternate-reverse ease-in-out; + &.collapsed { + overflow-y: hidden; + height: calc( + var(--vp-collapsed-lines) * var(--code-line-height) * + var(--code-font-size) + var(--code-padding-y) + 28px + ); } &:not(.collapsed) { @@ -76,30 +95,8 @@ div[class*='language-'].has-collapsed-lines { padding-bottom: max(var(--code-padding-y), 28px); } - .collapsed-lines:hover { - --vp-collapsed-lines-bg: transparent !important; - } - - .collapsed-lines::before { + .collapsed-lines { --vp-collapsed-lines-rotate: 180deg; } } } - -@property --vp-collapsed-lines-bg { - inherits: false; - initial-value: #fff; - syntax: ''; -} - -@keyframes code-collapsed-lines { - 0% { - opacity: 0.3; - transform: translateY(-2px) rotate(var(--vp-collapsed-lines-rotate)); - } - - 100% { - opacity: 1; - transform: translateY(2px) rotate(var(--vp-collapsed-lines-rotate)); - } -}