valaxy 0.28.5 → 0.28.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.
@@ -1,9 +1,9 @@
1
1
  import type { ComputedRef, MaybeRef } from 'vue'
2
2
  import type { CollectionConfig } from '../types'
3
- import collections from '#valaxy/blog/collections'
4
-
5
3
  import { computed, unref } from 'vue'
4
+
6
5
  import { useRoute } from 'vue-router'
6
+ import collections from '#valaxy/blog/collections'
7
7
  import { usePageList } from './post'
8
8
 
9
9
  export interface PostCollectionInfo {
@@ -2,6 +2,98 @@ import mediumZoom from 'medium-zoom'
2
2
  import { useSiteConfig } from 'valaxy'
3
3
  import { onMounted } from 'vue'
4
4
 
5
+ interface ZoomRectLike {
6
+ left: number
7
+ top: number
8
+ width: number
9
+ height: number
10
+ }
11
+
12
+ interface ZoomImageStyle {
13
+ transform: string
14
+ width: string
15
+ height: string
16
+ }
17
+
18
+ interface SavedZoomImageStyle extends ZoomImageStyle {
19
+ element: HTMLElement
20
+ }
21
+
22
+ /**
23
+ * Convert the opened transform into real dimensions while keeping the same viewport rect.
24
+ * This avoids Chrome rendering the zoomed image blurry because of transform scaling.
25
+ *
26
+ * @see https://github.com/francoischalifour/medium-zoom/issues/151
27
+ * @see https://github.com/YunYouJun/valaxy/issues/317
28
+ * @see https://github.com/YunYouJun/valaxy/issues/692
29
+ */
30
+ export function computeZoomRectStyle(
31
+ visualRect: ZoomRectLike,
32
+ layoutOffset: Pick<ZoomRectLike, 'left' | 'top'>,
33
+ scrollOffset = { x: 0, y: 0 },
34
+ ): ZoomImageStyle {
35
+ return {
36
+ transform: `translate3d(${visualRect.left + scrollOffset.x - layoutOffset.left}px, ${visualRect.top + scrollOffset.y - layoutOffset.top}px, 0)`,
37
+ width: `${visualRect.width}px`,
38
+ height: `${visualRect.height}px`,
39
+ }
40
+ }
41
+
42
+ function getDocumentScroll() {
43
+ return {
44
+ x: window.pageXOffset || document.documentElement.scrollLeft || document.body.scrollLeft || 0,
45
+ y: window.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop || 0,
46
+ }
47
+ }
48
+
49
+ function getStylePixelValue(value: string, fallback: number) {
50
+ const parsed = Number.parseFloat(value)
51
+ return Number.isFinite(parsed) ? parsed : fallback
52
+ }
53
+
54
+ function applyWithoutTransformTransition(element: HTMLElement, apply: () => void) {
55
+ const transition = element.style.transition
56
+ const transitionPriority = element.style.getPropertyPriority('transition')
57
+
58
+ element.style.setProperty('transition', 'none', 'important')
59
+ apply()
60
+ void element.offsetWidth
61
+
62
+ if (transition)
63
+ element.style.setProperty('transition', transition, transitionPriority)
64
+ else
65
+ element.style.removeProperty('transition')
66
+ }
67
+
68
+ function freezeOpenedImage(element: HTMLElement): SavedZoomImageStyle | null {
69
+ const scaleMatch = element.style.transform.match(/scale\(([^)]+)\)/)
70
+ const scale = scaleMatch ? Number.parseFloat(scaleMatch[1]) : 1
71
+ if (!scale || scale === 1)
72
+ return null
73
+
74
+ const rect = element.getBoundingClientRect()
75
+ const scrollOffset = getDocumentScroll()
76
+ const layoutOffset = {
77
+ left: getStylePixelValue(element.style.left, rect.left + scrollOffset.x),
78
+ top: getStylePixelValue(element.style.top, rect.top + scrollOffset.y),
79
+ }
80
+ const nextStyle = computeZoomRectStyle(rect, layoutOffset, scrollOffset)
81
+ const savedStyle: SavedZoomImageStyle = {
82
+ element,
83
+ transform: element.style.transform,
84
+ width: element.style.width,
85
+ height: element.style.height,
86
+ }
87
+
88
+ applyWithoutTransformTransition(element, () => {
89
+ element.style.transform = nextStyle.transform
90
+ element.style.width = nextStyle.width
91
+ element.style.height = nextStyle.height
92
+ })
93
+
94
+ return savedStyle
95
+ }
96
+
5
97
  /**
6
98
  * @description image preview by medium-zoom
7
99
  */
