@ansidev-oss/vitepress-theme-ansidev 1.0.12 → 1.2.0

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.
Files changed (86) hide show
  1. package/dist/client/Donation-BBPb8v5p.js +66 -0
  2. package/dist/client/GoogleAnalytics-34XEM7eO.js +25 -0
  3. package/dist/client/Sharing-CpagX9oy.js +185 -0
  4. package/dist/client/Swetrix-GKEyGNrV.js +647 -0
  5. package/dist/client/components/PostDate.test.d.ts +1 -0
  6. package/dist/client/composables/index.d.ts +1 -0
  7. package/dist/client/composables/lang.d.ts +27 -0
  8. package/dist/client/composables/lang.test.d.ts +1 -0
  9. package/dist/client/index.js +3769 -9
  10. package/dist/client/medium-zoom-DPl0T7qn.js +266 -0
  11. package/dist/client/record-Cgv_fXDH.js +2739 -0
  12. package/dist/client/styles.css +2 -1
  13. package/dist/client/types/index.d.ts +8 -0
  14. package/dist/node/composables.d.mts +16 -17
  15. package/dist/node/composables.mjs +39 -15
  16. package/dist/node/config.d.mts +2 -17
  17. package/dist/node/config.mjs +9 -6
  18. package/package.json +27 -21
  19. package/src/client/components/DonationWidget.vue +9 -3
  20. package/src/client/components/Footer.vue +11 -4
  21. package/src/client/components/Link.vue +11 -5
  22. package/src/client/components/LocaleSwitcher.vue +5 -5
  23. package/src/client/components/PostDate.test.ts +82 -0
  24. package/src/client/components/PostDate.vue +32 -13
  25. package/src/client/components/PostItem.vue +17 -7
  26. package/src/client/components/PostList.vue +7 -4
  27. package/src/client/components/ProjectCard.vue +42 -14
  28. package/src/client/components/ProjectList.vue +12 -5
  29. package/src/client/components/RouterLink.vue +3 -3
  30. package/src/client/components/SharingWidget.vue +3 -1
  31. package/src/client/components/StatusBadge.vue +10 -10
  32. package/src/client/components/TermBadge.vue +11 -7
  33. package/src/client/components/TermLink.vue +4 -4
  34. package/src/client/composables/date.ts +1 -1
  35. package/src/client/composables/index.ts +1 -0
  36. package/src/client/composables/lang.test.ts +56 -0
  37. package/src/client/composables/lang.ts +65 -0
  38. package/src/client/composables/locale.ts +4 -1
  39. package/src/client/composables/project.ts +3 -3
  40. package/src/client/composables/rank.ts +5 -2
  41. package/src/client/index.ts +5 -0
  42. package/src/client/layouts/Layout.vue +2 -2
  43. package/src/client/pages/ArchivesPage.vue +10 -5
  44. package/src/client/pages/CategoriesPage.vue +13 -5
  45. package/src/client/pages/HomePage.vue +3 -3
  46. package/src/client/pages/Page.vue +13 -11
  47. package/src/client/pages/PostsPage.vue +4 -4
  48. package/src/client/pages/ProjectsPage.vue +10 -7
  49. package/src/client/pages/TagsPage.vue +10 -4
  50. package/src/client/plugins/donation/components/Donation.vue +5 -3
  51. package/src/client/plugins/donation/components/DonationButton.vue +3 -1
  52. package/src/client/plugins/donation/index.ts +11 -8
  53. package/src/client/plugins/i18n/index.ts +2 -2
  54. package/src/client/plugins/sharing/components/Sharing.vue +39 -15
  55. package/src/client/plugins/sharing/components/SharingButton.vue +34 -14
  56. package/src/client/plugins/sharing/index.ts +6 -2
  57. package/src/client/styles/base.css +30 -0
  58. package/src/client/styles/blog.css +376 -0
  59. package/src/client/styles/index.css +2 -30
  60. package/src/client/types/index.ts +8 -0
  61. package/src/node/composables/excerpt.test.ts +88 -0
  62. package/src/node/composables/excerpt.ts +57 -0
  63. package/src/node/composables/index.ts +1 -0
  64. package/src/node/composables/markdown.ts +8 -2
  65. package/src/node/composables/route.ts +15 -7
  66. package/src/node/composables/slug.ts +12 -4
  67. package/src/node/config.ts +20 -14
  68. package/src/shared/composables/useSlug.ts +2 -1
  69. package/src/vite-env.d.ts +1 -0
  70. package/dist/client/Donation-D9Xn0MPt.js +0 -76
  71. package/dist/client/GoogleAnalytics-Dp3EGn1x.js +0 -29
  72. package/dist/client/Sharing-ZrU7YF-x.js +0 -190
  73. package/dist/client/Swetrix-CM1mbD6X.js +0 -450
  74. package/dist/client/VPAlgoliaSearchBox-C18GEhLm.js +0 -126
  75. package/dist/client/VPCarbonAds-DwoamMao.js +0 -31
  76. package/dist/client/VPLocalSearchBox-DyteUinQ.js +0 -4086
  77. package/dist/client/index-BtT3qA6T.js +0 -12663
  78. package/dist/client/index-CCR5sUM8.js +0 -10688
  79. package/dist/client/index-CkmxezC0.js +0 -7616
  80. package/dist/client/index-DuYWs0vj.js +0 -265
  81. package/dist/node/composables/index.d.ts +0 -3
  82. package/dist/node/composables/markdown.d.ts +0 -2
  83. package/dist/node/composables/route.d.ts +0 -15
  84. package/dist/node/composables/slug.d.ts +0 -7
  85. package/dist/node/composables/types.d.ts +0 -7
  86. package/dist/node/config.d.ts +0 -45
