@ansidev-oss/vitepress-theme-ansidev 1.0.10 → 1.0.11

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 (45) hide show
  1. package/dist/client/Sharing-ZrU7YF-x.js +190 -0
  2. package/dist/client/Swetrix-CM1mbD6X.js +450 -0
  3. package/dist/client/{VPAlgoliaSearchBox-DXE-LCVf.js → VPAlgoliaSearchBox-BAhNuo7n.js} +1 -1
  4. package/dist/client/{VPCarbonAds-Czmm53YE.js → VPCarbonAds-CSK7KnIu.js} +1 -1
  5. package/dist/client/{VPLocalSearchBox-ihwA4uH-.js → VPLocalSearchBox-TYbANUkJ.js} +1 -1
  6. package/dist/client/composables/index.d.ts +1 -1
  7. package/dist/client/{index-CZCdwVUW.js → index-C43AnX3f.js} +1623 -1573
  8. package/dist/client/index-DuYWs0vj.js +265 -0
  9. package/dist/client/index.d.ts +2 -1
  10. package/dist/client/index.js +7 -2
  11. package/dist/client/plugins/medium-zoom/index.d.ts +14 -0
  12. package/dist/client/plugins/sharing/index.d.ts +59 -0
  13. package/dist/client/plugins/swetrix/index.d.ts +10 -0
  14. package/dist/client/styles.css +1 -1
  15. package/dist/client/types/index.d.ts +14 -1
  16. package/dist/node/composables/index.d.ts +3 -0
  17. package/dist/node/composables/markdown.d.ts +2 -0
  18. package/dist/node/composables/route.d.ts +15 -0
  19. package/dist/node/composables/slug.d.ts +7 -0
  20. package/dist/node/composables/types.d.ts +7 -0
  21. package/dist/node/composables.d.mts +4 -2
  22. package/dist/node/composables.mjs +4 -1
  23. package/dist/node/config.d.mts +90 -8
  24. package/dist/node/config.d.ts +45 -0
  25. package/dist/node/config.mjs +19 -10
  26. package/dist/shared/composables/index.d.ts +1 -0
  27. package/package.json +11 -3
  28. package/src/client/components/DonationWidget.vue +22 -0
  29. package/src/client/components/Footer.vue +12 -12
  30. package/src/client/components/SharingWidget.vue +19 -0
  31. package/src/client/composables/index.ts +1 -1
  32. package/src/client/index.ts +2 -8
  33. package/src/client/layouts/Layout.vue +48 -2
  34. package/src/client/plugins/medium-zoom/index.ts +30 -0
  35. package/src/client/plugins/sharing/components/Sharing.vue +81 -0
  36. package/src/client/plugins/sharing/components/SharingButton.vue +115 -0
  37. package/src/client/plugins/sharing/index.ts +76 -0
  38. package/src/client/plugins/swetrix/components/Swetrix.vue +43 -0
  39. package/src/client/plugins/swetrix/index.ts +11 -0
  40. package/src/client/types/index.ts +14 -1
  41. package/src/node/composables/slug.ts +3 -13
  42. package/src/node/config.ts +22 -13
  43. package/src/shared/composables/index.ts +1 -0
  44. /package/dist/{client/composables/slug.d.ts → shared/composables/useSlug.d.ts} +0 -0
  45. /package/src/{client/composables/slug.ts → shared/composables/useSlug.ts} +0 -0
