feat(theme): 优化文章标签颜色生成方式,所有文章支持标签

This commit is contained in:
pengzhanbo 2024-06-05 00:32:37 +08:00
parent ddb6834a7c
commit a0eac82f4e
15 changed files with 169 additions and 99 deletions

View File

@ -2,7 +2,6 @@ import type { BlogPostData } from '../shared/index.js'
declare module '@internal/blogData' {
const blogPostData: BlogPostData
const extraBlogData: Record<string, any>
export { blogPostData, extraBlogData }
export { blogPostData }
}

View File

@ -1,6 +1,5 @@
import {
blogPostData as blogPostDataRaw,
extraBlogData as extraBlogDataRaw,
} from '@internal/blogData'
import { ref } from 'vue'
import type { Ref } from 'vue'
@ -18,19 +17,8 @@ export function useBlogPostData<
return blogPostData as BlogDataRef<T>
}
export type ExtraBlogDataRef = Ref<Record<string, any>>
export const extraBlogData: ExtraBlogDataRef = ref(extraBlogDataRaw)
export function useExtraBlogData(): ExtraBlogDataRef {
return extraBlogData as ExtraBlogDataRef
}
if (__VUEPRESS_DEV__ && (import.meta.webpackHot || import.meta.hot)) {
__VUE_HMR_RUNTIME__.updateBlogData = (data: BlogPostData) => {
blogPostData.value = data
}
__VUE_HMR_RUNTIME__.updateExtraBlogData = (data: Record<string, any>) => {
extraBlogData.value = data
}
}

View File