@@ -1,4 +1,4 @@
1
- <script setup lang='ts'>
1
+ <script setup lang="ts">
2
2
  import { computed, type PropType } from 'vue'
3
3
  import { usePostDate } from '../composables'
4
4
  import type { Post } from '../types'
@@ -7,8 +7,8 @@ import TermLink from './TermLink.vue'
7
7
  const props = defineProps({
8
8
  post: {
9
9
  type: Object as PropType<Post>,
10
- required: true,
11
- },
10
+ required: true
11
+ }
12
12
  })
13
13
 
14
14
  const postDate = computed(() => usePostDate(props.post.date))
@@ -16,18 +16,28 @@ const postDate = computed(() => usePostDate(props.post.date))
16
16
 
17
17
  <template>
18
18
  <div class="flex p-4 component-border">
19
- <div class="text-center" style="min-width: 80px;">
19
+ <div class="text-center" style="min-width: 80px">
20
20
  <div class="text-6xl font-bold">{{ postDate?.day }}</div>
21
21
  <div>{{ postDate?.month }}, {{ postDate?.year }}</div>
22
22
  </div>
23
23
  <div class="pl-8">
24
24
  <div class="flex flex-wrap mb-3">
25
- <a class="text-2xl font-bold leading-8 tracking-tight" :href="post?.url" :title="post.title">{{ post.title }}</a>
25
+ <a
26
+ class="text-2xl font-bold leading-8 tracking-tight"
27
+ :href="post?.url"
28
+ :title="post.title"
29
+ >{{ post.title }}</a
30
+ >
26
31
  </div>
27
32
  <div class="flex flex-wrap mb-3">
28
- <TermLink v-for="category in post.categories" :key="category" :text="category" basePath="/categories/" />
33
+ <TermLink
34
+ v-for="category in post.categories"
35
+ :key="category"
36
+ :text="category"
37
+ basePath="/categories/"
38
+ />
29
39
  </div>
30
- <p>{{ post?.excerpt }}</p>
40
+ <div v-html="post?.excerpt" />
31
41
  </div>
32
42
  </div>
33
43
  </template>
@@ -1,4 +1,4 @@
1
- <script setup lang='ts'>
1
+ <script setup lang="ts">
2
2
  import type { PropType } from 'vue'
3
3
  import { useI18n } from 'vue-i18n'