@@ -19,54 +111,40 @@ export function useMediumZoom() {
19
111
  },
20
112
  )
21
113
 
22
- // Fix blurry images after zoom animation completes.
23
- // medium-zoom uses CSS transform: scale() which causes browsers (especially
24
- // Chrome) to render scaled images at lower quality.
25
- // After the open animation, we replace the transform with actual dimensions
26
- // to force full-resolution rendering. Before closing, we restore the original
27
- // transform so the close animation works correctly.
28
- // @see https://github.com/francoischalifour/medium-zoom/issues/151
29
- let savedStyles: { transform: string, width: string, height: string } | null = null
114
+ let savedStyles: SavedZoomImageStyle[] = []
30
115
 
31
116
  zoom.on('opened', () => {
32
- const zoomed = document.querySelector('.medium-zoom-image--opened') as HTMLElement | null
33
- if (!zoomed)
34
- return
35
-
36
- const { transform } = zoomed.style
37
- const scaleMatch = transform.match(/scale\(([^)]+)\)/)
38
- if (!scaleMatch)
39
- return
40
-
41
- const scale = Number.parseFloat(scaleMatch[1])
42
- if (!scale || scale === 1)
43
- return
44
-
45
- savedStyles = {
46
- transform,
47
- width: zoomed.style.width,
48
- height: zoomed.style.height,
49
- }
50
- const rect = zoomed.getBoundingClientRect()
51
-
52
- // Replace scale transform with actual width/height
53
- zoomed.style.transform = transform.replace(/scale\([^)]+\)/, 'scale(1)')
54
- zoomed.style.width = `${rect.width}px`
55
- zoomed.style.height = `${rect.height}px`
117
+ savedStyles = Array
118
+ .from(document.querySelectorAll<HTMLElement>('.medium-zoom-image--opened'))
119
+ .map(freezeOpenedImage)
120
+ .filter((style): style is SavedZoomImageStyle => style !== null)
56
121
  })
57
122
 
58
123
  zoom.on('close', () => {
59
- if (!savedStyles)
124
+ if (!savedStyles.length)
60
125
  return
61
126
 
62
- const zoomed = document.querySelector('.medium-zoom-image--opened') as HTMLElement | null
63
- if (zoomed) {
64
- // Restore original styles for close animation
65
- zoomed.style.transform = savedStyles.transform
66
- zoomed.style.width = savedStyles.width
67
- zoomed.style.height = savedStyles.height
127
+ for (const style of savedStyles) {
128
+ const { element } = style
129
+ if (!document.body.contains(element))
130
+ continue
131
+
132
+ // Restore original medium-zoom styles (without transition) so the element
133
+ // is back to the state medium-zoom expects for its close animation.
134
+ applyWithoutTransformTransition(element, () => {
135
+ element.style.transform = style.transform
136
+ element.style.width = style.width
137
+ element.style.height = style.height
138
+ })
139
+
140
+ // medium-zoom sets `transform = ''` BEFORE dispatching the close event,
141
+ // but we just overwrote it above. Re-apply `''` to actually trigger the
142
+ // close transition; otherwise transitionend never fires and the cloned
143
+ // image element is never removed from the DOM.
144
+ element.style.transform = ''
68
145
  }
69
- savedStyles = null
146
+
147
+ savedStyles = []
70
148
  })
71
149
  })
72
150
  }
@@ -1,10 +1,10 @@
1
1
  import type { Post, SiteConfig } from 'valaxy'
2
2
  import type { ComputedRef } from 'vue'
3
3
  import type { CollectionConfig } from '../../types'
