diff --git a/packages/core/src/canvaskit.ts b/packages/core/src/canvaskit.ts
index 134c8fd38..af7926025 100644
--- a/packages/core/src/canvaskit.ts
+++ b/packages/core/src/canvaskit.ts
@@ -1,3 +1,4 @@
+///
import CanvasKitInit, { type CanvasKit } from 'canvaskit-wasm'
import { IS_BROWSER } from './constants'
@@ -12,8 +13,10 @@ export async function getCanvasKit(options?: CanvasKitOptions): Promise {
- if (IS_BROWSER) return `/${file}`
- return file
+ if (!IS_BROWSER) return file
+ const base = 'env' in import.meta ? import.meta.env.BASE_URL : '/'
+ const prefix = base === '/' ? '' : base.replace(/\/$/, '')
+ return `${prefix}/${file}`
}
instance = await CanvasKitInit({
diff --git a/packages/core/src/constants.ts b/packages/core/src/constants.ts
index 13ed5daf9..690904438 100644
--- a/packages/core/src/constants.ts
+++ b/packages/core/src/constants.ts
@@ -25,9 +25,8 @@ export const CANVAS_BG_COLOR_DARK = { r: 0.173, g: 0.173, b: 0.173, a: 1 } satis
*/
export function getDefaultCanvasBgColor(): Color {
if (IS_BROWSER) {
- const env = (import.meta as ImportMeta & { env?: { DEV?: boolean } }).env
const params = new URLSearchParams(window.location.search)
- if (env?.DEV === true && params.has('test')) {
+ if ('env' in import.meta && import.meta.env.DEV && params.has('test')) {
return CANVAS_BG_COLOR
}
}
@@ -331,10 +330,12 @@ export const CJK_FALLBACK_FAMILIES_MACOS = [
export const CJK_FALLBACK_FAMILIES_WINDOWS = [
'Microsoft YaHei',
+ 'Microsoft YaHei UI',
'Microsoft JhengHei',
'Yu Gothic',
'Malgun Gothic',
- 'SimHei'
+ 'SimHei',
+ 'SimSun'
]
export const CJK_FALLBACK_FAMILIES_LINUX = [
diff --git a/packages/core/src/text/fonts.ts b/packages/core/src/text/fonts.ts
index 76ad18997..c615b59d1 100644
--- a/packages/core/src/text/fonts.ts
+++ b/packages/core/src/text/fonts.ts
@@ -105,7 +105,9 @@ async function fetchGoogleFontFiles(family: string): Promise }> }
+ const data = (await response.json()) as {
+ items?: Array<{ files?: Record }>
+ }
const files = data.items?.[0]?.files
if (!files) return retryWithNormalizedFamily(family)
@@ -135,7 +137,13 @@ async function fetchGoogleFont(family: string, style: string): Promise {
+type FindLocalFontOptions = { allowVariable?: boolean }
+
+async function findLocalFont(
+ family: string,
+ style?: string,
+ options: FindLocalFontOptions = {}
+): Promise {
if (!IS_BROWSER || !window.queryLocalFonts) return null
try {
const fonts = await window.queryLocalFonts()
@@ -153,9 +161,10 @@ async function findLocalFont(family: string, style?: string): Promise {
if (cjkFallbackPromise) return cjkFallbackPromise
cjkFallbackPromise = (async () => {
- // Try local system fonts first
+ // Try local system fonts first (allow variable fonts — common for 微软雅黑 / PingFang).
for (const family of getCJKCandidates()) {
- const buffer = await findLocalFont(family)
+ const buffer = await findLocalFont(family, undefined, {
+ allowVariable: true
+ })
if (buffer && registerAndCache(family, 'Regular', buffer)) {
cjkFallbackFamilies.push(family)
}
}
- // Load all CJK Google Fonts in parallel for full coverage
if (cjkFallbackFamilies.length === 0) {
const results = await Promise.allSettled(
CJK_GOOGLE_FONTS.map(async (family) => {
diff --git a/public/_headers b/public/_headers
new file mode 100644
index 000000000..f5d558447
--- /dev/null
+++ b/public/_headers
@@ -0,0 +1,5 @@
+/canvaskit.wasm
+ Content-Type: application/wasm
+
+/canvaskit-webgpu/canvaskit.wasm
+ Content-Type: application/wasm
diff --git a/vite/canvaskit-assets.ts b/vite/canvaskit-assets.ts
index d69d2fb73..dfaffc578 100644
--- a/vite/canvaskit-assets.ts
+++ b/vite/canvaskit-assets.ts
@@ -1,27 +1,73 @@
-import { copyFileSync, existsSync, mkdirSync } from 'fs'
+import { copyFileSync, createReadStream, existsSync, mkdirSync } from 'fs'
+import { dirname, resolve } from 'node:path'
+import type { Connect, ResolvedConfig } from 'vite'
import type { Plugin } from 'vite'
-function copyIfMissing(source: string, destination: string) {
- if (!existsSync(source) || existsSync(destination)) return
- const directory = destination.slice(0, destination.lastIndexOf('/'))
- if (directory) mkdirSync(directory, { recursive: true })
- copyFileSync(source, destination)
+function syncWasmFromNodeModules(root: string, source: string, destination: string) {
+ const sourcePath = resolve(root, source)
+ const destinationPath = resolve(root, destination)
+ if (!existsSync(sourcePath)) {
+ console.warn(`[copy-canvaskit-wasm] Missing source (run \`bun install\`): ${sourcePath}`)
+ return
+ }
+ mkdirSync(dirname(destinationPath), { recursive: true })
+ copyFileSync(sourcePath, destinationPath)
+}
+
+function serveCanvasKitWasm(root: string): Connect.NextHandleFunction {
+ return (req, res, next) => {
+ const pathname = req.url?.split('?')[0] ?? ''
+ if (pathname !== '/canvaskit.wasm' && pathname !== '/canvaskit-webgpu/canvaskit.wasm') {
+ next()
+ return
+ }
+
+ const publicPath = resolve(root, pathname.slice(1))
+ const fallbackPath =
+ pathname === '/canvaskit.wasm'
+ ? resolve(root, 'node_modules/canvaskit-wasm/bin/canvaskit.wasm')
+ : resolve(root, 'packages/core/vendor/canvaskit-webgpu/canvaskit.wasm')
+
+ const file = existsSync(publicPath) ? publicPath : fallbackPath
+ if (!existsSync(file)) {
+ next()
+ return
+ }
+
+ res.setHeader('Content-Type', 'application/wasm')
+ res.setHeader('Cache-Control', 'no-cache')
+ createReadStream(file).on('error', next).pipe(res)
+ }
}
export function copyCanvasKitAssetsPlugin(): Plugin {
+ let root = process.cwd()
+
return {
name: 'copy-canvaskit-wasm',
+ enforce: 'pre',
+ configResolved(config: ResolvedConfig) {
+ root = config.root
+ },
buildStart() {
- copyIfMissing('node_modules/canvaskit-wasm/bin/canvaskit.wasm', 'public/canvaskit.wasm')
- copyIfMissing(
+ syncWasmFromNodeModules(root, 'node_modules/canvaskit-wasm/bin/canvaskit.wasm', 'public/canvaskit.wasm')
+ syncWasmFromNodeModules(
+ root,
'packages/core/vendor/canvaskit-webgpu/canvaskit.wasm',
- 'public/canvaskit-webgpu/canvaskit.wasm',
+ 'public/canvaskit-webgpu/canvaskit.wasm'
)
- copyIfMissing(
+ syncWasmFromNodeModules(
+ root,
'packages/core/vendor/canvaskit-webgpu/canvaskit.js',
- 'public/canvaskit-webgpu/canvaskit.js',
+ 'public/canvaskit-webgpu/canvaskit.js'
)
},
+ configureServer(server) {
+ server.middlewares.use(serveCanvasKitWasm(server.config.root))
+ },
+ configurePreviewServer(server) {
+ server.middlewares.use(serveCanvasKitWasm(server.config.root))
+ }
}
}