4
4
  import type { Post } from '../types'
@@ -8,15 +8,18 @@ defineProps({
8
8
  posts: {
9
9
  type: Object as PropType<Post[]>,
10
10
  required: true,
11
- default: [],
12
- },
11
+ default: []
12
+ }
13
13
  })
14
14
 
15
15
  const { t } = useI18n()
16
16
  </script>
17
17
 
18
18
  <template>
19
- <div v-if="posts.length > 0" class="grid grid-cols-1 lg:grid-cols-2 gap-4 mt-4">
19
+ <div
20
+ v-if="posts.length > 0"
21
+ class="grid grid-cols-1 lg:grid-cols-2 gap-4 mt-4"
22
+ >
20
23
  <PostItem v-for="post of posts" :key="post?.url" :post="post" />
21
24
  </div>
22
25
  <p v-if="posts.length === 0">{{ t('no_post') }}</p>
@@ -1,4 +1,4 @@
1
- <script setup lang='ts'>
1
+ <script setup lang="ts">
2
2
  import { computed, type PropType } from 'vue'
3
3
  import { useI18n } from 'vue-i18n'
4
4
  import { useProjectStatusStyle } from '../composables'
@@ -11,28 +11,46 @@ import TermLink from './TermLink.vue'
11
11
  const props = defineProps({
12
12
  project: {
13
13
  type: Object as PropType<Project>,
14
- required: true,
15
- },
14
+ required: true
15
+ }
16
16
  })
17
17
 
18
18
  const { t } = useI18n()
19
- const projectStatusStyles = computed(() => useProjectStatusStyle(props.project.developmentStatus))
19
+ const projectStatusStyles = computed(() =>
20
+ useProjectStatusStyle(props.project.developmentStatus)
21
+ )
20
22
  </script>
21
23
 
22
24
  <template>
23
25
  <div class="flex flex-col p-4 component-border">
24
26
  <div class="flex flex-row justify-between items-center mb-4">
25
- <a :href="project.url" class="text-2xl font-bold leading-8 tracking-tight">
27
+ <a
28
+ :href="project.url"
29
+ class="text-2xl font-bold leading-8 tracking-tight"
30
+ >
26
31
  {{ project.title }}
27
32
  </a>
28
33
  <div class="flex flex-row">
29
- <a v-if="project.showcaseUrl" class="mx-1 text-gray-500 transition hover:text-gray-600" target="_blank"
30
- rel="noopener noreferrer" :href="project.showcaseUrl">
34
+ <a
35
+ v-if="project.showcaseUrl"
36
+ class="mx-1 text-gray-500 hover:text-gray-600"
37
+ target="_blank"
38
+ rel="noopener noreferrer"
39
+ :href="project.showcaseUrl"
40
+ >
31
41
  <span class="sr-only">external-link</span>
32
- <Icon icon="bi:box-arrow-up-right" class="text-dark dark:text-white ml-2" />
42
+ <Icon
43
+ icon="bi:box-arrow-up-right"
44
+ class="text-dark dark:text-white ml-2"
45
+ />
33
46
  </a>
34
- <a v-if="project.repositoryUrl" class="mx-1 text-gray-500 transition hover:text-gray-600" target="_blank"
35
- rel="noopener noreferrer" :href="project.repositoryUrl">
47
+ <a
48
+ v-if="project.repositoryUrl"
49
+ class="mx-1 text-gray-500 hover:text-gray-600"
50
+ target="_blank"
51
+ rel="noopener noreferrer"
52
+ :href="project.repositoryUrl"
53
+ >
36
54
  <span class="sr-only">github</span>
37
55
  <Icon icon="bi:github" class="text-dark dark:text-white ml-2" />
38
56
  </a>