@@ -0,0 +1,81 @@
1
+ <script setup lang="ts">
2
+ import { useData } from 'vitepress'
3
+ import { computed } from 'vue'
4
+ import type { SharingComponentProps } from '..'
5
+ import SharingButton from './SharingButton.vue'
6
+
7
+ const defaultConfigs = {
8
+ wrapperCssClass: 'pl-4 py-4',
9
+ titleCssClass: 'text-sm/[32px] font-semibold mb-2',
10
+ buttonCssClass: 'flex flex-wrap space-x-1',
11
+ title: 'Share',
12
+ networks: ['email', 'facebook', 'twitter', 'telegram', 'reddit', 'linkedin', 'whatsapp', 'pinterest', 'tumblr', 'vk'],
13
+ buttonSize: 18,
14
+ iconSize: 20,
15
+ iconStyle: 'solid',
16
+ displayMode: 'both',
17
+ }
18
+
19
+ const props = defineProps<SharingComponentProps>()
20
+
21
+ const { theme } = useData()
22
+ const pluginConfig = computed(() => theme.value.sharing || false)
23
+
24
+ const wrapperCssClass = computed(() =>
25
+ pluginConfig.value && typeof pluginConfig.value.wrapperCssClass === 'string'
26
+ ? pluginConfig.value.wrapperCssClass
27
+ : defaultConfigs.wrapperCssClass,
28
+ )
29
+ const title = computed(() =>
30
+ pluginConfig.value && typeof pluginConfig.value.title === 'string' ? pluginConfig.value.title : defaultConfigs.title,
31
+ )
32
+ const titleCssClass = computed(() =>
33
+ pluginConfig.value && typeof pluginConfig.value.titleCssClass === 'string'
34
+ ? pluginConfig.value.titleCssClass
35
+ : defaultConfigs.titleCssClass,
36
+ )
37
+ const buttonCssClass = computed(() =>
38
+ pluginConfig.value && typeof pluginConfig.value.buttonCssClass === 'string'
39
+ ? pluginConfig.value.buttonCssClass
40
+ : defaultConfigs.buttonCssClass,
41
+ )
42
+ const networks = computed(() =>
43
+ pluginConfig.value && Array.isArray(pluginConfig.value.networks)
44
+ ? pluginConfig.value.networks
45
+ : defaultConfigs.networks,
46
+ )
47
+ const buttonSize = computed(() =>
48
+ pluginConfig.value && Number.isInteger(pluginConfig.value.buttonSize) && pluginConfig.value.buttonSize > 0
49
+ ? pluginConfig.value.buttonSize
50
+ : defaultConfigs.buttonSize,
51
+ )
52
+ const iconSize = computed(() =>
53
+ pluginConfig.value && Number.isInteger(pluginConfig.value.iconSize) && pluginConfig.value.iconSize > 0
54
+ ? pluginConfig.value.iconSize
55
+ : defaultConfigs.iconSize,
56
+ )
57
+ const iconStyle = computed(() =>
58
+ pluginConfig.value && typeof pluginConfig.value.iconStyle === 'string'
59
+ ? pluginConfig.value.iconStyle
60
+ : defaultConfigs.iconStyle,
61
+ )
62
+ const displayMode = computed(() =>
63
+ pluginConfig.value && typeof pluginConfig.value.displayMode === 'string'
64
+ ? pluginConfig.value.displayMode
65
+ : defaultConfigs.displayMode,
66
+ )
67
+ </script>
68
+
69
+ <template>
70
+ <div :class="wrapperCssClass">
71
+ <p :class="titleCssClass">
72
+ {{ title }}
73
+ </p>
74
+ <div :class="buttonCssClass">
75
+ <template v-for="network in networks" :key="network">
76
+ <SharingButton :url="props.url" :text="props.text" :network="network" :button-size="buttonSize"
77
+ :icon-size="iconSize" :icon-style="iconStyle" :display-mode="displayMode" />
78
+ </template>
79
+ </div>
80
+ </div>
81
+ </template>
@@ -0,0 +1,115 @@
1
+ <script setup lang="ts">
2
+ import { Icon } from '@iconify/vue'
3
+ import { computed } from 'vue'
4
+ import { SharingButtonComponentProps, useUrlQueryParamsBuilder } from '..'
5
+
6
+ const props = withDefaults(defineProps<SharingButtonComponentProps>(), {
7
+ iconSize: 32,
8
+ displayMode: 'icon',
9
+ })
10
+
11
+ const provider = computed(() => {
12
+ switch (props.network) {
13
+ case 'email':
14
+ return {
15
+ name: 'E-mail',
16
+ shareBaseURL: 'mailto:',
17
+ icon: 'bi:envelope-fill',
18
+ params: { subject: props.text, body: props.url },
19
+ allowedParams: ['subject', 'body'],
20
+ }
21
+ case 'facebook':
22
+ return {
23
+ name: 'Facebook',
24
+ shareBaseURL: 'https://facebook.com/sharer/sharer.php',
25
+ icon: 'bi:facebook',
26
+ params: { u: props.url },
27
+ allowedParams: ['u', 'display', 'redirect_uri'],
28
+ }
29
+ case 'linkedin':
30
+ return {
31
+ name: 'LinkedIn',
32
+ shareBaseURL: 'https://www.linkedin.com/sharing/share-offsite',
33
+ icon: 'bi:linkedin',
34
+ params: { url: props.url },
35
+ allowedParams: ['url'],
36
+ }
37
+ case 'pinterest':
38
+ return {
39
+ name: 'Pinterest',
40
+ shareBaseURL: 'https://pinterest.com/pin/create/button',
41
+ icon: 'bi:pinterest',
42
+ params: { posttype: 'link', title: props.text, url: props.url },
43
+ allowedParams: ['posttype', 'tags', 'title', 'content', 'caption', 'show-via', 'canonicalUrl'],
44
+ }
45
+ case 'reddit':
46
+ return {
47
+ name: 'Reddit',
48
+ shareBaseURL: 'https://www.reddit.com/submit',
49
+ icon: 'bi:reddit',
50
+ params: { title: props.text, url: props.url },
51
+ allowedParams: ['title', 'url'],
52
+ }
53
+ case 'telegram':
54
+ return {
55
+ name: 'Telegram',
56
+ shareBaseURL: 'https://telegram.me/share/url',
57
+ icon: 'bi:telegram',
58
+ params: { text: props.text, url: props.url },
59
+ allowedParams: ['text', 'url'],
60
+ }
61
+ // case 'tumblr':
62
+ // return {
63
+ // name: 'Tumblr',
64
+ // shareBaseURL: 'https://www.tumblr.com/widgets/share/tool',
65
+ // icon: 'bi:tumblr',
66
+ // params: { posttype: 'link', title: props.text, url: props.url },
67
+ // allowedParams: ['posttype', 'tags', 'title', 'content', 'caption', 'show-via', 'canonicalUrl'],
68
+ // }
69
+ case 'twitter':
70
+ return {
71
+ name: 'Twitter',
72
+ shareBaseURL: 'https://twitter.com/intent/tweet',
73
+ icon: 'bi:twitter',
74
+ params: { text: props.text, url: props.url },
75
+ allowedParams: ['text', 'url', 'hashtag', 'via'],
76
+ }
77
+ // case 'vk':
78
+ // return {
79
+ // name: 'VK',
80
+ // shareBaseURL: 'https://vk.com/share.php',
81
+ // icon: 'vk-icon',
82
+ // params: { title: props.text, url: props.url },
83
+ // allowedParams: ['title', 'url'],
84
+ // }
85
+ case 'whatsapp':
86
+ return {
87
+ name: 'WhatsApp',
88
+ shareBaseURL: 'whatsapp://send',
89
+ icon: 'bi:whatsapp',
90
+ params: { text: props.url },
91
+ allowedParams: ['text'],
92
+ }
93
+ default:
94
+ return {}
95
+ }
96
+ })
97
+
98
+ const networkName = computed(() => provider.value.name)
99
+ const networkIcon = computed(() => provider.value.icon)
100
+
101
+ const shareURL = computed(() => {
102
+ const url = provider.value.shareBaseURL
103
+ const urlQueryParams = useUrlQueryParamsBuilder(provider.value.params)
104
+ return `${url}${urlQueryParams}`
105
+ })
106
+ </script>
107
+
108
+ <template>
109
+ <a :href="shareURL" target="_blank" rel="noopener" :aria-label="networkName"
110
+ style="padding-left: 0.25rem; padding-right: 0.25rem;">
111
+ <Icon v-if="['icon', 'both'].includes(props.displayMode)" :icon="networkIcon" :width="iconSize" :height="iconSize"
112
+ class="fill-current text-gray-700 dark:text-gray-200 hover:text-blue-500 dark:hover:text-blue-400" />
113
+ <template v-if="['text', 'both'].includes(props.displayMode)">&nbsp;{{ networkName }}</template>
114
+ </a>
115
+ </template>
@@ -0,0 +1,76 @@
1
+ export declare interface SharingPluginConfig {
2
+ networks?: string[]
3
+ displayMode?: string
4
+ buttonSize?: number
5
+ iconSize?: number
6
+ iconStyle?: string
7
+ }
8
+
9
+ export type SharingButtonComponentProps = {
10
+ /**
11
+ * URL to share
12
+ * @type string
13
+ */
14
+ url: string
15
+ /**
16
+ * Share text
17
+ * @type string
18
+ */
19
+ text: string
20
+ /**
21
+ * Social network
22
+ * @type string
23
+ */
24
+ network: string
25
+ /**
26
+ * Button size
27
+ * @type number
28
+ */
29
+ buttonSize: number
30
+ /**
31
+ * Icon style
32
+ * @type string
33
+ */
34
+ iconStyle: string
35
+ /**
36
+ * Icon size
37
+ * @type number
38
+ * @default 32
39
+ */
40
+ iconSize?: number
41
+ /**
42
+ * Display mode
43
+ * @type string
44
+ * @default icon
45
+ */
46
+ displayMode?: string
47
+ }
48
+
49
+ export type SharingComponentProps = {
50
+ /**
51
+ * URL to share
52
+ * @type string
53
+ */
54
+ url: string
55
+ /**
56
+ * Share text
57
+ * @type string
58
+ */
59
+ text: string
60
+ }
61
+
62
+ export const useUrlQueryParamsBuilder = (params: Record<string, string | undefined> | undefined): string => {
63
+ if (!params) {
64
+ return ''
65
+ }
66
+ const esc = encodeURIComponent
67
+ const paramKeys = Object.keys(params)
68
+ if (paramKeys.length < 1) return ''
69
+
70
+ const s = paramKeys
71
+ .filter((key) => params[key] !== undefined)
72
+ .map((key: string) => `${esc(key)}=${esc(params[key] as string)}`)
73
+ .join('&')
74
+
75
+ return `?${s}`
76
+ }
@@ -0,0 +1,43 @@
1
+ <script setup lang="ts">
2
+ import * as Swetrix from 'swetrix'
3
+ import { onMounted } from 'vue'
4
+ import { SwetrixOptions } from '..'
5
+
6
+ // biome-ignore lint/suspicious/noExplicitAny: ignore check for this line
7
+ declare const dataLayer: any[]
8
+ // biome-ignore lint/suspicious/noExplicitAny: ignore check for this line
9
+ declare const gtag: (...args: any[]) => void
10
+ declare global {
11
+ interface Window {
12
+ dataLayer?: typeof dataLayer
13
+ gtag?: typeof gtag
14
+ }
15
+ }
16
+
17
+ const props = defineProps<{
18
+ swetrix: SwetrixOptions
19
+ }>()
20
+
21
+ const pluginOptions = props.swetrix
22
+
23
+ let isInitialized = false
24
+
25
+ function init() {
26
+ if (typeof window === 'undefined') {
27
+ return
28
+ }
29
+
30
+ if (!isInitialized) {
31
+ isInitialized = true
32
+ Swetrix.init(pluginOptions.id, pluginOptions.options)
33
+ Swetrix.trackViews()
34
+ Swetrix.trackErrors()
35
+ }
36
+ }
37
+
38
+ if (pluginOptions?.id) {
39
+ onMounted(init)
40
+ }
41
+ </script>
42
+
43
+ <template />
@@ -0,0 +1,11 @@
1
+ import type * as Swetrix from 'swetrix'
2
+
3
+ export interface SwetrixOptions {
4
+ /**
5
+ * The Swetrix ID.
6
+ *
7
+ * @default process.env.VITE_SWETRIX_ID
8
+ */
9
+ id: string
10
+ options?: Swetrix.LibOptions
11
+ }
@@ -1,6 +1,9 @@
1
1
  import type { DefaultTheme } from 'vitepress/theme'