@ -9,15 +9,11 @@ if (import.meta.webpackHot) {
if (__VUE_HMR_RUNTIME__.updateBlogData) {
__VUE_HMR_RUNTIME__.updateBlogData(blogPostData)
}
if (__VUE_HMR_RUNTIME__.updateExtraBlogData) {
__VUE_HMR_RUNTIME__.updateExtraBlogData(extraBlogData)
}
}
if (import.meta.hot) {
import.meta.hot.accept(({ blogPostData, extraBlogData }) => {
import.meta.hot.accept(({ blogPostData }) => {
__VUE_HMR_RUNTIME__.updateBlogData(blogPostData)
__VUE_HMR_RUNTIME__.updateExtraBlogData(extraBlogData)
})
}
`
@ -56,15 +52,10 @@ export async function preparedBlogData(app: App, pageFilter: (id: string) => boo
})
}
const extraBlogData: Record<string, any> = {}
if (typeof options.extraBlogData === 'function')
options.extraBlogData(extraBlogData)
const blogData: BlogPostData = pages.map((page: Page) => {
let extended: Partial<BlogPostDataItem> = {}
if (typeof options.extendBlogData === 'function')
extended = options.extendBlogData(page, extraBlogData)
extended = options.extendBlogData(page)
const data = {
path: page.path,
@ -87,9 +78,6 @@ export async function preparedBlogData(app: App, pageFilter: (id: string) => boo
export const blogPostData = JSON.parse(${JSON.stringify(
JSON.stringify(blogData),
)});
export const extraBlogData = JSON.parse(${JSON.stringify(
JSON.stringify(extraBlogData),
)});
`
// inject HMR code

View File

@ -5,10 +5,8 @@ export interface BlogDataPluginOptions {
exclude?: string | string[]
sortBy?: 'createTime' | false | (<T>(prev: T, next: T) => boolean)
excerpt?: boolean
extendBlogData?: <T = any>(page: T, extra: Record<string, any>) => Record<string, any>
extendBlogData?: <T = any>(page: T) => Record<string, any>
pageFilter?: (page: Page) => boolean
extraBlogData?: (extra: Record<string, any>) => void
}
export type BlogPostData<T extends object = object> = BlogPostDataItem<T>[]

View File

@ -1,14 +1,14 @@
<script lang="ts" setup>
import { computed } from 'vue'
import type { PlumeThemeBlogPostItem } from '../../../shared/index.js'
import { useExtraBlogData } from '../../composables/index.js'
import { useTagColors } from '../../composables/index.js'
import AutoLink from '../AutoLink.vue'
const props = defineProps<{
post: PlumeThemeBlogPostItem
}>()
const extraData = useExtraBlogData()
const colors = useTagColors()
const categoryList = computed(() =>
props.post.categoryList ?? [],
@ -19,7 +19,7 @@ const tags = computed(() =>
.slice(0, 4)
.map(tag => ({
name: tag,
colors: extraData.value.tagsColorsPreset[extraData.value.tagsColors[tag]],
className: `vp-tag-${colors.value[tag]}`,
})),
)
@ -55,7 +55,7 @@ const createTime = computed(() =>
<template v-for="tag in tags" :key="tag.name">
<span
class="tag"
:style="{ '--vp-tag-color': tag.colors[0], '--vp-tag-bg-color': tag.colors[2] }"
:class="tag.className"
>
{{ tag.name }}
</span>
@ -188,7 +188,7 @@ const createTime = computed(() =>
font-size: 12px;
line-height: 1;
color: var(--vp-tag-color);
background-color: var(--vp-tag-bg-color);
background-color: var(--vp-tag-bg);
border-radius: 3px;
transition: color var(--t-color), background-color var(--t-color);
}

View File

@ -18,8 +18,7 @@ const { tags: tagsLink } = useBlogExtract()
v-for="tag in tags"
:key="tag.name"
class="tag"
:class="{ active: tag.name === currentTag }"
:style="{ '--vp-tag-color': tag.colors[0], '--vp-tag-hover-color': tag.colors[1] }"
:class="{ active: tag.name === currentTag, [tag.className]: true }"
@click="handleTagClick(tag.name)"
>
<span class="tag-name">{{ tag.name }}</span>

View File

@ -1,15 +1,11 @@
import { usePageLang } from 'vuepress/client'
import { useExtraBlogData as _useExtraBlogData, useBlogPostData } from '@vuepress-plume/plugin-blog-data/client'
import { type Ref, computed } from 'vue'
import { useBlogPostData } from '@vuepress-plume/plugin-blog-data/client'
import { computed } from 'vue'
import { useMediaQuery } from '@vueuse/core'
import type { PlumeThemeBlogPostItem } from '../../shared/index.js'
import { useData, useLocaleLink, useRouteQuery } from '../composables/index.js'
import { toArray } from '../utils/index.js'
export const useExtraBlogData = _useExtraBlogData as () => Ref<{
tagsColorsPreset: (readonly [string, string, string])[]
tagsColors: Record<string, number>
}>
import { useTagColors } from './tag-colors.js'
const DEFAULT_PER_PAGE = 10
@ -179,7 +175,7 @@ export type ShortPostItem = Pick<PlumeThemeBlogPostItem, 'title' | 'path' | 'cre
export function useTags() {
const list = useLocalePostList()
const extraData = useExtraBlogData()
const colors = useTagColors()
const tags = computed(() => {
const tagMap: Record<string, number> = {}
@ -196,7 +192,7 @@ export function useTags() {
return Object.keys(tagMap).map(tag => ({
name: tag,
count: tagMap[tag] > 99 ? '99+' : tagMap[tag],
colors: extraData.value.tagsColorsPreset[extraData.value.tagsColors[tag]] ?? [],
className: `vp-tag-${colors.value[tag]}`,
}))
})

View File

@ -6,6 +6,7 @@ export * from './sidebar.js'
export * from './aside.js'
export * from './page.js'
export * from './blog.js'
export * from './tag-colors.js'
export * from './locale.js'
export * from './useRouteQuery.js'
export * from './watermark.js'

View File

@ -0,0 +1,16 @@
import { articleTagColors } from '@internal/articleTagColors'
import { type Ref, ref } from 'vue'
export type TagColors = Record<string, string>
export type TagColorsRef = Ref<TagColors>
const tagColorsRef: TagColorsRef = ref(articleTagColors)
export const useTagColors = (): TagColorsRef => tagColorsRef
if (__VUEPRESS_DEV__ && (import.meta.webpackHot || import.meta.hot)) {
__VUE_HMR_RUNTIME__.updateArticleTagColor = (data: TagColors) => {
tagColorsRef.value = data
}
}

View File

@ -6,3 +6,10 @@ declare module '*.vue' {
}
declare const __VUEPRESS_DEV__: string
declare module '@internal/articleTagColors' {
const articleTagColors: Record<string, string>
export {
articleTagColors,
}
}

View File

@ -1,41 +0,0 @@
import { hasOwn, random, toArray } from '@pengzhanbo/utils'
export type BlogTagsColorsItem = readonly [
string, // normal color
string, // hover color
string, // background color
]
export const BLOG_TAGS_COLORS_PRESET: BlogTagsColorsItem[] = [
['#6aa1b7', '#5086a1', 'rgba(131, 208, 218, 0.314)'],
['#299764', '#18794e', 'rgba(16, 185, 129, 0.14)'],
['#946300', '#915930', 'rgba(234, 179, 8, 0.14)'],
['#d5393e', '#b8272c', 'rgba(244, 63, 94, 0.14)'],
['#7e4cc9', '#6f42c1', 'rgba(159, 122, 234, 0.14)'],
['#3a5ccc', '#3451b2', 'rgba(100, 108, 255, 0.14)'],
['#fab10f', '#f39c12', 'rgba(255, 213, 0, 0.14)'],
['#cc6699', '#c75191', 'rgba(255, 153, 204, 0.14)'],
]
const len = BLOG_TAGS_COLORS_PRESET.length
let prevIndex: number[] = []
function getRandom() {
let index: number
do
index = random(0, len - 1)
while (prevIndex.includes(index))
prevIndex.push(index)
prevIndex = prevIndex.slice(-5)
return index
}
export function generateBlogTagsColors(map: Record<string, any>, tags?: string[]) {
if (!tags || tags.length === 0)
return
toArray(tags).forEach((tag) => {
if (!hasOwn(map, tag))
map[tag] = getRandom()
})
}

View File

@ -6,10 +6,6 @@ import {
} from '../config/index.js'
import { normalizePath } from '../utils.js'
import type { PlumeThemeEncrypt, PlumeThemeLocaleOptions } from '../..//shared/index.js'
import {
BLOG_TAGS_COLORS_PRESET,
generateBlogTagsColors,
} from './blogTags.js'
export function resolveBlogDataOptions(
localeOptions: PlumeThemeLocaleOptions,
@ -35,13 +31,8 @@ export function resolveBlogDataOptions(
pageFilter: (page: any) => page.frontmatter.article !== undefined
? !!page.frontmatter.article
: true,
extraBlogData(extra) {
extra.tagsColorsPreset = BLOG_TAGS_COLORS_PRESET
extra.tagsColors = {}
},
extendBlogData: (page: any, extra) => {
extendBlogData: (page: any) => {
const tags = page.frontmatter.tags
generateBlogTagsColors(extra.tagsColors, tags)
const data: Record<string, any> = {
categoryList: page.data.categoryList,
tags,

View File

@ -0,0 +1,20 @@
import type { App } from 'vuepress'
import { watch } from 'chokidar'
import { prepareArticleTagColors } from './prepareArticleTagColor.js'
export async function setupPrepare(app: App): Promise<void> {
await prepareArticleTagColors(app)
}
export function watchPrepare(app: App, watchers: any[]): void {
const watcher = watch('pages/**', {
cwd: app.dir.temp(),
ignoreInitial: true,
})
watcher.on('change', () => prepareArticleTagColors(app))
watcher.on('add', () => prepareArticleTagColors(app))
watcher.on('unlink', () => prepareArticleTagColors(app))
watchers.push(watcher)
}

View File

@ -0,0 +1,108 @@
import { toArray } from '@pengzhanbo/utils'
import type { App } from 'vuepress'
import { nanoid } from '../utils.js'
export type TagsColorsItem = readonly [
string, // normal color
string, // hover color
string, // background color
]
export const PRESET: TagsColorsItem[] = [
['#6aa1b7', '#5086a1', 'rgba(131, 208, 218, 0.314)'],
['#299764', '#18794e', 'rgba(16, 185, 129, 0.14)'],
['#946300', '#915930', 'rgba(234, 179, 8, 0.14)'],
['#d5393e', '#b8272c', 'rgba(244, 63, 94, 0.14)'],
['#7e4cc9', '#6f42c1', 'rgba(159, 122, 234, 0.14)'],
['#3a5ccc', '#3451b2', 'rgba(100, 108, 255, 0.14)'],
['#fab10f', '#f39c12', 'rgba(255, 213, 0, 0.14)'],
['#cc6699', '#c75191', 'rgba(255, 153, 204, 0.14)'],
['#55AAEE', '#0088CC', 'rgba(0, 136, 204, 0.14)'],
['#AA66CC', '#9933CC', 'rgba(153, 121, 204, 0.14)'],
['#9933CC', '#993399', 'rgba(153, 151, 204, 0.14)'],
['#CC9999', '#CC8888', 'rgba(204, 153, 153, 0.14)'],
['#9999CC', '#9999FF', 'rgba(153, 153, 204, 0.14)'],
['#66CCCC', '#66CCAA', 'rgba(102, 204, 204, 0.14)'],
['#CCBC99', '#CCAA99', 'rgba(204, 204, 153, 0.14)'],
]
const HMR_CODE = `
if (import.meta.webpackHot) {
import.meta.webpackHot.accept()
if (__VUE_HMR_RUNTIME__.updateArticleTagColors) {
__VUE_HMR_RUNTIME__.updateArticleTagColor(articleTagColors)
}
}
if (import.meta.hot) {
import.meta.hot.accept(({ articleTagColors }) => {
__VUE_HMR_RUNTIME__.updateArticleTagColor(articleTagColors)
})
}
`
// { index: className }
const cache: Record<number, string> = {}
export async function prepareArticleTagColors(app: App): Promise<void> {
const articleTagColors: Record<string, string> = {}
const tagList = new Set<string>()
app.pages.forEach((page) => {
const { frontmatter: { tags } } = page
if (tags) {
toArray(tags).forEach((tag) => {
tag && tagList.add(tag as string)
})
}
})
tagList.forEach((tag) => {
const code = getTagCode(tag)
if (!cache[code]) {
cache[code] = nanoid(4)
}
if (!articleTagColors[tag]) {
articleTagColors[tag] = cache[code]
}
})
let code = `\
import './articleTagColors.css'
export const articleTagColors = ${JSON.stringify(articleTagColors)}
`
if (app.env.isDev) {
code += HMR_CODE
}
await app.writeTemp('internal/articleTagColors.css', genTagColorsStyle())
await app.writeTemp('internal/articleTagColors.ts', code)
}
function getTagCode(tag: string): number {
tag = tag.toLowerCase()
let code = 0
for (let i = 0; i < tag.length; i++) {
code += tag.charCodeAt(i)
}
return code % PRESET.length
}
function genTagColorsStyle(): string {
let css = ''
for (const [code, className] of Object.entries(cache)) {
const index = Number(code)
const [color, hoverColor, backgroundColor] = PRESET[index]
css += `\
.vp-tag-${className} {
--vp-tag-color: ${color};
--vp-tag-hover-color: ${hoverColor};
--vp-tag-bg: ${backgroundColor};
}
`
}
return css
}

View File

@ -3,7 +3,7 @@
"compilerOptions": {
"baseUrl": ".",
"rootDir": "./src",
"types": ["vuepress/client-types", "vite/client"],
"types": ["vuepress/client-types", "vite/client", "webpack-env"],
"outDir": "./lib"
},
"files": [],