@@ -43,18 +61,28 @@ const projectStatusStyles = computed(() => useProjectStatusStyle(props.project.d
43
61
  <div class="flex flex-col justify-between">
44
62
  <div v-if="project.license" class="mb-3">
45
63
  {{ t('license') }}:
46
- <Link v-if="project.licenseUrl" :href="project.licenseUrl" class="link-alt">
47
- {{ project.license }}
64
+ <Link
65
+ v-if="project.licenseUrl"
66
+ :href="project.licenseUrl"
67
+ class="link-alt"
68
+ >
69
+ {{ project.license }}
48
70
  </Link>
49
71
  <span v-else>{{ project.license }}</span>
50
72
  </div>
51
73
  <div class="mb-3">
52
- {{ t('development_status.title') }}: <StatusBadge v-bind="projectStatusStyles">
74
+ {{ t('development_status.title') }}:
75
+ <StatusBadge v-bind="projectStatusStyles">
53
76
  {{ $t(`development_status.${project.developmentStatus}`) }}
54
77
  </StatusBadge>
55
78
  </div>
56
79
  <div class="flex flex-wrap mb-3">
57
- <TermLink v-for="tech in project.techs" :key="tech" :text="tech" basePath="/projects?tech=" />
80
+ <TermLink
81
+ v-for="tech in project.techs"
82
+ :key="tech"
83
+ :text="tech"
84
+ basePath="/projects?tech="
85
+ />
58
86
  </div>
59
87
  <PostDate :date="project.date" text-size="sm" />
60
88
  </div>
@@ -1,4 +1,4 @@
1
- <script setup lang='ts'>
1
+ <script setup lang="ts">
2
2
  import type { PropType } from 'vue'
3
3
  import { useI18n } from 'vue-i18n'
4
4
  import type { Project } from '../types'
@@ -8,16 +8,23 @@ defineProps({
8
8
  projects: {
9
9
  type: Object as PropType<Project[]>,
10
10
  required: true,
11
- default: [],
12
- },
11
+ default: []
12
+ }
13
13
  })
14
14
 
15
15
  const { t } = useI18n()
16
16
  </script>
17
17
 
18
18
  <template>
19
- <div v-if="projects.length > 0" class="grid grid-cols-1 lg:grid-cols-2 gap-4 mt-4">
20
- <ProjectCard v-for="project of projects" :key="project?.url" :project="project" />
19
+ <div
20
+ v-if="projects.length > 0"
21
+ class="grid grid-cols-1 lg:grid-cols-2 gap-4 mt-4"
22
+ >
23
+ <ProjectCard
24
+ v-for="project of projects"
25
+ :key="project?.url"
26
+ :project="project"
27
+ />
21
28
  </div>
22
29
  <p v-if="projects.length === 0">{{ t('no_post') }}</p>
23
30
  </template>
@@ -1,12 +1,12 @@
1
- <script setup lang='ts'>
1
+ <script setup lang="ts">
2
2
  import { useRouter } from 'vitepress'
3
3
 
4
4
  defineProps({
5
5
  to: {
6
6
  type: String,
7
7
  required: true,
8
- default: '#',
9
- },
8
+ default: '#'
9
+ }
10
10
  })
11
11
 
12
12
  const router = useRouter()
@@ -7,7 +7,9 @@ const route = useRoute()
7
7
  const { theme, frontmatter, page } = useData()
8
8
 
9
9
  const Sharing = theme.value.sharing
10
- ? defineAsyncComponent(() => import('../plugins/sharing/components/Sharing.vue'))
10
+ ? defineAsyncComponent(
11
+ () => import('../plugins/sharing/components/Sharing.vue')
12
+ )
11
13
  : () => null
12
14
 
13
15
  const postURL = new URL(route.path, theme.value.siteURL).href
