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)