4
- import collections from '#valaxy/blog/collections'
5
4
  import { orderByMeta, useSiteConfig } from 'valaxy'
6
5
  import { computed } from 'vue'
7
6
  import { useI18n } from 'vue-i18n'
7
+ import collections from '#valaxy/blog/collections'
8
8
  import { useRouterStore } from '../../stores'
9
9
  import { isLocaleKey, stripLocalePrefix, tObject } from '../../utils'
10
10
 
package/client/config.ts CHANGED
@@ -4,7 +4,7 @@ import type { ComputedRef, InjectionKey } from 'vue'
4
4
  // fix build caused by pnpm
5
5
  // This is likely not portable. A type annotation is necessary.
6
6
  // https://github.com/microsoft/TypeScript/issues/42873
7
- import type { DefaultTheme, ValaxyConfig } from '../types'
7
+ import type { DefaultTheme, ValaxyAddon, ValaxyConfig } from '../types'
8
8
  import type { ValaxyData } from './app/data'
9
9
  import { computed, hasInjectionContext, inject, readonly, shallowRef } from 'vue'
10
10
 
@@ -102,3 +102,36 @@ export function useRuntimeConfig() {
102
102
  const config = useValaxyConfig()
103
103
  return computed(() => config!.value.runtimeConfig)
104
104
  }
105
+
106
+ /**
107
+ * Get addon config from runtime config.
108
+ *
109
+ * A generic, type-safe shortcut that every addon and theme can use instead of
110
+ * manually calling `useRuntimeConfig()` + `computed(...)` + type assertion.
111
+ *
112
+ * Must be called inside `<script setup>` or a Vue lifecycle hook (same
113
+ * constraint as `useRuntimeConfig()`).
114
+ *
115
+ * @example
116
+ * ```ts
117
+ * // Inside an addon — replaces the boilerplate `useAddonXxxConfig()` pattern
118
+ * import type { MyOptions } from '../types'
119
+ * const config = useAddonConfig<MyOptions>('valaxy-addon-xxx')
120
+ * config.value?.options // MyOptions | undefined
121
+ * ```
122
+ *
123
+ * @example
124
+ * ```ts
125
+ * // Inside a theme component — no dynamic import needed
126
+ * const algolia = useAddonConfig<AlgoliaSearchOptions>('valaxy-addon-algolia')
127
+ * if (algolia.value) { /* addon is installed and configured *\/ }
128
+ * ```
129
+ *
130
+ * @public
131
+ */
132
+ export function useAddonConfig<AddonOptions = Record<string, any>>(addonName: string) {
133
+ const runtimeConfig = useRuntimeConfig()
134
+ return computed(() =>
135
+ runtimeConfig.value.addons[addonName] as ValaxyAddon<AddonOptions> | undefined,
136
+ )
137
+ }
@@ -1,7 +1,7 @@
1
1
  import 'node:process';
2
2
  import 'yargs';
3
3
  import 'yargs/helpers';
4
- export { c as cli, J as registerDevCommand, X as run, _ as startValaxyDev } from '../../shared/valaxy.CC8moxgJ.mjs';
4
+ export { c as cli, J as registerDevCommand, X as run, _ as startValaxyDev } from '../../shared/valaxy.BteINvcX.mjs';
5
5
  import 'node:os';
6
6
  import 'node:path';
7
7
  import 'consola';
