diff --git a/.env.online b/.env.online
new file mode 100644
index 0000000..4136852
--- /dev/null
+++ b/.env.online
@@ -0,0 +1 @@
+VITE_IS_ONLINE=true
\ No newline at end of file
diff --git a/package.json b/package.json
index c822c7d..90404c7 100644
--- a/package.json
+++ b/package.json
@@ -4,8 +4,9 @@
"version": "0.0.0",
"type": "module",
"scripts": {
- "dev": "vite",
- "build": "tsc && vite build",
+ "dev": "vite --mode online",
+ "build": "tsc && vite build --mode online",
+ "build-singlefile": "tsx tools/generate_assets.ts && tsc && vite build --mode singlefile && tsx tools/fix-singlefile.ts ",
"preview": "vite preview",
"format": "prettier --write src"
},
@@ -36,8 +37,10 @@
"@types/react": "^18.0.27",
"@types/react-dom": "^18.0.10",
"@types/three": "^0.149.0",
+ "@types/fs-extra": "^11.0.1",
"@vitejs/plugin-react": "^3.1.0",
"typescript": "^4.9.3",
- "vite": "^4.1.0"
+ "vite": "^4.1.0",
+ "vite-plugin-conditional-compiler": "^0.1.1"
}
}
\ No newline at end of file
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 8b5a345..2fc57c8 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -3,6 +3,7 @@ lockfileVersion: 5.4
specifiers:
'@mediapipe/pose': ^0.5.1675469404
'@types/dat.gui': ^0.7.7
+ '@types/fs-extra': ^11.0.1
'@types/react': ^18.0.27
'@types/react-dom': ^18.0.10
'@types/three': ^0.149.0
@@ -26,6 +27,7 @@ specifiers:
type-fest: ^3.6.1
typescript: ^4.9.3
vite: ^4.1.0
+ vite-plugin-conditional-compiler: ^0.1.1
vite-plugin-pwa: ^0.14.4
workbox-window: ^6.5.4
@@ -53,12 +55,14 @@ dependencies:
devDependencies:
'@types/dat.gui': 0.7.7
+ '@types/fs-extra': 11.0.1
'@types/react': 18.0.28
'@types/react-dom': 18.0.11
'@types/three': 0.149.0
'@vitejs/plugin-react': 3.1.0_vite@4.1.4
typescript: 4.9.5
vite: 4.1.4
+ vite-plugin-conditional-compiler: 0.1.1
packages:
@@ -1830,13 +1834,25 @@ packages:
resolution: {integrity: sha512-WulqXMDUTYAXCjZnk6JtIHPigp55cVtDgDrO2gHRwhyJto21+1zbVCtOYB2L1F9w4qCQ0rOGWBnBe0FNTiEJIQ==}
dev: false
+ /@types/fs-extra/11.0.1:
+ resolution: {integrity: sha512-MxObHvNl4A69ofaTRU8DFqvgzzv8s9yRtaPPm5gud9HDNvpB3GPQFvNuTWAI59B9huVGV5jXYJwbCsmBsOGYWA==}
+ dependencies:
+ '@types/jsonfile': 6.1.1
+ '@types/node': 18.14.6
+ dev: true
+
/@types/json-schema/7.0.11:
resolution: {integrity: sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ==}
dev: false
+ /@types/jsonfile/6.1.1:
+ resolution: {integrity: sha512-GSgiRCVeapDN+3pqA35IkQwasaCh/0YFH5dEF6S88iDvEn901DjOeH3/QPY+XYP1DFzDZPvIvfeEgk+7br5png==}
+ dependencies:
+ '@types/node': 18.14.6
+ dev: true
+
/@types/node/18.14.6:
resolution: {integrity: sha512-93+VvleD3mXwlLI/xASjw0FzKcwzl3OdTCzm1LaRfqgS21gfFtK3zDXM5Op9TeeMsJVOaJ2VRDpT9q4Y3d0AvA==}
- dev: false
/@types/prop-types/15.7.5:
resolution: {integrity: sha512-JCB8C6SnDoQf0cNycqd/35A7MjcnK+ZTqE7judS6o7utxUCg6imJg3QK2qzHKszlTjcj2cn+NwMB2i96ubpj7w==}
@@ -3244,6 +3260,13 @@ packages:
dependencies:
'@jridgewell/sourcemap-codec': 1.4.14
+ /magic-string/0.30.0:
+ resolution: {integrity: sha512-LA+31JYDJLs82r2ScLrlz1GjSgu66ZV518eyWT+S8VhyQn/JL0u9MeBOvQMGYiPk1DBiSN9DDMOcXvigJZaViQ==}
+ engines: {node: '>=12'}
+ dependencies:
+ '@jridgewell/sourcemap-codec': 1.4.14
+ dev: true
+
/merge-stream/2.0.0:
resolution: {integrity: sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==}
dev: false
@@ -3951,6 +3974,12 @@ packages:
punycode: 2.3.0
dev: false
+ /vite-plugin-conditional-compiler/0.1.1:
+ resolution: {integrity: sha512-1QKTJ9/kWuqwWxtIEhtxENUQlfRfgjDyeAtic91ArQFjzI71necB7Sp9F5IGR/L1zIQXkkXBvidLLJkTJcp/OQ==}
+ dependencies:
+ magic-string: 0.30.0
+ dev: true
+
/vite-plugin-pwa/0.14.4_pchkumgzq6po4w4enhsumrku2u:
resolution: {integrity: sha512-M7Ct0so8OlouMkTWgXnl8W1xU95glITSKIe7qswZf1tniAstO2idElGCnsrTJ5NPNSx1XqfTCOUj8j94S6FD7Q==}
peerDependencies:
diff --git a/src/assets.ts b/src/assets.ts
new file mode 100644
index 0000000..93ed23b
--- /dev/null
+++ b/src/assets.ts
@@ -0,0 +1,9 @@
+import handFBXFileUrl from '../models/hand.fbx?url'
+import footFBXFileUrl from '../models/foot.fbx?url'
+import posesLibraryUrl from './poses/data.bin?url'
+
+export default {
+ 'models/hand.fbx': handFBXFileUrl,
+ 'models/foot.fbx': footFBXFileUrl,
+ 'src/poses/data.bin': posesLibraryUrl,
+}
diff --git a/src/env.d.ts b/src/env.d.ts
new file mode 100644
index 0000000..c78eb96
--- /dev/null
+++ b/src/env.d.ts
@@ -0,0 +1,3 @@
+interface ImportMetaEnv {
+ readonly VITE_IS_ONLINE?: boolean
+}
diff --git a/src/environments/online/assets.ts b/src/environments/online/assets.ts
deleted file mode 100644
index e971c54..0000000
--- a/src/environments/online/assets.ts
+++ /dev/null
@@ -1,9 +0,0 @@
-import handFBXFileUrl from '../../../models/hand.fbx?url'
-import footFBXFileUrl from '../../../models/foot.fbx?url'
-import posesLibraryUrl from '../../poses/data.bin?url'
-
-export default {
- 'models/hand.fbx': handFBXFileUrl,
- 'models/foot.fbx': footFBXFileUrl,
- 'src/poses/data.bin': posesLibraryUrl,
-}
diff --git a/src/environments/online/helper.ts b/src/environments/online/helper.ts
index 1da7145..53ecf58 100644
--- a/src/environments/online/helper.ts
+++ b/src/environments/online/helper.ts
@@ -10,7 +10,7 @@ import Swal from 'sweetalert2'
import { BodyEditor } from '../../editor'
import i18n from '../../i18n'
import { Oops } from '../../components'
-import assets from './assets'
+import assets from '../../assets'
import * as dat from 'dat.gui'
export class Helper {
diff --git a/src/environments/online/main.tsx b/src/environments/online/main.tsx
index 9104e06..a468e30 100644
--- a/src/environments/online/main.tsx
+++ b/src/environments/online/main.tsx
@@ -9,7 +9,7 @@ import { canvasElement, createDatGui, statsElement } from './gui'
import Swal from 'sweetalert2'
import { CreateTemplateBody, LoadFoot, LoadHand } from '../../body'
-import assets from './assets'
+import assets from '../../assets'
import { Helper } from './helper'
async function LoadBodyData() {
diff --git a/src/environments/online/update.ts b/src/environments/online/update.ts
index 92e49eb..1bfd6b9 100644
--- a/src/environments/online/update.ts
+++ b/src/environments/online/update.ts
@@ -1,6 +1,9 @@
///
+// #v-ifdef VITE_IS_ONLINE
import { registerSW } from 'virtual:pwa-register'
+// #v-endif
+
import Swal from 'sweetalert2'
import i18n from '../../i18n'
@@ -13,6 +16,7 @@ async function PWAPopup(update: (reloadPage?: boolean) => Promise) {
}
}
export function PWACheck() {
+ if (import.meta.env.MODE !== 'online') return
const updateSW = registerSW({
onNeedRefresh() {
console.log('有更新,需要刷新!!')
diff --git a/src/utils/detect.ts b/src/utils/detect.ts
index 0f167d6..c13f37b 100644
--- a/src/utils/detect.ts
+++ b/src/utils/detect.ts
@@ -3,6 +3,7 @@ import { Class } from 'type-fest'
// https://github.com/google/mediapipe/blob/master/docs/solutions/pose.md#resources
import type { Results, Pose, PoseConfig } from '@mediapipe/pose'
import * as MediapipePose from '@mediapipe/pose'
+import assets from '../assets'
// @mediapipe/pose is not an es module ??
// Extract Pose from the window to solve the problem
@@ -15,6 +16,10 @@ console.log('MyPose', MyPose)
const pose = new MyPose({
locateFile: (file) => {
+ if (file in assets) {
+ console.log('local', file)
+ return (assets as any)[file]
+ }
const url = `https://cdn.jsdelivr.net/npm/@mediapipe/pose/${file}`
console.log('load pose model', url)
diff --git a/tools/fix-singlefile.ts b/tools/fix-singlefile.ts
new file mode 100644
index 0000000..f0d37cb
--- /dev/null
+++ b/tools/fix-singlefile.ts
@@ -0,0 +1,8 @@
+import fs from 'fs-extra'
+
+const content = fs.readFileSync('dist/index.html').toString()
+
+fs.writeFile(
+ 'dist/index.html',
+ content.replace(`type="module" crossorigin`, 'defer')
+)
diff --git a/tools/generate_assets.ts b/tools/generate_assets.ts
new file mode 100644
index 0000000..bc84fd4
--- /dev/null
+++ b/tools/generate_assets.ts
@@ -0,0 +1,51 @@
+import fs from 'fs-extra'
+import path from 'path'
+const list: Record<
+ string,
+ {
+ prefix?: string
+ mimetype: string
+ }
+> = {
+ 'models/foot.fbx': { mimetype: 'application/octet-stream' },
+ 'models/hand.fbx': { mimetype: 'application/octet-stream' },
+ 'src/poses/data.bin': { mimetype: 'application/octet-stream' },
+ 'pose_landmark_full.tflite': {
+ prefix: 'node_modules/@mediapipe/pose/',
+ mimetype: 'application/octet-stream',
+ },
+ 'pose_web.binarypb': {
+ prefix: 'node_modules/@mediapipe/pose/',
+ mimetype: 'application/octet-stream',
+ },
+ 'pose_solution_packed_assets.data': {
+ prefix: 'node_modules/@mediapipe/pose/',
+ mimetype: 'application/octet-stream',
+ },
+ 'pose_solution_simd_wasm_bin.wasm': {
+ prefix: 'node_modules/@mediapipe/pose/',
+ mimetype: 'application/wasm',
+ },
+ 'pose_solution_packed_assets_loader.js': {
+ prefix: 'node_modules/@mediapipe/pose/',
+ mimetype: 'application/javascript',
+ },
+ 'pose_solution_simd_wasm_bin.js': {
+ prefix: 'node_modules/@mediapipe/pose/',
+ mimetype: 'application/javascript',
+ },
+}
+
+const output = Object.fromEntries(
+ Object.entries(list).map(([file, { prefix, mimetype }]) => [
+ file,
+ `data:${mimetype};base64,${fs
+ .readFileSync(path.join(prefix ?? '.', file))
+ .toString('base64')}`,
+ ])
+)
+
+fs.writeFile(
+ 'src/assets.ts',
+ 'export default ' + JSON.stringify(output, null, 4)
+)
\ No newline at end of file
diff --git a/vite.config.ts b/vite.config.ts
index 82bff1f..6a98997 100644
--- a/vite.config.ts
+++ b/vite.config.ts
@@ -1,37 +1,44 @@
-import { defineConfig, type UserConfigExport } from 'vite'
+import { defineConfig, UserConfigFn } from 'vite'
import react from '@vitejs/plugin-react'
import { VitePWA } from 'vite-plugin-pwa'
import { visualizer } from 'rollup-plugin-visualizer'
import { resolve } from 'path'
+import ConditionalCompile from 'vite-plugin-conditional-compiler'
// https://vitejs.dev/config/
-const config: UserConfigExport = {
- base: '/open-pose-editor/',
- define: {
- global: {},
- __APP_VERSION__: JSON.stringify('v0.0.2'),
- __APP_BUILD_TIME__: Date.now(),
- },
- build: {},
- plugins: [
- react(),
- VitePWA({
- workbox: {
- globPatterns: [
- '**/*.{js,css,html,ico,png,svg,mp3,obj,fbx,bin}',
- ],
- },
- manifest: {
- name: 'open pose editor',
- short_name: 'open pose editor',
- description: 'open pose editor (Yu Zhu)',
- theme_color: '#ffffff',
- background_color: '#ffffff',
- display: 'standalone',
- },
- }),
- visualizer(),
- ],
+const config: UserConfigFn = ({ command, mode, ssrBuild }) => {
+ const pwa = VitePWA({
+ workbox: {
+ globPatterns: ['**/*.{js,css,html,ico,png,svg,mp3,obj,fbx,bin}'],
+ },
+ manifest: {
+ name: 'open pose editor',
+ short_name: 'open pose editor',
+ description: 'open pose editor (Yu Zhu)',
+ theme_color: '#ffffff',
+ background_color: '#ffffff',
+ display: 'standalone',
+ },
+ })
+
+ return {
+ base: mode === 'singlefile' ? './' : '/open-pose-editor/',
+ define: {
+ global: {},
+ __APP_VERSION__: JSON.stringify('v0.0.2'),
+ __APP_BUILD_TIME__: Date.now(),
+ },
+ build: {
+ assetsDir: mode === 'singlefile' ? '.' : 'assets',
+ emptyOutDir: true,
+ },
+ plugins: [
+ react(),
+ mode === 'online' ? pwa : null,
+ visualizer(),
+ ConditionalCompile(),
+ ],
+ }
}
export default defineConfig(config)