@@ -1,40 +1,40 @@
1
- <script setup lang='ts'>
1
+ <script setup lang="ts">
2
2
  defineProps({
3
3
  backgroundColor: {
4
4
  type: String,
5
5
  required: false,
6
- default: 'bg-blue-100',
6
+ default: 'bg-blue-100'
7
7
  },
8
8
  darkBackgroundColor: {
9
9
  type: String,
10
10
  required: false,
11
- default: 'dark:bg-blue-800',
11
+ default: 'dark:bg-blue-800'
12
12
  },
13
13
  textColor: {
14
14
  type: String,
15
15
  required: false,
16
- default: 'text-blue-800',
16
+ default: 'text-blue-800'
17
17
  },
18
18
  darkTextColor: {
19
19
  type: String,
20
20
  required: false,
21
- default: 'dark:text-blue-100',
21
+ default: 'dark:text-blue-100'
22
22
  },
23
23
  fontSize: {
24
24
  type: String,
25
25
  required: false,
26
- default: 'text-base',
26
+ default: 'text-base'
27
27
  },
28
28
  fontWeight: {
29
29
  type: String,
30
30
  required: false,
31
- default: 'font-normal',
31
+ default: 'font-normal'
32
32
  },
33
33
  rounded: {
34
34
  type: Boolean,
35
35
  required: false,
36
- default: true,
37
- },
36
+ default: true
37
+ }
38
38
  })
39
39
  </script>
40
40
 
@@ -48,7 +48,7 @@ defineProps({
48
48
  darkTextColor,
49
49
  fontSize,
50
50
  fontWeight,
51
- rounded ? 'rounded' : '',
51
+ rounded ? 'rounded' : ''
52
52
  ]"
53
53
  >
54
54
  <slot />
@@ -4,25 +4,29 @@ import Link from './Link.vue'
4
4
  defineProps({
5
5
  title: {
6
6
  type: String,
7
- required: true,
7
+ required: true
8
8
  },
9
9
  count: {
10
10
  type: Number,
11
- required: true,
11
+ required: true
12
12
  },
13
13
  href: {
14
14
  type: String,
15
- required: true,
16
- },
15
+ required: true
16
+ }
17
17
  })
18
18
  </script>
19
19
 
20
20
  <template>
21
- <Link :key="title" :href="href"
22
- class="inline-flex items-center mt-2 mb-2 mr-2 px-5 py-2.5 text-sm font-medium text-center text-white component-border">
21
+ <Link
22
+ :key="title"
23
+ :href="href"
24
+ class="inline-flex items-center mt-2 mb-2 mr-2 px-5 py-2.5 text-sm font-medium text-center text-white component-border"
25
+ >
23
26
  {{ title }}
24
27
  <span
25
- class="inline-flex items-center justify-center w-4 h-4 ms-2 text-xs font-semibold text-gray-900 dark:text-white bg-gray-300 dark:bg-gray-700 p-2.5 rounded">
28
+ class="inline-flex items-center justify-center w-4 h-4 ms-2 text-xs font-semibold text-gray-900 dark:text-white bg-gray-300 dark:bg-gray-700 p-2.5 rounded"
29
+ >
26
30
  {{ count }}
27
31
  </span>
28
32
  </Link>
@@ -1,4 +1,4 @@
1
- <script setup lang='ts'>
1
+ <script setup lang="ts">
2
2
  import { computed } from 'vue'
3
3
  import { useSlug } from '../composables'
4
4
  import RouterLink from './RouterLink.vue'
@@ -6,12 +6,12 @@ import RouterLink from './RouterLink.vue'
6
6
  const props = defineProps({
7
7
  text: {
8
8
  type: String,
9
- required: true,
9
+ required: true
10
10
  },
11
11
  basePath: {
12
12
  type: String,
13
- required: true,
14
- },
13
+ required: true
14
+ }
15
15
  })
16
16
 
17
17
  const slug = computed(() => useSlug(props.text))
@@ -7,6 +7,6 @@ export const usePostDate: (raw: Date) => PostDate = (raw: Date) => {
7
7
  time: +date,
8
8
  day: date.toLocaleDateString('en-US', { day: 'numeric' }),
9
9
  month: date.toLocaleDateString('en-US', { month: 'short' }),
10
- year: date.toLocaleDateString('en-US', { year: 'numeric' }),
10
+ year: date.toLocaleDateString('en-US', { year: 'numeric' })
11
11
  }
12
12
  }
@@ -1,4 +1,5 @@
1
1
  export * from '../../shared/composables'
2
2
  export * from './date'
3
+ export * from './lang'
3
4
  export * from './project'
