feat(theme): add support for article copyright (#345)

This commit is contained in:
pengzhanbo 2024-11-20 19:52:17 +08:00 committed by GitHub
parent bdcb49661c
commit 6e037dcdc0
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
16 changed files with 714 additions and 15 deletions

View File

@ -113,6 +113,7 @@ export const themeGuide = defineNoteConfig({
'加密',
'文章贡献者',
'文章变更历史',
'文章版权所有',
'文章水印',
'友情链接页',
'seo',

View File

@ -199,6 +199,16 @@ permalink: /config/frontmatter/basic/
当前页面是否显示页面变更历史。
### copyright
- 类型: `boolean | CopyrightLicense | CopyrightFrontmatter`
- 默认值: `false`
- 详情:
当前文章是否 显示 版权信息。
完整信息请查看 [copyright](../../guide/功能/文章版权所有.md)
### editLink
- 类型: `boolean`

View File

@ -838,6 +838,50 @@ interface SidebarItem {
- 默认值: `'View All Changelog'`
- 详情: 变更记录的按钮文本
### copyright
- 类型: `boolean | CopyrightLicense | CopyrightOptions`
- 默认值: `false`
- 详情: 版权配置
详情请参考 [版权所有](../guide/功能/文章版权所有.md)
### copyrightText
- 类型: `string`
- 默认值: `'Copyright'`
- 详情: 版权所有的文本
### copyrightAuthorText
- 类型: `string`
- 默认值: `'Copyright Ownership:'`
- 详情: 版权所有者的文本
### copyrightCreationOriginalText
- 类型: `string`
- 默认值: `'This article link:'`
- 详情: 本文链接的文本
### copyrightCreationTranslateText
- 类型: `string`
- 默认值: `'This article translated from:'`
- 详情: 本文翻译的文本
### copyrightCreationReprintText
- 类型: `string`
- 默认值: `'This article reprint from:'`
- 详情: 本文转载的文本
### copyrightLicenseText
- 类型: `string`
- 默认值: `'License under:'`
- 详情: 版权许可证的文本
### prevPage
- 类型: `boolean`

View File

@ -0,0 +1,254 @@
---
title: 文章版权所有
icon: lucide:creative-commons
badge:
type: tip
text: v1.0.0-rc.118 +
createTime: 2024/11/20 10:52:49
permalink: /guide/features/copyright/
---
<script setup>
import VPCopyright from '@theme/VPCopyright.vue'
</script>
## 概述
主题支持为文章添加 文章 **版权所有** 声明。
文章通常来源于 原创、转载、翻译等。针对于不同的来源,添加版权声明信息能够更好地保护知识产权,
以及避免产生版权纠纷。
### Creative Commons
主题默认支持 [Creative Commons](https://creativecommons.org/) 许可协议的版权声明,包括:
<style>
.doc-cc-list [class^="vpi-license-"] {
margin-left: 8px;
width: 1.4em;
height: 1.4em;
color: var(--vp-c-text-2);
transition: color var(--vp-t-color);
}
</style>
<div class="doc-cc-list">
- [CC0 1.0 通用 (CC0)](https://creativecommons.org/publicdomain/zero/1.0/)
<span class="vpi-license-zero" />
- [署名 4.0 国际 (CC-BY-4.0)](https://creativecommons.org/licenses/by/4.0/)
<span class="vpi-license-cc" /><span class="vpi-license-by" />
- [署名-相同方式共享 4.0 国际 (CC-BY-SA-4.0)](https://creativecommons.org/licenses/by-sa/4.0/)
<span class="vpi-license-cc" /><span class="vpi-license-by" /><span class="vpi-license-sa" />
- [署名-非商业性 4.0 国际 (CC-BY-NC-4.0)](https://creativecommons.org/licenses/by-nc/4.0/)
<span class="vpi-license-cc" /><span class="vpi-license-by" /><span class="vpi-license-nc" />
- [署名-禁止演绎 4.0 国际 (CC-BY-ND-4.0)](https://creativecommons.org/licenses/by-nd/4.0/)
<span class="vpi-license-cc" /><span class="vpi-license-by" /><span class="vpi-license-nd" />
- [署名-非商业性-相同方式共享 4.0 国际 (CC-BY-NC-SA-4.0)](https://creativecommons.org/licenses/by-nc-sa/4.0/)
<span class="vpi-license-cc" /><span class="vpi-license-by" /><span class="vpi-license-nc" /><span class="vpi-license-sa" />
- [署名-非商业性-禁止演绎 4.0 国际 (CC-BY-NC-ND-4.0)](https://creativecommons.org/licenses/by-nc-nd/4.0/)
<span class="vpi-license-cc" /><span class="vpi-license-by" /><span class="vpi-license-nc" /><span class="vpi-license-nd" />
</div>
您可以根据需要选择不同的许可协议,或者自定义许可协议。
### 版权信息
版权信息包括:
- 版权所有者,版权所有者链接
- 版权许可证,版权许可证链接
- 作品原文链接
这些信息将显示在文章的底部。
::: tip 使用此功能建议同时启用 [贡献者](./文章贡献者.md) 功能。对于原创文章,主题会自动将文章的第一位贡献者作为版权所有者。你也可以在文章 frontmatter 中手动指定版权所有者。
:::
## 全局配置
您可以通过以下配置为您的站点的所有文章,声明版权许可证为 `CC-BY-4.0`:
::: code-tabs
@tab .vuepress/config.ts
```ts
import { defineUserConfig } from 'vuepress'
import { plumeTheme } from 'vuepress-theme-plume'
export default defineUserConfig({
theme: plumeTheme({
copyright: 'CC-BY-4.0' // [!code hl]
})
})
```
:::
您可以通过以下配置为您的站点的所有文章 声明自定义的版权许可证:
::: code-tabs
@tab .vuepress/config.ts
```ts :no-line-numbers
import { defineUserConfig } from 'vuepress'
import { plumeTheme } from 'vuepress-theme-plume'
export default defineUserConfig({
theme: plumeTheme({
copyright: { // [!code hl:6]
license: {
name: 'MIT', // 许可证名称
url: 'https://your-license-url' // 许可证地址
}
}
})
})
```
:::
**配置类型:**
```ts
export type CopyrightLicense =
| 'CC-BY-4.0'
| 'CC-BY-SA-4.0'
| 'CC-BY-NC-4.0'
| 'CC-BY-NC-SA-4.0'
| 'CC-BY-ND-4.0'
| 'CC-BY-NC-ND-4.0'
| 'CC0'
| string
/**
* - 配置为 `true` 时,默认为 `CC-BY-4.0`
* - 配置为 `false` 时,不显示版权,但可以在文章 frontmatter.copyright 中覆盖配置
*/
type CopyrightOptions = boolean | string | CopyrightLicense | {
/**
* 许可证
*/
license: CopyrightLicense | {
name: CopyrightLicense | string
url: string
}
}
```
::: warning 全局配置只适用于 原创文章,对于非原创文章,您应该在文章 frontmatter 中配置版权信息。
:::
## 文章 frontmatter 配置
您可以在文章 frontmatter 中为单个文章配置版权信息,以覆盖全局配置:
```md
---
title: 我的文章
copyright: CC-BY-4.0
---
```
**配置类型:**
```ts
/**
* 配置为 `false` 时,不显示版权
* 配置为 `true` 时,则默认为 全局配置的 copyright
*/
export type CopyrightFrontmatter = boolean | string | CopyrightLicense | {
/**
* 版权许可
*/
license?: CopyrightLicense | { name: string, url: string }
/**
* 版权所有者
* - 原创文章时默认为文章的第一位贡献者
* - 非原创文章时需要声明版权所有者
*/
author?: string | { name: string, url?: string }
/**
* 作品的创作方式, 原创、翻译、转载
* @default 'original'
*/
creation?: 'original' | 'translate' | 'reprint'
/**
* 原文地址,非原创作品时需要声明原文地址
* @default ''
*/
source?: string
}
```
## 文章配置示例
### 原创文章
```md
---
title: 我的文章
copyright: CC-BY-4.0
---
```
<VPCopyright license="CC-BY-4.0" />
### 转载文章
```md
---
title: 转载的文章
copyright:
creation: reprint
license: CC-BY-4.0
source: https://example.com/origin
author:
name: 转载者
url: https://example.com/author
---
```
<VPCopyright
license="CC-BY-4.0" source="https://example.com/origin" creation="reprint"
:author="{name: '转载者', url: 'https://example.com/author'}"
/>
### 翻译文章
```md
---
title: 翻译的文章
copyright:
creation: translate
license: CC-BY-4.0
source: https://example.com/origin
author:
name: 原文作者
url: https://example.com/author
---
```
<VPCopyright
license="CC-BY-4.0" source="https://example.com/origin" creation="translate"
:author="{name: '原文作者', url: 'https://example.com/author'}"
/>
### 自定义许可证
```md
---
title: 我的文章
copyright:
license:
name: MIT
url: https://example.com/mit
---
```
<VPCopyright :license="{name: 'MIT', url: 'https://example.com/mit'}" />

View File

@ -0,0 +1,92 @@
<script setup lang="ts">
import type { CopyrightFrontmatter } from '../../shared/index.js'
import VPLink from '@theme/VPLink.vue'
import { computed } from 'vue'
import { useCopyright, useData } from '../composables/index.js'
const props = defineProps<CopyrightFrontmatter>()
const { theme } = useData()
const {
author,
creation,
creationText,
license,
sourceUrl,
} = useCopyright(computed(() => props))
</script>
<template>
<div class="hint-container tip copyright-container">
<p v-if="author">
<span>{{ theme.copyrightAuthorText || 'Copyright Ownership:' }}</span>
<VPLink :href="author.url" no-icon>
{{ author.name }}
</VPLink>
</p>
<p v-if="sourceUrl">
<span>{{ creationText }}</span>
<VPLink :href="sourceUrl" :no-icon="creation === 'original'" data-allow-mismatch>
{{ sourceUrl }}
</VPLink>
</p>
<p v-if="license">
<span>{{ theme.copyrightLicenseText || 'License under' }}</span>
<VPLink :href="license.url" no-icon>
{{ license.name }}
</VPLink>
<template v-if="license.icons">
<span v-for="icon in license.icons" :key="icon" :class="`vpi-license-${icon}`" />
</template>
</p>
</div>
</template>
<style scoped>
.vp-doc .copyright-container p {
display: flex;
flex-wrap: nowrap;
gap: 4px;
align-items: center;
justify-content: flex-start;
margin: 8px 0;
line-height: 20px;
}
.vp-doc .copyright-container p span:first-of-type {
font-weight: bold;
}
.vp-doc .copyright-container [class*="vpi-"] {
width: 1.2em;
height: 1.2em;
color: var(--vp-c-text-2);
transition: color var(--vp-t-color);
}
</style>
<style>
.vpi-license-cc {
--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='%23000' d='M9 8c1.104 0 2.105.448 2.829 1.173l-1.414 1.413a2 2 0 1 0 0 2.828l1.413 1.414A4.001 4.001 0 0 1 5 12c0-2.208 1.792-4 4-4m9.829 1.173A4.001 4.001 0 0 0 12 12a4.001 4.001 0 0 0 6.828 2.828l-1.414-1.414a2 2 0 1 1 0-2.828zM2 12C2 6.477 6.477 2 12 2s10 4.477 10 10s-4.477 10-10 10S2 17.523 2 12m10-8a8 8 0 1 0 0 16a8 8 0 0 0 0-16'/%3E%3C/svg%3E");
}
.vpi-license-by {
--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='%23000' d='M14 7a2 2 0 1 1-4 0a2 2 0 0 1 4 0m1 4a1 1 0 0 0-1-1h-4a1 1 0 0 0-1 1v4h1.5v4h3v-4H15zm-3-9C6.477 2 2 6.477 2 12s4.477 10 10 10s10-4.477 10-10S17.523 2 12 2M4 12a8 8 0 1 1 16 0a8 8 0 0 1-16 0'/%3E%3C/svg%3E");
}
.vpi-license-nc {
--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='%23000' d='M12 2C6.477 2 2 6.477 2 12s4.477 10 10 10s10-4.477 10-10S17.523 2 12 2M7.094 5.68A8 8 0 0 1 18.32 16.905l-2.154-2.154A2.5 2.5 0 0 0 14 11h-4a.5.5 0 0 1 0-1.001h5.5V8H13V6h-2v2h-1q-.273.001-.53.056zM5.68 7.094L7.835 9.25A2.5 2.5 0 0 0 10 13h4a.5.5 0 0 1 0 1.001H8.5v2H11v2h2v-2h1q.273-.001.53-.056l2.376 2.376A8 8 0 0 1 5.68 7.095'/%3E%3C/svg%3E");
}
.vpi-license-nd {
--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='%23000' d='M8 9h8v2H8zm0 6v-2h8v2zm-6-3C2 6.477 6.477 2 12 2s10 4.477 10 10s-4.477 10-10 10S2 17.523 2 12m10-8a8 8 0 1 0 0 16a8 8 0 0 0 0-16'/%3E%3C/svg%3E");
}
.vpi-license-sa {
--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='%23000' d='M17 12c0-3.314-2.238-6-5-6c-2.177 0-4.03 1.67-4.716 4H6l2.5 3l2.5-3H9.401C9.92 8.805 10.89 8 12 8c1.657 0 3 1.79 3 4s-1.343 4-3 4c-1.11 0-2.08-.804-2.598-1.999H7.285C7.97 16.33 9.823 18 12 18c2.762 0 5-2.686 5-6M12 2a9.97 9.97 0 0 0-7.07 2.93A9.97 9.97 0 0 0 2 12a9.97 9.97 0 0 0 2.93 7.07A9.97 9.97 0 0 0 12 22a9.97 9.97 0 0 0 7.07-2.93A9.97 9.97 0 0 0 22 12a9.97 9.97 0 0 0-2.93-7.07A9.97 9.97 0 0 0 12 2M6.344 6.344A7.97 7.97 0 0 1 12 4c2.208 0 4.206.895 5.656 2.344A7.97 7.97 0 0 1 20 12a7.97 7.97 0 0 1-2.344 5.656A7.97 7.97 0 0 1 12 20a7.97 7.97 0 0 1-5.656-2.344A7.97 7.97 0 0 1 4 12c0-2.208.895-4.206 2.344-5.656'/%3E%3C/svg%3E");
}
.vpi-license-zero {
--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='%23000' d='M17 12c0-3.314-2.238-6-5-6s-5 2.686-5 6s2.239 6 5 6s5-2.686 5-6m-6.237 3.645l3.562-6.173c.422.69.675 1.57.675 2.528c0 2.21-1.343 4-3 4c-.441 0-.86-.127-1.237-.355M9 12c0-2.21 1.343-4 3-4c.441 0 .86.127 1.237.355l-3.562 6.173C9.253 13.838 9 12.958 9 12m3-10a9.97 9.97 0 0 0-7.07 2.93A9.97 9.97 0 0 0 2 12a9.97 9.97 0 0 0 2.93 7.07A9.97 9.97 0 0 0 12 22a9.97 9.97 0 0 0 7.07-2.93A9.97 9.97 0 0 0 22 12a9.97 9.97 0 0 0-2.93-7.07A9.97 9.97 0 0 0 12 2M6.344 6.344A7.97 7.97 0 0 1 12 4c2.208 0 4.206.895 5.656 2.344A7.97 7.97 0 0 1 20 12a7.97 7.97 0 0 1-2.344 5.656A7.97 7.97 0 0 1 12 20a7.97 7.97 0 0 1-5.656-2.344A7.97 7.97 0 0 1 4 12c0-2.208.895-4.206 2.344-5.656'/%3E%3C/svg%3E");
}
</style>

View File

@ -3,6 +3,7 @@ import VPDocAside from '@theme/VPDocAside.vue'
import VPDocBreadcrumbs from '@theme/VPDocBreadcrumbs.vue'
import VPDocChangelog from '@theme/VPDocChangelog.vue'
import VPDocContributor from '@theme/VPDocContributor.vue'
import VPDocCopyright from '@theme/VPDocCopyright.vue'
import VPDocFooter from '@theme/VPDocFooter.vue'
import VPDocMeta from '@theme/VPDocMeta.vue'
import VPEncryptPage from '@theme/VPEncryptPage.vue'
@ -129,6 +130,7 @@ watch(
<Content />
<VPDocContributor />
<VPDocChangelog />
<VPDocCopyright />
</div>
</main>
<VPDocFooter v-if="isPageDecrypted">

View File

@ -101,6 +101,12 @@ function resolveSidebar(
transition: border-left var(--vp-t-color);
}
@media print {
.vp-breadcrumb {
display: none;
}
}
.vp-breadcrumb ol {
display: flex;
flex-wrap: wrap;

View File

@ -69,6 +69,12 @@ const hasChangelog = computed(() =>
display: block;
}
@media print {
.vp-doc-changelog {
display: none;
}
}
.vp-doc-changelog .changelog-header > span {
display: block;
}

View File

@ -0,0 +1,40 @@
<script setup lang="ts">
import type { CopyrightFrontmatter } from '../../shared/index.js'
import VPCopyright from '@theme/VPCopyright.vue'
import VPDocHeader from '@theme/VPDocHeader.vue'
import { computed } from 'vue'
import { isPlainObject } from 'vuepress/shared'
import { useData } from '../composables/index.js'
const { theme, frontmatter } = useData()
const copyright = computed<CopyrightFrontmatter | null>(() => {
if ((frontmatter.value.copyright ?? theme.value.copyright ?? false) === false) {
return null
}
const docCopyright = (isPlainObject(frontmatter.value.copyright)
? frontmatter.value.copyright
: { license: frontmatter.value.copyright === true ? '' : frontmatter.value.copyright }) as CopyrightFrontmatter
if (!theme.value.copyright)
return docCopyright
const themeCopyright = (isPlainObject(theme.value.copyright)
? theme.value.copyright
: { license: theme.value.copyright === true ? '' : theme.value.copyright }) as CopyrightFrontmatter
return {
...themeCopyright,
...docCopyright,
}
})
</script>
<template>
<div v-if="copyright" class="vp-doc-copyright">
<VPDocHeader anchor="doc-copyright">
{{ theme.copyrightText || 'Copyright' }}
</VPDocHeader>
<VPCopyright v-bind="copyright" />
</div>
</template>

View File

@ -0,0 +1,113 @@
import type { CopyrightFrontmatter, CopyrightLicense, CopyrightOptions, GitContributor, KnownCopyrightLicense } from '../../shared/index.js'
import { computed, type ComputedRef } from 'vue'
import { useRoute, useRouteLocale } from 'vuepress/client'
import { useContributors } from './contributors.js'
import { useData } from './data.js'
import { getPresetLocaleData } from './preset-locales.js'
const LICENSE_URL: Record<KnownCopyrightLicense, { url: string, icons: string[] }> = {
'CC0': {
url: 'https://creativecommons.org/publicdomain/zero/1.0/',
icons: ['zero'],
},
'CC-BY-4.0': {
url: 'https://creativecommons.org/licenses/by/4.0/',
icons: ['cc', 'by'],
},
'CC-BY-NC-4.0': {
url: 'https://creativecommons.org/licenses/by-nc/4.0/',
icons: ['cc', 'by', 'nc'],
},
'CC-BY-NC-SA-4.0': {
url: 'https://creativecommons.org/licenses/by-nc-sa/4.0/',
icons: ['cc', 'by', 'nc', 'sa'],
},
'CC-BY-NC-ND-4.0': {
url: 'https://creativecommons.org/licenses/by-nc-nd/4.0/',
icons: ['cc', 'by', 'nc', 'nd'],
},
'CC-BY-ND-4.0': {
url: 'https://creativecommons.org/licenses/by-nd/4.0/',
icons: ['cc', 'by', 'nd'],
},
'CC-BY-SA-4.0': {
url: 'https://creativecommons.org/licenses/by-sa/4.0/',
icons: ['cc', 'by', 'sa'],
},
}
export function useCopyright(copyright: ComputedRef<CopyrightFrontmatter>) {
const { theme } = useData<'post'>()
const route = useRoute()
const routeLocale = useRouteLocale()
const { contributors } = useContributors()
const hasCopyright = computed(() => Boolean(copyright.value))
const creation = computed(() => copyright.value.creation || 'original')
const license = computed(() => resolveLicense(copyright.value.license, routeLocale.value))
const author = computed(() => resolveAuthor(copyright.value.author, creation.value, contributors.value))
const sourceUrl = computed(() => {
if (creation.value === 'original')
return __VUEPRESS_SSR__ ? route.fullPath : location.href.split('#')[0]
return copyright.value.source
})
const creationText = computed(() => {
const creation = copyright.value.creation
if (creation === 'translate') {
return theme.value.copyrightCreationTranslateText || 'This article is translated from'
}
else if (creation === 'reprint') {
return theme.value.copyrightCreationReprintText || 'This article is reprint from'
}
return theme.value.copyrightCreationOriginalText || 'This article link: '
})
return { license, author, hasCopyright, creation, creationText, sourceUrl }
}
interface License {
name: string
url?: string
icons?: string[]
}
function resolveLicense(
license: CopyrightOptions['license'] = 'CC-BY-4.0',
locale: string,
): License {
const result: License = typeof license === 'string' ? { name: license } : { ...license }
const fallback = LICENSE_URL[result.name]
const name = getPresetLocaleData(locale, result.name as CopyrightLicense)
if (name) {
result.name = `${name} (${result.name})`
}
result.url ||= fallback?.url
result.icons = fallback?.icons
return result
}
function resolveAuthor(
author: CopyrightFrontmatter['author'],
creation: CopyrightFrontmatter['creation'],
contributors: GitContributor[],
): { name: string, url?: string } | undefined {
const contributor = contributors[0]
const options = typeof author === 'string' ? { name: author } : author
if (options && !options.url) {
const contributor = contributors.find(c => c.name === options.name)
if (contributor)
options.url = contributor.url
}
if (creation === 'original' && contributor)
return contributor
return options
}

View File

@ -7,6 +7,7 @@ export * from './blog-post-list.js'
export * from './blog-tags.js'
export * from './bulletin.js'
export * from './contributors.js'
export * from './copyright.js'
export * from './dark-mode.js'
export * from './data.js'
export * from './edit-link.js'

View File

@ -12,11 +12,17 @@ export const enLocale: PlumeThemeLocaleData = {
editLinkText: 'Edit this page',
contributorsText: 'Contributors',
lastUpdatedText: 'Last Updated',
changelogText: 'Changelog',
changelogOnText: 'On',
changelogButtonText: 'View All Changelog',
copyrightText: 'Copyright',
copyrightAuthorText: 'Copyright Ownership:',
copyrightCreationOriginalText: 'This article link:',
copyrightCreationTranslateText: 'This article is translated from:',
copyrightCreationReprintText: 'This article is reprint from:',
copyrightLicenseText: 'License under:',
encryptButtonText: 'Confirm',
encryptPlaceholder: 'Enter password',
encryptGlobalText: 'Only password can access this site',
@ -29,12 +35,21 @@ export const enLocale: PlumeThemeLocaleData = {
}
export const enPresetLocale: PresetLocale = {
home: 'Home',
blog: 'Blog',
tag: 'Tags',
archive: 'Archives',
category: 'Categories',
archiveTotal: '{count} articles',
'home': 'Home',
'blog': 'Blog',
'tag': 'Tags',
'archive': 'Archives',
'category': 'Categories',
'archiveTotal': '{count} articles',
// ------ copyright license ------
'CC0': 'CC0 1.0 Universal',
'CC-BY-4.0': 'Attribution 4.0 International',
'CC-BY-NC-4.0': 'Attribution-NonCommercial 4.0 International',
'CC-BY-NC-SA-4.0': 'Attribution-NonCommercial-ShareAlike 4.0 International',
'CC-BY-NC-ND-4.0': 'Attribution-NonCommercial-NoDerivatives 4.0 International',
'CC-BY-ND-4.0': 'Attribution-NoDerivatives 4.0 International',
'CC-BY-SA-4.0': 'Attribution-ShareAlike 4.0 International',
}
export const enSearchLocale: Partial<SearchLocaleOptions> = {

View File

@ -22,6 +22,13 @@ export const zhLocale: PlumeThemeLocaleData = {
changelogOnText: '于',
changelogButtonText: '查看全部变更历史',
copyrightText: '版权所有',
copyrightAuthorText: '版权归属于:',
copyrightCreationOriginalText: '本文链接:',
copyrightCreationTranslateText: '本文翻译自:',
copyrightCreationReprintText: '本文转载自:',
copyrightLicenseText: '许可证:',
notFound: {
code: '404',
title: '页面未找到',
@ -41,12 +48,21 @@ export const zhLocale: PlumeThemeLocaleData = {
}
export const zhPresetLocale: PresetLocale = {
home: '首页',
blog: '博客',
tag: '标签',
archive: '归档',
category: '分类',
archiveTotal: '{count} 篇',
'home': '首页',
'blog': '博客',
'tag': '标签',
'archive': '归档',
'category': '分类',
'archiveTotal': '{count} 篇',
// ------ copyright license ------
'CC0': 'CC0 1.0 通用',
'CC-BY-4.0': '署名 4.0 国际',
'CC-BY-NC-4.0': '署名-非商业性 4.0 国际',
'CC-BY-NC-SA-4.0': '署名-非商业性-相同方式共享 4.0 国际',
'CC-BY-NC-ND-4.0': '署名-非商业性-禁止演绎 4.0 国际',
'CC-BY-ND-4.0': '署名-禁止演绎 4.0 国际',
'CC-BY-SA-4.0': '署名-相同方式共享 4.0 国际',
}
export const zhDocsearchLocale: DocSearchLocaleOptions = {

View File

@ -1,3 +1,12 @@
/**
* A literal type that supports custom further strings but preserves autocompletion in IDEs.
*
* @see [copied from issue](https://github.com/microsoft/TypeScript/issues/29729#issuecomment-471566609)
*/
export type LiteralUnion<Union extends Base, Base = string> =
| Union
| (Base & { zz_IGNORE_ME?: never })
export type ThemeImage =
| string
| { src: string, alt?: string }
@ -41,7 +50,7 @@ export type SocialLinkIconUnion =
export type SocialLinkIcon = SocialLinkIconUnion | { svg: string, name?: string }
export interface PresetLocale {
export interface PresetLocale extends Record<CopyrightLicense, string> {
home: string
blog: string
tag: string
@ -67,3 +76,45 @@ export interface ThemeTransition {
*/
appearance?: boolean | 'fade' | 'circle-clip' | 'horizontal-clip' | 'vertical-clip' | 'skew-clip'
}
export type KnownCopyrightLicense =
| 'CC-BY-4.0'
| 'CC-BY-SA-4.0'
| 'CC-BY-NC-4.0'
| 'CC-BY-NC-SA-4.0'
| 'CC-BY-ND-4.0'
| 'CC-BY-NC-ND-4.0'
| 'CC0'
export type CopyrightLicense = LiteralUnion<KnownCopyrightLicense>
export interface CopyrightOptions {
/**
*
* @see https://creativecommons.org/share-your-work/cclicenses/
* @default 'CC-BY-4.0'
*/
license?: CopyrightLicense | { name: string, url: string }
}
export interface CopyrightFrontmatter extends CopyrightOptions {
/**
*
*
* contributors
* @default ''
*/
author?: string | { name: string, url?: string }
/**
*
* @default 'original'
*/
creation?: 'original' | 'translate' | 'reprint'
/**
*
* @default ''
*/
source?: string
}

View File

@ -1,3 +1,4 @@
import type { CopyrightFrontmatter, CopyrightLicense } from '../base.js'
import type { BlogPostCover } from '../blog.js'
import type { PlumeThemePageFrontmatter } from './page.js'
@ -38,4 +39,9 @@ export interface PlumeThemePostFrontmatter extends PlumeThemePageFrontmatter {
* string `<!-- more -->`
*/
excerpt?: boolean | string
/**
*
*/
copyright?: boolean | CopyrightLicense | CopyrightFrontmatter
}

View File

@ -1,5 +1,5 @@
import type { LocaleData } from 'vuepress/core'
import type { SocialLink, SocialLinkIconUnion, ThemeOutline, ThemeTransition } from '../base.js'
import type { CopyrightLicense, CopyrightOptions, SocialLink, SocialLinkIconUnion, ThemeOutline, ThemeTransition } from '../base.js'
import type { PlumeThemeBlog } from '../blog.js'
import type { NavItem } from '../navbar.js'
import type { NotesOptions } from '../notes.js'
@ -115,6 +115,48 @@ export interface PlumeThemeLocaleData extends LocaleData {
*/
bulletin?: true | BulletinOptions
/**
*
* @default false
*/
copyright?: boolean | CopyrightLicense | CopyrightOptions
/**
*
* @default 'Copyright'
*/
copyrightText?: string
/**
*
* @default 'Copyright Ownership:'
*/
copyrightAuthorText?: string
/**
*
* @default 'This article link:'
*/
copyrightCreationOriginalText?: string
/**
*
* @default 'This article is translated from:'
*/
copyrightCreationTranslateText?: string
/**
*
* @default 'This article is reprint from:'
*/
copyrightCreationReprintText?: string
/**
*
* @default 'License under:'
*/
copyrightLicenseText?: string
/**
*
*