Skip to content

Commit

Permalink
feat: add reading time
Browse files Browse the repository at this point in the history
  • Loading branch information
dribble-njr committed Jan 8, 2025
1 parent fc1712f commit 654ed54
Show file tree
Hide file tree
Showing 6 changed files with 111 additions and 18 deletions.
10 changes: 9 additions & 1 deletion package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@
"pinia": "^2.2.7",
"qiniu-js": "^3.4.2",
"radix-vue": "^1.9.10",
"reading-time": "^1.5.0",
"tailwind-merge": "^2.5.5",
"tailwindcss-animate": "^1.0.7",
"tiny-oss": "^0.5.1",
Expand Down
36 changes: 34 additions & 2 deletions src/components/CodemirrorEditor/EditorHeader/index.vue
Original file line number Diff line number Diff line change
Expand Up @@ -71,9 +71,9 @@ const formatItems = [
const store = useStore()
const displayStore = useDisplayStore()
const { isDark, isCiteStatus, output, primaryColor } = storeToRefs(store)
const { isDark, isCiteStatus, isCountStatus, output, primaryColor } = storeToRefs(store)
const { toggleDark, editorRefresh, citeStatusChanged } = store
const { toggleDark, editorRefresh, citeStatusChanged, countStatusChanged } = store
const copyMode = useStorage(addPrefix(`copyMode`), `txt`)
const source = ref(``)
Expand Down Expand Up @@ -173,6 +173,13 @@ const formatOptions = ref<Format[]>([`rgb`, `hex`, `hsl`, `hsv`])
>
微信外链转底部引用
</MenubarCheckboxItem>
<MenubarSeparator />
<MenubarCheckboxItem
:checked="isCountStatus"
@click="countStatusChanged()"
>
统计字数和阅读时间
</MenubarCheckboxItem>
</MenubarContent>
</MenubarMenu>
<EditDropdown />
Expand Down Expand Up @@ -384,6 +391,31 @@ const formatOptions = ref<Format[]>([`rgb`, `hex`, `hsl`, `hsv`])
</Button>
</div>
</div>
<div class="space-y-2">
<h2>统计字数和阅读时间</h2>
<div class="grid grid-cols-5 justify-items-center gap-2">
<Button
class="w-full"
variant="outline"
:class="{
'border-black dark:border-white': store.isCountStatus,
}"
@click="!store.isCountStatus && store.countStatusChanged()"
>
开启
</Button>
<Button
class="w-full"
variant="outline"
:class="{
'border-black dark:border-white': !store.isCountStatus,
}"
@click="store.isCountStatus && store.countStatusChanged()"
>
关闭
</Button>
</div>
</div>
<div class="space-y-2">
<h2>段落首行缩进</h2>
<div class="grid grid-cols-5 justify-items-center gap-2">
Expand Down
19 changes: 17 additions & 2 deletions src/stores/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,10 @@ export const useStore = defineStore(`store`, () => {
const isCiteStatus = useStorage(`isCiteStatus`, false)
const toggleCiteStatus = useToggle(isCiteStatus)

// 是否统计字数和阅读时间
const isCountStatus = useStorage(`isCountStatus`, false)
const toggleCountStatus = useToggle(isCountStatus)

// 是否开启段落首行缩进
const isUseIndent = useStorage(addPrefix(`use_indent`), false)
const toggleUseIndent = useToggle(isUseIndent)
Expand Down Expand Up @@ -177,11 +181,14 @@ export const useStore = defineStore(`store`, () => {
// 更新编辑器
const editorRefresh = () => {
codeThemeChange()
renderer.reset({ citeStatus: isCiteStatus.value, legend: legend.value, isUseIndent: isUseIndent.value })
renderer.reset({ citeStatus: isCiteStatus.value, legend: legend.value, isUseIndent: isUseIndent.value, countStatus: isCountStatus.value })

const { markdownContent } = renderer.parseFrontMatterAndContent(editor.value!.getValue())
const { markdownContent, readingTime } = renderer.parseFrontMatterAndContent(editor.value!.getValue())
let outputTemp = marked.parse(markdownContent) as string

// 阅读时间及字数统计
outputTemp = renderer.buildReadingTime(readingTime) + outputTemp

// 去除第一行的 margin-top
outputTemp = outputTemp.replace(/(style=".*?)"/, `$1;margin-top: 0"`)
// 引用脚注
Expand Down Expand Up @@ -275,6 +282,7 @@ export const useStore = defineStore(`store`, () => {
const resetStyle = () => {
isCiteStatus.value = false
isMacCodeBlock.value = true
isCountStatus.value = false

theme.value = themeOptions[0].value
fontFamily.value = fontFamilyOptions[0].value
Expand Down Expand Up @@ -366,6 +374,10 @@ export const useStore = defineStore(`store`, () => {
toggleCiteStatus()
})

const countStatusChanged = withAfterRefresh(() => {
toggleCountStatus()
})

const useIndentChanged = withAfterRefresh(() => {
toggleUseIndent()
})
Expand Down Expand Up @@ -427,6 +439,9 @@ export const useStore = defineStore(`store`, () => {
isUseIndent,
useIndentChanged,

isCountStatus,
countStatusChanged,

output,
editor,
cssEditor,
Expand Down
1 change: 1 addition & 0 deletions src/types/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ export interface IOpts {
isUseIndent: boolean
legend?: string
citeStatus?: boolean
countStatus?: boolean
}

export type ThemeStyles = Record<Block | Inline, ExtendedProperties>
Expand Down
62 changes: 49 additions & 13 deletions src/utils/renderer.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,18 @@
import type { ExtendedProperties, IOpts, ThemeStyles } from '@/types'
import type { PropertiesHyphen } from 'csstype'
import type { Renderer, RendererObject, Tokens } from 'marked'
import type { ReadTimeResults } from 'reading-time'
import { cloneDeep, toMerged } from 'es-toolkit'
import frontMatter from 'front-matter'

import hljs from 'highlight.js'
import { marked } from 'marked'
import mermaid from 'mermaid'
import readingTime from 'reading-time'

import { getStyleString } from '.'
import markedAlert from './MDAlert'

import { MDKatex } from './MDKatex'

marked.setOptions({
Expand Down Expand Up @@ -109,6 +113,36 @@ const macCodeSvg = `
</svg>
`.trim()

interface ParseResult {
yamlData: Record<string, any>
markdownContent: string
readingTime: ReadTimeResults
}

function parseFrontMatterAndContent(markdownText: string): ParseResult {
try {
const parsed = frontMatter(markdownText)
const yamlData = parsed.attributes
const markdownContent = parsed.body

const readingTimeResult = readingTime(markdownContent)

return {
yamlData: yamlData as Record<string, any>,
markdownContent,
readingTime: readingTimeResult,
}
}
catch (error) {
console.error(`Error parsing front-matter:`, error)
return {
yamlData: {},
markdownContent: markdownText,
readingTime: readingTime(markdownText),
}
}
}

export function initRenderer(opts: IOpts) {
const footnotes: [number, string, string][] = []
let footnoteIndex: number = 0
Expand All @@ -121,19 +155,6 @@ export function initRenderer(opts: IOpts) {
return getStyles(styleMapping, tag, addition)
}

function parseFrontMatterAndContent(markdownText: string) {
try {
const parsed = frontMatter(markdownText)
const yamlData = parsed.attributes
const markdownContent = parsed.body
return { yamlData, markdownContent }
}
catch (error) {
console.error(`Error parsing front-matter:`, error)
return { yamlData: {}, markdownContent: markdownText }
}
}

function styledContent(styleLabel: string, content: string, tagName?: string): string {
const tag = tagName ?? styleLabel
return `<${tag} ${styles(styleLabel)}>${content}</${tag}>`
Expand All @@ -156,6 +177,20 @@ export function initRenderer(opts: IOpts) {
marked.use(markedAlert({ styles: styleMapping }))
}

function buildReadingTime(readingTime: ReadTimeResults): string {
if (!opts.countStatus) {
return ``
}
if (!readingTime.words) {
return ``
}
return `
<blockquote ${styles(`blockquote`)}>
<p ${styles(`blockquote_p`)}>约 ${readingTime.words} 字,需 ${readingTime.text.replace(`read`, `阅读。`)}</p>
</blockquote>
`
}

const buildFootnotes = () => {
if (!footnotes.length) {
return ``
Expand Down Expand Up @@ -305,6 +340,7 @@ export function initRenderer(opts: IOpts) {
setOptions,
reset,
parseFrontMatterAndContent,
buildReadingTime,
createContainer(content: string) {
return styledContent(`container`, content, `section`)
},
Expand Down

0 comments on commit 654ed54

Please sign in to comment.