4
5
  export * from './rank'
@@ -0,0 +1,56 @@
1
+ import { describe, expect, it, vi } from 'vite-plus/test'
2
+ import { nextTick, ref } from 'vue'
3
+ import { createI18n } from 'vue-i18n'
4
+ import { matchLocale, usePageLocale } from './lang'
5
+
6
+ const data = { frontmatter: ref<Record<string, unknown>>({}), lang: ref('en') }
7
+ vi.mock('vitepress', () => ({ useData: () => data }))
8
+
9
+ describe('matchLocale', () => {
10
+ const available = ['en', 'vi']
11
+
12
+ it('matches exactly', () => {
13
+ expect(matchLocale('vi', available)).toBe('vi')
14
+ })
15
+
16
+ it('matches by base language and is case-insensitive', () => {
17
+ expect(matchLocale('vi-VN', available)).toBe('vi')
18
+ expect(matchLocale('EN-us', available)).toBe('en')
19
+ })
20
+
21
+ it('returns undefined when unsupported', () => {
22
+ expect(matchLocale('fr', available)).toBeUndefined()
23
+ })
24
+ })
25
+
26
+ describe('usePageLocale', () => {
27
+ const setup = () => {
28
+ const i18n = createI18n({
29
+ legacy: false,
30
+ locale: 'en',
31
+ messages: { en: {}, vi: {} }
32
+ })
33
+ usePageLocale(i18n)
34
+ return i18n
35
+ }
36
+
37
+ it('sets the locale from front matter lang immediately', () => {
38
+ data.frontmatter.value = { lang: 'vi' }
39
+ expect(setup().global.locale.value).toBe('vi')
40
+ })
41
+
42
+ it('keeps a manual choice until the page lang changes', async () => {
43
+ data.frontmatter.value = { lang: 'vi' }
44
+ const i18n = setup()
45
+ i18n.global.locale.value = 'en'
46
+ data.frontmatter.value = { lang: 'vi' }
47
+ await nextTick()
48
+ expect(i18n.global.locale.value).toBe('en')
49
+ data.frontmatter.value = {}
50
+ await nextTick()
51
+ expect(i18n.global.locale.value).toBe('en')
52
+ data.frontmatter.value = { lang: 'vi' }
53
+ await nextTick()
54
+ expect(i18n.global.locale.value).toBe('vi')
55
+ })
56
+ })
@@ -0,0 +1,65 @@
1
+ import { useData } from 'vitepress'
2
+ import { type ComputedRef, computed, onMounted, watch, watchEffect } from 'vue'
3
+ import type { Locale } from 'vue-i18n'
4
+
5
+ /**
6
+ * Resolved language of the current page: `frontmatter.lang`, falling back to the site lang.
7
+ */
8
+ export const usePageLang = (): ComputedRef<string> => {
9
+ const { frontmatter, lang } = useData()
10
+ return computed(() => {
11
+ const pageLang = frontmatter.value.lang
12
+ return typeof pageLang === 'string' && pageLang.trim()
13
+ ? pageLang.trim()
14
+ : lang.value
15
+ })
16
+ }
17
+
18
+ /**
19
+ * Keeps `<html lang>` in sync with the resolved page lang.
20
+ * VitePress resets it to the site lang on mount, so re-apply it after mount and on navigation.
21
+ */
22
+ export const useHtmlLang = (): void => {
23
+ const pageLang = usePageLang()
24
+ // Registered inside onMounted so it runs after VitePress's own mount-time reset.
25
+ onMounted(() => {
26
+ watchEffect(
27
+ () => {
28
+ document.documentElement.lang = pageLang.value
29
+ },
30
+ { flush: 'post' }
31
+ )
32
+ })
33
+ }
34
+
35
+ /**
36
+ * Picks the available locale matching `lang` exactly or by base language (`vi-VN` -> `vi`).
37
+ */
38
+ export const matchLocale = (
39
+ lang: string,
40
+ availableLocales: readonly Locale[]
41
+ ): Locale | undefined => {
42
+ const normalized = lang.toLowerCase()
43
+ return (
44
+ availableLocales.find((l) => l.toLowerCase() === normalized) ??
45
+ availableLocales.find((l) => l.toLowerCase() === normalized.split('-')[0])
46
+ )
47
+ }
48
+
49
+ /**
50
+ * Syncs the vue-i18n locale with the resolved page lang, synchronously so SSR renders the right strings.
51
+ * A manual `LocaleSwitcher` choice lasts until the page lang changes.
52
+ */
53
+ export const usePageLocale = (i18n: {
54
+ global: { locale: { value: Locale }; availableLocales: Locale[] }
55
+ }): void => {
56
+ const pageLang = usePageLang()
57
+ watch(
58
+ pageLang,
59
+ (lang) => {
60
+ const matched = matchLocale(lang, i18n.global.availableLocales)
61
+ if (matched) i18n.global.locale.value = matched
62
+ },
63
+ { immediate: true }
64
+ )
65
+ }
@@ -1,7 +1,10 @@
1
1
  import type { WritableComputedRef } from 'vue'
