@ansidev-oss/vitepress-theme-ansidev 1.0.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/dist/client/Donation-D9Xn0MPt.js +76 -0
- package/dist/client/GoogleAnalytics-Dp3EGn1x.js +29 -0
- package/dist/client/VPAlgoliaSearchBox-DXE-LCVf.js +126 -0
- package/dist/client/VPCarbonAds-Czmm53YE.js +31 -0
- package/dist/client/VPLocalSearchBox-ihwA4uH-.js +4086 -0
- package/dist/client/composables/date.d.ts +2 -0
- package/dist/client/composables/index.d.ts +4 -0
- package/dist/client/composables/locale.d.ts +3 -0
- package/dist/client/composables/project.d.ts +13 -0
- package/dist/client/composables/rank.d.ts +1 -0
- package/dist/client/composables/slug.d.ts +2 -0
- package/dist/client/index-BtT3qA6T.js +12663 -0
- package/dist/client/index-CCR5sUM8.js +10688 -0
- package/dist/client/index-CZCdwVUW.js +7566 -0
- package/dist/client/index.d.ts +6 -0
- package/dist/client/index.js +5 -0
- package/dist/client/plugins/donation/index.d.ts +19 -0
- package/dist/client/plugins/google-analytics/index.d.ts +8 -0
- package/dist/client/plugins/i18n/index.d.ts +53 -0
- package/dist/client/styles.css +1 -0
- package/dist/client/types/index.d.ts +45 -0
- package/dist/node/composables.d.mts +30 -0
- package/dist/node/composables.mjs +51 -0
- package/dist/node/config.d.mts +46 -0
- package/dist/node/config.mjs +47 -0
- package/package.json +87 -0
- package/src/client/components/EmptyFooter.vue +3 -0
- package/src/client/components/Footer.vue +87 -0
- package/src/client/components/Link.vue +31 -0
- package/src/client/components/LocaleSwitcher.vue +59 -0
- package/src/client/components/PostDate.vue +42 -0
- package/src/client/components/PostItem.vue +33 -0
- package/src/client/components/PostList.vue +23 -0
- package/src/client/components/ProjectCard.vue +68 -0
- package/src/client/components/ProjectList.vue +23 -0
- package/src/client/components/RouterLink.vue +19 -0
- package/src/client/components/StatusBadge.vue +56 -0
- package/src/client/components/TermBadge.vue +29 -0
- package/src/client/components/TermLink.vue +25 -0
- package/src/client/composables/date.ts +12 -0
- package/src/client/composables/index.ts +4 -0
- package/src/client/composables/locale.ts +7 -0
- package/src/client/composables/project.ts +30 -0
- package/src/client/composables/rank.ts +13 -0
- package/src/client/composables/slug.ts +12 -0
- package/src/client/index.ts +43 -0
- package/src/client/layouts/Layout.vue +25 -0
- package/src/client/pages/ArchivesPage.vue +43 -0
- package/src/client/pages/CategoriesPage.vue +30 -0
- package/src/client/pages/HomePage.vue +18 -0
- package/src/client/pages/Page.vue +20 -0
- package/src/client/pages/PostsPage.vue +23 -0
- package/src/client/pages/ProjectsPage.vue +65 -0
- package/src/client/pages/TagsPage.vue +30 -0
- package/src/client/plugins/donation/components/Donation.vue +43 -0
- package/src/client/plugins/donation/components/DonationButton.vue +22 -0
- package/src/client/plugins/donation/index.ts +42 -0
- package/src/client/plugins/google-analytics/components/GoogleAnalytics.vue +52 -0
- package/src/client/plugins/google-analytics/index.ts +8 -0
- package/src/client/plugins/i18n/index.ts +13 -0
- package/src/client/plugins/i18n/locales/en.json +25 -0
- package/src/client/plugins/i18n/locales/vi.json +25 -0
- package/src/client/styles/index.css +30 -0
- package/src/client/types/index.ts +50 -0
- package/src/node/composables/index.ts +3 -0
- package/src/node/composables/markdown.ts +14 -0
- package/src/node/composables/route.ts +26 -0
- package/src/node/composables/slug.ts +40 -0
- package/src/node/composables/types.ts +7 -0
- package/src/node/config.ts +79 -0
- package/src/vite-env.d.ts +7 -0
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import { onMounted } from 'vue'
|
|
3
|
+
import { GoogleAnalyticsOptions } from '..'
|
|
4
|
+
|
|
5
|
+
// biome-ignore lint/suspicious/noExplicitAny: ignore check for this line
|
|
6
|
+
declare const dataLayer: any[]
|
|
7
|
+
// biome-ignore lint/suspicious/noExplicitAny: ignore check for this line
|
|
8
|
+
declare const gtag: (...args: any[]) => void
|
|
9
|
+
declare global {
|
|
10
|
+
interface Window {
|
|
11
|
+
dataLayer?: typeof dataLayer
|
|
12
|
+
gtag?: typeof gtag
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
const props = defineProps<{
|
|
17
|
+
googleAnalytics: GoogleAnalyticsOptions
|
|
18
|
+
}>()
|
|
19
|
+
|
|
20
|
+
const googleAnalyticsOptions = props.googleAnalytics
|
|
21
|
+
|
|
22
|
+
let isInitialized = false
|
|
23
|
+
|
|
24
|
+
function init() {
|
|
25
|
+
if (window.dataLayer && window.gtag) {
|
|
26
|
+
return
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
if (!isInitialized) {
|
|
30
|
+
isInitialized = true
|
|
31
|
+
const externalScript = document.createElement('script')
|
|
32
|
+
externalScript.src = `https://www.googletagmanager.com/gtag/js?id=${googleAnalyticsOptions.id}`
|
|
33
|
+
externalScript.async = true
|
|
34
|
+
document.head.appendChild(externalScript)
|
|
35
|
+
|
|
36
|
+
const inlineScript = document.createElement('script')
|
|
37
|
+
inlineScript.textContent = `
|
|
38
|
+
window.dataLayer = window.dataLayer || [];
|
|
39
|
+
function gtag(){dataLayer.push(arguments);}
|
|
40
|
+
gtag('js', new Date());
|
|
41
|
+
gtag('config', '${googleAnalyticsOptions.id}');
|
|
42
|
+
`
|
|
43
|
+
document.head.appendChild(inlineScript)
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
if (googleAnalyticsOptions) {
|
|
48
|
+
onMounted(init)
|
|
49
|
+
}
|
|
50
|
+
</script>
|
|
51
|
+
|
|
52
|
+
<template />
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { createI18n } from 'vue-i18n'
|
|
2
|
+
|
|
3
|
+
import en from './locales/en.json' with { type: 'json' }
|
|
4
|
+
import vi from './locales/vi.json' with { type: 'json' }
|
|
5
|
+
|
|
6
|
+
export default createI18n({
|
|
7
|
+
legacy: false,
|
|
8
|
+
locale: 'en',
|
|
9
|
+
messages: {
|
|
10
|
+
en,
|
|
11
|
+
vi,
|
|
12
|
+
},
|
|
13
|
+
})
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
{
|
|
2
|
+
"button": {
|
|
3
|
+
"toggle_locale": "Change language",
|
|
4
|
+
"read_more": "Read more"
|
|
5
|
+
},
|
|
6
|
+
"all_posts": "All posts",
|
|
7
|
+
"posted_on": "Posted on",
|
|
8
|
+
"my_projects": "My projects",
|
|
9
|
+
"categories": "Categories",
|
|
10
|
+
"tags": "Tags",
|
|
11
|
+
"archives": "Archives",
|
|
12
|
+
"no_post": "No post.",
|
|
13
|
+
"no_category": "No category.",
|
|
14
|
+
"no_tag": "No tag.",
|
|
15
|
+
"no_archive": "No archive.",
|
|
16
|
+
"license": "License",
|
|
17
|
+
"source_code": "Source Code",
|
|
18
|
+
"technologies": "Technologies",
|
|
19
|
+
"development_status": {
|
|
20
|
+
"title": "Development status",
|
|
21
|
+
"active": "Active",
|
|
22
|
+
"inactive": "Inactive",
|
|
23
|
+
"unmaintained": "Unmaintained"
|
|
24
|
+
}
|
|
25
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
{
|
|
2
|
+
"button": {
|
|
3
|
+
"toggle_locale": "Đổi ngôn ngữ",
|
|
4
|
+
"read_more": "Đọc thêm"
|
|
5
|
+
},
|
|
6
|
+
"all_posts": "Tất cả bài viết",
|
|
7
|
+
"posted_on": "Đăng vào",
|
|
8
|
+
"my_projects": "Các dự án của tôi",
|
|
9
|
+
"categories": "Chủ đề",
|
|
10
|
+
"tags": "Thẻ",
|
|
11
|
+
"archives": "Lưu trữ",
|
|
12
|
+
"no_post": "Không có bài viết nào.",
|
|
13
|
+
"no_category": "Không có chủ đề nào.",
|
|
14
|
+
"no_archive": "Không có bản lưu trữ nào.",
|
|
15
|
+
"no_tag": "Không có thẻ nào.",
|
|
16
|
+
"license": "Giấy phép",
|
|
17
|
+
"source_code": "Mã nguồn",
|
|
18
|
+
"technologies": "Công nghệ",
|
|
19
|
+
"development_status": {
|
|
20
|
+
"title": "Trạng thái dự án",
|
|
21
|
+
"active": "Đang phát triển",
|
|
22
|
+
"inactive": "Không hoạt động",
|
|
23
|
+
"unmaintained": "Ngừng phát triển"
|
|
24
|
+
}
|
|
25
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
@import "tailwindcss";
|
|
2
|
+
@variant dark (&:where(.dark, .dark *));
|
|
3
|
+
|
|
4
|
+
@theme {
|
|
5
|
+
--color-primary: rgb(59, 130, 246);
|
|
6
|
+
--color-primary-alt: rgb(29, 78, 216);
|
|
7
|
+
--color-primary-alt-dark: rgb(147, 197, 253);
|
|
8
|
+
--color-secondary: rgb(239, 68, 68);
|
|
9
|
+
--color-secondary-dark: rgb(234, 179, 8);
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
@utility component-border {
|
|
13
|
+
@apply border rounded-md border-gray-500 hover:border-secondary dark:hover:border-secondary-dark;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
@utility link {
|
|
17
|
+
@apply no-underline hover:text-secondary dark:hover:text-secondary-dark;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
@utility link-alt {
|
|
21
|
+
@apply text-primary-alt! dark:text-primary-alt-dark! link!;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
:root {
|
|
25
|
+
--vp-c-brand-1: var(--color-primary);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
.vp-doc a {
|
|
29
|
+
@apply link;
|
|
30
|
+
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import type { DefaultTheme } from 'vitepress/theme'
|
|
2
|
+
import type { DonationPluginConfig } from '../plugins/donation'
|
|
3
|
+
import type { GoogleAnalyticsOptions } from '../plugins/google-analytics'
|
|
4
|
+
|
|
5
|
+
export interface ThemeConfig extends DefaultTheme.Config {
|
|
6
|
+
/**
|
|
7
|
+
* The site URL.
|
|
8
|
+
*
|
|
9
|
+
* @default process.env.VITE_BASE_URL
|
|
10
|
+
*/
|
|
11
|
+
siteURL?: string
|
|
12
|
+
googleAnalytics?: GoogleAnalyticsOptions
|
|
13
|
+
donation?: DonationPluginConfig
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export type { DonationPluginConfig } from '../plugins/donation'
|
|
17
|
+
export type { GoogleAnalyticsOptions } from '../plugins/google-analytics'
|
|
18
|
+
|
|
19
|
+
export interface Post {
|
|
20
|
+
type: string
|
|
21
|
+
title: string
|
|
22
|
+
author: string
|
|
23
|
+
gravatar: string
|
|
24
|
+
twitter: string
|
|
25
|
+
url?: string
|
|
26
|
+
date: Date
|
|
27
|
+
excerpt: string | undefined
|
|
28
|
+
categories: string[]
|
|
29
|
+
tags: string[]
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export interface PostDate {
|
|
33
|
+
time: number
|
|
34
|
+
day: string
|
|
35
|
+
month: string
|
|
36
|
+
year: string
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export interface Project {
|
|
40
|
+
title: string
|
|
41
|
+
url: string
|
|
42
|
+
showcaseUrl: string
|
|
43
|
+
repositoryUrl: string
|
|
44
|
+
excerpt: string | undefined
|
|
45
|
+
license: string
|
|
46
|
+
licenseUrl: string
|
|
47
|
+
developmentStatus: string
|
|
48
|
+
techs: string[]
|
|
49
|
+
date: Date
|
|
50
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import fs from 'node:fs'
|
|
2
|
+
import { resolve } from 'node:path'
|
|
3
|
+
import { globSync } from 'glob'
|
|
4
|
+
import matter from 'gray-matter'
|
|
5
|
+
import type { Frontmatter, Pattern } from './types'
|
|
6
|
+
|
|
7
|
+
export const useMarkdownFrontmatter = (pattern: Pattern, excerptSeparator: string = '---'): Array<Frontmatter> => {
|
|
8
|
+
const files = globSync(pattern, { ignore: ['node_modules/**', '.git/**'] })
|
|
9
|
+
return files.map((file) => {
|
|
10
|
+
const src = fs.readFileSync(resolve(file), 'utf-8')
|
|
11
|
+
const { data, excerpt } = matter(src, { excerpt: true, excerpt_separator: excerptSeparator })
|
|
12
|
+
return { ...data, excerpt }
|
|
13
|
+
})
|
|
14
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { useSlugFromMarkdownFrontMatter } from './slug'
|
|
2
|
+
import type { Frontmatter, Pattern } from './types'
|
|
3
|
+
|
|
4
|
+
export const useMarkdownFrontmatterRoute = (
|
|
5
|
+
pattern: Pattern,
|
|
6
|
+
frontmatterKey: string,
|
|
7
|
+
excerptSeparator: string = '---',
|
|
8
|
+
) => {
|
|
9
|
+
return {
|
|
10
|
+
paths() {
|
|
11
|
+
return useSlugFromMarkdownFrontMatter(pattern, frontmatterKey, excerptSeparator)
|
|
12
|
+
},
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export const useArchiveRoute = (pattern: Pattern, excerptSeparator: string = '---') => {
|
|
17
|
+
return {
|
|
18
|
+
paths() {
|
|
19
|
+
return useSlugFromMarkdownFrontMatter(
|
|
20
|
+
pattern,
|
|
21
|
+
(frontmatter: Frontmatter) => new Date(frontmatter.date as string).getFullYear().toString(),
|
|
22
|
+
excerptSeparator,
|
|
23
|
+
)
|
|
24
|
+
},
|
|
25
|
+
}
|
|
26
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { useMarkdownFrontmatter } from './markdown'
|
|
2
|
+
import type { Frontmatter, FrontmatterToString, Pattern, StringOrFrontmatterToString } from './types'
|
|
3
|
+
|
|
4
|
+
export const useSlug = (str: string) => {
|
|
5
|
+
const s = str.replace(/#/, '-sharp-').replace(/\./, '-dot-').replace(/-$/, '')
|
|
6
|
+
return s
|
|
7
|
+
.toLowerCase()
|
|
8
|
+
.normalize('NFD') // Decompose Unicode characters
|
|
9
|
+
.replace(/[\u0300-\u036f]/g, '') // Remove diacritical marks
|
|
10
|
+
.replace(/đ/g, 'd') // Handle Vietnamese "đ"
|
|
11
|
+
.replace(/[^a-z0-9]+/g, '-') // Replace non-alphanumeric characters with dashes
|
|
12
|
+
.replace(/^-+|-+$/g, '') // Remove leading or trailing dashes
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export const useSlugFilter = (slug: string) => (str: string) => useSlug(str) === slug
|
|
16
|
+
|
|
17
|
+
export const useSlugFromMarkdownFrontMatter = (
|
|
18
|
+
pattern: Pattern,
|
|
19
|
+
frontmatterKeyOrMappingFn: StringOrFrontmatterToString,
|
|
20
|
+
excerptSeparator: string = '---',
|
|
21
|
+
) => {
|
|
22
|
+
const frontMatters = useMarkdownFrontmatter(pattern, excerptSeparator)
|
|
23
|
+
const _frontmatterMappingFn: FrontmatterToString =
|
|
24
|
+
typeof frontmatterKeyOrMappingFn === 'string'
|
|
25
|
+
? (frontMatter: Frontmatter) => frontMatter[frontmatterKeyOrMappingFn] as string
|
|
26
|
+
: frontmatterKeyOrMappingFn
|
|
27
|
+
|
|
28
|
+
const arr: string[] = frontMatters.flatMap(_frontmatterMappingFn)
|
|
29
|
+
|
|
30
|
+
const set = [...new Set(arr)]
|
|
31
|
+
const slugs = set.map((str: string) => {
|
|
32
|
+
const slug = useSlug(str)
|
|
33
|
+
if ('index' === slug) {
|
|
34
|
+
console.warn("[WARN] Generated routes contain the reserved route 'index'.")
|
|
35
|
+
}
|
|
36
|
+
return { params: { slug } }
|
|
37
|
+
})
|
|
38
|
+
// console.log(slugs)
|
|
39
|
+
return slugs
|
|
40
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export type Pattern = string | string[]
|
|
2
|
+
export type Frontmatter = Record<string, unknown> & {
|
|
3
|
+
date?: string
|
|
4
|
+
excerpt?: string
|
|
5
|
+
}
|
|
6
|
+
export type FrontmatterToString = (frontmatter: Frontmatter) => string
|
|
7
|
+
export type StringOrFrontmatterToString = string | FrontmatterToString
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
// @ts-nocheck
|
|
2
|
+
import { fileURLToPath, URL } from 'node:url'
|
|
3
|
+
import { loadEnv } from 'vitepress'
|
|
4
|
+
|
|
5
|
+
process.env.VITE_EXTRA_EXTENSIONS = 'rss'
|
|
6
|
+
globalThis.__VUE_PROD_DEVTOOLS__ = process.env.NODE_ENV === 'development'
|
|
7
|
+
|
|
8
|
+
const env = loadEnv(process.env.NODE_ENV || 'development', process.cwd(), 'VITE_')
|
|
9
|
+
|
|
10
|
+
const siteURL = env.VITE_BASE_URL
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* This file is intended to be required from VitePress
|
|
14
|
+
* consuming project's config file.
|
|
15
|
+
*
|
|
16
|
+
* It runs in Node.js.
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
// for local-linked development
|
|
20
|
+
const deps = ['vitepress-theme-ansidev']
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* @type {import('vitepress').UserConfig}
|
|
24
|
+
*/
|
|
25
|
+
const config = {
|
|
26
|
+
themeConfig: {
|
|
27
|
+
siteURL,
|
|
28
|
+
googleAnalytics: {
|
|
29
|
+
id: env.VITE_GA_ID,
|
|
30
|
+
},
|
|
31
|
+
search: {
|
|
32
|
+
provider: 'local',
|
|
33
|
+
options: {
|
|
34
|
+
/**
|
|
35
|
+
* @param {string} src
|
|
36
|
+
* @param {import('vitepress').MarkdownEnv} env
|
|
37
|
+
* @param {import('markdown-it')} md
|
|
38
|
+
*/
|
|
39
|
+
async _render(src, env, md) {
|
|
40
|
+
const html = await md.renderAsync(src, env)
|
|
41
|
+
if (env.frontmatter?.search === false) return ''
|
|
42
|
+
let renderedHtml = ''
|
|
43
|
+
if (env.frontmatter?.title) {
|
|
44
|
+
renderedHtml += `<h1>${env.frontmatter.title} <a href="#">​</a></h1>`
|
|
45
|
+
}
|
|
46
|
+
if (html.length > 0) {
|
|
47
|
+
renderedHtml += html
|
|
48
|
+
}
|
|
49
|
+
return renderedHtml
|
|
50
|
+
},
|
|
51
|
+
},
|
|
52
|
+
},
|
|
53
|
+
},
|
|
54
|
+
vite: {
|
|
55
|
+
ssr: {
|
|
56
|
+
noExternal: deps,
|
|
57
|
+
},
|
|
58
|
+
optimizeDeps: {
|
|
59
|
+
exclude: deps,
|
|
60
|
+
},
|
|
61
|
+
css: {
|
|
62
|
+
preprocessorOptions: {
|
|
63
|
+
scss: {
|
|
64
|
+
api: 'modern-compiler',
|
|
65
|
+
},
|
|
66
|
+
},
|
|
67
|
+
},
|
|
68
|
+
resolve: {
|
|
69
|
+
alias: [
|
|
70
|
+
{
|
|
71
|
+
find: /^.*\/VPFooter\.vue$/,
|
|
72
|
+
replacement: fileURLToPath(new URL('./components/EmptyFooter.vue', import.meta.url)),
|
|
73
|
+
},
|
|
74
|
+
],
|
|
75
|
+
},
|
|
76
|
+
},
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
export default config
|