2
2
  import type { DonationPluginConfig } from '../plugins/donation'
3
3
  import type { GoogleAnalyticsOptions } from '../plugins/google-analytics'
4
+ import type { MediumZoomPluginConfig } from '../plugins/medium-zoom'
5
+ import type { SharingPluginConfig } from '../plugins/sharing'
6
+ import type { SwetrixOptions } from '../plugins/swetrix'
4
7
 
5
8
  export interface ThemeConfig extends DefaultTheme.Config {
6
9
  /**
@@ -9,12 +12,22 @@ export interface ThemeConfig extends DefaultTheme.Config {
9
12
  * @default process.env.VITE_BASE_URL
10
13
  */
11
14
  siteURL?: string
12
- googleAnalytics?: GoogleAnalyticsOptions
13
15
  donation?: DonationPluginConfig
16
+ googleAnalytics?: GoogleAnalyticsOptions
17
+ mediumZoom?: MediumZoomPluginConfig
18
+ sharing?: SharingPluginConfig
19
+ swetrix?: SwetrixOptions
20
+ /**
21
+ * The footer configuration.
22
+ */
23
+ themeFooter?: DefaultTheme.Footer
14
24
  }
15
25
 
16
26
  export type { DonationPluginConfig } from '../plugins/donation'
17
27
  export type { GoogleAnalyticsOptions } from '../plugins/google-analytics'