2
2
  import type { Locale } from 'vue-i18n'
3
3
 
4
- export const switchLocale = (availableLocales: string[], locale: WritableComputedRef<Locale>) => {
4
+ export const switchLocale = (
5
+ availableLocales: string[],
6
+ locale: WritableComputedRef<Locale>
7
+ ) => {
5
8
  const locales = availableLocales
6
9
  locale.value = locales[(locales.indexOf(locale.value) + 1) % locales.length]
7
10
  }
@@ -6,7 +6,7 @@ export const useProjectStatusStyle = (developmentStatus: string) => {
6
6
  backgroundColor: 'bg-green-100',
7
7
  darkBackgroundColor: 'dark:bg-green-800',
8
8
  textColor: 'text-green-800',
9
- darkTextColor: 'dark:text-green-100',
9
+ darkTextColor: 'dark:text-green-100'
10
10
  }
11
11
  case 'inactive':
12
12
  return {
@@ -14,7 +14,7 @@ export const useProjectStatusStyle = (developmentStatus: string) => {
14
14
  backgroundColor: 'bg-yellow-100',
15
15
  darkBackgroundColor: 'dark:bg-yellow-800',
16
16
  textColor: 'text-yellow-800',
17
- darkTextColor: 'dark:text-yellow-100',
17
+ darkTextColor: 'dark:text-yellow-100'
18
18
  }
19
19
  case 'unmaintained':
20
20
  return {
@@ -22,7 +22,7 @@ export const useProjectStatusStyle = (developmentStatus: string) => {
22
22
  backgroundColor: 'bg-red-100',
23
23
  darkBackgroundColor: 'dark:bg-red-800',
24
24
  textColor: 'text-red-800',
25
- darkTextColor: 'dark:text-red-100',
25
+ darkTextColor: 'dark:text-red-100'
26
26
  }
27
27
  default:
28
28
  return {}
@@ -4,10 +4,13 @@ export const useRank = <T extends string>(arr: T[]): Record<T, number> => {
4
4
  acc[item] = (acc[item] || 0) + 1 // Increment the count if the key exists, otherwise initialize to 1
5
5
  return acc
6
6
  },
7
- {} as Record<T, number>,
7
+ {} as Record<T, number>
8
8
  )
9
9
 
10
10
  const entries = Object.entries(rank) as [T, number][]
11
11
 
12
- return Object.fromEntries(entries.sort(([, a], [, b]) => b - a)) as Record<T, number>
12
+ return Object.fromEntries(entries.sort(([, a], [, b]) => b - a)) as Record<
13
+ T,
14
+ number
15
+ >
13
16
  }
@@ -1,6 +1,7 @@
1
1
  import { Icon } from '@iconify/vue'
2
2
  import type { Theme } from 'vitepress'
3
3
  import DefaultTheme from 'vitepress/theme'
4
+ import { useHtmlLang, usePageLocale } from './composables/lang'
4
5
  import Layout from './layouts/Layout.vue'
5
6
  import ArchivesPage from './pages/ArchivesPage.vue'
6
7
  import CategoriesPage from './pages/CategoriesPage.vue'
@@ -29,6 +30,10 @@ const theme: Theme = {
29
30
  app.component('ArchivesPage', ArchivesPage)
30
31
  app.use(i18n)
31
32
  },
33
+ setup() {
34
+ useHtmlLang()
35
+ usePageLocale(i18n)
36
+ }
32
37
  } satisfies Theme