@@ -1,4 +1,4 @@
1
- export { A as ALL_ROUTE, E as EXCERPT_SEPARATOR, G as GLOBAL_STATE, P as PATHNAME_PROTOCOL_RE, V as ViteValaxyPlugins, b as build, c as cli, a as createServer, d as createValaxyPlugin, e as customElements, f as defaultSiteConfig, g as defaultValaxyConfig, h as defaultViteConfig, i as defineAddon, j as defineConfig, k as defineSiteConfig, l as defineTheme, m as defineValaxyAddon, n as defineValaxyConfig, o as defineValaxyTheme, p as encryptContent, q as fixViteSsgHtml, r as generateClientRedirects, s as getGitTimestamp, t as getIndexHtml, u as getServerInfoText, v as isExternal, w as isInstalledGlobally, x as isKatexEnabled, y as isKatexPluginNeeded, z as isMathJaxEnabled, B as isPath, C as loadConfigFromFile, D as mergeValaxyConfig, F as mergeViteConfigs, H as postProcessForSSG, I as processValaxyOptions, J as registerDevCommand, K as resolveAddonsConfig, L as resolveImportPath, M as resolveImportUrl, N as resolveOptions, O as resolveSiteConfig, Q as resolveSiteConfigFromRoot, R as resolveThemeConfigFromRoot, S as resolveThemeValaxyConfig, T as resolveUserThemeConfig, U as resolveValaxyConfig, W as resolveValaxyConfigFromRoot, X as run, Y as ssgBuild, Z as ssgBuildLegacy, _ as startValaxyDev, $ as toAtFS, a0 as transformObject, a1 as version } from '../shared/valaxy.CC8moxgJ.mjs';
1
+ export { A as ALL_ROUTE, E as EXCERPT_SEPARATOR, G as GLOBAL_STATE, P as PATHNAME_PROTOCOL_RE, V as ViteValaxyPlugins, b as build, c as cli, a as createServer, d as createValaxyPlugin, e as customElements, f as defaultSiteConfig, g as defaultValaxyConfig, h as defaultViteConfig, i as defineAddon, j as defineConfig, k as defineSiteConfig, l as defineTheme, m as defineValaxyAddon, n as defineValaxyConfig, o as defineValaxyTheme, p as encryptContent, q as fixViteSsgHtml, r as generateClientRedirects, s as getGitTimestamp, t as getIndexHtml, u as getServerInfoText, v as isExternal, w as isInstalledGlobally, x as isKatexEnabled, y as isKatexPluginNeeded, z as isMathJaxEnabled, B as isPath, C as loadConfigFromFile, D as mergeValaxyConfig, F as mergeViteConfigs, H as postProcessForSSG, I as processValaxyOptions, J as registerDevCommand, K as resolveAddonsConfig, L as resolveImportPath, M as resolveImportUrl, N as resolveOptions, O as resolveSiteConfig, Q as resolveSiteConfigFromRoot, R as resolveThemeConfigFromRoot, S as resolveThemeValaxyConfig, T as resolveUserThemeConfig, U as resolveValaxyConfig, W as resolveValaxyConfigFromRoot, X as run, Y as ssgBuild, Z as ssgBuildLegacy, _ as startValaxyDev, $ as toAtFS, a0 as transformObject, a1 as version } from '../shared/valaxy.BteINvcX.mjs';
2
2
  import 'node:path';
3
3
  import 'consola';
4
4
  import 'fs-extra';
@@ -213,8 +213,23 @@ function random(bytes) {
213
213
  return pool.subarray(poolOffset - bytes, poolOffset)
214
214
  }