28
+ export type { MediumZoomPluginConfig } from '../plugins/medium-zoom'
29
+ export type { SharingPluginConfig } from '../plugins/sharing'
30
+ export type { SwetrixOptions } from '../plugins/swetrix'
18
31
 
19
32
  export interface Post {
20
33
  type: string
@@ -1,19 +1,7 @@
1
+ import { useSlug } from '../../shared/composables'
1
2
  import { useMarkdownFrontmatter } from './markdown'
2
3
  import type { Frontmatter, FrontmatterToString, Pattern, StringOrFrontmatterToString } from './types'
3
4
 
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
5
  export const useSlugFromMarkdownFrontMatter = (
18
6
  pattern: Pattern,
19
7
  frontmatterKeyOrMappingFn: StringOrFrontmatterToString,
@@ -38,3 +26,5 @@ export const useSlugFromMarkdownFrontMatter = (
38
26
  // console.log(slugs)
39
27
  return slugs
40
28
  }
29
+
30
+ export { useSlug, useSlugFilter } from '../../shared/composables'
@@ -1,6 +1,7 @@
1
1
  // @ts-nocheck
2
- import { fileURLToPath, URL } from 'node:url'
3
- import { loadEnv } from 'vitepress'
2
+ import type { UserConfig } from 'vitepress'
3
+ import { loadEnv, mergeConfig } from 'vitepress'
4
+ import type { ThemeConfig } from '../client/types'
4
5
 
5
6
  process.env.VITE_EXTRA_EXTENSIONS = 'rss'
6
7
  globalThis.__VUE_PROD_DEVTOOLS__ = process.env.NODE_ENV === 'development'
@@ -17,12 +18,12 @@ const siteURL = env.VITE_BASE_URL
17
18
  */
18
19
 
19
20
  // for local-linked development
20
- const deps = ['vitepress-theme-ansidev']
21
+ const deps = ['@ansidev-oss/vitepress-theme-ansidev']
21
22
 
22
23
  /**
23
24
  * @type {import('vitepress').UserConfig}
24
25
  */
25
- const config = {
26
+ const baseConfig = {
26
27
  themeConfig: {
27
28
  siteURL,
28
29
  googleAnalytics: {
@@ -65,15 +66,23 @@ const config = {
65
66
  },
66
67
  },
67
68
  },
68
- resolve: {
69
- alias: [
70
- {
71
- find: /^.*\/VPFooter\.vue$/,
72
- replacement: fileURLToPath(new URL('./components/EmptyFooter.vue', import.meta.url)),
73
- },
74
- ],
75
- },
76
69
  },
77
70
  }
78
71
 
79
- export default config
72
+ /**
73
+ * Helper function to define theme configuration with proper typing
74
+ * Note: TailwindCSS plugin should be added in vite.config.ts, not here
75
+ */
76
+ export function defineWithDefaultThemeConfig(config: UserConfig<ThemeConfig> = {}): UserConfig<ThemeConfig> {
77
+ // Rename 'footer' to 'themeFooter' to avoid conflict with VitePress built-in footer config
78
+ if (config.themeConfig?.footer) {
79
+ config.themeConfig = {
80
+ ...config.themeConfig,
81
+ themeFooter: config.themeConfig.footer,
82
+ }
83
+ delete config.themeConfig.footer
84
+ }
85
+ return mergeConfig(baseConfig, config) as UserConfig<ThemeConfig>
86
+ }
87
+
88
+ export default baseConfig
@@ -0,0 +1 @@
1
+ export * from './useSlug'