33
38
 
34
39
  export * from './composables'
@@ -1,4 +1,4 @@
1
- <script setup lang='ts'>
1
+ <script setup lang="ts">
2
2
  import { useData, useRoute } from 'vitepress'
3
3
  import DefaultTheme from 'vitepress/theme'
4
4
  import { onMounted, watch } from 'vue'
@@ -30,7 +30,7 @@ watch(
30
30
  if (newPath !== oldPath) {
31
31
  initMediumZoom()
32
32
  }
33
- },
33
+ }
34
34
  )
35
35
  </script>
36
36
 
@@ -9,26 +9,31 @@ const props = defineProps({
9
9
  posts: {
10
10
  type: Object as PropType<Post[]>,
11
11
  required: true,
12
- default: [],
13
- },
12
+ default: []
13
+ }
14
14
  })
15
15
 
16
16
  const { t } = useI18n()
17
17
 
18
18
  const totalPostsByYears = computed(() =>
19
- useRank(props.posts.flatMap((post) => new Date(post.date).getFullYear().toString())),
19
+ useRank(
20
+ props.posts.flatMap((post) => new Date(post.date).getFullYear().toString())
21
+ )
20
22
  )
21
23
  const sortedYearDesc = computed(() =>
22
24
  Object.keys(totalPostsByYears.value)
23
25
  .map((str) => Number(str))
24
- .reverse(),
26
+ .reverse()
25
27
  )
26
28
  </script>
27
29
 
28
30
  <template>
29
31
  <Page :title="t('archives')">
30
32
  <div class="flex flex-wrap pt-4">
31
- <div v-if="Object.keys(sortedYearDesc).length === 0" class="mt-2 mb-2 mr-5">
33
+ <div
34
+ v-if="Object.keys(sortedYearDesc).length === 0"
35
+ class="mt-2 mb-2 mr-5"
36
+ >
32
37
  {{ t('no_archive') }}
33
38
  </div>
34
39
  <TermBadge
@@ -1,4 +1,4 @@
1
- <script setup lang='ts'>
1
+ <script setup lang="ts">
2
2
  import { computed, type PropType } from 'vue'
3
3
  import { useI18n } from 'vue-i18n'
4
4
  import TermBadge from '../components/TermBadge.vue'
@@ -9,13 +9,15 @@ const props = defineProps({
9
9
  posts: {
10
10
  type: Object as PropType<Post[]>,
11
11
  required: true,
12
- default: [],
13
- },
12
+ default: []
13
+ }
14
14
  })
15
15
 
16
16
  const { t } = useI18n()
17
17
 
18
- const categories = computed(() => useRank(props.posts.flatMap((post) => post.categories)))
18
+ const categories = computed(() =>
19
+ useRank(props.posts.flatMap((post) => post.categories))
20
+ )
19
21
  </script>
20
22
 
21
23
  <template>
@@ -24,7 +26,13 @@ const categories = computed(() => useRank(props.posts.flatMap((post) => post.cat
24
26
  <div v-if="Object.keys(categories).length === 0" class="mt-2 mb-2 mr-5">
25
27
  {{ t('no_category') }}
26
28
  </div>
27
- <TermBadge v-for="(count, category) in categories" :key="category" :title="category" :count="count" :href="`/categories/${useSlug(category)}`" />
29
+ <TermBadge
30
+ v-for="(count, category) in categories"
31
+ :key="category"
32
+ :title="category"
33
+ :count="count"
34
+ :href="`/categories/${useSlug(category)}`"
35
+ />
28
36
  </div>
29
37
  </Page>
30
38
  </template>