diff --git a/docs/notes/theme/guide/功能/文章水印.md b/docs/notes/theme/guide/功能/文章水印.md index f95ef450..56c3982e 100644 --- a/docs/notes/theme/guide/功能/文章水印.md +++ b/docs/notes/theme/guide/功能/文章水印.md @@ -32,6 +32,12 @@ export default defineUserConfig({ enabled: page => true, // function 类型 过滤哪些页面启用水印 delay: 500, // 添加水印的延时。以毫秒为单位。 + /** + * 是否全屏水印,默认为 `true`, + * 设置为 `false` 时,水印仅在 内容区域中显示。 + */ + fullPage: true, + /** @see https://zhensherlock.github.io/watermark-js-plus/zh/config/ */ watermarkOptions: { content: 'your watermark', @@ -175,6 +181,28 @@ watermark: --- ``` +## Frontmatter + +主题支持在 md 文件中添加 `frontmatter.watermark` 为单个页面设置水印。 + +```md +--- +watermark: + content: My Custom Content +--- +``` + +支持的配置项请参考:[watermark-js-plus](https://zhensherlock.github.io/watermark-js-plus/zh/config/) + +同时,还额外支持 `fullPage` 控制是否全屏显示。 + +```md +--- +watermark: + fullPage: false +--- +``` + ## 示例 - [内容水印](/article/2z59hh8g/) diff --git a/plugins/plugin-netlify-functions/src/node/netlify/netlifyServer.ts b/plugins/plugin-netlify-functions/src/node/netlify/netlifyServer.ts index b9d1f6bd..37b5f5ea 100644 --- a/plugins/plugin-netlify-functions/src/node/netlify/netlifyServer.ts +++ b/plugins/plugin-netlify-functions/src/node/netlify/netlifyServer.ts @@ -40,7 +40,7 @@ export async function netlifyServe({ // '--debug', ] - const { stdout, cancel } = execa( + const { stdout, kill } = execa( path.resolve(__dirname, '../../../node_modules/.bin/netlify'), argv, { @@ -54,6 +54,6 @@ export async function netlifyServe({ return { host: `http://localhost:${port}`, - close: () => cancel(), + close: () => kill(), } } diff --git a/theme/src/client/composables/waterMark.ts b/theme/src/client/composables/waterMark.ts index 8929e9ed..681ef911 100644 --- a/theme/src/client/composables/waterMark.ts +++ b/theme/src/client/composables/waterMark.ts @@ -3,12 +3,15 @@ import { computed } from 'vue' import { usePageFrontmatter } from 'vuepress/client' import type { PlumeThemePageFrontmatter } from '../../shared/index.js' +declare const __PLUME_WM_FP__: boolean + export function setupWatermark(): void { const frontmatter = usePageFrontmatter() - defineWatermarkConfig(computed(() => ({ - parent: typeof frontmatter.value.watermark === 'object' - ? frontmatter.value.watermark.fullPage === false ? '.plume-content' : 'body' - : 'body', - }))) + defineWatermarkConfig(computed(() => { + const disableFullPage = typeof frontmatter.value.watermark === 'object' && frontmatter.value.watermark.fullPage === false + return { + parent: !__PLUME_WM_FP__ || disableFullPage ? '.plume-content' : 'body', + } + })) } diff --git a/theme/src/node/resolvePageHead.ts b/theme/src/node/resolvePageHead.ts new file mode 100644 index 00000000..b7a48fd8 --- /dev/null +++ b/theme/src/node/resolvePageHead.ts @@ -0,0 +1,31 @@ +import type { Page } from 'vuepress' +import type { PlumeThemeLocaleOptions } from '../shared/index.js' + +export function resolvePageHead(page: Page, localeOptions: PlumeThemeLocaleOptions) { + page.frontmatter.head ??= [] + if (localeOptions.appearance ?? true) { + const appearance = typeof localeOptions.appearance === 'string' + ? localeOptions.appearance + : 'auto' + + page.frontmatter.head.push([ + 'script', + { id: 'check-dark-mode' }, + appearance === 'force-dark' + ? `document.documentElement.classList.add('dark')` + : `;(function () { + const um= localStorage.getItem('vuepress-theme-appearance') || '${appearance}'; + const sm = window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches; + if (um === 'dark' || (um !== 'light' && sm)) { + document.documentElement.classList.add('dark'); + } + })();`.replace(/^\s+|\s+$/gm, '').replace(/\n/g, ''), + ]) + } + + page.frontmatter.head?.push([ + 'script', + { id: 'check-mac-os' }, + `document.documentElement.classList.toggle('mac', /Mac|iPhone|iPod|iPad/i.test(navigator.platform))`, + ]) +} diff --git a/theme/src/node/theme.ts b/theme/src/node/theme.ts index 1d81baea..93c16525 100644 --- a/theme/src/node/theme.ts +++ b/theme/src/node/theme.ts @@ -1,12 +1,13 @@ import type { Page, Theme } from 'vuepress/core' import { logger, templateRenderer } from 'vuepress/utils' -import { addViteConfig } from '@vuepress/helper' +import { addViteConfig, isPlainObject } from '@vuepress/helper' import type { PlumeThemeOptions, PlumeThemePageData } from '../shared/index.js' import { mergeLocaleOptions } from './defaultOptions.js' import { setupPlugins } from './plugins.js' import { extendsPageData, setupPage } from './setupPages.js' import { getThemePackage, resolve, templates } from './utils.js' import { resolveEncrypt } from './resolveEncrypt.js' +import { resolvePageHead } from './resolvePageHead.js' const THEME_NAME = 'vuepress-theme-plume' @@ -18,6 +19,9 @@ export function plumeTheme({ }: PlumeThemeOptions = {}): Theme { const pluginsOptions = plugins ?? themePlugins ?? {} const pkg = getThemePackage() + const watermarkFullPage = isPlainObject(pluginsOptions.watermark) + ? pluginsOptions.watermark.fullPage !== false + : true if (themePlugins) { logger.warn( @@ -29,43 +33,25 @@ export function plumeTheme({ localeOptions = mergeLocaleOptions(app, localeOptions) return { name: THEME_NAME, + define: { ...resolveEncrypt(encrypt), + __PLUME_WM_FP__: watermarkFullPage, }, + templateBuild: templates('build.html'), + clientConfigFile: resolve('client/config.js'), + plugins: setupPlugins(app, pluginsOptions, localeOptions, encrypt), + onInitialized: app => setupPage(app, localeOptions), + extendsPage: (page) => { extendsPageData(app, page as Page, localeOptions) - - page.frontmatter.head ??= [] - if (localeOptions.appearance ?? true) { - const appearance = typeof localeOptions.appearance === 'string' - ? localeOptions.appearance - : 'auto' - - page.frontmatter.head.push([ - 'script', - { id: 'check-dark-mode' }, - appearance === 'force-dark' - ? `document.documentElement.classList.add('dark')` - : `;(function () { - const um= localStorage.getItem('vuepress-theme-appearance') || '${appearance}'; - const sm = window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches; - if (um === 'dark' || (um !== 'light' && sm)) { - document.documentElement.classList.add('dark'); - } - })();`.replace(/^\s+|\s+$/gm, '').replace(/\n/g, ''), - ]) - } - - page.frontmatter.head?.push([ - 'script', - { id: 'check-mac-os' }, - `document.documentElement.classList.toggle('mac', /Mac|iPhone|iPod|iPad/i.test(navigator.platform))`, - ]) + resolvePageHead(page, localeOptions) }, + templateBuildRenderer(template, context) { template = template .replace('{{ themeVersion }}', pkg.version || '') @@ -73,6 +59,7 @@ export function plumeTheme({ .replace(/\n/g, '') return templateRenderer(template, context) }, + extendsBundlerOptions: (options, app) => { addViteConfig(options, app, { server: { fs: { cachedChecks: false } }, diff --git a/theme/src/shared/options/plugins.ts b/theme/src/shared/options/plugins.ts index 783f19fc..fedba272 100644 --- a/theme/src/shared/options/plugins.ts +++ b/theme/src/shared/options/plugins.ts @@ -74,5 +74,5 @@ export interface PlumeThemePluginOptions { /** * 是否开启 水印 */ - watermark?: boolean | WatermarkPluginOptions + watermark?: boolean | (WatermarkPluginOptions & { fullPage?: boolean }) }