215
215
  function customRandom(alphabet, defaultSize, getRandom) {
216
- let mask = (2 << (31 - Math.clz32((alphabet.length - 1) | 1))) - 1;
217
- let step = Math.ceil((1.6 * mask * defaultSize) / alphabet.length);
216
+ let safeByteCutoff = 256 - (256 % alphabet.length);
217
+ if (safeByteCutoff === 256) {
218
+ let mask = alphabet.length - 1;
219
+ return (size = defaultSize) => {
220
+ if (!size) return ''
221
+ let id = '';
222
+ while (true) {
223
+ let bytes = getRandom(size);
224
+ let i = size;
225
+ while (i--) {
226
+ id += alphabet[bytes[i] & mask];
227
+ if (id.length >= size) return id
228
+ }
229
+ }
230
+ }
231
+ }
232
+ let step = Math.ceil((1.6 * 256 * defaultSize) / safeByteCutoff);
218
233
  return (size = defaultSize) => {
219
234
  if (!size) return ''
220
235
  let id = '';
@@ -222,8 +237,10 @@ function customRandom(alphabet, defaultSize, getRandom) {
222
237
  let bytes = getRandom(step);
223
238
  let i = step;
224
239
  while (i--) {
225
- id += alphabet[bytes[i] & mask] || '';
226
- if (id.length >= size) return id
240
+ if (bytes[i] < safeByteCutoff) {
241
+ id += alphabet[bytes[i] % alphabet.length];
242
+ if (id.length >= size) return id
243
+ }
227
244
  }
228
245
  }
229
246
  }
@@ -1733,7 +1750,7 @@ async function setupMarkdownPlugins(md, options, base = "/") {
1733
1750
  return md;
1734
1751
  }
1735
1752
 
1736
- const version = "0.28.5";
1753
+ const version = "0.28.6";
1737
1754
 
1738
1755
  const GLOBAL_STATE = {
1739
1756
  valaxyApp: void 0,
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "valaxy",
3
3
  "type": "module",
4
- "version": "0.28.5",
4
+ "version": "0.28.6",
5
5
  "description": "📄 Vite & Vue powered static blog generator.",
6
6
  "author": {
7
7
  "email": "me@yunyoujun.cn",
@@ -64,7 +64,7 @@
64
64
  "@antfu/utils": "^9.3.0",
65
65
  "@clack/prompts": "^1.2.0",
66
66
  "@iconify-json/ri": "^1.2.10",
67
- "@intlify/unplugin-vue-i18n": "^11.0.7",
67
+ "@intlify/unplugin-vue-i18n": "^11.1.2",
68
68
  "@shikijs/transformers": "^3.23.0",
69
69
  "@types/katex": "^0.16.8",
70
70
  "@unhead/addons": "^2.1.13",
@@ -85,7 +85,7 @@
85
85
  "ejs": "^5.0.2",
86
86
  "escape-html": "^1.0.3",
87
87
  "fast-glob": "^3.3.3",
88
- "feed": "^5.2.0",
88
+ "feed": "^5.2.1",
89
89
  "floating-vue": "^5.2.2",
90
90
  "fs-extra": "^11.3.4",
91
91
  "fuse.js": "^7.3.0",
@@ -113,7 +113,7 @@
113
113
  "mlly": "^1.8.2",
114
114
  "nprogress": "^0.2.0",
115
115
  "open": "10.1.0",
116
- "ora": "^9.3.0",
116
+ "ora": "^9.4.0",
117
117
  "p-map": "^7.0.4",
118
118
  "pascalcase": "^2.0.0",
119
119
  "pathe": "^2.0.3",
@@ -129,18 +129,18 @@
129
129
  "unplugin-vue-components": "28.0.0",
130
130
  "unplugin-vue-markdown": "^30.0.0",
131
131
  "vanilla-lazyload": "^19.1.3",
132
- "vite": "^8.0.8",
132
+ "vite": "^8.0.10",
133
133
  "vite-plugin-vue-devtools": "^8.1.1",
134
134
  "vite-plugin-vue-layouts-next": "^2.1.0",
135
135
  "vite-ssg": "^28.3.0",
136
136
  "vite-ssg-sitemap": "^0.10.0",
137
137
  "vitepress-plugin-group-icons": "^1.7.5",
138
138
  "vue": "3.5.22",
139
- "vue-i18n": "^11.3.2",
140
- "vue-router": "^5.0.4",
139
+ "vue-i18n": "^11.4.0",
140
+ "vue-router": "^5.0.6",
141
141
  "yargs": "^18.0.0",
142
- "@valaxyjs/utils": "0.28.5",
143
- "@valaxyjs/devtools": "0.28.5"
142
+ "@valaxyjs/devtools": "0.28.6",
143
+ "@valaxyjs/utils": "0.28.6"
144
144
  },
145
145
  "devDependencies": {
146
146
  "@mdit-vue/plugin-component": "^3.0.2",
@@ -163,7 +163,7 @@
163
163
  "@types/yargs": "^17.0.35",
164
164
  "gh-pages": "^6.3.0",
165
165
  "https-localhost": "^4.7.1",
166
- "nanoid": "^5.1.7",
166
+ "nanoid": "^5.1.9",
167
167
  "rollup-plugin-visualizer": "^7.0.1",
168
168
  "unbuild": "^3.6.1"
169
169
  },